在 STM32 HAL 库中启动 UART 接收线程后,UART 发送失败
UART Transmit failing after UART Receive thread starts in STM32 HAL Library
我正在使用 STM32F1 (STM32F103C8T6) 来开发一个使用 FreeRTOS 的项目。
下面是我的GPIO和USART1接口配置:
__GPIOA_CLK_ENABLE();
__USART1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;//115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart1);
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
问题是:为什么UART在线程启动之前传输工作,但在线程启动之后或从线程传输?我想从线程传输数据。即
int main(void)
{
Initializations();
//THIS WORKS!!
uart_transmit_buffer[0] = 'H';
uart_transmit_buffer[1] = 'R';
uart_transmit_buffer[2] = '#';
uint8_t nums_in_tr_buf = 0;
nums_in_tr_buf = sizeof(uart_transmit_buffer)/sizeof(uint8_t);
state = HAL_UART_Transmit(&huart1, uart_transmit_buffer, nums_in_tr_buf, 5000);
StartAllThreads();
osKernelStart();
for (;;);
}
static void A_Random_Thread(void const *argument)
{
for(;;)
{
if (conditionsMet()) //Executed once when a proper response received.
{
//BUT NOT THIS :(!!
uart_transmit_buffer[0] = 'H';
uart_transmit_buffer[1] = 'R';
uart_transmit_buffer[2] = '#';
uint8_t nums_in_tr_buf = 0;
nums_in_tr_buf = sizeof(uart_transmit_buffer)/sizeof(uint8_t);
state = HAL_UART_Transmit(&huart1, uart_transmit_buffer, nums_in_tr_buf, 5000);
}
}
}
我已确定没有线程处于死锁状态。问题是 UART_HAL_Transmit 给出 HAL_BUSY 状态。
此外,我有一个专门的线程来接收和解析来自 UART RX 的信息,我怀疑这可能是问题的原因。以下是代码:
static void UART_Receive_Thread(void const *argument)
{
uint32_t count;
(void) argument;
int j = 0, word_length = 0;
for (;;)
{
if (uart_line_ready == 0)
{
HAL_UART_Receive(&huart1, uart_receive_buffer, UART_RX_BUFFER_SIZE, 0xFFFF);
if (uart_receive_buffer[0] != 0)
{
if (uart_receive_buffer[0] != END_OF_WORD_CHAR)
{
uart_line_buffer[k] = uart_receive_buffer[0];
uart_receive_buffer[0] = 0;
k++;
}
else
{
uart_receive_buffer[0] = 0;
uart_line_ready = 1;
word_length = k;
k = 0;
}
}
}
if (uart_line_ready == 1)
{
//osThreadSuspend(OLEDThreadHandle);
for (j = 0; j <= word_length; j++)
{
UART_RECEIVED_COMMAND[j] = uart_line_buffer[j];
}
for (j = 0; j <= word_length; j++)
{
uart_line_buffer[j] = 0;
}
uart_line_ready = 0;
RECEIVED_COMMAND = ParseReceivedCommand(UART_RECEIVED_COMMAND);
if (RECEIVED_COMMAND != _ID_)
{
AssignReceivedData (word_length); //Results in uint8_t * RECEIVED_DATA
}
//osThreadResume(OLEDThreadHandle);
}
//Should be no delay in order not to miss any data..
}
}
我怀疑问题的另一个原因可能与系统中断有关(另请注意初始化部分,我配置了 NVIC):
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
}
非常感谢对此问题的任何帮助或指导。提前致谢。
当使用FreeRTOS时,您必须将中断优先级设置为5或更高,因为5以下是为OS保留的。
因此,更改您的代码以将优先级设置为:
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
问题原来与阻塞语句有关。
由于 UART_Receive_Thread 内部有 HAL_UART_Receive,这会阻塞线程直到收到某些东西,这会导致 HAL 繁忙(因此,HAL_BUSY 状态)。
解决方案是使用 非阻塞 语句而不更改任何其他内容。
即同时使用 HAL_UART_Receive_IT 和 HAL_UART_Transmit_IT 并忽略阻塞语句。
感谢促成此解决方案的所有建议。
据我所知,如果不是 __HAL_LOCK(huart)
,HAL_UART_Transmit
会使用 F4 HAL (v1.4.2)。 RX 线程会锁定句柄,然后 TX 线程也会尝试锁定 return HAL_BUSY。 HAL_UART_Transmit_IT
和 HAL_UART_Receive_IT
在 transmit/receive 期间不锁定句柄。
这可能会导致 State
成员出现问题,因为它是由辅助函数 UART_Receive_IT
和 UART_Transmit_IT
非原子更新的。虽然我认为这不会影响操作。
您可以修改函数以允许同时进行 RX 和 TX。每次他们发布新版本的 HAL 时,您都必须更新它。
问题是 ST HAL 不适合与 RTOS 一起使用。它在宏的定义中是这样说的 __HAL_LOCK
。重新定义它以使用 RTOS 的互斥锁也可能值得一试。同HAL_Delay()
使用RTOS的线程休眠功能
虽然一般来说,通过线程中的阻塞函数发送应该没问题,但我不会在线程中使用阻塞函数接收数据。这样你肯定会遇到超过 运行 个错误。
同样,如果您在接收中断中进行了太多处理,您也可能 运行 出现超过 运行 个错误。我更喜欢使用 DMA 进行接收,如果我 运行 没有 DMA 流则使用中断。中断仅将数据复制到缓冲区,类似于 DMA。然后使用 processRxData
线程来处理实际数据。
我正在使用 STM32F1 (STM32F103C8T6) 来开发一个使用 FreeRTOS 的项目。
下面是我的GPIO和USART1接口配置:
__GPIOA_CLK_ENABLE();
__USART1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;//115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart1);
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
问题是:为什么UART在线程启动之前传输工作,但在线程启动之后或从线程传输?我想从线程传输数据。即
int main(void)
{
Initializations();
//THIS WORKS!!
uart_transmit_buffer[0] = 'H';
uart_transmit_buffer[1] = 'R';
uart_transmit_buffer[2] = '#';
uint8_t nums_in_tr_buf = 0;
nums_in_tr_buf = sizeof(uart_transmit_buffer)/sizeof(uint8_t);
state = HAL_UART_Transmit(&huart1, uart_transmit_buffer, nums_in_tr_buf, 5000);
StartAllThreads();
osKernelStart();
for (;;);
}
static void A_Random_Thread(void const *argument)
{
for(;;)
{
if (conditionsMet()) //Executed once when a proper response received.
{
//BUT NOT THIS :(!!
uart_transmit_buffer[0] = 'H';
uart_transmit_buffer[1] = 'R';
uart_transmit_buffer[2] = '#';
uint8_t nums_in_tr_buf = 0;
nums_in_tr_buf = sizeof(uart_transmit_buffer)/sizeof(uint8_t);
state = HAL_UART_Transmit(&huart1, uart_transmit_buffer, nums_in_tr_buf, 5000);
}
}
}
我已确定没有线程处于死锁状态。问题是 UART_HAL_Transmit 给出 HAL_BUSY 状态。
此外,我有一个专门的线程来接收和解析来自 UART RX 的信息,我怀疑这可能是问题的原因。以下是代码:
static void UART_Receive_Thread(void const *argument)
{
uint32_t count;
(void) argument;
int j = 0, word_length = 0;
for (;;)
{
if (uart_line_ready == 0)
{
HAL_UART_Receive(&huart1, uart_receive_buffer, UART_RX_BUFFER_SIZE, 0xFFFF);
if (uart_receive_buffer[0] != 0)
{
if (uart_receive_buffer[0] != END_OF_WORD_CHAR)
{
uart_line_buffer[k] = uart_receive_buffer[0];
uart_receive_buffer[0] = 0;
k++;
}
else
{
uart_receive_buffer[0] = 0;
uart_line_ready = 1;
word_length = k;
k = 0;
}
}
}
if (uart_line_ready == 1)
{
//osThreadSuspend(OLEDThreadHandle);
for (j = 0; j <= word_length; j++)
{
UART_RECEIVED_COMMAND[j] = uart_line_buffer[j];
}
for (j = 0; j <= word_length; j++)
{
uart_line_buffer[j] = 0;
}
uart_line_ready = 0;
RECEIVED_COMMAND = ParseReceivedCommand(UART_RECEIVED_COMMAND);
if (RECEIVED_COMMAND != _ID_)
{
AssignReceivedData (word_length); //Results in uint8_t * RECEIVED_DATA
}
//osThreadResume(OLEDThreadHandle);
}
//Should be no delay in order not to miss any data..
}
}
我怀疑问题的另一个原因可能与系统中断有关(另请注意初始化部分,我配置了 NVIC):
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
}
非常感谢对此问题的任何帮助或指导。提前致谢。
当使用FreeRTOS时,您必须将中断优先级设置为5或更高,因为5以下是为OS保留的。 因此,更改您的代码以将优先级设置为:
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
问题原来与阻塞语句有关。 由于 UART_Receive_Thread 内部有 HAL_UART_Receive,这会阻塞线程直到收到某些东西,这会导致 HAL 繁忙(因此,HAL_BUSY 状态)。
解决方案是使用 非阻塞 语句而不更改任何其他内容。 即同时使用 HAL_UART_Receive_IT 和 HAL_UART_Transmit_IT 并忽略阻塞语句。
感谢促成此解决方案的所有建议。
据我所知,如果不是 __HAL_LOCK(huart)
,HAL_UART_Transmit
会使用 F4 HAL (v1.4.2)。 RX 线程会锁定句柄,然后 TX 线程也会尝试锁定 return HAL_BUSY。 HAL_UART_Transmit_IT
和 HAL_UART_Receive_IT
在 transmit/receive 期间不锁定句柄。
这可能会导致 State
成员出现问题,因为它是由辅助函数 UART_Receive_IT
和 UART_Transmit_IT
非原子更新的。虽然我认为这不会影响操作。
您可以修改函数以允许同时进行 RX 和 TX。每次他们发布新版本的 HAL 时,您都必须更新它。
问题是 ST HAL 不适合与 RTOS 一起使用。它在宏的定义中是这样说的 __HAL_LOCK
。重新定义它以使用 RTOS 的互斥锁也可能值得一试。同HAL_Delay()
使用RTOS的线程休眠功能
虽然一般来说,通过线程中的阻塞函数发送应该没问题,但我不会在线程中使用阻塞函数接收数据。这样你肯定会遇到超过 运行 个错误。
同样,如果您在接收中断中进行了太多处理,您也可能 运行 出现超过 运行 个错误。我更喜欢使用 DMA 进行接收,如果我 运行 没有 DMA 流则使用中断。中断仅将数据复制到缓冲区,类似于 DMA。然后使用 processRxData
线程来处理实际数据。