问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

STM32篇——串口DMA通信

创作时间:
作者:
@小白创作中心

STM32篇——串口DMA通信

引用
1
来源
1.
https://rymcu.com/article/1652

本文详细介绍了STM32单片机的串口DMA通信的实现方法。文章从前期准备、创建项目、编辑代码、编译下载等多个步骤,逐步讲解了如何通过DMA实现串口数据的发送和接收。此外,还介绍了如何添加printf_DMA函数以及处理不定长串口数据的方法。

1. 前期准备

  • 安装好STM32CubeMX
  • 安装好clion

串口DMA通信的优点:
串口接收数据时,主程序跳转至串口中断函数中完成数据的接收和存储。如果接收数据比较频繁时,会占用CPU大量的时间来接收这些数据。而DMA外设的作用就是为了解放CPU,由DMA来接收数据,CPU可以干别的重要事情,当DMA接收完成数据后告诉CPU即可。

2. 创建项目

  1. 设置SWD调试端口
  2. 设置串口
  3. 设置串口DMA
  4. 使能串口全局中断
  5. 设置系统时钟为72MHz

生成项目即可。

3. 编辑代码

实现功能为:
通过DMA串口发送字符串。
打开创建的工程,找到源文件夹Src的main.c,并在while(1)循环中添加添加串口发送语句。
main()函数中,其他代码均为系统生成的初始化代码,无需理会。

//main.c添加数组声明
#define Buff_Size 100
uint8_t rx_buff[Buff_Size] = "hell0,uart DMArn";
//main函数添加DMA发送代码   
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)rx_buff, sizeof(rx_buff));
HAL_Delay(1000);

4. 编译下载

将程序下载至开发板,并将开发板连接至PC,打开串口调试助手RYCOM,并设置为:
115200+8+N+1,接收结果如下。

5. 添加printf_DMA函数

在代码调试过程中,经常使用printf函数输出调试信息。我们加printf_DMA函数,通过串口输出调试信息。

  1. main.c文件中添加头文件及函数声明
#include "stdio.h"
#include "stdarg.h"
#include "stm32f1xx_hal_uart.h"
void printf_DMA(const char *format,...);
  1. 编写printf_DMA函数,代码添加至main.c
//添加DMA串口printf函数
uint8_t _dbg_Buff[150];
void printf_DMA(const char *format,...)
{
    uint32_t length;
    va_list args;
    uint8_t  temp=0;
    va_start(args, format);
    length = vsnprintf((char*)_dbg_Buff, sizeof(_dbg_Buff)+1, (char*)format, args);
    va_end(args);
    HAL_UART_Transmit_DMA(&huart1,_dbg_Buff,length);
    //等待串口发送完成,注意是串口发送完成,不是DMA传输完成
    while(!__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC));
    //如果没有上面这条语句,连续调用printf_DMA时可能输出错误。
}
  1. printf函数输出示例

main.c文件main()函数添加代码如下:

printf_DMA("DMA UART Printf test!rn");

输出结果如下:

6. 接收处理不定长的串口数据

单片机串口接收不定长数据时,必须面对的一个问题为,怎么判断这一包数据接收完成了呢?常见的方法主要有以下两种:

  • 1.在接收数据时启动一个定时器,在指定时间间隔内没有接收到新数据,则认为数据接收完成;
  • 2.在数据中加入帧头、帧尾,通过在程序中判断是否接收到帧尾来确定数据接收完毕。

这两种方法的缺点为,需要主程序来判断和处理,对主程序造成不小压力。

STM32单片机空闲检测中断可以很好的解决这个问题,他的工作原理为:
当STM32的串口接收完一包数据后,会产生一个空闲中断。这个中断在串口其他任何状态都不产生,只会在接收完一包数据后才会产生,一包数据可以是1个字节或者多个字节。因此,我们可以在这个空闲中断函数中,设置一个接收完成标志位。那么,我们只需要在主程序中检测这个标志位就知道数据是否接收完成了。具体应该怎么操作呢?在上述工程基础上,添加相应代码实现接收功能。

  1. 使能串口接收、空闲中断

在main.c的static void MX_USART1_UART_Init(void)函数最后添加代码如下:

/* USER CODE BEGIN USART1_Init 2 */
  //开启空闲中断
  __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
  //初始化时打开DMA接收,确保第一包数据能够收到!
  HAL_UART_Receive_DMA(&huart1,rx_buff,Buff_Size);
  /* USER CODE END USART1_Init 2 */
  1. 添加串口接收全局变量

main.h中声明变量:

/* USER CODE BEGIN ET */
//全局变量定义
#define Buff_Size 100
extern uint8_t rx_buff[100];  //接收缓存
extern uint8_t rx_done; //接收完成标志
extern uint8_t rx_cnt;//接收数据长度
/* USER CODE END ET */

main.c中变量初始化:

/* USER CODE BEGIN PTD */
uint8_t rx_buff[Buff_Size] = "hell0,uart DMArn";
uint8_t rx_done = 0; //接收完成标志
uint8_t rx_cnt = 0;//接收数据长度
/* USER CODE END PTD */
  1. 修改串口中断处理函数

stm32f1xx_it.c文件中找到串口中断函数void USART1_IRQHandler(void),修改代码如下:

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
    uint32_t tmp_flag = 0;
    uint32_t temp;
    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)==SET) //获取IDLE标志位
    {
        rx_done = 1;  // 接受完成标志位置1
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
        temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
        rx_cnt =  Buff_Size - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
        HAL_UART_DMAStop(&huart1); //关闭DMA
        HAL_UART_Receive_DMA(&huart1,rx_buff,Buff_Size);//重新打开DMA接收
    }
  /* USER CODE END USART1_IRQn 1 */
}
  1. 处理不定长数据示例

main函数中打印接收到的数据以及长度,代码如下:

while (1)
    {
    /* USER CODE END WHILE */
        if(rx_done == 1)//判读是否接收完成
        {
            rx_done = 0;//清除接收标志
            //数据处理,打印接收长度、接收的数据
            printf_DMA("length of rx data: %d!rn",rx_cnt);
            for(int i = 0;i<rx_cnt;i++) printf_DMA("%c",rx_buff[i]);
            printf_DMA("rn");
            rx_cnt =0;//清除接收长度
        }
    /* USER CODE BEGIN 3 */
    }

使用串口调试助手发送任意数据,查看接收情况如下:

如上图所示,通RYCOM助手发送数据给单片机,单片机正确接收,并正确打印数据及长度。

7. 小结

本章实现了单片机串口USART1发送数据,并定义printf_DMA函数,后续调试代码可直接使用。学习了如何通过串口接收不定长数据,并实现数据的处理。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号