打印
[STM32L4]

STM32 HAL库 串口中断接收不定长问题

[复制链接]
11728|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
三草木|  楼主 | 2018-5-19 17:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
最近接触使用了STM32L4系列产品(只支持HAL库),目前其它功能都调试差不多了。就在串口中断接收上有些问题。
希望过来人对操作方式给点指点指示,万分感激!
目前大致流程是这样的:
1、首先串口初始化,部分代码如下:
  Huart1.Instance        = COM1;
  Huart1.Init.BaudRate   = 115200;
  Huart1.Init.WordLength = UART_WORDLENGTH_8B;
  Huart1.Init.StopBits   = UART_STOPBITS_1;
  Huart1.Init.Parity     = UART_PARITY_NONE;
  Huart1.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
  Huart1.Init.Mode       = UART_MODE_TX_RX;
  if (HAL_UART_Init(&Huart1) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_NVIC_SetPriority(COM1_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(COM1_IRQn);
  HAL_UART_Receive_IT(&Huart1,aRxBuffer1,1);

2、中断函数处理代码:
void USART1_IRQHandler(void)  
{  
    HAL_UART_IRQHandler(&Huart1);
}  

3、中断回调函数(接收数据、并开启下个接收中断),部分代码:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  uint8_t ret = HAL_OK;       
  if(huart->Instance == COM1)   
  {
    //Temp = aRxBuffer1[0];   
    do  
    {  
      ret = HAL_UART_Receive_IT(&Huart1,aRxBuffer1,1); //重新使能中断  
    }while(ret != HAL_OK);  //等待接收所有字符   
    //HAL_UART_Receive_IT(&Huart1,aRxBuffer1,1);  
    usart1testcnt++;   
    Temp = aRxBuffer1[0];
    //此处把数据装到一个数组DATABUF中;
  }

现在的现象是这样的:
1、如果上位机给发送少于10个字节,没有发现会丢数据;
2、如果上位机发送多余10个字节,比如50个字节,就会在10个字节之后又不同程度的丢数,隔那么几个字节丢一个字节,暂未发现连续2个字节丢失;(已经验证与发送时间间隔无关)
3、如果中断每次接收100个字节//HAL_UART_Receive_IT(&Huart1,aRxBuffer1,100);  上位机发送100个字节都能接收完整不会丢失;

有点疑问:
1、为什么前10几个字节不会丢数?库代码正在研究中;

最重要的问题:
就HAL库,到底该如何实现不定长字节接收?望大家指导!各种思路都可以表达!
       




沙发
mintspring| | 2018-5-19 19:28 | 只看该作者
好像有人问过这个问题,你搜搜看。

使用特权

评论回复
板凳
三草木|  楼主 | 2018-5-20 21:12 | 只看该作者
mintspring 发表于 2018-5-19 19:28
好像有人问过这个问题,你搜搜看。

多谢了,琢磨出一个解决办法来!以后有时间再细研究!

使用特权

评论回复
地板
lydzzhx| | 2018-6-15 21:37 | 只看该作者
加我QQ吧:46439147 我也在研究不定长串口中断接收

使用特权

评论回复
5
pattywu| | 2018-6-16 12:16 | 只看该作者
HAL库,呵呵。

使用特权

评论回复
6
wandersky| | 2018-6-17 13:53 | 只看该作者
使用DMA环形缓冲接收

使用特权

评论回复
7
aizaixiyuanqian| | 2018-6-17 20:56 | 只看该作者
可以参考原子哥的,用标准库也行啊,采用定时器间隔方式。

使用特权

评论回复
8
admvip| | 2018-6-19 11:14 | 只看该作者
DMA加串口空闲中断,很好用

使用特权

评论回复
9
huzi2099| | 2018-6-19 12:17 | 只看该作者
你的中断处理上有些问题,HAL_UART_RxCpltCallback本身就是中断处理的一部分,在这个函数里调用 HAL_UART_Receive_IT(&Huart1,aRxBuffer1,1);是不妥的

HAL_UART_Receive_IT这个函数启动中断接收数据,设置完就返回了.

如果用时间间隔来分包要在接收字节的同时查看与上次接收数据的时间差

使用特权

评论回复
10
renzheshengui| | 2018-6-19 13:36 | 只看该作者
试试链表发送呢

使用特权

评论回复
11
宋晓鹏| | 2018-6-29 15:09 | 只看该作者
请问解决了吗? 我遇到了一样的问题,我想用dma+空闲中断接收,但是不知道怎么做

使用特权

评论回复
12
宋晓鹏| | 2018-6-29 15:13 | 只看该作者
admvip 发表于 2018-6-19 11:14
DMA加串口空闲中断,很好用

求教 具体怎么做呢? 我没有找到相应的接口

使用特权

评论回复
13
admvip| | 2018-6-29 15:47 | 只看该作者
宋晓鹏 发表于 2018-6-29 15:13
求教 具体怎么做呢? 我没有找到相应的接口

以下是我学习DMA接收不定长数据的学习笔记,没有完整的程序,但是根据笔记中的核心代码,编写相应的程序没什么难度。

使用串口DMA方式接收不定长数据学习笔记
编程思路:
1、通过检测串口空闲中断来检测一帧不定长数据接收完毕
2、当空闲中断发生后,停止DMA接收数据,检测DMA剩余接收字节和缓冲区固定长度计算已接收的字符数
3、数据处理后,恢复相关中断标志、变量等
4、继续打开串口DMA接收,为下一帧数据的接收做准备(如果DMA设置为循环方式的话,开启命令可以省略,但是计数器需要处理)


具体代码如下:

首先定义一个串口收发的缓冲区和相应的标志变量(如果只接收的话,TX可以不定义)

//用户全局变量定义
#ifndef Buffer_Size
        #define        Buffer_Size                255
#endif
uint8_t USART_TxBuffer[Buffer_Size];                //定义32字节串口发送缓冲区
uint8_t USART_RxBuffer[Buffer_Size];                //定义32字节串口接收缓冲区
uint8_t USART_Rx_OK = 0;                                                                //定义串口接收完成标志
uint8_t USART_Buffer_index;                                                        //缓冲区索引



在主程序初始化后开启空闲中断和DMA接收

__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);    //使能空闲中断
  HAL_UART_Receive_DMA(&huart1,USART_RxBuffer,Buffer_Size);        //开启串口DMA接收,开始需运行一次,后面会自动打开



在是stm32f0xx_it.c里
先将中断中用到的全局变量外部引用声明一下,免得编译时出错

#ifndef Buffer_Size
        #define        Buffer_Size                255
#endif

extern uint8_t USART_TxBuffer[Buffer_Size];                //定义32字节串口发送缓冲区
extern uint8_t USART_RxBuffer[Buffer_Size];                //定义32字节串口接收缓冲区
extern uint8_t USART_Rx_OK;                                                                //定义串口接收完成标志
extern uint8_t USART_Buffer_index;                                                        //缓冲区索引


在串口中断处理函数里加入判断是否是空闲中断的代码,如果发生了串口空闲中断,则进行进一步处理,并将串口完成全局标志变量置位
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  uint32_t temp;
  
        if(huart1.Instance == USART1)                //判断是否是串口1
        {
                if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET)        //判断是否是串口空闲中断
                {
                        __HAL_UART_CLEAR_IDLEFLAG(&huart1);                        //先清除串口空闲中断标志位
                        HAL_UART_DMAStop(&huart1);                                //停止DMA接收
                        temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);        //取DMA 未接收 的数据个数
                        USART_Buffer_index = Buffer_Size - temp;        //通过串口接收缓冲区长度计算实际接收的数据个数
                        USART_Rx_OK = 1;                                //将串口不定长接收标志位置位
               
                }
       
        }
       
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
       
  /* USER CODE END USART1_IRQn 1 */
}


