STM32 串口IDLE接收空闲中断+DMA

news2024/11/16 12:04:44

参考

http://t.csdnimg.cn/fAV38

1.基础知识

STM32 IDLE 接收空闲中断
功能:
在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。
接受完一帧数据,触发中断

STM32的IDLE的中断产生条件:
在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断

STM32 RXNE接收数据中断
功能:
当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断
接受到一个字节的数据,触发中断。
比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。

串口CR1寄存器
在这里插入图片描述
对bit4写1开启IDLE接受空闲中断
,对bit5写1开启RXNE接收数据中断。

串口ISR寄存器
在这里插入图片描述
此寄存器为串口状态查询寄存器
当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.

清除RXNE中断标志位的方法为:
只要把接收到的一个字节读出来,就会清除这个中断
在STM32F1 /STM32F4 系列中 清除IDLE中断标志位的方法为:
先读SR寄存器,
再读DR寄存器。
在这里插入图片描述

memset()函数

extern void *memset(void *buffer, int c, int count)        

buffer:为指针或是数组
c:是赋给buffer的值
count:是buffer的长度.

USART采用DMA接收时,如何读取当前接收字节数?

   #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);

DMA接收时该宏将返回当前接收空间剩余字节
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,

在这里插入图片描述

2.IDLE 接收空闲中断+DMA

实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节

本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节

应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。

本例程功能:
使用DMA+串口接受空闲中断 实现将接收的数据完整发送到上位机的功能
在这里插入图片描述
接收数据的流程:
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。

数据接收完成的流程:
当数据接收完成之后产生接收空闲中断④

在中断服务函数中做这几件事
判断是否为IDLE接受空闲中断
在中断服务函数中将接收完成标志位置1
关闭DMA防止在处理数据时候接收数据,产生干扰。
计算出接收缓存中的数据长度,清除中断位,

while循环 主程序流程:
主程序中检测到接收完成标志被置1
进入数据处理程序,现将接收完成标志位置0,
将接收到的数据重新发送到上位机
重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。

usart.c

#include "usart.h"

/* USER CODE BEGIN 0 */
 
volatile uint8_t rx_len = 0;  //接收一帧数据的长度
volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
uint8_t rx_buffer[100]={0};  //接收数据缓存数组

/* USER CODE END 0 */

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;

/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }

	__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断

//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
	HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);

	
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA1_Channel5;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);

    /* USART1_TX Init */
    hdma_usart1_tx.Instance = DMA1_Channel4;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();
  
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmarx);
    HAL_DMA_DeInit(uartHandle->hdmatx);

    /* USART1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
} 

usart.h

#ifndef __usart_H
#define __usart_H
#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

extern UART_HandleTypeDef huart1;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
/* USER CODE BEGIN Private defines */
 
 
 #define BUFFER_SIZE  100  
extern  volatile uint8_t rx_len ;  //接收一帧数据的长度
extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
extern uint8_t rx_buffer[100];  //接收数据缓存数组
/* USER CODE END Private defines */

void MX_USART1_UART_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /*__ usart_H */

main.c

/*
*********************************************************************************************************
* 函 数 名: DMA_Usart_Send
* 功能说明: 串口发送功能函数
* 形  参: buf,len
* 返 回 值: 无
*********************************************************************************************************
*/
void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
{
 if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK)   //判断是否发送正常,如果出现异常则进入异常中断函数
  {
   Error_Handler();
  }

}



/*
*********************************************************************************************************
* 函 数 名: DMA_Usart1_Read
* 功能说明: 串口接收功能函数
* 形  参: Data,len
* 返 回 值: 无
*********************************************************************************************************
*/
void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装
{
	HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
}

while循环

 while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		 if(recv_end_flag == 1)  //接收完成标志
		{
			
			
			DMA_Usart_Send(rx_buffer, rx_len);
			rx_len = 0;//清除计数
			recv_end_flag = 0;//清除接收结束标志位
//			for(uint8_t i=0;i<rx_len;i++)
//				{
//					rx_buffer[i]=0;//清接收缓存
//				}
				memset(rx_buffer,0,rx_len);
  }
		HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
}

代码下载

https://gitcode.com/ZXiaoxuan/STM32-DMA-IDLE-/overview?utm_source=csdn_github_accelerator&isLogin=1

3. IDLE 接收空闲中断+RXNE接收数据中断

UART中断使能函数

__HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)

