How to receive data via UART using Interrupt
This is the 4th tutorial in the AVR series using the xplained mini development board, and today we will continue with the UART peripheral. In the previous tutorial we saw how to configure the UART and how to send the data via the peripheral.
We will use the same configuration we did for the previous tutorial, with a little modification of course.
I am going to use a serial monitor on the computer, which will send the data to the MCU.
The xplained mini development board supports the virtual com port, which means that we don’t need an external hardware to communicate to the computer via the UART. We can simply use the USB used for programming/debugging to send and receive the data from the computer.
Below is the image showing the virtual com port gets detected on the computer when the USB is connected to the board.
As you can see above, the mEDBG Virtual COM Port is connected to the COM11.
If you still want to use any USB to UART converter, the connection for the same is shown below.
The TX pin from the MCU connects to the RX pin of the module, and the RX pin from the MCU connects to the TX pin of the module. The UART connection is always made in the cross. Also note that the pin PB2 is the TX pin, which we will set as the output and the pin PB3 is the RX pin which we will set as the input.
Configure the UART
We will start with the USART Control A Register.
The CTRLA Register is used to enable/disable different interrupts. We will only use the interrupt to receive the data, so set the bit 7 of this register.
USART0.CTRLA = (1<<7); // Enable RXCIE interrupt
We will start with the USART Control B Register.
The Bits 6 and 7 of this register are used to enable/disable the Transmitter and Receiver for the UART. We will enable these bits so to enable the Transmitter and Receiver. Also the bits [2:1] sets the Receiver in the different available modes. We will leave these bits to 0 to set the receiver in the normal mode.
The reset of the bits of this register are kept to their default states, i.e. diabled.
USART0.CTRLB = (1<<7)|(1<<6); // RX & TX enable, Normal mode, SOF disabled
The next register is USART Control C Register.
Here we are setting the commonly used UART configuration, which consists of 8 Data bit with 1 stop bit and no Parity. The UART mode is set to asynchronous mode.
The final value for the Register is 0x03.
USART0.CTRLC = 0x03; // Asynch mode, no parity, 1 stop bit, 8 data bits
The next register is the Baud Register. This is a 16 bit register and the value in this register decides the baud rate of the UART. Below the image shows the formula to calculate the Baud Register value for the desired baud rate.
Our clock is configured to use the 20MHz internal oscillator with the prescaler of 4. This reduces the clock to 5MHz. The same clock is being used by the CPU and the peripheral (CLK_PER). This is shown in the image below.
For the peripheral clock of 5MHz and the desired baud rate of 115200, the Baud register value calculation is shown below.
The parameter S is the number of samples per bit, and for the Asynchronous normal mode, it’s value is 16.
We can not input the decimal value in a 16 bit register, so we will input the value 174 into it.
USART0.BAUD = 174;
We will use the STATUS Register to monitor if the data transfer has been finished or not.
The bit 6 of this register (TXCIF) sets when the data in the TXDATA register has been shifted out. During the configuration, we will clear this bit by writing a 1 into it.
USART0.STATUS |= 1<<6; // clear TXCIF flag
Now we will configure the pins for the UART. The pin PB2 is the UART TX pin and it must be set as the output, while the pin PB3 is the UART RX pin and it must be set as the input.
PORTB.DIR = 0x04; //0b00000100 -> PB2->OUT, PB3->IN
This completes the UART configuration. Below is the final code that contains the registers configuration explained above.
void uartInit (void)
{
USART0.CTRLA = (1<<7); // Enable RXCIE interrupt
USART0.CTRLB = (1<<7)|(1<<6); // RX & TX enable, Normal mode, SOF disabled
USART0.CTRLC = 0x03; // Asynch mode, no parity, 1 stop bit, 8 data bits
USART0.BAUD = 174;
USART0.STATUS |= 1<<6; // clear TXCIF flag
PORTB.DIR = 0x04; // PB2->OUT, PB3->IN
}
Receive Data
As we are receiving the data using interrupt, we don’t need to write a separate function for it. Let’s see the main function.
char RxData[10];
int indx = 0;
int main(void)
{
clkInit();
uartInit();
sei();
while(1)
{
}
}
Here we will first define an array to store the received data, and an index variable to track the received data.
Inside the main function, we will first initialize the clock, and then initialize the UART. Then enable the global interrupt by calling the function sei().
Once the data is received in the receive buffer, the RXC (Receive Complete) interrupt will be triggered. We will write an Interrupt Service Routine (ISR) to handle this interrupt.
ISR (USART0_RXC_vect)
{
RxData[indx++] = USART0.RXDATAL;
if (indx>=10) indx =0;
}
The interrupt is set for each byte of data received by the receive buffer. Inside the ISR function we will copy the data from the RXDATAL register and store in the RxData array.
Then increment the indx variable so that the new data byte can be stored at a new position. Our RxData buffer can store a maximum of 10 bytes, so if the indx variable is greater than or equal to 10, we will reset the variable to 0. This will start storing the data from the beginning again.
Result
Below the images shows the data sent by the serial monitor on the computer, and the data received by the xplained mini.
You can see the 10 bytes of data sent by the serial monitor and the same data is stored in the RxData array. The debugger in the microchip studio does not support the display in the ascii format, but we can check the memory location where the RxData array is stored.