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
CSRC | Clock source | |
Synchronous mode | ||
1 | master, clock from BRG | |
0 | slave, clock from CK | |
Asynchronous mode | ||
Not used | ||
TX9 | 9-bit transmission enable? | |
1 | enabled | |
0 | disabled | |
TXEN | Transmission on/off | |
1 | on | |
0 | off | |
SYNC | 1 | synchronous mode |
0 | asynchronous mode | |
BRGH | High-speed mode | |
Synchronous mode | ||
Irrelevant | ||
Asynchronous mode | ||
1 | high speed | |
0 | low speed | |
TMRT | TSR clear flag | |
1 | TSR is empty | |
0 | TSR is full | |
TX9D | 9-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:
- Setup rate with SPBRG and BRGH bit.
- Choose asynchronous mode by clearing the SYNC bit and setup SPEN to 1.
- Enable interrupts if you need them.
- If the transmission has 9 bits, set up TX9 to 1.
- Switch on transmission mode (TXEN = 1).
- If the transmission has 9 bits, move each 9 bit to TX9D.
- Write your data to TXREG.
Here is a timing diagram for one-byte transmission:
RCSTA register – receiver:
SPEN | Enabling bit | |
1 | USART is enabled | |
0 | USART is disabled | |
RX9 | 9-bit receiving | |
1 | allowed | |
0 | not allowed | |
SREN | One-byte receiving permission | |
Synchronous mode | ||
1 | allowed | |
0 | not allowed | |
Asynchronous mode | ||
Irrelevant | ||
CREN | Receiving permission | |
Synchronous mode | ||
1 | allowed | |
0 | not allowed | |
Asynchronous mode | ||
1 | allowed | |
0 | not allowed | |
ADDEN | Address detection permission | |
Asynchronous 9-bit receiving | ||
1 | allowed | |
0 | not allowed | |
FERR | Frame error | |
1 | Error has occurred | |
0 | Error hasn’t occurred | |
OERR | Buffer overflow error | |
1 | Error has occurred | |
0 | Error hasn’t occurred | |
RX9D | 9-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:
- Setup rate with SPBRH and BRGH bit.
- Choose asynchronous mode by clearing the SYNC bit and setup SPEN to 1.
- Enable interrupts if you need them.
- If the transmission has 9 bits, set RX9 to 1.
- Allow receiving mode by setting CREN to 1.
- Wait for RCIF or interrupt event.
- Read 9th data bit.
- Read the whole RCREG.
- If an error has occurred set CREN to 0.
Receiver work timing diagram:
Let’s move to practice. I have two ways of testing:
- Proteus.
- 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; }