Stm32407串口1空闲中断+DMA收发(基于标准库实现)

这篇具有很好参考价值的文章主要介绍了Stm32407串口1空闲中断+DMA收发(基于标准库实现)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 简介

stm32串口的配置很简单,这里就不赘述了,使用USART_SendData() 阻塞模式发送数据,或是接收中断配置 “接收缓冲区非空” USART_IT_RXNE,这种做法效率很低,而且来一个数据中断一次数据处理起来也麻烦。
这里基于STM32F407提供一种串口空闲中断+DMA接收的方式,通过库函数编程实现。

1.1 什么是串口空闲中断

初学者一开始学习配置串口中断时经常将中断条件配置为USART_IT_RXNE,也就是接收缓冲区非空,每接收一个U8数据便产生一次中断,非常消耗系统资源。
而空闲中断USART_IT_IDLE,俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(就是没数据接收的状态),检测到此空闲状态后即执行中断程序。进入中断程序即意味着已经接收到一组完整帧数据,仅需及时对数据处理或将数据转移出缓冲区即可。

1.2 DMA简介

DMA–DirectMemoryAccess(存储器直接访问)是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过CPU,也不需要CPU干预。整个数据传输操作在一个称为“DMA控制器”的控制下进行。CPU除了在数据传输开始和结束时做一点处理外,在传输过程中还可以进行其他的工作。这样,在大部分时间里,CPU和输入输出都处于并行操作,因此使整个计算机系统的效率大大提高。

1.3 DMA模式

STM32中DMA有3种传输模式,如下表所示
stm32f407 串口dma,stm32,单片机,c语言
其中DMA2支持上表3种传输模式,而DMA1只支持外设到存储器和存储器到外设两种模式。

1.4 DMA请求映射

每一个外设请求都会占用一个数据流通道,同一个外设请求可以占用不同的数据流通道,如下表所示
stm32f407 串口dma,stm32,单片机,c语言
stm32f407 串口dma,stm32,单片机,c语言
例如本次使用的是STM32F407的USART1的,查上表发现串口1的发送USART1_TX是通道4数据流7,USART1_RX是通道4数据流5或通道4数据流2

1.4 DMA配置简述

1.配置DMA结构体 DMA_InitTypeDef ,使用DMA_Init()初始化DMA配置
2.启用外设(串口)DMA接口 USART_DMACmd()
3.使能DMA DMA_Cmd()
DMA结构体 DMA_InitTypeDef 具体成员含义自行参考库函数源代码说明。

2 DMA收发代码实现

2.1 定义收发结构体

为了方便管理,定义了收发缓冲区的结构体。

#define RECEIVE_BUF_MAX_SIZE 	100   //DMA单次最大搬运数据量
#define TRANSMIT_BUF_MAX_SIZE 50   //DMA单次最大搬运数据量
///定义数据接收结构体
typedef struct _ReceiveBuffer{
	uint8_t Buffer[RECEIVE_BUF_MAX_SIZE];//用于接收DMA搬运的接收数据
	uint16_t Lenth;//接收的数据长度
}ReceiveBuffer_t;
///定义数据发送结构体
typedef struct _TransmitBuffer{
	uint8_t Buffer[TRANSMIT_BUF_MAX_SIZE];//用于接收DMA搬运的发送数据
	uint16_t Lenth;//发送的数据长度
}TransmitBuffer_t;

static ReceiveBuffer_t ReceiveBuffer;//数据接收结构体
static TransmitBuffer_t TransmitBuffer;//数据发送结构体
/*DMA接收配置结构体*/
static DMA_InitTypeDef DMA_TransmitInitStruct;

2.2 DMA配置

1.DMA接收配置
配置根据上表查询的数据流通道,设置方向为外设到存储器,配置外设地址及存储器地址,设置最大搬运数量。

static void USART_ReceiveDMA_init(void){
	DMA_InitTypeDef DMA_ReceiveInitStruct;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
	DMA_ReceiveInitStruct.DMA_Channel = DMA_Channel_4;	//设置DMA通道
    DMA_ReceiveInitStruct.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR;//设置外设地址
  	DMA_ReceiveInitStruct.DMA_Memory0BaseAddr = (uint32_t) ReceiveBuffer.Buffer;//设置内存地址
  	DMA_ReceiveInitStruct.DMA_DIR =  DMA_DIR_PeripheralToMemory;//设置搬运方向:外设到内存
  	DMA_ReceiveInitStruct.DMA_BufferSize = RECEIVE_BUF_MAX_SIZE;//搬运数据长度
  	DMA_ReceiveInitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址在搬运过程中是否自增(设置不自增)
  	DMA_ReceiveInitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址在搬运过程中是否自增(设置自增)
  	DMA_ReceiveInitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据大小为一个字节
  	DMA_ReceiveInitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//数据大小为一个字节
	DMA_ReceiveInitStruct.DMA_Mode = DMA_Mode_Normal;//无需循环
	DMA_ReceiveInitStruct.DMA_Priority = DMA_Priority_Medium;
 	DMA_ReceiveInitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
  	DMA_ReceiveInitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_ReceiveInitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_ReceiveInitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  	DMA_Init(DMA2_Stream5, &DMA_ReceiveInitStruct);
	USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
	DMA_Cmd(DMA2_Stream5, ENABLE);//默认开启DMA接收
}

