博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
STM32 HAL库利用DMA实现串口不定长度接收方法
阅读量:4957 次
发布时间:2019-06-12

本文共 3578 字,大约阅读时间需要 11 分钟。

参考:

我这里使用的芯片是 F1 系列的,主要是利用 DMA 数据传输方式实现的,在配置工程的时候要注意配置好 DMA,并开启中断。

  

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

  

1、利用STM32 cubemx 建立一个工程,工程建立请参考我以前的文章:

2、利用STM32 cubemx 生成代码后,我们先定义一些变量来使用

/*    自己添加代码部分    */volatile uint8_t rx_len=0;                            //接收数据长度volatile uint8_t recv_end_flag=0;                     //接收完成标记位uint8_t  rx_buffer[100];                              //接收缓存char          BUFFER_SIZE=100;                        //不定长数据的最大长度,设置为100则最大长度为100

这里为什么要定义volatile 关键字呢?

       主要是因为volatile 关键字提醒编译器定义的变量是易变的,编译后的程序每次需要存储或读取该变量时,会直接从变量地址读取数据。在中断或多线程中使用volatile关键字可以避免不同优化等级时程序出错,提高程序的鲁棒性。

接着对串口初始化添加一些代码,程序如下:

/* USART2 init function */static void MX_USART2_UART_Init(void){  huart2.Instance = USART2;  huart2.Init.BaudRate = 115200;  huart2.Init.WordLength = UART_WORDLENGTH_8B;  huart2.Init.StopBits = UART_STOPBITS_1;  huart2.Init.Parity = UART_PARITY_NONE;  huart2.Init.Mode = UART_MODE_TX_RX;  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;  huart2.Init.OverSampling = UART_OVERSAMPLING_16;  if (HAL_UART_Init(&huart2) != HAL_OK)  {    _Error_Handler(__FILE__, __LINE__);  }        /*    自己添加代码部分    */   __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);        //使能idle中断   HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE);  //打开DMA接收,数据存入rx_buffer数组中。}

3、接收函数我写在了另一个文件上,其他文件要用到上面 main文件里面定义的变量就要声明一个外部变量

extern volatile uint8_t rx_len;extern volatile uint8_t recv_end_flag;extern uint8_t  rx_buffer[100];extern char      BUFFER_SIZE;

4、接着修改串口中断服务函数,在串口中断服务函数里添加接收代码,代码如下:

void USART2_IRQHandler(void){  /* USER CODE BEGIN USART2_IRQn 0 */        /*    自己添加代码部分    */    uint32_t tmp_flag = 0;    uint32_t temp;    tmp_flag =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);   //获取IDLE标志位    if((tmp_flag != RESET))  //idle标志被置位    {         __HAL_UART_CLEAR_IDLEFLAG(&huart2);  //清除标志位        temp = huart2.Instance->SR;    //清除状态寄存器SR(F0的HAL库USART_TypeDef结构体中名字为ISR),读取SR(F0中为DSR)寄存器可以实现清除SR寄存器的功能        temp = huart2.Instance->DR;    //读取数据寄存器中的数据        HAL_UART_DMAStop(&huart2);              temp  = hdma_usart2_rx.Instance->CNDTR;   //获取DMA中未传输的数据个数,NDTR寄存器分析见下面        rx_len =  BUFFER_SIZE - temp;         //总计数减去未传输的数据个数,得到已经接收的数据个数        recv_end_flag = 1;                //接受完成标志位置1         }   /* USER CODE END USART2_IRQn 0 */   HAL_UART_IRQHandler(&huart2);  /* USER CODE BEGIN USART2_IRQn 1 */  /* USER CODE END USART2_IRQn 1 */}

  上面的 CNDTR 寄存器在 DMA 通道结构体中定义了 CNDTR 寄存器,这个不同的芯片HAL库里面定义的命名有点不同,有兴趣的可以自己去查看一下,那为什么是未传输的数据数呢,STM32的中文手册给出了该寄存器的具体说明。(注意:建立工程的时候要添加串口TX RX 的DMA通道,以及打开DMA中断)

/**   * @brief DMA Controller  */typedef struct{  __IO uint32_t CCR;  __IO uint32_t CNDTR;  __IO uint32_t CPAR;  __IO uint32_t CMAR;} DMA_Channel_TypeDef;

寄存器说明如下:

5、接着编写接收处理函数,代码如下:

/***************************************************************    *函数名:Data_Turn    *输  入:无    *说  明:串口接收完成,返回串口查看接收情况    *返回值:无  **/void Data_Turn(void){  if(recv_end_flag ==1)          {      printf("rx_len=%d\r\n",rx_len);                           //打印接收长度      HAL_UART_Transmit(&huart2,rx_buffer, rx_len,200);        //接收数据打印出来      for(uint8_t i=0;i

 6.再主函数里的的while循环里再次打开DMA中断接收

HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE);            //重新打开DMA接收

 

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

 

补充一点最近新发现的关于串口中断接收的问题:

  串口中断接收如果使用HAL库的中断接收函数,接收到的数据量远小于设定要接收的数据量,串口一直处于Busy状态,会出现接收死循环的情况,接收的数据跟设定不符会出错

注意:

  测试过程中发现,用接收串口助手的所有数据都没问题,不过接收模块的不定长数据时,如果这个数据之间包含回车换行符会接收不全,例如:AT\r\nOK\r\n,这个就只能接收到AT\r,具体什么原因造成的还没找出原因,有知道的可以告诉我一下。

  不过如果是串口助手发这串数据下来,却又可以全部接收完成,想不通。为了解决这个问题,我又找出了一个中断接收的方法,可以实现了不管中间有没有回车,都可以接收完成。传输门:

 

转载于:https://www.cnblogs.com/xingboy/p/9714907.html

你可能感兴趣的文章
简易爬虫(爬取本地数据)
查看>>
python 进程间通信
查看>>
深拷贝 vs 浅拷贝 释放多次
查看>>
Javascript 有用参考函数
查看>>
点群的判别(三)
查看>>
GNSS 使用DFT算法 能量损耗仿真
查看>>
【转】Simulink模型架构指导
查看>>
MYSQL数据库的导出的几种方法
查看>>
SQL Server-5种常见的约束
查看>>
硬件之美
查看>>
[转载]java开发中的23种设计模式
查看>>
表格的拖拽功能
查看>>
函数的形参和实参
查看>>
文字过长 用 ... 表示 CSS实现单行、多行文本溢出显示省略号
查看>>
1Caesar加密
查看>>
【TP SRM 703 div2 500】 GCDGraph
查看>>
MapReduce 重要组件——Recordreader组件 [转]
查看>>
webdriver api
查看>>
apache 实现图标缓存客户端
查看>>
MediaWiki左侧导航栏通过特殊页面就可以设置。
查看>>