Login - Register -

Communicating with the Outside - Serial Ports

This workshop is predominantly about how to communicate between the PIC and a Desktop PC via the UART (Serial Port).

The page refers heavily to the Data Sheet for the PIC16F1455.

PIC16F1455 Chapter 23.0 Enhanced Universal Synchronous Asynchronous Transmitter

We will be using Asynchronous mode, same as an RS232 serial port.

Setup

Use the same circuit as GettingStarted.

Baud Rates and Config

The EUSART uses the TX and RX signals, these are assigned to pins 6 & 5 (RC4 & RC5)

First we should configure a baud rate, 19200 (19.2k) is a good compromise between reliability and speed, see TABLE 23-5 on page 272 for some example settings combinations to write to SYNC, BRGH, BRG16, SPBRG.

To enable async transmission and reception we must set TXEN=1, CREN=1, SYNC=0, SPEN=1, these are in the TXSTA and RCSTA registers, see 23.1.1 page 259, 23.1.2.1 page 262 and 23.2 page 267 and on.

You also need to make sure that the RX pin is set to an input (using TRISC), the TX pin was automatically set to be an output when it was enabled via TXEN.

We will use 8 bits per character, 1 stop bit, no parity, which are the default settings.

Read and writing

There are two main methods of handling the serial port, either via interrupts (covered later), or by direct access, like so...

To transmit, first check that the transmit is not busy TXIF in PIR1 is set, then write the character to TXREG, after two instructions the TXIF flag will be cleared to show it is busy transmitting, and will be set again when it is complete.

To receive, the RCIF bit of PIR1 will indicate that a character is ready to be read from RCREG, reading that value will clear the flag for the next one.

Thats it for the simple case, there is no buffering so your code has to keep looping around waiting.

Using Interrupts

The more advanced way to handle the serial port is to use the hardware interrupts to call your code when there is work to be done.

First we must enable interrupts for these events:

  • RCIE in PIE1 to enable receive interrupts
  • TXIE in PIE1 to enable transmit interrupts 1
  • PEIE in INTCON to enable peripheral generated interrupts
  • GIE in INTCON to globally enable interrupts

Note 1 : Only enable TXIE when you have chars waiting to be sent.

Now you can write some routines to buffer the input and output, e.g.:

  1. one to read a char and add to the end of an input buffer
  2. one to write the next char in an output buffer
  3. one to add chars to the output buffer
  4. one to read chars from the input buffer

You will want routines 1 & 2 to be called when the interrupt goes off to tell you the action is ready, this if done via an interrupt handler routine, when any interrupt is triggered the cpu will jump to the program at a specific memory location for you to check what happened, the compiler will automatically locate your function at this address when you mark it with the interrupt keyword, there can only be one of these per program. e.g.


void interrupt isr(void)
{
    if (PIE1bits.TXIE && PIR1bits.TXIF)
        msg_sendnext();
    if (PIE1bits.RCIE && PIR1bits.RCIF)
        msg_recvnext();
}

When the transmit hardware is ready to send another character, and you have enabled the interrupt to say you have more, then the msg_sendnext() routine will be called, which can take then next char off your transmit buffer/array and send it. When the last char is sent you should disable TXIE again.

When the receive hardware has a character ready for you to read, the msg_recvnext() routine will get called, and it can copy the char onto the end of your incoming/receive buffer.

Both receive and transmit may also wish to provide a method to signal to your main body of code that there are/aren't chars in the buffers to be dealt with.

With these methods the main part of your code is free to perform other tasks, like monitoring buttons, without having to constantly deal with the serial comms part, merely checking occasionally if there is something it needs to do.

An example library that implements this is  serial.c  serial.h

Using the library

Now ssuming that we have set a suitable CPU clock, attached the serial routines, and configured the LED ports as per the GettingStarted tutorial.

We could impliment some simple interaction, if you send the 'r' or 'g' characters to the device, using a suitable terminal emulator / comms program on your PC.


while (1)
{
    char cmd;

    /* make sure we have finished sending everything */
    if (!msg_empty()) continue;

    /* if there is nothing waiting go around again */
    if (!msg_recvready()) continue;

    /* there is a character, read it */
    cmd = msg_recv();

    /* now decide what to do */
    if (cmd == 'r') {
        if (PORTCbits.RC1 == 0) {
            PORTCbits.RC1 = 1;
            msg_write("Red ON\r\n");
        } else {
            PORTCbits.RC1 = 0;
            msg_write("Red OFF\r\n");
        }
    }
}

Now expand this to toggle the green LED in response to the 'g' key.

- Last change October 29, 2013, at 10:16 PM
- Registered in England and Wales 08777436