STM32·

STM32篇——串口DMA通信

ronger

ronger

8400 1

1. 前期准备

  • 安装好STM32CubeMX
  • 安装好clion

串口DMA通信的优点:

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

2. 创建项目

  1. 设置SWD调试端口

  1. 设置串口

  1. 设置串口DMA

  1. 使能串口全局中断

  1. 设置系统时钟为72MHz

生成项目即可。

3. 编辑代码

实现功能为:

通过DMA串口发送字符串。

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

//main.c添加数组声明
#define Buff_Size 100
uint8_t rx_buff[Buff_Size] = "hell0,uart DMA\r\n";
//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!\r\n");

输出结果如下:

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

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

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

这两种方法的缺点为,需要主程序来判断和处理,对主程序造成不小压力。 STM32 单片机空闲检测中断可以很好的解决这个问题,他的工作原理为:

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

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

main.cstatic 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 DMA\r\n";
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!\r\n",rx_cnt);
            for(int i = 0;i<rx_cnt;i++) printf_DMA("%c",rx_buff[i]);
            printf_DMA("\r\n");

            rx_cnt =0;//清除接收长度
        }
    /* USER CODE BEGIN 3 */
    }

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

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

7. 小结

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

所属系列

从当前文章继续阅读它所在合集中的前后内容。

关于我和 Hugh 学嵌入式开发这件事 —— STM32 篇 第 17 / 17 篇
查看合集

> 本作品集内教程基于 [Hugh](https://rymcu.com/user/hugh) 的创作基础上进行修订发布 关于我和 Hugh 学嵌入式开发这件事

上一篇
STM32篇——SPI_FLASH模拟U盘FATFS
当前已是该系列最后一篇

相关文章

优先推荐同专题、同标签和同作者内容,补足热门文章。

评论 1

登录 后参与评论

评论 1

2692430508
26924305088月15日 11:53

测试测试