2024-09-16 11:11 PM
As per my understanding of stm32 uart driver interrupt code the usual process for receive interrupt is,
1. HAL_UART_Receive_IT:
It checks if huart->RxState is HAL_UART_STATE_READY.
If true, it sets huart->RxState to HAL_UART_STATE_BUSY_RX.
2. When Data is received into DataRegister HAL_UART_IRQHandler calls UART_Receive_IT.
3. UART_Receive_IT:
checks if huart->RxState is HAL_UART_STATE_BUSY_RX.
If true, and when all data bytes are received, it sets huart->RxState to HAL_UART_STATE_READY.
Issue Scenario: Suppose due to some noise on UART line USART_SR_RXNE bit is set and HAL_UART_IRQHandler is triggered, and it calls UART_Receive_IT. Now UART_Receive_IT checks if huart->RxState is HAL_UART_STATE_BUSY_RX , its Not since HAL_UART_Receive_IT was not called by user. In this case the UART_Receive_IT exits without doing anything and HAL_UART_IRQHandler keeps getting triggered repeatedly since USART_SR_RXNE is not cleared and execution is stuck.
Am I missing something here? or by design stm32 uart interrupt is stuck and system hangs.
I'm using STM32F407IGTx microcontroller.
Kindly advice.
2024-09-17 06:26 AM
If you are not actively receiving data (i.e. in HAL_UART_STATE_READY state), then the interrupt is disabled and the interrupt handler never gets called. Receiving characters in this state doesn't cause any code to run, but it will cause an overrun condition which causes problems the next time you try to receive characters.
2024-09-17 01:19 PM
Show your code. It's more than likely something that you're doing incorrectly.
Probably calling UART_Receive_IT in the wrong place(s);
2024-09-17 02:37 PM
Please don't post screen shots of code..
Use the </> icon in the editor to post enough code to be helpful.
2024-09-29 08:40 PM
I did some further debugging and found out what is happening. This issue happens whenever there is a noise on uart line. Sequence is as follows:
1. DeInit and Init UART which sets huart->RxState is HAL_UART_STATE_READY.
2. Transmit data using interrupt by calling HAL_UART_Transmit_IT, I see HAL_UART_TxCpltCallback is called and transmission is successful.
3. Next I call HAL_UART_Receive_IT which sets USART_SR_RXNE and wait for HAL_UART_RxCpltCallback, may be due to noise the HAL_UART_RxCpltCallback is not called and reception is unsuccessful, my timeout timer callback is called and I exit. But at this point USART_SR_RXNE is still set since HAL_UART_RxCpltCallback is never called.
4. for next data, I call DeInit and Init UART which sets huart->RxState is HAL_UART_STATE_READY. (Note that init doesn't clear USART_SR_RXNE)
5. Transmit data using interrupt by calling HAL_UART_Transmit_IT, At this point UART HAL_UART_IRQHandler is called. Inside the HAL_UART_IRQHandler first they check for Receive - UART_Receive_IT. In UART_Receive_IT it checks if(huart->RxState == HAL_UART_STATE_BUSY_RX) , since during init huart->RxState is HAL_UART_STATE_READY. the check is not satisfied and it exits UART IRQ handler without doing anything. But still since USART_SR_RXNE bit is not cleared the interrupt keeps repeatedly calling HAL_UART_IRQHandler without giving time for any other tasks and system hangs.
void UART_driver_Deinit(void)
{
HAL_UART_DeInit(&huart6);
}
void MX_USART6_UART_Init(uint32_t baudrate)
{
huart6.Instance = USART6;
huart6.Init.BaudRate = baudrate;
huart6.Init.WordLength = UART_WORDLENGTH_8B;
huart6.Init.StopBits = UART_STOPBITS_1;
huart6.Init.Parity = UART_PARITY_NONE;
huart6.Init.Mode = UART_MODE_TX_RX;
huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart6.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_HalfDuplex_Init(&huart6) != HAL_OK)
{
_Error_Handler();
}
}
static void thrUART(void *argument) {
uint32_t ErrorCode = 0U;
static uint32_t PrvErrorCode_uart6 = 0;
OneWire_Init(UART6_SELECT);
for (;;) {
ErrorCode = osThreadFlagsWait (UART_THREAD_SIGNAL_MASK, osFlagsWaitAny, osWaitForever); // Wait forever until thread flag
if(ErrorCode & UART6_THREAD_SUCCESS)
{
uart6_comm_status = UART_NO_ERROR;
}
if(ErrorCode & UART6_THREAD_FAILED)
{
uart6_comm_status = UART_COMMUNICATION_ERROR;
if(PrvErrorCode_uart6 != ErrorCode)
{
/*Error handling of communication failure*/
PrvErrorCode_uart6 = ErrorCode;
}
}
osThreadYield();
}
}
int8_t create_Thread_UART (void)
{
int8_t retvalue = 0;
tid_thrUART = osThreadNew (thrUART, NULL, &Thread_UART_attr);
if(tid_thrUART == NULL)
{
retvalue = UART_COMMUNICATION_ERROR;
_Error_Handler(); // Thread object not created, handle failure
}
return retvalue;
}
/*
Return value:
-2 = Transfer not complete due to Wait timer expired
-1 = Transfer not complete due to failed callback
0 = Transfer complete
*/
int32_t UART6_WriteBuf (uint8_t *buf, uint32_t len) {
uart6_comm_status = UART_COMMUNICATION_ERROR;
if(HAL_UART_Transmit_IT(&huart6, buf, len) == HAL_OK)
{
// Wait for the transfer complete and exit the wait if transfer timed out
while(uart6_comm_status != UART_NO_ERROR && uart6_comm_status != UART_TIMEOUT_ERROR);
}
return uart6_comm_status;
}
int32_t UART6_ReadBuf (uint8_t *buf, uint32_t len) {
uart6_comm_status = UART_COMMUNICATION_ERROR;
if(HAL_UART_Receive_IT(&huart6, buf, len) == HAL_OK)
{
uart_callingThreadID = osThreadGetId(); //Get the caller ID to return from callback
osThreadFlagsWait (UART6_THREAD_SUCCESS, osFlagsWaitAll, UART_WAIT_DELAY); // Wait forever until thread flag
}
return uart6_comm_status;
}
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(huart->Instance==USART6)
{
/* Peripheral clock enable */
__HAL_RCC_USART6_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/**USART6 GPIO Configuration
PG14 ------> USART6_TX
*/
GPIO_InitStruct.Pin = OneWire_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
HAL_GPIO_Init(OneWire_GPIO_Port, &GPIO_InitStruct);
/* USART6 interrupt Init */
HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART6_IRQn);
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{
if(huart->Instance==USART6)
{
/* Peripheral clock enable */
__HAL_RCC_USART6_CLK_DISABLE();
__HAL_RCC_GPIOG_CLK_DISABLE();
HAL_NVIC_DisableIRQ(USART6_IRQn);
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART6)
{
if(huart->ErrorCode == 0)
{
uart6_comm_status = UART_NO_ERROR;
osThreadFlagsSet(uart_callingThreadID, UART6_THREAD_SUCCESS);
}
else
{
osThreadFlagsSet(tid_thrUART, UART6_THREAD_FAILED);
}
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART6)
{
if(huart->ErrorCode == 0)
{
uart6_comm_status = UART_NO_ERROR;
}
else
{
osThreadFlagsSet(tid_thrUART, UART6_THREAD_FAILED);
}
}
}
void Uart_timer_callback(void)
{
uart6_comm_status = UART_TIMEOUT_ERROR; //Force exit the write complete waiting flag
}
2024-09-30 02:02 AM
Yeah, you wrote your HAL_UART_Receive_IT in the wrong place.
You should call it once before your tasks start to run, to first initialize the UART interrupt.
Then in the HAL_UART_RxCpltCallback, you need to copy the byte(s) from the primary buffer to a secondary buffer, set your ready flag and call HAL_UART_Receive_IT. You need to that before you exit the interrupt so that way you don't miss any incoming bytes. Then in your tasks, you check the ready flag and process the data from the secondary buffer while the primary buffer is receiving data.
Because you exit the interrupt before you call HAL_UART_RxCpltCallback, your tasks, how many there may be, could be doing other stuff before you enable the interrupt again. If you're looking for a specific amount of bytes like 10 bytes, and you enable the interrupt later, you may have missed a byte or two? So what happens is that HAL_UART_RxCpltCallback is not called because it is still waiting for 2 more bytes.
You're not showing enough code so no telling when you're saving the byte(s) to a buffer and when you're calling UART6_ReadBuf
2024-09-30 02:30 AM
Thank you, Please find the UART6_ReadBuf calling code
static uint8_t reset_slave(void)
{
uint8_t cmd = 0x0F;
uint8_t ret_val = 0xFF;
UART_driver_Deinit();
MX_USART6_UART_Init(9600);
Start_uart_timer(200);
UART6_WriteBuf(&cmd, 1);
UART6_ReadBuf(&cmd, 1);
Stop_uart_timer();
if(cmd == 0xE0)
{
MX_USART6_UART_Init(115200);
osDelay(20);
ret_val = 0x00;
}
return ret_val;
}