Universal Asynchronous Receiver/Transmitter or UART is a simple and common serial communication protocol. It uses separate transmission and reception lines making it a full duplex communication.
A simple full-duplex UART communication requires the following registers as explained in our previous chapter UART Interfacing with PIC Microcontroller. We will focus only on the bits relevant to our cause.
TXSTA
This register controls the baud rate generation and some other UART features such as automatic baud rate generation, inversion of receiving data etc.
TXREG is the transmit buffer register in which data to be transmitted is stored.
RCREG is the receive buffer register in which the received data is stored.
These registers are used to specify the baud rate. When BRG16 is cleared the baud rate is calculated with the formula
Baud rate = FOSC/[64 x (SPBRG+1)]
The data direction of the Transmitter and Receiver pins must also be set with the TRISC register.
Here we intend to use the UART in both transmit and receive mode at a baud rate of 9600 bits per second with 20Mhz clock frequency. We need to set the registers accordingly.
Firmware Example:
TRISC |= 0x80; //Rx-Input Tx-Output TXSTA = 0x20; //TXEN enabled RCSTA = 0x90; //SPEN and CREN enabled SPBRG = 31; //9600 baud rate @20Mhz clock freq
We also need functions to send and receive data
Firmware Example:
char rx_char(void) { while(!RCIF); //Wait till RCREG is full return RCREG; //Return value in received data } void tx_char(char a) { TXREG=a; //Load TXREG register with data to be sent while(!TRMT); //Wait till transmission is complete }
An interrupt is a signal to the processor of the occurrence of an event by hardware or software. The processor may choose to accept or ignore this signal. The response of the processor to an interrupt is configured with the help of registers such as INTCON, PIR, PIE and IPR registers as specified in our previous chapter on Interrupt Handling in PIC18F4550. Upon receiving the interrupt the processor branches to a vector called Interrupt vector or Interrupt Service Routine(ISR), which is executed before resuming previous code sequence.
The UART Interrupt is used to detect the completion of data transmission or reception without continuously polling the flag bit, thus saving processor time. The interrupt is detected with the help of the TXIF and RCIF bits. To use these interrupts, we must first enable GIE and PEIE bits in the INTCON register.
INTCON
GIE=1; PEIE=1;
Reception interrupt is requested when the receiver buffer RCREG is full. The flag bit used is RCIF and it is automatically cleared by hardware when the buffer is read. This helps us in receiving UART data without continuously checking for data in the RCREG.
To enable Reception interrupt we need to enable the RCIE bit.
RCIE=1;
In the ISR, we need to poll for the RCIF flag and receive the data in RCREG if the flag is set. Here we are sending the data received back through the transmission channel.
__interrupt() void ISR(void) { char a; if(RCIF==1) //Polling for reception interrupt { a=rx_char(); //Receiving data tx_char(a); //Transmitting back received data } }
In UART transmission, the data uploaded to the TXREG register is first transferred to a buffer register called TSR. In a specified baud rate, the data is shifted out to the transmission line. A transmission interrupt is obtained when the data in the register TXREG is fully transferred to the TSR register and becomes empty. The application of transmission interrupt is mainly for transmitting a string of data without manually polling for completion of each character transmitted.
This can be done by first enabling the transmission interrupt and disabling it after transmission of complete data. Since interrupt does not accept any input parameters, a global variable is used instead. To initiate the data transfer after loading the global variable, the interrupt flag is manually set once.
Firmware Example:
char *str; //Global variable used for interrupt void tx_str(char *a) { TXIE = 1; //Enable transmission interrupt str = a; //pointing str pointer to data to send TXIF = 1; //Manually enabling interrupt while(*(str-1)); //Wait for transmission of complete string TXIE = 0; //Disable transmission interrupt tx_char(0x0D); //New Line }
Note: We wait till *(str-1) is null (and not *str) since disabling the interrupt immediately at last character will prevent transmission of the last character.
In the ISR, we need to poll for the TXIF flag, send data addressed by a pointer to the TXREG register and increment the pointer to the next data.
Firmware Example:
__interrupt() void ISR(void) { char a; if(TXIF==1) //Polling for transmission interrupt { TXREG=*str; //Load data to TXREG register str++; //increment pointer to next data } }
The tx_str() function used to send a string directly in the format
tx_str((char *)"Sending string through UART");
Note: String is an array of characters, here we are explicitly converting it to a character pointer type.