硬汉嵌入式论坛

 找回密码
 立即注册
查看: 5404|回复: 4
收起左侧

[其它] HAL库串口接收中断解决方案

[复制链接]

7

主题

15

回帖

36

积分

新手上路

积分
36
发表于 2020-12-27 04:28:58 | 显示全部楼层 |阅读模式
本帖最后由 quhaton 于 2020-12-27 04:54 编辑

本文只讨论HAL库串口接收中断解决方案,所以不提DMA的方案。之所以讨论HAL库串口接收中断,原因有:
1.HAL库的串口封装逻辑有点复杂,逻辑不够简洁,2.HAL库封装的串口接收回调是“接收定长数据”后回调,所以串口接收不定长数据就成了问题。
解决方案之一:自己实现串口接收中断处理,即可简化逻辑,又可实现不定长数据接收
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
        if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)!= RESET)
        {
                buff[index++]=huart1.Instance->DR;//不同的芯片不同的库版本,DR寄存器的名字可能不一样;此处为了简洁,暂时未作数组溢出限制
                return;
        }
        __HAL_UART_CLEAR_PEFLAG(&huart1);//此处清除多种中断,包括常见的 UART_FLAG_ORE:  Overrun Error flag,不同的芯片不同的库版本,清楚中断标志的函数/宏可能不一样
        return;
  /* USER CODE END USART1_IRQn 0 */
        
  //HAL_UART_IRQHandler(&huart1);//不能让这个自动生成的函数执行,上面的语句已经加了 return,所以这一行屏蔽不屏蔽都无所谓
        
  /* USER CODE BEGIN USART1_IRQn 1 */
        
        /* USER CODE END USART1_IRQn 1 */
}

如此,便跳过了HAL_UART_IRQHandler对串口的各种操作,完全自己控制接收,可以利用以前 使用标志库时的处理方法。
这里还有一个问题:何时知道一包数据接收完成?
解决方案是:开启串口空闲中断 或者 开启一个定时器(开启定时器的作用是检测串口接收数据空闲了多久),传输结束一定的时间,在中断里做个标志。
说说利用空闲中断吧,代码如下:
开启空闲中断部分:
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
        __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //开启接收中断
        __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //开启空闲中断
  /* USER CODE END 2 */

中断处理部分:
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
        if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)!= RESET)
        {
                buff[index++]=huart1.Instance->DR;//不同的芯片不同的库版本,DR寄存器的名字可能不一样;此处为了简洁,暂时未作数组溢出限制
                return;
        }
       if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!= RESET)
        {
                __HAL_UART_CLEAR_PEFLAG(&huart1);
                receive_over_flag=1;
                return;
        }

        __HAL_UART_CLEAR_PEFLAG(&huart1);//此处清除多种中断,包括常见的 UART_FLAG_ORE:  Overrun Error flag,不同的芯片不同的库版本,清除中断标志的函数/宏可能不一样
        return;
  /* USER CODE END USART1_IRQn 0 */
        
//HAL_UART_IRQHandler(&huart1);//不能让这个自动生成的函数执行,上面的语句已经加了 return,所以这一行屏蔽不屏蔽都无所谓
        
  /* USER CODE BEGIN USART1_IRQn 1 */
        
        /* USER CODE END USART1_IRQn 1 */
}

以上方案经验证,可行。
解决方案之二:使用HAL库提供的API实现串口接收中断处理,可实现不定长数据接收
网上许多人说HAL库的串口接收只能接收定长数据,他们这么说的原因是:串口buff数据存满时才调用回调函数HAL_UART_RxCpltCallback()。
这是对HAL库串口接收逻辑的误解,我们完全可以在任意时刻提取数据加以处理,不必理会回调函数HAL_UART_RxCpltCallback()。
那么什么时候处理数据?还是利用空闲中断或者定时器,得到一包数据接收完成的信息,然后随时可以处理接收到的数据,而不必等待回调函数HAL_UART_RxCpltCallback()的执行。
需要注意的是:串口buff数据存满时会会关闭串口中断,然后调用HAL_UART_RxCpltCallback(),所以,程序中必须要做相应的处理,方法有两种:一种是限制接收计数器(huart的成员变量之一)的值,以防止触发 关闭中断 ,另一种是在HAL_UART_RxCpltCallback()中重新开启中断,可以调用函数 HAL_UART_Receive_IT(),代码如下:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef*UartHandle)//处理:接收缓冲区满了,中断被关闭了
{
    HAL_UART_Receive_IT(&huart1, (uint8_t *)RxBuff, 100); //复位多种标志,重新开启中断
}


分享一点思想经验,写的很乱,见谅。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107759
QQ
发表于 2020-12-27 09:32:48 | 显示全部楼层
谢谢分享。
回复

使用道具 举报

6

主题

646

回帖

664

积分

金牌会员

积分
664
QQ
发表于 2020-12-29 10:42:24 | 显示全部楼层
串口感觉还是使用LL库,按照以前标准库的写就可以了,比HAL库好
回复

使用道具 举报

4

主题

160

回帖

172

积分

初级会员

积分
172
发表于 2020-12-29 11:48:00 | 显示全部楼层
不定长,最好使用 DMA 循环接收 配合 空闲中断。 完美的方案
回复

使用道具 举报

8

主题

145

回帖

169

积分

初级会员

积分
169
发表于 2021-1-6 22:15:28 | 显示全部楼层
fyyxxm 发表于 2020-12-29 11:48
不定长,最好使用 DMA 循环接收 配合 空闲中断。 完美的方案

空闲中断也有个蛋疼的地方是很多设备发的一帧数据并不是连续,有时候两个byte之间的时间超过了1个byte的时间长度,意外触发空闲中断,还得去额外处理。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-6-13 05:56 , Processed in 0.276782 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表