检测串口完成标志,如置位则进一步处理,这段程序可以放在主循环里,也可以放在定时器中断或者系统滴答中断里,如果放在中断里面执行,需要确保代码运行时间不要超过中断的总时长,以免影响中断功能。在中断里尽量少使用printf()等运行时间长的函数。可以使用DMA发送函数来解决运行时长的问题。

if(USART_Rx_OK == 1)                //串口传输结束标志
        {
                printf("接收字符数量:%d\r\n",USART_Buffer_index);   //此次接收到了多少数据
                for(i=0;i<USART_Buffer_index;i++)
                {
                        printf("%c",USART_RxBuffer);                //用户数据处理
                }
                for(i=0;i<Buffer_Size;i++)                        //缓冲区清零,实际有准确的接收字符个数后,可以不清零
                {
                        USART_RxBuffer = 0;
                }
                USART_Buffer_index = 0;
                USART_Rx_OK = 0;
        }
HAL_UART_Receive_DMA(&huart1,USART_RxBuffer,Buffer_Size);        //打开串口DMA接收,为接收下一帧数据做准备

使用特权

评论回复
14
小欧阳11| | 2018-7-13 19:48 | 只看该作者
我最近也遇到了这样的问题 楼主有没有解决啊 不过我是stm32l0系列的 求加个qq咨询一下1658348073

使用特权

评论回复
15
小欧阳11| | 2018-7-19 17:01 | 只看该作者
这个问题我已经搞定了 用串口中断和定时器中断

使用特权

评论回复
16
caijie001| | 2018-7-19 18:56 | 只看该作者
使用DMA+空闲中断,如果必要的话,可以使用环形缓冲区

使用特权

评论回复
17
shipeng1989| | 2018-11-21 18:39 | 只看该作者
为什么没人告诉我IDLE中断不会调用中断回调函数HAL_UART_RxCpltCallback

使用特权

评论回复
18
亡羊补牢| | 2024-12-10 11:23 | 只看该作者
3、如果中断每次接收100个字节//HAL_UART_Receive_IT(&Huart1,aRxBuffer1,100);  上位机发送100个字节都能接收完整不会丢失;

HAL_UART_Receive_IT好像是发送字节数必须和接收的字节数一样,多了少了都不行,除非你加结束符判断 \n \r等,但是好多命令加个结束符怪怪的,客户不接受

使用特权

评论回复
19
AloneKaven| | 2024-12-10 11:33 | 只看该作者
这个直接被写成函数了,很方便用

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

39

帖子

2

粉丝