Pic Lab, PIC16, Experiment #14, USART (UART) module

Let’s look at USART module in pic microcontroller (uart module).

I’m going to move in two ways:

1. Using existing functions from a hi-tech compiler.

2. Writing my own functions.

USART = UART = SCI – the universal data transmitting/receiving protocol, which can work in synchronous and asynchronous modes. We pay attention to the second mode at the moment, this one is used for communications mostly.

Data transmitting example:

Some remarks about an asynchronous mode:

  • There is a high logic level at the line at standby.
  • New data transmission has a zero bit at the start.
  • LSB is transmitted first.
  • All transmissions have a stop bit with high logic level.

To start, let me deal with the baud rate generator (BRG).

A SPBRG register and a BRGH bit determine the period of BRG. Here is the equation for rate:

For example, I’m going to calculate SPBRG value for the next conditions:

Fosc = 16 MHz

Baud rate = 9600

BRGH = 0

SYNC = 0

Calculation:

9600 = 16 000 000/(64(Х+1)) => X = 25.042 = 25

Ok, seems like we’ve done with BRG, now what about transmitter and receiver?

TXSTA register – transmitter

CSRCClock source
Synchronous mode
1master, clock from BRG
0slave, clock from CK
Asynchronous mode
Not used
TX99-bit transmission enable?
1enabled
0disabled
TXENTransmission on/off
1on
0off
SYNC1synchronous mode
0asynchronous mode
BRGHHigh-speed mode
Synchronous mode
Irrelevant
Asynchronous mode
1high speed
0low speed
TMRTTSR clear flag
1TSR is empty
0TSR is full
TX9D9-th data bit

Transmitter functional diagram:

The main part of the transmitter is the shift register TSR, which gets the data from the transmitter’s buffer TXREG. When the stop bit is transmitted, TSR is loaded by new data from TXREG (if there is something), after that TXIF interrupt flag is set. There is one feature with interrupts – the TXIF flag clears only when data has been loaded to TXREG, so you can’t clear it by yourself in code.

Recommended sequencing for transmitting at asynchronous mode:

  1. Setup rate with SPBRG and BRGH bit.
  2. Choose asynchronous mode by clearing the SYNC bit and setup SPEN to 1.
  3. Enable interrupts if you need them.
  4. If the transmission has 9 bits, set up TX9 to 1.
  5. Switch on transmission mode (TXEN = 1).
  6. If the transmission has 9 bits, move each 9 bit to TX9D.
  7. Write your data to TXREG.

Here is a timing diagram for one-byte transmission:


RCSTA register – receiver:

SPENEnabling bit
1USART is enabled
0USART is disabled
RX99-bit receiving
1allowed
0not allowed
SRENOne-byte receiving permission
Synchronous mode
1allowed
0not allowed
Asynchronous mode
Irrelevant
CRENReceiving permission
Synchronous mode
1allowed
0not allowed
Asynchronous mode
1allowed
0not allowed
ADDENAddress detection permission
Asynchronous 9-bit receiving
1allowed
0not allowed
FERRFrame error
1Error has occurred
0Error hasn’t occurred
OERRBuffer overflow error
1Error has occurred
0Error hasn’t occurred
RX9D9-th bit of data

Receiver functional diagram:

For the receiver the main part is shift register RSR, data from input RB1 loads direct into it. After receiving a stop bit, data will be loaded to RCREG, which has two buffers. So, the receiver gets three bytes – two bytes in FIFO buffers and one in the shift register. There is OERR in “1” when the third byte has been received. After that, you need to clear OERR by switching OFF/ON CREN.

Recommended sequencing for receiving at asynchronous mode:

  1. Setup rate with SPBRH and BRGH bit.
  2. Choose asynchronous mode by clearing the SYNC bit and setup SPEN to 1.
  3. Enable interrupts if you need them.
  4. If the transmission has 9 bits, set RX9 to 1.
  5. Allow receiving mode by setting CREN to 1.
  6. Wait for RCIF or interrupt event.
  7. Read 9th data bit.
  8. Read the whole RCREG.
  9. If an error has occurred set CREN to 0.

