CubeMX STM32 HAL库中串口收发的正确打开方式(串口失效及解决方法)

问题:

HAL库在大数据量频繁收发时出现串口接收失效。

分析:

HAL库对串口中断进行了封装,留给用户的接口只有一个回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)	//接收中断回调函数

平时在中断函数中写的东西,现在统统写在这个回调函数中。

在使用库函数或寄存器时,我们通常要自己清除中断标志位,且接收中断的使能和失能可以由用户直接操作寄存器控制,而这些都被HAL库进行了封装,呈现在用户面前的只有两个函数:

HAL_UART_Receive(&huart2,(uint8_t*)Data_Buf, Data_Length,Timeout)//阻塞式接收,在指定时间内阻塞等待数据到来
HAL_UART_Receive_IT(&huart2,(uint8_t*)Data_Buf,Data_Length);//中断式接收

第一个函数使用频率较低,一般情况下,我们想要复现库函数或寄存器编程时的那种单字节接收中断的方式来处理数据,就可以使用第二个函数,将Data_Length设为1,这样接收到一字节数据就会进入一次回调函数。如下:

HAL_UART_Receive_IT(&huart2,(uint8_t*)&UART2_RX_TEMP,1);//开启串口2接收中断

HAL库之所以把这些封装起来,就是为了大数据量的吞吐(因为可以可以指定数据长度,且清除标志位等操作HAL库都自己做了)。
现在拿它当单字节的接收中断用反而很低效,因为每一字节都要做很多判断再进入回调函数。
并且HAL_UART_Receive_IT这个函数每次使用时都在函数内部对串口中断进行使能。
因为HAL库在串口中断中加入了这样一个机制:在规定字节接收完成后,对中断进行失能。
为了保证能连续接收,我们通常要在回调函数结束时,重新调用该函数使能接收中断用以下一字节的接收,如下:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance==USART2)
  {
    UART1_Rx_Buf[UART1_Rx_cnt] = UART2_temp;
    UART1_Rx_cnt++;
    HAL_UART_Receive_IT(&huart2,(uint8_t*)&UART2_RX_TEMP,1);//重新使能接收中断
  }
}

我们进入HAL_UART_Receive_IT()观察一下函数原型

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(huart);

    huart->pRxBuffPtr = pData;
    huart->RxXferSize = Size;
    huart->RxXferCount = Size;

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->RxState = HAL_UART_STATE_BUSY_RX;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    /* Enable the UART Parity Error Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_PE);

    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);

    /* Enable the UART Data Register not empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

可以看到该函数使能中断时,是有可能失败的,如果使能失败,返回值为 HAL_BUSY
这里失败的具体原因我自己研究了一会儿,水平还是不太够,网上有大佬给出了答案(链接在文末)。
大概的问题是:
串口的发送和接收出现冲突,HAL_UART_Transmit是阻塞式调用,阻塞中发生中断后就会出问题。
我模拟大数据量的收发试验了一下。

if(HAL_UART_Receive_IT(&huart2,(uint8_t*)&UART2_RX_TEMP,1) == HAL_BUSY);//开启串口2接收中断
{
	printf("Uart2 is BUSY!\r\n");
}

如果使能串口失败,就提示Uart2 is BUSY!,串口助手界面结果如下:
实验结果在数据量过大时,就出现了使能串口中断失败的情况,之后串口接收便失效了,程序其他部分运行正常,串口发送也正常。

解决办法

检测到HAL_UART_Receive_IT()返回HAL_BUSY时就重新使能,注意不要迅速使能,在中断里千万不要这么写:

while(HAL_UART_Receive_IT(&huart2,(uint8_t*)&UART2_RX_TEMP,1) == HAL_BUSY);

亲测上面的代码要是卡在中断中的话会让程序崩溃,最好是这样:

if(HAL_UART_Receive_IT(&huart2,(uint8_t*)&UART2_RX_TEMP,1) == HAL_BUSY)//开启串口2接收中断
{
	UART_ERR = 1;
}

然后在循环(或另一个任务中)检测UART_ERR,重新使能:

if(UART_ERR)
{
	UART_ERR = 0;
	printf("\r\nUart2 is BUSY!-------------------->\r\n");
	while(HAL_UART_Receive_IT(&huart2,(uint8_t*)&UART2_RX_TEMP,1) == HAL_BUSY);//开启串口2接收中断
}

这种方法只能充当一个看门狗的角色,不能从根本上解决HAL库的串口接收问题。
如果在平时的应用中只是偶尔出现这样的问题,数据也有重发的机制的话,可以用这种方法解决。
我觉得这也不能算是HAL库的BUG,因为这里使能失败是可以读到的,并不是未知的错误,只能说是这样的机制没考虑到普通的小型应用设计的方便性吧。

下面帖子里详细说明了HAL库的这个问题,可以通过修改HAL库的相关部分代码解决该问题:
https://blog.csdn.net/suxingtian/article/details/86526746

4.29更新

建议不要用HAL库的串口接收函数了,直接把原来生成的串口中断函数删掉,自己写一个就好,。
HAL库的初始化配置不用更改,只需在初始化时加一句开启串口中断:

	__HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);	//使能接收中断

之后就可以像使用标准库/寄存器那样的写法去处理HAL库的串口中断了:

void USART3_IRQHandler(void)		//接收中断
{
	uint8_t UART3_RX_TEMP  = 0;
	if(huart3.Instance->SR & UART_FLAG_RXNE)//接收到数据
	{
		UART3_RX_TEMP = USART3->DR;				//读出数据(读DR寄存器会自动清除标志位)
		/***user code begin***/
		
		/***user code end***/
	}
}

处理串口数据的两种常用方式:
串口单字节中断:小数据量时适合处理帧协议,
DMA+空闲中断+缓冲区:适用于各种情况。

  • 13
    点赞
  • 97
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
STM32 HAL提供了一种使用DMA和空闲断(IDLE)来实现不定长数据接收的方法。首先,你需要下载并安装串口调试助手,然后将STM32连接到计算机上。使用串口调试助手设置串口号和波特率等参数。接下来,你需要在STM32上配置USART1,并编写一个接收字符串的函数。当STM32接收到字符"stop stm32!"时,停止持续发送"hello windows!";当接收到字符"go stm32!"时,持续发送"hello windows!"。你可以参考STM32 HAL的示例代码来实现这个功能。不过要注意,这个过程涉及到硬件和软件的配置和编程,所以你需要准备好相应的硬件和软件材料,包括STM32F103C8T6最小开发版、CH340USB转TTL模块、杜邦线、面包板、LED灯等。软件方面,你需要安装KEIL5、STM32CubeMX、FlyMcu和FireTools等软件。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [stm32使用HAL配置串口收发数据(保姆级教程)](https://blog.csdn.net/qq_52062938/article/details/130494097)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [STM32 HAL串口+DMA空闲断接收不定长数据](https://download.csdn.net/download/chenyongfeng123/13087603)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [stm32使用hal断控制串口通信](https://blog.csdn.net/weixin_54435584/article/details/127468012)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值