I’m writing code for a Microchip PIC16F1829 and thought I’d share the serial communication routine. I’ve completed the schematic capture and PCB layout for the servo motor controller design. But it’s a good idea to get some code written before moving forward with the PCB prototype production. I’ve written a good chunk of the code for this design. When the design is complete we’ll open source the files and place them on our web site (including the firmware for the microcontroller).
I’m writing the code using Microchip’s free C compiler xc8 and using their IDE MPLAB X. To test the code with hardware I’ve connected a PICkit3 to a breadboarded DIP package of the PIC16F1829. A serial port is connected to the PIC16F1829 through our USB to serial converter breakout board (BM010, will be listed on our web site Dec-2012).
Early in the code, after the power on reset, I configure the serial related registers and set up a timer that will timeout after 20+ bit periods (two bytes ~2ms). Not shown in this code is the fact that I’ve also configured the TX line as an output and the RX line as an input. These serial port hardware connections default to pins RB7 and RB5 respectively, but there are alternate pins you can select if you want to. Also not shown are two buffer arrays I’ve defined (intRXBuffer and intTXBuffer).
The design makes heavy use of interrupts. Serial data reception interrupts are enabled at power up. Once a serial data byte is received the TMR0 interrupt is enabled. Every time a byte is received I reset the TMR0 timer. If no byte is received within 20 bit periods the serial receive timer is turned off, the TMR0 is turned off, and a flag is set indicating data is ready for processing. This allows serial data reception to occur in the background so you don’t have to worry about coordinating it with other program timing. The serial data transmission routine is also interrupt driven, but I’ll get to that a little later.
Here’s the function where the serial reception interrupt is enabled.
Once the interrupt is enabled the ISR (interrupt service routine) will be entered when any byte is received. The byte is shoved into a buffer, and the timer I mentioned earlier (TMR0) is cleared and its interrupt is enabled. If another byte is received before the timer elapses the ISR will just keep sticking bytes into the buffer and resetting the timer. As soon as the timer elapses the receive and timer interrupts are disabled, and a flag is set telling which is checked in the main program loop. Note that if the number of bytes received exceeds the buffer size the pointer is reset to 0. This will prevent an overrun of data that corrupts program RAM.
In the main program I check for the data ready flag roughly every 4ms. This is my program loop period. This causes the response time to a serial command to also be 4ms. Since I’m still writing this code I have not completed the serial communication protocol. Currently my serial communication handling routine just takes the data in the receive buffer and copies it to the transmit buffer. It then enables the transmit interrupt causing the data to be transmitted on the serial port automatically. I also re-enable the serial receive interrupt here and reset pointers to the beginning of my serial buffer.
Later I’ll actually check the serial data in the buffer and implement write, read, and other commands. Currently, this interrupt driven serial communication simply mirrors data back to me. If I type “I love circuits” into a terminal program I will receive that back.
There’s another issue you need to handle when using the PIC’s serial port features. You can have framing and overrun errors. An overrun error occurs when the PIC’s FIFO buffer receives more than 2 full bytes before the RCREG is read. This error is dreadful because if you don’t clear the error the serial port will not receive any new data. A power on reset is necessary if you don’t handle this error in your code. Since I’m using interrupts this shouldn’t be an issue, but let’s be safe. The function below is called every 4ms and checks for serial port errors. If it sees one it resets everything to a default state.
That’s the gist of it. A few functions to set things up, and all of your serial data reception and transmission occurs in the background.
It’s not finished, or completely tested. I’m sure there’s some typos and a logic error here and there. But it probably 80% there, and good enough to share.