2014-05-15 04:44 AM
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
2014-05-15 01:34 PM
This problem is masked by the unconditional enabling of interrupt at the end of routine (which in itself is not a good idea either).
The enabling of the interrupt is relatively harmless, it will kick start new data transmission, and allows the bytes to be dispatched at a single point, the IRQHandler, in the worst case scenario it will cause a spurious interrupt where the handler will realize it already just sent the last byte, and immediately disable it again. The key failure is not recognizing that the buffer is full before trying to insert additional data, and trying to advance both head and tail in an unsafe manner. If the desired solution is to eat data, then that should be the new data, not the old data that is already queued.2014-05-18 12:11 PM
I check if the buffer is full before I call the BufferWrite function. I understand the possible problems you mentioned and will have a look at the buffer again (I'm pretty knew to this kind of communication).
However the buffer seems to work without problems know. I changed the code in the ISR as follows: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);
}
if (USART_BufferIsEmpty((USART_FIFO_TypeDef*) &USART_TxBuffer))
{
//disable Transmit Data Register empty interrupt
USART_ITConfig(USART_CCU, USART_IT_TXE, DISABLE);
}
}
}
(Just replaced the else with
if (USART_BufferIsEmpty((USART_FIFO_TypeDef*) )
If the buffer is empty, the interrupt is know directly disabled instead of being triggered again just to disable the interrupt. I'm not really sure why it does work now, perhaps any of you can see what causes this behaviour.