STM32 HAL库串口同时收发,接收卡死?_hal库串口异常的问题(1)

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


然后在对应的中断处理函数中,直接读取 DR 寄存器(STM32F103 而言为 DR,STM32L051 为RDR),然后当 IDLE 中断产生,也可以处理一下标志位:



//自己用的,用到了 USART_IT_IDLE 标志位,有时候不合适
void USART3_IRQHandler(void) //串口3中断服务程序
{
u8 clear3=clear3; //消除编译器没有用到的提醒
// u8 Res;

if(USART\_GetITStatus(USART3, USART_IT_RXNE) != RESET)  
	{
		USART_Enocean_BUF[Enocean_Data++] = USART3->DR;
		USART3_RX_BUF[USART3_Data++] = USART\_ReceiveData(USART3);
		// Res= USART\_ReceiveData(USART3);
		// USART\_SendData(USART1,Res);while(!(USART1->SR&USART\_FLAG\_TXE)); 
	}

	else if(USART\_GetITStatus(USART3, USART_IT_IDLE) != RESET)		  
	{
		clear3=USART3->SR; //读SR寄存器 可以清空寄存器
		clear3=USART3->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断) 
		USART3_RX_STA=1;	  //标记接收到了一帧数据
		//USART3\_Data=0;
	}	              //enocean是不是读不到一帧数据,不用一帧数据测试一下 

}


#### 1.2.1 HAL库接收


##### HAL库接收方式一


在 HAL 库函数接收的时候,其实也可以使用标准库上一样的中断标志使能:



MX_LPUART1_UART_Init();
__HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_RXNE);


IDLE 中断使能:



__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);


这种方式的处理方式,可以和标准库差不多。


 在 HAL 库中,外设的中断的入口函数都放在`stm32l0xx_it.c` 这个文件夹中( 以STM32L051 为例),在这个文件中可以找到和标准库一样的 中断入口函数,我们可以进行如下处理:



void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */

/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_RXNE) == SET){
// USART_Enocean_BUF[Enocean_Data++] = huart2.Instance->RDR
// RXNE 数据处理,直接读取数据
}
if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != RESET))
{
__HAL_UART_CLEAR_IT(&huart2,UART_CLEAR_IDLEF); //Çå³ýÖжÏ
// ReceiveState = 1;
// IDLE 数据处理,一帧数据接收标志位置位
}
}


##### HAL库接收方式二


但是在于 HAL 中,还有一种比较常用的开启中断方法,不是直接使能中断,而是通过调用 HAL 库函数 `HAL_UART_Receive_IT` :


