你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32 HAL库利用DMA实现串口不定长度接收方法

[复制链接]
STMCU小助手 发布时间:2022-1-27 22:31
我这里使用的芯片是 F1 系列的,主要是利用 DMA 数据传输方式实现的,在配置工程的时候要注意配置好 DMA,并开启中断。
  
1433771-20190322131402229-2018486803.png

  如果出现数据长度对,可是数据接收不完整,把Memory勾选即可:

1433771-20190806091840276-1993520895.png
  
1、利用STM32 cubemx 建立一个工程
2、利用STM32 cubemx 生成代码后,我们先定义一些变量来使用
  1. /*    自己添加代码部分    */
  2. volatile uint8_t rx_len=0;                            //接收数据长度
  3. volatile uint8_t recv_end_flag=0;                     //接收完成标记位
  4. uint8_t  rx_buffer[100];                              //接收缓存
  5. char          BUFFER_SIZE=100;                        //不定长数据的最大长度,设置为100则最大长度为100
复制代码

这里为什么要定义volatile 关键字呢?
       主要是因为volatile 关键字提醒编译器定义的变量是易变的,编译后的程序每次需要存储或读取该变量时,会直接从变量地址读取数据。在中断或多线程中使用volatile关键字可以避免不同优化等级时程序出错,提高程序的鲁棒性。
接着对串口初始化添加一些代码,程序如下:
  1. /* USART2 init function */
  2. static void MX_USART2_UART_Init(void)
  3. {

  4.   huart2.Instance = USART2;
  5.   huart2.Init.BaudRate = 115200;
  6.   huart2.Init.WordLength = UART_WORDLENGTH_8B;
  7.   huart2.Init.StopBits = UART_STOPBITS_1;
  8.   huart2.Init.Parity = UART_PARITY_NONE;
  9.   huart2.Init.Mode = UART_MODE_TX_RX;
  10.   huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  11.   huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  12.   if (HAL_UART_Init(&huart2) != HAL_OK)
  13.   {
  14.     _Error_Handler(__FILE__, __LINE__);
  15.   }
  16.    
  17.     /*    自己添加代码部分    */
  18.    __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);        //使能idle中断
  19.    HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE);  //打开DMA接收,数据存入rx_buffer数组中。

  20. }
复制代码
3、接收函数我写在了另一个文件上,其他文件要用到上面 main文件里面定义的变量就要声明一个外部变量
  1. extern volatile uint8_t rx_len;
  2. extern volatile uint8_t recv_end_flag;
  3. extern uint8_t  rx_buffer[100];
  4. extern char      BUFFER_SIZE;
复制代码

4、接着修改串口中断服务函数,在串口中断服务函数里添加接收代码,代码如下:
  1. void USART2_IRQHandler(void)
  2. {
  3.   /* USER CODE BEGIN USART2_IRQn 0 */
  4.    
  5.     /*    自己添加代码部分    */
  6.     uint32_t tmp_flag = 0;
  7.     uint32_t temp;
  8.     tmp_flag =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);   //获取IDLE标志位
  9.     if((tmp_flag != RESET))  //idle标志被置位
  10.     {
  11.         __HAL_UART_CLEAR_IDLEFLAG(&huart2);  //清除标志位
  12.         temp = huart2.Instance->SR;    //清除状态寄存器SR(F0的HAL库USART_TypeDef结构体中名字为ISR:USART Interrupt and status register),读取SR可以清楚该寄存器
  13.         temp = huart2.Instance->DR;    //读取数据寄存器中的数据,读取DR(F0中为RDR:USART Receive Data register)
  14.         HAL_UART_DMAStop(&huart2);      temp  = hdma_usart2_rx.Instance->CNDTR;   //获取DMA中未传输的数据个数,NDTR寄存器分析见下面
  15.         rx_len =  BUFFER_SIZE - temp;         //总计数减去未传输的数据个数,得到已经接收的数据个数
  16.         recv_end_flag = 1;                //接受完成标志位置1   
  17.      }

  18.   /* USER CODE END USART2_IRQn 0 */
  19.   HAL_UART_IRQHandler(&huart2);
  20.   /* USER CODE BEGIN USART2_IRQn 1 */

  21.   /* USER CODE END USART2_IRQn 1 */
  22. }
复制代码

  上面的 CNDTR 寄存器在 DMA 通道结构体中定义了 CNDTR 寄存器,这个不同的芯片HAL库里面定义的命名有点不同,有兴趣的可以自己去查看一下,那为什么是未传输的数据数呢,STM32的中文手册给出了该寄存器的具体说明。(注意:建立工程的时候要添加串口TX RX 的DMA通道,以及打开DMA中断)
  1. /**
  2.   * @brief DMA Controller
  3.   */
  4. typedef struct
  5. {
  6.   __IO uint32_t CCR;
  7.   __IO uint32_t CNDTR;
  8.   __IO uint32_t CPAR;
  9.   __IO uint32_t CMAR;
  10. } DMA_Channel_TypeDef;
复制代码

寄存器说明如下:

1433771-20180927183749492-1406746456.png

5、接着编写接收处理函数,代码如下:
  1. /***************************************************************
  2.     *函数名:Data_Turn
  3.     *输  入:无
  4.     *说  明:串口接收完成,返回串口查看接收情况
  5.     *返回值:无
  6.   **/
  7. void Data_Turn(void)
  8. {

  9.   if(recv_end_flag ==1)        
  10.   {
  11.       printf("rx_len=%d\r\n",rx_len);                           //打印接收长度
  12.       HAL_UART_Transmit(&huart2,rx_buffer, rx_len,200);        //接收数据打印出来
  13.       for(uint8_t i=0;i<rx_len;i++)
  14.       {
  15.          rx_buffer[i]=0;   //清接收缓存
  16.       }
  17.       rx_len=0;        //清除计数
  18.       recv_end_flag=0;    //清除接收结束标志位
  19.    }
  20. }
复制代码

6.再主函数里的的while循环里再次打开DMA中断接收
  1. HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE);            //重新打开DMA接收
复制代码

运行效果如下图,我的代码是接收到Do-0:1字符串,判断字符串,返回我需要的字符串,效果正确。

补充一点最近新发现的关于串口中断接收的问题:
  串口中断接收如果使用HAL库的中断接收函数,接收到的数据量远小于设定要接收的数据量,串口一直处于Busy状态,会出现接收死循环的情况,接收的数据跟设定不符会出错
注意:
  测试过程中发现,用接收串口助手的所有数据都没问题,不过接收模块的不定长数据时,如果这个数据之间包含回车换行符会接收不全,例如:AT\r\nOK\r\n,这个就只能接收到AT\r,具体什么原因造成的还没找出原因,有知道的可以告诉我一下。

1433771-20180927183233020-1397923511.png
收藏 评论0 发布时间:2022-1-27 22:31

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版