2.DMA发送配置
与DMA接收类似,只是现在是从存储器到外设。

static void USART_TransmitDMA_init(void){
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
	DMA_TransmitInitStruct.DMA_Channel = DMA_Channel_4;
  	DMA_TransmitInitStruct.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR;
  	DMA_TransmitInitStruct.DMA_Memory0BaseAddr = (uint32_t) TransmitBuffer.Buffer;
  	DMA_TransmitInitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
  	DMA_TransmitInitStruct.DMA_BufferSize =TRANSMIT_BUF_MAX_SIZE;
  	DMA_TransmitInitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  	DMA_TransmitInitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
  	DMA_TransmitInitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  	DMA_TransmitInitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  	DMA_TransmitInitStruct.DMA_Mode = DMA_Mode_Normal;
  	DMA_TransmitInitStruct.DMA_Priority = DMA_Priority_Medium;
  	DMA_TransmitInitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
  	DMA_TransmitInitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  	DMA_TransmitInitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  	DMA_TransmitInitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  	DMA_Init(DMA2_Stream7, &DMA_TransmitInitStruct);
	USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
	DMA_Cmd(DMA2_Stream7, DISABLE);//默认不开启DMA发送,需要后续触发
}

2.3 串口配置

串口配置很简单,这里不赘述。

static void USART_init(void){

  	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//GPIO端口设置
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIO复用
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIO复用
	
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //开漏
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化管脚
   //USART1 初始化设置
	USART_InitStructure.USART_BaudRate = 115200;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
  	USART_Init(USART1, &USART_InitStructure); //初始化串口
	USART_Cmd(USART1, ENABLE);  //使能串口
//收发DMA配置	
	BLE_USART_ReceiveDMA_init();//接收DMA配置
	BLE_USART_TransmitDMA_init();//发送DMA配置
//配置中断
	BLE_USART_NVIC_Config();
}

2.4 中断配置

1.中断配置文章来源地址https://www.toymoban.com/news/detail-604186.html

static void USART_NVIC_Config(void){
	//配置中断
	NVIC_InitTypeDef NVIC_InitStructure;
	//USART 空闲NVIC
	NVIC_InitStructure.NVIC_IRQChannel =USART1_IRQn;//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断
	//DMA NVIC
  NVIC_InitStructure.NVIC_IRQChannel =DMA2_Stream5_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			
	NVIC_Init(&NVIC_InitStructure);	
	DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE);  
}
  1. 串口中断函数
    串口中断配置为空闲中断,当一帧数据接收完毕后与下一帧数据到达之前有一段空闲,系统会在这段空闲时进入中断,我们通过DMA搬运计数可以计算出DMA搬运了多少数据(即一帧数据的大小),并在该中断中处理这个数据帧。
void USART1_IRQHandler(void)                
{
	//空闲中断
	if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET){
		uint8_t clear;
		DMA_Cmd(DMA2_Stream5, DISABLE);  //关闭接收DMA,防止处理其间有数据
    	clear = USART1->SR;
   		clear = USART1->DR;  		//清除IDLE标志			
		USART_ClearITPendingBit(USART1,USART_IT_IDLE);//清除标志位	
		ReceiveBuffer.Lenth=RECEIVE_BUF_MAX_SIZE - DMA_GetCurrDataCounter(DMA2_Stream5);
		/*
		此处添加处理数据的代码
		*/
		DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//清除DMA2_Steam5传输完成标志  
    DMA_SetCurrDataCounter(DMA2_Stream5, RECEIVE_BUF_MAX_SIZE);
    DMA_Cmd(DMA2_Stream5, ENABLE);
	}
}
  1. DMA中断函数
void DMA2_Stream5_IRQHandler(void)  
{  
    //清除标志  
    if(DMA_GetFlagStatus(DMA2_Stream5,DMA_FLAG_TCIF5)!=RESET)//等待DMA2_Steam7传输完成  
    {   
        DMA_Cmd(DMA2_Stream5, DISABLE); //关闭DMA,防止处理期间有数据  
  
        ReceiveBuffer.Lenth =RECEIVE_BUF_MAX_SIZE - DMA_GetCurrDataCounter(DMA2_Stream5);  
        if(ReceiveBuffer.Lenth !=0)  
        {  
			/*RC_Process();//处理缓冲区ReceiveBuffer的数据*/
        }            
        DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//清除DMA2_Steam7传输完成标志  
        DMA_SetCurrDataCounter(DMA2_Stream5, RECEIVE_BUF_MAX_SIZE);  
        DMA_Cmd(DMA2_Stream5, ENABLE);     //打开DMA 
    }  
}

2.5 DMA发送

