cancel
Showing results for 
Search instead for 
Did you mean: 

USART Interrupt triggered too often

simon239955
Associate II
Posted on May 15, 2014 at 13:44

I'm using USART with circular buffers and interrupts to transmit and receive.

To transmit, a byte is written to the buffer, the TXE interrupt is enabled and then the byte is sent and theTXE Flag is cleared in the interrupt. At certain speed and send frequency (often I send a certain string every 20ms) it works fine, but sometimes, the interrupt seems to be called immediately again after being left and the whole buffer is sent via USART. I found the following answer with SPI in the forum:

Watch the following well known issue regarding disabling the interrupt inside the ISR: due to the cortex core architecture the interrupt being disabled in the ISR shortly before the ISR returns can cause the ISR to trigger again immediately. There are two solutions:

1 - to place enough code between the instruction that disables the interrupt and the end of the ISR - often that is the case, and people never experience the bug.

2 - to place __DSB() call at the end of the ISR - see core_cmInstr.h from CMSIS.

I placed the __DSB() call at the end of the ISR, but this doesn't seem to change anything. Are there other possibilities/solutions or any ideas that can cause this behaviour? The same buffer algorithm works fine for CAN transmission and receive which is much faster so I can't imagine that the buffer causes the problem. This is my ISR:

void
USART1_IRQHandler(
void
)
{
uint8_t ch;
if
(USART_GetITStatus(USART_CCU, USART_IT_RXNE) != RESET)
{
ch = (uint8_t) USART_ReceiveData(USART_CCU);
//put char to the buffer
USART_BufferWrite((USART_FIFO_TypeDef*) &USART_RxBuffer, ch);
}
if
(USART_GetITStatus(USART_CCU, USART_IT_TXE) != RESET)
{
if
(!USART_BufferIsEmpty((USART_FIFO_TypeDef*) &USART_TxBuffer))
{
USART_BufferRead((USART_FIFO_TypeDef*) &USART_TxBuffer, &ch);
USART_SendData(USART_CCU, ch);
}
else
//if buffer empty
{
//disable Transmit Data Register empty interrupt
USART_ITConfig(USART_CCU, USART_IT_TXE, DISABLE);
}
}
}

And if it helps, the USART Setup:

void
USART_Setup(uint32_t USART_BaudRate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_Initstructure;
USART_BufferInit((USART_FIFO_TypeDef*) &USART_RxBuffer);
USART_BufferInit((USART_FIFO_TypeDef*) &USART_TxBuffer);
USART_InitStructure.USART_BaudRate = USART_BaudRate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
//configure clock for USART
RCC_APB2PeriphClockCmd(USART_CLOCK, ENABLE);
//configure AF
GPIO_PinAFConfig(USART_PORT, USART_TX_SOURCE, GPIO_USART_MAPPING);
GPIO_PinAFConfig(USART_PORT, USART_RX_SOURCE, GPIO_USART_MAPPING);
//configure ports, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = USART_RXD | USART_TXD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(USART_PORT, &GPIO_InitStructure);
USART_Init(USART_CCU, &USART_InitStructure);
USART_Cmd(USART_CCU, ENABLE);
NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 0x0;
NVIC_Initstructure.NVIC_IRQChannelSubPriority = 0x6;
NVIC_Init(&NVIC_Initstructure);
USART_ITConfig(USART_CCU, USART_IT_TXE, DISABLE);
USART_ITConfig(USART_CCU, USART_IT_RXNE, ENABLE);
//Settings for printf and scanf
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
}

#stm32 #uart #interrupt #usart
11 REPLIES 11
Posted on May 15, 2014 at 14:03

Read back the USART_CR1 register after you write it (through

USART_ITConfig()

). I don't speak the ''library'' goobledygook so I don't know a ''function'' to do it, but in C it's enough to write USART_CR1; as it's declared as volatile. JW
Posted on May 15, 2014 at 15:20

Are there other possibilities/solutions or any ideas that can cause this behaviour?

I don't think it's the tail chaining pipeline hazard, you have some other system level interaction outside the scope of the code shown.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on May 15, 2014 at 15:56

> I don't think it's the tail chaining pipeline hazard,

I believe it may be - for the case when TXE is signalled and there is no more data to send in the buffer, the turnaround time from the disable command (which is the last in that branch) is executed by processor until it actually throttles the TXE signal towards NVIC may be too long for the interrupt to be invoked again during tail chaining. That's why my suggestion above.

JW

Posted on May 15, 2014 at 16:57

Ok, but by simply reordering the servicing of TXE before the RXNE this wouldn't even be an issue.

The stated condition is that all of a sudden it sticks in TXE mode and shovels all the data out, which isn't really the second spurious re-entry issue, or that the back-to-back USART transmissions could occur at a rate sufficient to saturate the processor. We're talking tens of thousands of cycles.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on May 15, 2014 at 17:17

> Ok, but by simply reordering the servicing of TXE before the RXNE this wouldn't even be an issue.

Probably yes, and if the status register is re-read (as it is now), almost certainly so.

> The stated condition is that all of a sudden it sticks in TXE mode and shovels all the data out,

I read the description as ''when buffer is completely sent, TXE interrupt is disabled but one more interrupt occurs''. I may have misunderstood it, but then I don't have any explanation... 🙂