功能: 该函数的作用是使能对应的UART中断

我们现在所用到的为:

  *  @arg UART_IT_RXNE: Receive Data register not empty interrupt
  *  @arg UART_IT_IDLE: Idle line detection interrupt

RXNE接收数据中断
IDLE 接收空闲中断

在这里插入图片描述
本例程功能:
使用RXNE接收数据中断+IDLE串口接受空闲中断 实现将接收的数据完整发送到上位机的功能

接收数据的流程:
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,同样初始化配置的时候设置好配置就可以了。

接收到一个字节数据:
接收到一个字节的数据之后,便会进入RXNE接收数据中断,

接受完一帧数据之后,便会进入IDLE 接收空闲中断

usart.c

void MX_USART1_UART_Init(void)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }

	__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能IDLE中断
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断

//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
	HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);

	
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1643327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV(五) —— 人脸识别模型训练与 Windows 下的人脸识别

本文主要内容&#xff1a; 如何训练 OpenCV 的人脸识别模型如何在 Windows 下利用 OpenCV 进行人脸识别 1、概述 人脸识别需要人脸模型&#xff08;特征集合&#xff09;的支持&#xff0c;人脸定位的速度与准确度取决于模型。 OpenCV 提供了已经训练好的模型&#xff0c;无…

RK3568平台(基础篇)linux错误码

一.概述 linux应用程序开发过程中&#xff0c;经常会遇到一些错误信息的返回&#xff0c;存在的可能性有&#xff0c;参数有误、非法访问、系统资源限制、设备/文件不存在、访问权限限制等等。对于这类错误&#xff0c;可以通过perror函数输出具体描述&#xff0c;或者通过str…

轨道交通巡检机器人的应用范围

在现代轨道交通系统的庞大网络中&#xff0c;无数的轨道、设备和设施交织在一起&#xff0c;如同一个精密的机器在高效运转。而在这背后&#xff0c;轨道交通巡检机器人正悄然登场&#xff0c;它们如同一个个智能的守护者&#xff0c;穿梭于各个场景之中。那么&#xff0c;这些…

2. 深度学习笔记--损失函数

在机器学习中&#xff0c;损失函数是代价函数的一部分&#xff0c;而代价函数则是目标函数的一种类型。 Loss function&#xff0c;即损失函数&#xff1a;用于定义单个训练样本与真实值之间的误差&#xff1b; Cost function&#xff0c;即代价函数&#xff1a;用于定义单个批…

富文本编辑器CKEditor4简单使用-08(段落首行缩进插件 + 处理粘贴 Microsoft Word 中的内容后保持原始内容格式(包括首行缩进))

富文本编辑器CKEditor4简单使用-08&#xff08;段落首行缩进插件 处理粘贴 Microsoft Word 中的内容后保持原始内容格式&#xff08;包括首行缩进&#xff09;&#xff09; 1. 缩进&#xff0c;特殊方式处理——修改原工具栏里的增加缩进量2 缩进&#xff0c;插件处理2.1 下载…

【Java基础】Maven的生命周期(clean+site+default)

1. 前言 在 Maven 出现之前&#xff0c;项目构建的生命周期就已经存在&#xff0c;开发人员每天都在对项目进行清理&#xff0c;编译&#xff0c;测试及部署&#xff0c;但由于没有统一的规范&#xff0c;不同公司甚至不同项目之间的构建的方式都不尽相同。 Maven 从大量项目…

ES集群数据备份与迁移

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、文章涉及概念讲解二、操作步骤1.创建 snapshot repository操作主机hadoop1分别操作从机hadoop2和hadoop3 2. 查看仓库信息3. 备份索引&#xff0c;生成快照…

k8s Dashboard 运维维护记录

k8s Dashboard 运维维护记录 k8s Dashboard 运维维护记录 Q1&#xff1a;需要使用firefox浏览器访问 提示了证书错误NET::ERR_CERT_INVALID&#xff0c;原因是由于物理机的浏览器证书不可用 需要注意的是&#xff0c;若提示“连接不安全”的警告时&#xff0c;点击“高级”…

2024新蓝海项目 零门槛高收益持续稳定 纯手机操控 单日盈利3000 新手当日入门

这个项目具有极高的稳定性&#xff01;通过积累的里程积分&#xff0c;我们可以以较低的成本获取机票。然后&#xff0c;我们以远低于官方网站的价格出售这些机票&#xff0c;从而获得利润。随着我们经营的时间增长&#xff0c;我们积累的客户群体也会越来越大。 项目 地 址 …

