STM32CubeMX—串口空闲中断+DMA接收

2023-11-02

一.串口中断通信

串口中断方式的特点

  1. 发送数据时,将一字节数据放入数据寄存器DR;接收数据时,将DR的内容存放到用户存储区;
  2. 中断方式不必等待数据的传输过程,只需要在每字节数据收发完成后,由中断标志位触发中断,在中断服务程序中放入新的一字数据或者读取接收到 的一字节数据;
  3. 在传输数据量较大,且通信波特率较高(大于38400)时,如果采用中断方式,每收发一个字节的数据,CPU都会被打断,造成CPU无法处理其他事务。因此在批量数据传输,通信波特率较高时,建议采用DMA方式。

 中断代码如下

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
  {
    if (rxReceiveCount1 < 20)
    {
      rxBuffer1[rxReceiveCount1]=(uint8_t)(huart1.Instance->DR);
      ++rxReceiveCount1;
    }
    else
    {
      rxReceiveCount1 = 0;
    }
    __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
  }
  else if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)
  {

    __HAL_UART_CLEAR_IDLEFLAG(&huart1);
    uint32_t tmp1;
    tmp1 = huart1.Instance->SR;
    tmp1 = huart1.Instance->DR;


    rxFrameFlag1 = 1;
  }

  /* USER CODE END USART1_IRQn 0 */

  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

/**

HAL_UART_Receive_IT(&huart1, (uint8_t *)&rc_data, 1)的作用是:

  1. 启动UART1的中断接收模式:该代码将启动UART1的中断接收模式,使UART1开始等待接收数据。

  2. 指定接收缓冲区:将rc_data变量的地址作为接收数据的缓冲区地址。这意味着当UART1接收到数据时,数据将存储在rc_data变量中。

  3. 指定缓冲区大小:1表示接收数据的大小为1个字节。这意味着当UART1接收到1个字节的数据时,将触发一个接收中断,并将数据存储在rc_data变量中。

需要注意的是,在初始化UART1时,需要先调用一次该函数,来启动UART1的中断接收模式;再接受完一个数据后,也需要再调用该函数来重新启动UART1的中断接收模式,以等待接收下一个字节的数据。

二.串口空闲中断+DMA

1.空闲中断接收

        空闲中断接收,当一帧数据接收完成之后,串口会进入到空闲中断中去,然后在空闲中断中处理收到的数据。这种模式对处理不定长数据帧带来很大的便利,我们不必频繁的进入接收中断处理数据,但是弊端也是明显的,由于每次都要接收完一个完整的数据帧后才空闲中断,所以当一帧数据出错时,我们也不得不接收这帧错误的数据。在通讯可靠的场合,使用空闲中断接收模式接收串口数据,将会大大提高系统的性能。

2.DMA

1.简介

DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数
据,但是不需要占用 CPU ,即在传输数据的时候, CPU 可以干其他的事情,好像是多线程一样。

2、使用场景

DMA用在只需要传输数据,不需要处理数据的地方,有三种传输方式:

  • 外设→存储器(例:从串口RDR寄存器写入某数据buf)
  • 存储器→外设(例:从某数据buf写入串口TDR寄存器)
  • 存储器→存储器(例:复制某特别大的数据buf)

3、指针递增

在传输数据时,可以配置指向传输双方数据的指针是否自动向后递增。通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。
通常如下图配置:

Cubemx配置

 串口设置

 DMA设置

 开启全局中断

最后直接生成文件 

代码编写

1.在usart.c文件中添加

定义变量

/* USER CODE BEGIN 0 */
volatile uint8_t rx_len=0;  //接收到的数据长度
volatile uint8_t recv_end_flag=0;//接收完成标志
uint8_t rx_buffer[200];//缓存数组
/* USER CODE END 0 */

 开启空闲中断和dma接收

  /* USER CODE BEGIN USART1_Init 2 */
  __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能空闲中断
  HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,200); //开启DMA接收

  /* USER CODE END USART1_Init 2 */

重定义fputc,是为了能使用printf函数 

/* USER CODE BEGIN 1 */
//重定向fputc函数
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
    return ch;
}
/* USER CODE END 1 */


