In previous part of tutorial we have covered simple USART routines that sends data directly to USART peripheral. This is OK to use such approach when project isn’t time critical and processing resources are far from limits. But most often we stuck with these limiting factors especially when RTOS is used or when we perform critical real time data processing. And having USART routines with while loop based wait isn’t a good idea – it simply steals processing power only to send a data.

As you may guessed – next step is to employ interrupts.

As you can see there are many sources to trigger interrupts and each of them are used for different purpose. In order to use one or another interrupt first it has to be enabled in USART control register (USART_CR1, USART_CR2 or USART_CR3). Then NVIC USART_IRQn channel has to be enabled in order to map interrupt to its service routine. Because NVIC has only one vector for all USART interrupt triggers, service routine has to figure out which of interrupts has triggered an event. This is done by checking flags in USART status register (USART_SR).

Another important thing about using interrupt based transmission is buffering. Using an interrupts is a “set and forget” method and it is a bit hard predict when it will occur especially when system is complex with multiple different priority interrupt. It can be situation when higher priority preempts USART interrupt during transfer and tries to send data via same USART channel. Without buffering this might become impossible.

As you can see we can continue stacking data FIFO buffer and once USART interrupt routine gets it’s control it will transmit accumulated data. Then only problem here that may occur is a buffer overflow – so make sure that FIFO is large enough to deal worth case scenario when buffer gets overfilled and no more data can be accepted that leads to loss of bytes until USART interrupt routine gets chance to sip few bytes off.

In following example we are going to create s simple FIFO implementation that allows us to create send and transmit buffers. For this we create another two files in project – buffer.c and buffer.h. In header file we define FIFO_TypeDef type:

 

Members of this structure are as follows:

in – indicates input data location. It points to last written data to buffer;

out – indicates next data byte to be sent via USART;

count – indicates the number of bytes currently stored in FIFO;

buff[] – array storing data;

USARTBUFFSIZE – is the size of FIFO.

Having all this we can create a simple circular FIFO or so called ring buffer:

First of all when FIFO is created we have to initialize it by setting initial values of indexes and count:

 

This will give us an empty buffer.

Next follows writing byte to buffer:

 

As you can see before writing to buffer we have to make sure its not full. If its full we simply return error – in program this would mean loss of data (if no special care is taken). Otherwise if buffer isn’t full, then it adds byte to the end (tail) of queue and increases element count. And since this is circular buffer once index reaches end of array it cycles to the beginning. Similar situation is with reading from buffer:
Here we firs check if there is data in buffer and return error if its empty. If data is present in FIFO then we take first byte from beginning (head) of FIFO and decrease count of data in it and update index.

Continuing with our example we are going to update the code that does same task, but using buffers and interrupts.

First of all we implement buffers – one for receiving data and another for transmitting:

 

and in USART1Init function we add couple lines where these buffers are initialized:
Since we are going to use interrupt based USART communication we also have to enable NVIC channel for USART1 by toUSART1Init() following:
We simply enable USART1_IRQn channel with 0 priority and 0 sub-priority. Next thing is to decide which interrupt sources will be used to trigger transmission and reception. For reception it is obvious that Received Data Ready to be Read (RXNE) interrupt will rise once data from receive shift register is transferred to USART_DR data register. So during this interrupt we simply need to read out the value from it. A bit different situation is with transmitting. For this we are going to use Transmit Data Register Empty (TXE) interrupt source to trigger transfer. This interrupt raises every time contents of data register is transferred to output shift register meaning readiness for another byte to transfer. Initially we disable this interrupt because we don’t need to trigger it since we don’t have anything to send.

Now we can modify byte send function:

It simply adds byte to buffer and then enables TXE interrupt so it could be triggered to transmit. Similar situation is with reading byte function:
There we need to check if there is data in buffer. A simple helper function BufferIsEmpty() checks if FIFO is empty by reading buffer count value and returns SUCCESS if its empty. If its empty we simply wait for data to be received. Once its here it is passed to variable and returned.

And the last thing we have to do is to write our interrupt handler where all magic happens:

 

As I mentioned before first of all we need to clear out which event triggered an interrupt. This is done by checking flags in status register. This is done with

function which simply checks selected flag and returns logical ’1′ if particular flag is set. Then conditional code is executed. So if we get receive (RXNE) interrupt then we simply read data value from USART data register and place it in to receive buffer.

If we find that this is transmit interrupt (TXE), then we take data byte from buffer and place in to USART data register to send. And once transmit buffer is empty (TXE) interrupt is disabled to avoid chain interrupt triggering. And this practically it. Same example from part1 works fine. I modified code so that it works either in buffered and in non buffered without interrupts mode. All you need to comment or comment out

 

 

in usart.h file. If you need more info about print format check out any external source like this.

Download CodeSourcery+Eclipse project files here to give it a try:
[download id=”5″]