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

embedded/2024/9/23 15:22:20/

参考

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_TXPA10     ------> 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_TXPA10     ------> 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 __cplusplusextern "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.ppmy.cn/embedded/22137.html

相关文章

EditText与NestScrollView嵌套使用时,滑动冲突处理

期望 在Android开发中经常会有在一个大页面中&#xff0c;包含一个EditText的情况&#xff0c;一般情况下&#xff0c;大页面会通过NestScrollView或者ScrollView当作根View。 于是在布局文件中&#xff0c;我们常常这么写&#xff1a; <?xml version"1.0" en…

鸿蒙launcher浅析

鸿蒙launcher浅析 鸿蒙launcher源码下载鸿蒙launcher模块launcher和普通的应用ui展示的区别 鸿蒙launcher源码下载 下载地址如下&#xff1a; https://gitee.com/openharmony/applications_launcher 鸿蒙launcher模块 下载页面已经有相关文件结构的介绍了 使用鸿蒙编辑器D…

《面向云计算的零信任体系第1部分:总体架构》行业标准正式发布

中华人民共和国工业和信息化部公告2024年第4号文件正式发布行业标准&#xff1a;YD/T 4598.1-2024《面向云计算的零信任体系 第1部分&#xff1a;总体架构》&#xff08;后简称“总体架构”&#xff09;&#xff0c;并于2024年7月1日正式施行。 该标准由中国信通院牵头&#xf…

基于STM32和阿里云的智能台灯(STM32+ESP8266+MQTT+阿里云+语音模块)

一、主要完成功能 1、冷光模式和暖光模式两种灯光 主要支持冷光和暖光模式两种&#xff0c;可以通过语音模块或手机app远程切换冷暖光 2、自动模式和手动模式 主要支持手动模式和自动两种模式&#xff08;app或语音助手切换&#xff09; (1)自动模式&#xff1a;根据环境光照…

本地微服务springboot集成ftp服务器

可分为四步&#xff0c;话不多说 1、引入apache ftp依赖 <dependency><groupId>org.apache.ftpserver</groupId><artifactId>ftpserver-core</artifactId><version>1.1.1</version></dependency> 2、创建ftp所需用户信息 us…

『大模型笔记』Code Example: Function Calling with ChatGPT

Code Example: Function Calling with ChatGPT 文章目录 一. Code Example: Function Calling with ChatGPT二. 参考文献一. Code Example: Function Calling with ChatGPT from openai import OpenAI from dotenv import load_dotenv import json# -------------------------…

Git快速入门技巧常见问题

文章目录 Git快速入门技巧&常见问题Git安装使用&配置1.Git安装2.Git使用自建本地仓库克隆仓库创建远程代码仓库分支branch 3.Git工作流程 Git快速入门技巧&常见问题 前情提要 ​ 最近在来回不停切环境开发项目&#xff0c;总是发现各种奇奇怪怪的问题&#xff0c;解…

【网络原理】TCP协议的连接管理机制(三次握手和四次挥手)

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序&#xff08;万字博文&#xff09; 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制&#xff08;CRC算法、MD5算法&#xff09; 【网络…