![在这里插入图片描述](https://img-blog.csdnimg.cn/ed22849986264c0cb2e4c43957482c11.png)


一般的使用方式步骤:


![在这里插入图片描述](https://img-blog.csdnimg.cn/dca090e7ad0f4a17922def23200ded35.png)


对于本次需要说明的问题,就是使用了 `HAL_UART_Receive_IT` 函数导致的,下文我们会说明,这里列出了基本的使用步骤。


## 二、 收发同时串口卡死?


### 2.1 问题说明


最近测试部反馈,产品有些时候的下行没反应,这里所说的下行,其实就是串口接收。


霹雳扒拉一大堆多余的省略 … … 只说几个重点:


出问题的最后现象就是串口发送正常,但是永远接收不到数据了,其他程序正常运行。


 出问题只存在于串口又有接收,又有发送的产品上。


 产品发送一般是周期性的,但是接收是随机的,无线信号串口接收,所以产品的出问题的情况也是随机的,但是数据量大起来肯定就会出现永远接收不到的问题。


### 2.2 尝试的处理方式


因为所有的一些都是按照正常流程设计的,按理来说实在是不知道为什么会这样,所以网上查询测试了好久,现在我把尝试的处理方式以及步骤记录说明一下:


#### 2.2.1 清除错误标志位


在使用 HAL 库的时候,有4个错误 flag,如下图:


![在这里插入图片描述](https://img-blog.csdnimg.cn/a87e03adba9141e9aef810a849af82ac.png)


期初还以为是某些异常错误导致的,经过网上的的一些查询,刚开始是添加了清除错误标志位:



\_\_HAL\_UART\_CLEAR\_FLAG(&hlpuart1, UART_FLAG_PE);//清标志
\_\_HAL\_UART\_CLEAR\_FLAG(&hlpuart1, UART_FLAG_FE);
\_\_HAL\_UART\_CLEAR\_FLAG(&hlpuart1, UART_FLAG_NE);
\_\_HAL\_UART\_CLEAR\_FLAG(&hlpuart1, UART_FLAG_ORE);

在需要的地方加上错误标志位清除,我是在清除串口缓存中处理的:


![在这里插入图片描述](https://img-blog.csdnimg.cn/db6c24ffda2a4b50a25ea2dde34c467c.png)


#### 2.2.2 串口溢出错误


其实串口溢出错误在上面的已经清除过标志位,因为这个问题着实搞得我头有点大,所以看到网上前人的处理方式和上面直接清除不一样,还是试了一把。


这里简单说明一下,我还特意去看了下自己的 CubeMX 设置,在设置的时候 有一个 Overrun 错误标志位,平时我们设置都不一定往下拉着看 = =!:


![在这里插入图片描述](https://img-blog.csdnimg.cn/9ef46c2bc11645d599767b25c6f9e2a6.png)


然后确定了开启了串口溢出错误检测以后,我根据网上的方式,加了一个`HAL_UART_ErrorCallback`函数,其实就类似于`HAL_UART_RxCpltCallback` 函数:


![在这里插入图片描述](https://img-blog.csdnimg.cn/66733dd1e56f4678bac3bda7a4f492e1.png)


自己加了一个 出错处理函数,其实现在看来,当然也是没有用的。


#### 2.2.3 HAL库的半双工处理?


折腾了好长一段时间,其实一开始就知道问题在于 同时收发会出问题的情况,那么继续上网找问题。


最终确定了一个问题就是:


我们都知道 STM32 串口是全双工的, STM32 HAL库在处理接收的时候会锁一下串口一会,导致变成某个短时间的“半双工”。


这个时候如果同时收发就会出现问题,最后解决的办法在这篇文章中看到了: [STM32 F103串口同时收发出现死锁问题解决办法](https://bbs.csdn.net/topics/618679757)


问题在于我们使用的`HAL_UART_Receive_IT`函数中,有对串口加锁的操作:


![在这里插入图片描述](https://img-blog.csdnimg.cn/2e4fbbea63e54afeb3bd3552d17778b3.png)  
 虽然在后面有解锁:


![在这里插入图片描述](https://img-blog.csdnimg.cn/ae89fbaeecd6468fa433879cebd91abc.png)


但是根据后期的解决方式来说,确实就是这个`HAL_UART_Receive_IT`函数的问题,最后使用的方式为,在产生一次中断以后开启的时候手动解锁:


![在这里插入图片描述](https://img-blog.csdnimg.cn/10d05e19d5244eba84a69e83077af99a.png)



void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == LPUART1){
Enocean_Data++;
if(Enocean_Data > 98)Enocean_Data = 0;
while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){
hlpuart1.RxState = HAL_UART_STATE_READY;
__HAL_UNLOCK(&hlpuart1);
}
}
else if(huart->Instance==USART1)
{
}
}


终于,串口不再卡死 , 成功!


## 三、 使用 FreeRTOS 后的新情况



> 
> 本示例说明硬件芯片为: STM32L051C8T6
> 
> 
> 


上面的处理方式,在使用裸机的时候,确实是能够完全处理好的,理论上来说,使用不使用操作系统处理方式是一样,所以在某个产品上,我确实也是这么做的。


因为 RTOS 使用消息队列保存串口数据,所以与裸机处理方式稍微有一点不同,第一步与上面提到的


HAL库接收方式一 类似:



MX_LPUART1_UART_Init();
__HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_RXNE); //


但是我们并不用继续使能 IDLE 中断,我们直接在 `stm32l0xx_it.c` 文件的中断响应函数中做数据处理:


![在这里插入图片描述](https://img-blog.csdnimg.cn/fdb0e4bce74e46948efb614d6e1c6ff4.png)


按理来说,这样就可以了,然后就是 FreeRTOS 消息队列的处理。


但是因为我们上面阐述的 HAL 库的问题(亦或者是STM32L0的问题,其实到目前我都不确定是库问题,还是芯片的某些问题),所以直接按照上面的方式处理,串口同时收发的压力测试的时候是肯定会卡死,最终结果就是: 串口发送正常,但是再也收不到数据了。


所以我还是把上面的方式加上了,我第一次的处理方式是,不管他有没有问题,我直接都是给他手动解锁一下,我甚至还每次都中断都手动清除了一些标志位:


![在这里插入图片描述](https://img-blog.csdnimg.cn/93666f10340f4e90845458910367abfa.png)


这种方式用起来不会影响程序,但是最后却发现,在压力测试下面,接收该卡死还是会卡死 = =!


虽然中途修改过多处细节,但是还是逃不过卡死的命运,于是又测试了一大段时间,然后网上找到一篇参考博文:


[STM32使用HAL库,多串口接收一段时间后程序卡死](https://bbs.csdn.net/topics/618679757)


其实上面的处理方式,在我上面的文章中参考的博文里面也提到过,就是加上一个错误中断接收:


![在这里插入图片描述](https://img-blog.csdnimg.cn/8cd39d736bdc42fd8705483e1ada6206.png)


当然也要手动实现以下 `错误中断的CallBack` 函数,下面直接上最后修改的程序。


串口中断部分:



void LPUART1_IRQHandler(void)
{

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

VFblIHzy-1715907081670)]
[外链图片转存中…(img-v9Yd4ILJ-1715907081670)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

  • 18
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值