void DMA_Send(char* data){
	uint16_t datalenth=strlen(data);
	memcpy(TransmitBuffer.Buffer,data,datalenth);
	DMA_TransmitInitStruct.DMA_BufferSize=datalenth;
	DMA_DeInit(DMA2_Stream7);//先关闭DMA
	while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){}//等待DMA可配置 
	DMA_Init(DMA2_Stream7, &DMA_TransmitInitStruct);
	DMA_Cmd(DMA2_Stream7, ENABLE);                      //开启DMA传输 
}

到了这里,关于Stm32407串口1空闲中断+DMA收发(基于标准库实现)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32+HAL库+CubeMX】UART轮询收发、中断收发、DMA收发方法及空闲中断详解

    Author: DrinkCat(szt@drinkcat.com) Copyright © 2023 DrinkCat Original link: DrinkCat’s Blog UART是一种异步串行通信接口,常用于通过串口与外部设备进行通信。它通过发送和接收数据帧来实现数据传输,使用起来相对简单。UART通常包含发送器(Transmitter)和接收器(Receiver),通过两根信号线

    2024年02月10日
    浏览(37)
  • stm32串口空闲中断+DMA传输接受不定长数据+letter shell 实现命令行

    空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。 空闲中断的优点在于省去了帧头帧尾的检测 ,进入中断程序即意味着已经接收到一组完整数据,仅需

    2024年02月03日
    浏览(59)
  • STM32 cubemx+串口空闲中断+DMA双缓冲

            写这篇文章是为了记录下之前做过的项目中用到的一部分关键技术,之前做过的项目中涉及到 采用最小开销来实时接收遥控器数据、能够准确验证传输过来数据的准确性 ,减小误差率,要求能稳定适用于不同的环境。 目录 1、为什么要用到串口空闲中断? 2、为

    2024年02月09日
    浏览(58)
  • STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流

    方法一、使用宏定义判断IDLE标志位 空闲的定义是总线上在一个字节的时间内没有再接收到数据,USART_IT_IDLE空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。 串口空闲中断(UART_IT_IDLE):STM32的IDLE的中断在串口无数据接收的情况

    2024年01月23日
    浏览(57)
  • STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

    能够点进这篇文章的小伙伴肯定是对STM32串口DMA空闲中断接收数据感兴趣的啦,今天用这一功能实现串口解析航模遥控器sbus信号时,查阅了很多网友发布的文章(勤劳的搬运工~),包括自己之前写过一篇博客 STM32_HAL库_CubeMx串口DMA通信(DMA发送+DMA空闲接收不定长数据)。本文

    2024年02月09日
    浏览(64)
  • GD32实现串口空闲(IDLE)中断 + DMA机制接收数据

    前言 串口功能在单片机开发中,是比较常用的外设,熟练使用串口功能也是驱动开发必备的技能之一。 DMA 是一种CPU辅助手段,可以在CPU不参与的情况下,是做一些辅助CPU的事情,如通常的数据搬运。 在没有DMA之前,数据读取时,需要CPU的处理,在多任务处理时,增加资源紧

    2023年04月13日
    浏览(41)
  • STM32基于HAL库的串口接受中断和空闲中断

    在通信方面。UART由于全双工通信,可以同时接受数据和发送数据而被广泛使用。 而接受数据则又有很多种方法 比如: 1根据结束符判断,数据是字符串形式,所以一般串口接受的接受符就是 \\\"rn\\\"  换成16进制ascil码显示就是 0X0D   0X0A (对应rn)  2定时器中断,设计\\\"喂狗信号量

    2023年04月08日
    浏览(43)
  • 环形队列+DMA空闲中断+接收串口数据

    本次实验利用环形队列+DMA空闲中断+串口。。通过这个实验可以非常深入的理解队列,DMA,串口的知识。如果你能自己实现掌握这个实验,那么你应该基本掌握了队列,DMA,串口的知识。 本次使用的是用环形队列当缓冲器区接收串口数据。我们可以先区了解DMA的空闲中断。本次

    2024年02月13日
    浏览(39)
  • 【STM32】-串口开发经验分享-基于RTOS+空闲中断

    目录 1. 概述     2.串口介绍 2.1 原理框图 2.2 RS-232C 2.3 RS-422 2.4 RS-485 2.5 UART 3. STM32 USART介绍 4. CubeMx生成Uart初始化代码 4.1 NewProject选择单片机型号 4.2 设置rcc时钟  4.3 设置Usart 4.4 初始化代码 4.5 注意 5 工程源码解析 5.1 程序架构 5.2 源码 fml_ring_buffer.c fml_usart.c app_usart_task.c stm3

    2023年04月16日
    浏览(47)
  • 衔尾法解决当无法使用空闲中断以及DMA中断时配置DMA接收串口不定长数据

    问题:类似K线与蓝牙接收模块,要求由原来的接收串口中断改为DMA接收。据说要用到空闲中断与DMA中断,但是经仿真发现DMA每完成传输一个数据(比如1BYTE)就会进入空闲中断(k线发现这种情况),考虑到这样进入中断的频率和以前串口接收中断的频率差不多,所以放弃此方案,

    2024年02月09日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包