2.在usart.h文件中添加

#include <stdio.h>

/* USER CODE BEGIN Includes */
extern  volatile uint8_t rx_len ;  //接收一帧数据的长度
extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
extern uint8_t rx_buffer[200];  //接收数据缓存数组

注意这里的#include <stdio.h>,不添加的话重定向fputc函数那里会报错

3.在中断函数中添加

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */    
  uint32_t tmp_flag = 0;
    uint32_t temp;
    tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
    if((tmp_flag != RESET))
    { 
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);//
        temp = huart1.Instance->SR;  
        temp = huart1.Instance->DR; 
        HAL_UART_DMAStop(&huart1); 
        temp  = hdma_usart1_rx.Instance->CNDTR;//获取此时DMA中未传输的数据个数
        rx_len =  200 - temp; //总计数减去未传输的数据个数,得到此次接收到的数据个数
        recv_end_flag = 1;		
    }
    HAL_UART_IRQHandler(&huart1);	

  /* USER CODE END USART1_IRQn 0 */
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

这里不使用回调函数

4.在main.c中添加

  while (1)
  {
    /* USER CODE END WHILE */
    if(recv_end_flag ==1)			
		{	
			printf("接收到的数据长度为%d\r\n",rx_len);
			HAL_UART_Transmit(&huart1,rx_buffer, rx_len,200);
			for(uint8_t i=0;i<rx_len;i++)
			{
				rx_buffer[i]=0;
			}
			printf("\r\n");
			rx_len=0;
			recv_end_flag=0;
		}
		HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,200);//再次开启DMA接收	
		/* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

这里只是测试代码,将接收到的数据再利用串口输出到串口助手

5.结果

当数据接收间隔时间很短的情况下,这里是1ms,数据接收会出错;在10ms后就正常

注意事项

1.程序下载到板子上没反应,只有在一步一步调试时才有输出。最后勾选了Use MicoLIB后程序就正常运行了

 

2.输出中文时串口助手上显示乱码,代码格式要改为GB 2312,但直接在keil上修改没什么用,我这里是在vscode上修改格式后保存,在keil上运行就行了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32CubeMX—串口空闲中断+DMA接收 的相关文章

随机推荐

  • 结构体的定义以及初始化

    一 结构体定义类型 变量的定义 struct是关键字 是结构体类型的标志 在结构体里可以定义相应的成员信息 同时 结构体同样要以分号结尾 struct person char name 20 char sex 20 int age int
  • eclipse 找不到 tomcat 的解决方案

    前言 查了很多教程上配置tomcat时都是直接 File gt gt New gt gt Other gt gt Server gt gt Server gt gt Apache gt gt tomcat 然而我发现在这两个eclipse中
  • 日志大文件拆分

    前言 生产环境没设置好日志文件大小的最大值 导致日志文件过大 普通编辑器根本没法打开 要排查日志改怎么办 拆分日志大文件 如何拆分 split命令专门用来将一个大文件分割成很多个小文件 我把split命令的选项做一个简要说明 选项 含义 b
  • Python opencv库 tkinter 设计屏幕录制工具

    有时 我们在电脑上需要录屏 或制作gif动画 用于演示电脑操作等 如何使用Python来实现 目录 1 使用cv2库生成视频 2 使用tkinter选择录制区域 3 再次实现 4 最终的程序 5 拓展 创建gif动画 1 使用cv2库生成视
  • Docker日志日期时间精确查询

    docker logs since 2020 07 30T10 14 00 until 2020 07 30T10 15 00 tomcat80 这条代码可以通过2个时间来查询指定范围的时间日志 since起始时间 你要从什么时候开始查询
  • 【数据结构】UnionFind 并查集-2

    数据结构源码 UnionFind1 接口 public interface UnionFind int getSize boolean isConnected int p int q void unionElements int p int
  • 华大HC32L176与三相四线计量模块JSY_333通讯例程以及对三相三线认识误区

    在某宝购买这个产品后 需要编写程序读取数据 这款产品可以使用TTL和RS485进行通讯 我用的是用华大单片机HC32L176 首先对串口进行初始化 程序可以自行下载 链接 https pan baidu com s 1FD2VecV64ZH
  • 从端到端打通模型端侧部署流程(NCNN)

    文章目录 背景介绍 为什么要做端侧推理 端侧深度学习部署流程 一条主要技术路线 ONNX NCNN框架 NCNN的官方介绍 NCNN问题解决 NCNN使用样例 快速在NCNN框架下验证自己的模型 一般流程 YOLOv5的demo测试 全新部
  • CGSS中国综合社会调查

    数据详情 1 包含数据库和问卷 2 数据包含的年份为2003 2005 2006 2008 2010 2011 2012 2013 2015 2017 3 2017年数据为SPSS和STATA 14版 CSV EXCEL 编码表 4 15年
  • 8.14 ARM

    1 练习一 text 文本段 global start 声明一个 start函数入口 start start标签 相当于C语言中函数 mov r0 0x2 mov r1 0x3 cmp r0 r1 beq stop subhi r0 r0
  • python的类写法_python类写法

    广告关闭 腾讯云11 11云上盛惠 精选热门产品助力上云 云服务器首年88元起 买的越多返的越多 最高返5000元 在python中这一点仍然成立 in class fatboy object pass in fb fatboy in pr
  • 刷脸发甚至改变整个支付行业和零售行业

    在今年4月17日 蚂蚁金服在北京发布新一代刷脸支付产品 蜻蜓2 0 并宣称未来将会投入30亿让刷脸支付全国普及 助力商家数字化 让商家快速结付 提高商家运营效率 为顾客便利服务 为商家引流 支付宝蜻蜓二代接入刷脸即会员等数字化经营能力 试点
  • vue el-option只回显数字问题

    1 value前面没有加冒号说明是字符串 加个冒号即可回显label名称 2 后端返回的值可能已经将id类型返回为String 此时转换为number即可回显 3 也可用v for循环渲染选项 回显时肯定能回显label名称
  • 机器人避障路径规划--基于人工势场算法

    机器人避障路径规划 基于人工势场算法 机器人避障路径规划是机器人导航和控制中的一个基本问题 它的目标是在给定环境中找到一条安全可行的路径 使得机器人能够从起点到达目标点 并尽可能地避免与环境发生碰撞 人工势场算法是一种常用的机器人避障路径规
  • error: could not delete '/usr/local/lib/python3.6/site-packages/pip/_internal/configuration.py': Per

    brew install python 报错 error could not delete usr local lib python3 6 site packages pip internal configuration py Permis
  • 黑马程序员Javaweb学习笔记02【request和response】

    该博客主要记录在学习黑马程序员Javaweb过程的一些笔记 方便复习以及加强记忆 系列文章 JavaWeb学习笔记01 BS架构 Maven Tomcat Servlet JavaWeb学习笔记02 request和response Jav
  • 【三维语义分割】PointNet++ (二):模型结构详解

    本文为博主原创文章 未经博主允许不得转载 本文为专栏 python三维点云从基础到深度学习 系列文章 地址为 https blog csdn net suiyingy article details 124017716 本节主要介绍Poin
  • 电机的堵转检测及处理

    基于L298N控制的电机的堵转检测及其处理 一 L298N原理 二 电机堵转检测 三 电机堵转处理 一 L298N原理 1 L298N datasheet 2 使用须知 工作电压高 最高工作电压可达46V 输出电流大 瞬间峰值电流可达3A
  • jeesite框架分析理解

    前文 jeesite代码生成器的使用 实例 报销表 地址 http blog csdn net m0 38021128 article details 68490920 前文中使用了jeesite框架的代码生成功能实现了一个小实例 但是实际
  • STM32CubeMX—串口空闲中断+DMA接收

    一 串口中断通信 串口中断方式的特点 发送数据时 将一字节数据放入数据寄存器DR 接收数据时 将DR的内容存放到用户存储区 中断方式不必等待数据的传输过程 只需要在每字节数据收发完成后 由中断标志位触发中断 在中断服务程序中放入新的一字数据