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:
typedef struct{
uint8_t in;
uint8_t out;
uint8_t count;
uint8_t buff[USARTBUFFSIZE];
}FIFO_TypeDef;
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:
void BufferInit(__IO FIFO_TypeDef *buffer)
{
buffer->count = 0;//0 bytes in buffer
buffer->in = 0;//index points to start
buffer->out = 0;//index points to start
}
Next follows writing byte to buffer:
ErrorStatus BufferPut(__IO FIFO_TypeDef *buffer, uint8_t ch)
{
if(buffer->count==USARTBUFFSIZE)
return ERROR;//buffer full
buffer->buff[buffer->in++]=ch;
buffer->count++;
if(buffer->in==USARTBUFFSIZE)
buffer->in=0;//start from beginning
return SUCCESS;
}
ErrorStatus BufferGet(__IO FIFO_TypeDef *buffer, uint8_t *ch)
{
if(buffer->count==0)
return ERROR;//buffer empty
*ch=buffer->buff[buffer->out++];
buffer->count--;
if(buffer->out==USARTBUFFSIZE)
buffer->out=0;//start from beginning
return SUCCESS;
}
ErrorStatus BufferIsEmpty(__IO FIFO_TypeDef buffer)
{
if(buffer.count==0)
return SUCCESS;//buffer full
return ERROR;
}
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:
//initialize buffers
volatile FIFO_TypeDef U1Rx, U1Tx;
BufferInit(&U1Rx);
BufferInit(&U1Tx);
//configure NVIC
NVIC_InitTypeDef NVIC_InitStructure;
//select NVIC channel to configure
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
//set priority to lowest
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
//set subpriority to lowest
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
//enable IRQ channel
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//update NVIC registers
NVIC_Init(&NVIC_InitStructure);
//disable Transmit Data Register empty interrupt
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
//enable Receive Data register not empty interrupt
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
Now we can modify byte send function:
void Usart1Put(uint8_t ch)
{
//put char to the buffer
BufferPut(&U1Tx, ch);
//enable Transmit Data Register empty interrupt
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}
uint8_t Usart1Get(void){
uint8_t ch;
//check if buffer is empty
while (BufferIsEmpty(U1Rx) ==SUCCESS);
BufferGet(&U1Rx, &ch);
return ch;
}
And the last thing we have to do is to write our interrupt handler where all magic happens:
void USART1_IRQHandler(void)
{
uint8_t ch;
//if Receive interrupt
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
ch=(uint8_t)USART_ReceiveData(USART1);
//put char to the buffer
BufferPut(&U1Rx, ch);
}
if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
{
if (BufferGet(&U1Tx, &ch) == SUCCESS)//if buffer read
{
USART_SendData(USART1, ch);
}
else//if buffer empty
{
//disable Transmit Data Register empty interrupt
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
}
}
USART_GetITStatus(USART1, USART_IT_RXNE)
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
#define BUFFERED
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″]
The post Programming STM32 USART using GCC tools: Part 2 appeared first on Key to Smart.