Receiver work timing diagram:

Let’s move to practice. I have two ways of testing:

  1. Proteus.
  2. Checking with my dev board, connecting it by max232 to rs232 level converter.

For a start, I’m going to move by the first way, and in the end, I just write a big test program and check it at the terminal of a PC.

Transmitting. Go to the compiler work directory and find the “examples” folder. There are 3 files for this part:

  • usart.h
#ifndef _SERIAL_H_
#define _SERIAL_H_
#define BAUD 9600           //baud rate
#define FOSC 4000000L       //xtall frequency
#define NINE 0              //9-bit?
#define DIVIDER ((int)(FOSC/(16UL * BAUD) -1)) //SPBRG register
#define HIGH_SPEED 1        //high speed communications
#if NINE == 1
#define NINE_BITS 0x40
#else
#define NINE_BITS 0
#endif
#if HIGH_SPEED == 1
#define SPEED 0x4
#else
#define SPEED 0
#endif
 
#define RX_PIN TRISB1        //RX pin
#define TX_PIN TRISB2        //TX pin
 
/* Initialization */
 
#define init_comms()
 
RX_PIN = 1;
TX_PIN = 1;
SPBRG = DIVIDER;
RCSTA = (NINE_BITS|0x90);
TXSTA = (SPEED|NINE_BITS|0x20)
 
void putch(unsigned char);      //One symbol transmitting
unsigned char getch(void);      //One symbol receiving
unsigned char getche(void);     //I don't know for a what this function :)
 
#endif
  • usart.c
#include <htc.h>
#include <stdio.h>
#include "usart.h"
 
void
putch(unsigned char byte)
 
{
/* One byte transmission */
while(!TXIF)    /* TXIF = 1 when register is empty */
continue;
TXREG = byte;
}
 
unsigned char
getch() {
/* one byte receiving */

while(!RCIF)
continue;
return RCREG;
 
}
 
unsigned char      //again I don't know :)
getche(void)
{
 
unsigned char c;
putch(c = getch());
return c;
 
}
  • main.c
#include <stdio.h>
#include <htc.h>
#define _XTAL_FREQ 4000000
#include "usart.h"
 
__CONFIG(LVPDIS & WDTDIS & MCLREN & UNPROTECT & HS);
 
void main(void){
unsigned char input;
INTCON=0;       //interrupts disabled
init_comms();   // usart initialization
putch('K');     // transmit K symbol
 
while(1){
 
}
 
}

The result:

Seems like it works, let’s transmit this one by existing functions.

#include <htc.h>
#define _XTAL_FREQ 4000000
 
__CONFIG(LVPDIS & WDTDIS & MCLREN & UNPROTECT & HS);
 
void main(void){
TRISB = 0xFF;   //B input
BRGH = 0;           //low speed mode
SPBRG = 0x05;      //calculated value
SYNC = 0;          //asynchronous mode
SPEN = 1;
TXEN = 1;          //transmission is allowed
TXREG =0b01001011;           //push К symbol
 
while(1){
 
}
 
}

Here is the output (don’t forget that 7 bit is second) :

And for the receiver I’ve used existing functions too:

#include <stdio.h>
#include <htc.h>
#define _XTAL_FREQ 4000000
#include "usart.h"
 
__CONFIG(LVPDIS & WDTDIS & MCLREN & UNPROTECT & HS);
 
void main(void){
unsigned char input;
 
INTCON=0;   // purpose of disabling the interrupts.
init_comms();   // set up the USART - settings defined in usart.h
// Output a message to prompt the user for a keypress
printf("\rPress a key and I will echo it back:\n");
while(1){
input = getch();    // read a response from the user
printf("\rI detected [%c]",input);  // echo it back
}
}

This code waits if any button is pressed, the value of the button will be returned immediately:

I will not write about my own function for receiving, because the settings are the same as for the transmitter and the function will look like the existing one in an example file:

unsigned char
 
getch() {
 
/* retrieve one byte */
 
while(!RCIF)    /* set when register is not empty */
 
continue;
 
return RCREG;
 
}

The sources

Leave a Reply

Your email address will not be published.