游戏AI的智能化:机器学习在虚拟生命中的应用

文章目录 写在前面游戏AI的智能化&#xff1a;机器学习在虚拟生命中的应用游戏内容的自动化创作&#xff1a;机器学习的革新性应用玩家体验的个性化优化&#xff1a;机器学习的定制化力量未来展望&#xff1a;机器学习塑造游戏行业新纪元游戏AI的智能化发展自动化内容生成的革命…

什么是脏读?幻读?不可重复读?

脏读(Drity Read)&#xff1a;某个事务 A 已更新一份数据&#xff0c;另一个事务 B 在此时读取了同一份数据&#xff0c;由于某些原因&#xff0c;事务 A 回滚&#xff0c;而事务B读取到事务 A 回滚前的数据。 例子:小明读取到小红提交的100数据.但是小红异常回滚了数据,100变…

【前端学习——正则】

https://www.bilibili.com/video/BV1da4y1p7iZ/?spm_id_from333.337.search-card.all.click&vd_source5cef5968d539682b683e7d01b00ad01b 学习网站 https://github.com/ziishaned/learn-regex/blob/master/translations/README-cn.md

CCPC训练赛题解补题

B. Memory Problem - B - Codeforces&#xff08;原题出处点击这里&#xff09; 题意&#xff1a;根据题目中给出的求和公式&#xff0c;将输入的数据进行判断&#xff0c;输出”“或”-“或”0“&#xff1b;因为范围特别大&#xff0c;精度特别高&#xff0c;所以循环遍历的…

mac监听 linux服务器可视化(Grafana+Promethus+Node_exporter)

Grafana和promethus(普罗米修斯)的安装和使用 监控系统的Prometheus类似于一个注册中心&#xff0c;我们可以只需要配置一个Prometheus,而在其他服务器&#xff0c;只需要安装node_exporter,它们的数据流转就是通过exporter采集数据信息&#xff0c;然后告诉prometheus它的位置…

leetCode68. 文本左右对齐

基本思路&#xff1a; leetCode68. 文本左右对齐 代码 class Solution { public:vector<string> fullJustify(vector<string>& words, int maxWidth) {vector<string> res;for(int i 0; i < words.size(); i){ // 枚举有多少个单词int j i 1; //…

Springboot+vue基于协同过滤算法的电商系统

项目由maven管理依赖&#xff0c;mybatis&#xff08;plue&#xff09;与数据库交互&#xff0c;一共14张表&#xff0c;功能都经过测试无问题&#xff0c;需要可在最下方扫码联系我。&#xff08;有文档&#xff0c;调试&#xff0c;讲解可加购&#xff09; 商品推荐板块基于…

如何选购骨传导耳机?精选五大拔尖宝藏骨传导耳机,闭眼入也不踩雷!

尽管目前市面上的骨传导耳机热度非常高&#xff0c;一度成为当下最热门的耳机款式&#xff0c;但作为有着资深工作经验的数码测评师&#xff0c;我仍然要提醒大家&#xff1a;在选择骨传导耳机的时候&#xff0c;不要盲目选择网红品牌&#xff0c;因为市场上的许多骨传导耳机过…

【Python基础】进程

文章目录 [toc]程序与进程的区别与联系同步任务示例 并行任务示例进程调度的“随机性” 进程属性与方法process_object.start()方法process_object.join()方法process_object.daemon属性没有设置守护进程的情况设置守护进程的情况 process_object.current_process()方法 进程通…

MySql主从复制(多主多从)

多主多从 序言前期准备工作创建主机和从机的配置文件和数据存储目录配置master1配置文件配置master2配置文件配置slave1配置文件配置slave2配置文件docker-compose-mysql.yml启动文件启动master1启动master2两个主机分别创建用户两个主机分别刷新权限查看两个主机binlog状态mas…

UDP报头结构 和注意事项

UDP协议 UDP这个协议的学习 ,最只要学习的就是报文格式 UDP数据报 UDP报头 UDP载荷 UDP协议端口格式 报头由4个部分组成 : 源端口号 、目的端口号、UDP长度、UDP校验和 UDP长度描述了整个UDP数据报&#xff0c;占多少个字节 通过UDP长度 就可以知道&#xff0c;当前载荷一…