JW

Posted on May 15, 2014 at 17:33

Like I said, I think there are other issues here, or the issue is poorly framed/described.

''At certain speed and send frequency (often I send a certain string every 20ms) it works fine, but sometimes, the interrupt seems to be called immediately again after being left and the whole buffer is sent via USART. ''

The job of the interrupt is to shovel any pending data out the USART, when there is data it isn't disabling the TXE interrupt, but it should clear. The condition where it enters a second time could be addressed by servicing TXE first, and the subsequent read of USARTx->SR, and DR and the write to the FIFO would all act to fence the clearing or disabling of TXE.

So either the text I highlighted is irrelevant, or it is a bigger systemic problem with the code, I chose the latter interpretation because otherwise why mention it as a symptom.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
simon239955
Associate II
Posted on May 15, 2014 at 19:42

Thanks for the fast support! Perhaps I described the problem imprecisely. I tested some different baudrates and sending frequencies and the behaviour is as follows:

At a baudrate of 9600 a string of 10bytes can be sent via printf every 10ms without any errors. At a baudrate of 19200 almost 40000 bytes are sent correctly, then the jump occurs. At any other Baudrates ca. 5000 - 8000 bytes are sent correctly in the 10bytes/10ms intervall, then the RX Counter in the receive console (HTerm on PC) jumps about buffer size (buffersize 1000 -> jumps e.g. from 6000 to 7000, buffersize 3000 it would jump to 9000) and the receive console is filled immediately (maximum speed I guess) with these bytes. I tried reordering the servicing of TXE before RXNE as well as reading the CR1 register before leaving the interrupt, and it seems to send more bytes without any jumps now, but the jumps still occur. I can print other code snippets as well, these are the functions I use for the circular buffer:

void
USART_BufferInit(__IO USART_FIFO_TypeDef *buffer)
{
buffer->size = USART_BUFF_SIZE + 1;
buffer->start = 0;
buffer->end = 0;
}
int
USART_BufferIsFull(__IO USART_FIFO_TypeDef *buffer)
{
return
(buffer->end + 1) % buffer->size == buffer->start;
}
int
USART_BufferIsEmpty(__IO USART_FIFO_TypeDef *buffer)
{
return
buffer->end == buffer->start;
}
void
USART_BufferWrite(__IO USART_FIFO_TypeDef *buffer, uint8_t elem)
{
buffer->elems[buffer->end] = elem;
buffer->end = (buffer->end + 1) % buffer->size;
if
(buffer->end == buffer->start)
buffer->start = (buffer->start + 1) % buffer->size; 
USART_ITConfig(USART_CCU, USART_IT_TXE, ENABLE);
}
void
USART_BufferRead(__IO USART_FIFO_TypeDef *buffer, uint8_t *elem)
{
*elem = buffer->elems[buffer->start];
buffer->start = (buffer->start + 1) % buffer->size;
}

If the buffer isn't appropiate or if you know any better algorithm I could try that as well, all I can say is that I use the buffer algorithm with CAN messages too, and there I never had any problems.
Posted on May 15, 2014 at 20:13

This odd

void USART_BufferWrite(__IO USART_FIFO_TypeDef *buffer, uint8_t elem)

{

buffer->elems[buffer->end] = elem;

buffer->end = (buffer->end + 1) % buffer->size;

if (buffer->end == buffer->start)

buffer->start = (buffer->start + 1) % buffer->size;

USART_ITConfig(USART_CCU, USART_IT_TXE, ENABLE);

}

What's this about?

 

if

 

(buffer->end == buffer->start)

buffer->start = (buffer->start + 1) % buffer->size;

You look to be bumping the bottom/tail of the buffer, and creating a race condition in the FIFO, one potential side effect would be a dumping of the entire buffer. I wouldn't touch the head AND the tail in one routine, and if I did it would be guarded by a disable/enable interrupt, or Enter/Leave Critical Section.

It would be better for you to check if the buffer is full, and NOT insert additional characters, for the printf() perhaps you could stall.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on May 15, 2014 at 22:07

void
USART_BufferWrite(__IO USART_FIFO_TypeDef *buffer, uint8_t elem)
{
buffer->elems[buffer->end] = elem;
buffer->end = (buffer->end + 1) % buffer->size;
if
(buffer->end == buffer->start)
buffer->start = (buffer->start + 1) % buffer->size; 
USART_ITConfig(USART_CCU, USART_IT_TXE, ENABLE);
}

There are two scenarios how this may go wrong. If the buffer was full before calling the routine, and interrupt kicks in after head meets tail before tail is bumped, its ''is empty?'' test returns true and it stops transmitting and disables the interrupt. This problem is masked by the unconditional enabling of interrupt at the end of routine (which in itself is not a good idea either). Other scenario starts with empty buffer but the last byte from previous buffer being still transmitted, so head==tail but the interrupt is not disabled yet. Then this routine is called, byte is inserted into buffer and just after head is bumped, the ISR kicks in. It sees a byte in the buffer, so pops it into the UART transmitting register, thus head==tail again. ISR returns just before the head==tail test in above routine, and as it is true, tail is bumped, so now head is just one position before tail, which is means full buffer, which subsequently gets all transmitted. JW