STM32F4应用DMA——串口收发不定长数据

2023-05-16

STM32F4应用DMA——串口收发不定长数据

使用STM32自带DMA传输数据,可以减轻CPU负担,只需设置一些参数即可发送想要发送的数据,以下是STM32F407VE芯片测试过的部分代码,可实现DMA串口收发数据。下图来自STM32官网的手册,RM0009.pdf
发送数据逻辑图:
在这里插入图片描述
接收数据逻辑图
在这里插入图片描述
下面是使用STM32 HAL库进行配置,大致实现思路都是一样的,先开启串口初始化(开启DMA传输),相应的DMA初始化,然后设置好传输地址,传输字节个数,然后启动使能

一、初始化部分

uint8_t u8txbuff[1024]; //发送缓冲区
uint8_t u8rxbuff[1024];	//接收缓冲区

//串口1初始化
//m_u32BaudRate:设置波特率
void usart1_init(uint32_t m_u32BaudRate)
{
		GPIO_InitTypeDef GPIO_InitStruct = {0};
	
		//设置GPIOA和串口时钟
		__HAL_RCC_GPIOA_CLK_ENABLE();  
		__HAL_RCC_USART1_CLK_ENABLE();

    	//设置串口GPIO
    	GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    	GPIO_InitStruct.Pull = GPIO_NOPULL;
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    	GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	
		//设置串口USART1参数
		huart1.Instance = USART1;
		huart1.Init.BaudRate = m_u32BaudRate;    			//ÉèÖò¨ÌØÂÊ
		huart1.Init.WordLength = UART_WORDLENGTH_8B;	//Êý¾Ýλ£º8¸ö
		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)
			while(1);
		
		//设置串口中断和中断优先级
		HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
		HAL_NVIC_EnableIRQ(USART1_IRQn);
		
		
		//DMA时钟设置
		__HAL_RCC_DMA2_CLK_ENABLE();  

		//启动DMA传输 PA10 RX
    	hdma_usart1_tx.Instance = DMA2_Stream7;							//Êý¾ÝÁ÷7
    	hdma_usart1_tx.Init.Channel = DMA_CHANNEL_4;				//ͨµÀ4
    	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_HIGH;			//´«ÊäµÈ¼¶¸ß   	
    	hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;			//FIFOģʽ²»Ê¹ÓÃ
    	if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
			while(1);
		
		//PA9 TX
		hdma_usart1_rx.Instance = DMA2_Stream2;						//DMA2数据流
    	hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;				//通道4
    	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_CIRCULAR;					//Ñ­»·Ä£Ê½
    	hdma_usart1_rx.Init.Priority = DMA_PRIORITY_MEDIUM;			//´«ÊäµÈ¼¶ÖеÈ
    	hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;	//FIFOģʽ²»Ê¹ÓÃ
		if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
			while(1);
		
		//设置接收完成中断
		__HAL_USART_ENABLE_IT(&huart1,USART_IT_TC); //设置发送完成中断
		__HAL_USART_CLEAR_FLAG(&huart1, UART_FLAG_TC);//清除发送完成标志位
		
		//设置DMA接收
		SET_BIT(huart1.Instance->CR3, USART_CR3_DMAT);//串口启用DMA接收数据
		HAL_DMA_Start(&hdma_usart1_tx,(uint32_t)I_USART.u8txBuffer,(uint32_t)&USART1->DR,0);//设定传输地址,传输源地址,传送字节个数
		
		//设置DMA发送
		SET_BIT(huart1.Instance->CR3, USART_CR3_DMAR);//串口启用DMA发送数据
		HAL_DMA_Start(&hdma_usart1_rx,(uint32_t)&USART1->DR,(uint32_t)I_USART.u8rxBuffer,BUFFSIZE); //设定传输地址,传输源地址,传送字节个数

}

二、串口中断部分(发送数据部分)

这个需要开启串口发送完成中断,当DMA往串口传送全部的数据完成后,串口会产生发送完成中断。

//串口2发送中断
void USART2_IRQHandler(void)
{
  if((USART2->SR & UART_FLAG_TC)!=RESET)    
	{    
		//当DMA传输数据完成以后,就会进入一次这个中断
		USART2->SR = ~(UART_FLAG_TC);
		
		u8txmode = SEND_FINISH//此处设置:发送完成标志
	}
}

三、发送数据函数

每当调用这个函数就会,要求DMA发送一段数据给USART的数据寄存器当中,接下来等待进入串口发送中断后,就可以判定DMA传送数据到串口已经完成发送。

//入口参数
//u16txlen: 发送数据长度
void USART2_TxdSend(uint16_t u16txlen)
{
	__HAL_DMA_DISABLE(&hdma_usart2_tx); 						//暂停DMA
	__HAL_UART_CLEAR_FLAG(&I_huart2, UART_FLAG_TC); 			//清除发送中断
	__HAL_DMA_GET_COUNTER(&hdma_usart2_tx) = u16txlen; 			//设置发送数据长度
 	__HAL_DMA_ENABLE(&hdma_usart2_tx); 							//启动DMA

}

三、开启定时器

开启定时器是希望周期性的去查询DMA传送了多少的数据到内存,也就是说接收了多少个字节。接收状态时,超过一定时间后,DMA传送字节个数没有变化(也就是说DMA_CNDTR寄存器没有变化)就可以判定一帧数据已经接收完成了。

void Timer4_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};
	
	//01-定时器4时钟
  __HAL_RCC_TIM4_CLK_ENABLE();
	
	//02-定时器4
  I_htim4.Instance = TIM4;			//TIM4
  I_htim4.Init.Prescaler = 35;		//35分频
  I_htim4.Init.CounterMode = TIM_COUNTERMODE_UP;  //向上计数
  I_htim4.Init.Period = 10000;				//重装载值   0 ->> 10000
  I_htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;		//时钟源不分频
  I_htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;	//启动重装载值
  if (HAL_TIM_Base_Init(&I_htim4) != HAL_OK)
		while(1);

  //03-设置中断优先级
  HAL_NVIC_SetPriority(TIM4_IRQn, 3, 0);  
  HAL_NVIC_EnableIRQ(TIM4_IRQn);
	
  //05-设置中断源
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;	//更新中断
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&I_htim4, &sMasterConfig) != HAL_OK)
		while(1);
	
	//06-清除中断标志位
	__HAL_TIM_CLEAR_FLAG(&I_htim4,TIM_FLAG_UPDATE);
	
	//07-打开定时器中断
	HAL_TIM_Base_Start_IT(&I_htim4);
	
	
}

四、定时器中断(接收数据部分)

DMA的DMA_CNDTR寄存器每当传输一个数据时,这个寄存器会自动减1,即接收一个数据。当串口处于接收状态时,使用定时器中断1ms周期查询DMA的DMA_CNDTR寄存器变化。

假定BUFFMAX为缓冲区长度:
①当BUFFMAX等于DMA_CNDTR寄存器值,没有输入接收(空闲状态)
②当BUFFMAX不等于DMA_CNDTR寄存器值,有新的数据接收(接收状态)
③当DMA_CNDTR寄存器超过20ms没有变化,判定接收数据完成。

#define		RECE_FINISH		0   //接收数据完成标志
#define		RECE_ING		1	//正在就收数据
#define		RECE_ILDE		2	//空闲状态

uint16_t  u16rxp;   //现在接收数据长度
uint16_t  u16timer; //接收计时器
uint8_t	  u8rxmode; //接收状态 

//定时器4(每1毫秒进入一次中断)
void TIM4_IRQHandler(void)
{
	 //获取定时器更新
	if(__HAL_TIM_GET_FLAG(&I_htim4,TIM_FLAG_UPDATE) != RESET)
	{
		//清除定时器中断
		__HAL_TIM_CLEAR_FLAG(&I_htim4,TIM_FLAG_UPDATE);
		
		if(u8rxmode != RECE_FINISH)
		{
			if(u16rxp != (BUFFMAX - DMA1_Channel6->CNDTR))
			{//查询接收数据有变化
				u16timer =0;
				u16rxp = (BUFFMAX - DMA1_Channel6->CNDTR);  //当前已经获取的数据长度
			}
				
			if(u16timer < 20 )
				u16timer ++; //接收数据计数
				
			if(u16timer ==20)
			{//超过一定时间,DMA没传过任何数据,则认为完成
				u16timer =0;
				u8rxmode = RECE_FINISH;
				__HAL_DMA_DISABLE(&hdma_usart2_rx); //暂停DMA2		
				DMA1_Channel6->CNDTR = BUFFMAX;   	//写入缓冲区长度
				__HAL_DMA_ENABLE(&hdma_usart2_rx);  //启动DMA2
			}
		}

	}
}

五、测试

当往usart2串口发送数据,数据会发送回来,速度取决于对每一帧数据接收超时判断

#include <string.h>

extern uint16_t u16rxp;
extern uint8_t u8txbuff;
extern uint8_t u8rxbuff;
extern uint8_t u8rxmode;

int main(void)
{
	uart2_init(115200); //波特率设置 115200
	Timer4_Init();	//开启定时器
	while(1)
	{
		if(u8rxmode==RECE_FINISH) //判定接收完成
		{
			memcpy(u8txbuff,u8rxbuff,u16rxp); //将接收的数据放入发送缓冲区
			USART2_TxdSend(u16rxp); //发送数据
			u8rxmode=RECE_ILDE;//进入空闲状态
		}
			
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F4应用DMA——串口收发不定长数据 的相关文章

  • 正点原子STM32F4笔记

    使用寄存器操作 xff0c 不错的博客 xff1a https blog csdn net w471176877 article category 1230060 https blog csdn net w471176877 article
  • STM32CUBEMX使用PWM+DMA驱动WS2812

    STM32CUBEMX使用PWM 43 DMA驱动WS2812 首先在stm32cubemx中设置pwm和dma 我设置了TIM1的CH1为PWM引脚编写DMA响应函数 xff0c 即PWM DMA完成数据发送后的回调函数 PWM DMA
  • STM32F4 SPI RX/TX DMA 读写FLASH数据

    STM32 使用DMA读写FLASH数据需要注意以下几点 xff1a 1 SPI全双工模式下 xff0c 无论读写FLASH数据均需要同时使能RX TX DMA 2 写数据时回读数据应当丢弃 xff0c 读数据时应当发送0xff来启动SPI
  • s3c6410 DMA

    S3C6410中DMA操作步骤 xff1a 1 决定使用安全DMAC SDMAC 还是通用DMAC DMAC xff1b 2 开始相应DMAC的系统时钟 xff0c 并关闭另外一组的时钟 xff08 系统默认开启SDMA时钟 xff09 x
  • Xilinx的Zynq系列,ARM和PL通过DMA通信时如何保证DDR数据的正确性。

    使用ZYNQ或者MPSoC的好处是可以通过PL逻辑设计硬件加速器 xff0c 对功能进行硬件加速 加速器和ARM之间的交互信息一般包含自定义加速指令传递 待计算数据以及计算结果 这三种交互信息为了实现高性能往往需要使用DMA进行通信 考虑两
  • 中断与DMA

    中断 Cortex M3256 个优先级和 128 个抢占级悬起pending 中断中断的类型中断结构 DMA通道映射源传输和目标传输寄存器中断状态寄存器和中断标志清除寄存器通道x配置DMA stream x configuration r
  • stm32f4串口接收与发送

    之前有写一篇stm32f1串口接收与发送的文章 xff0c stm32f4与f1只有配置上的一点不同 xff0c 今天把f4的串口接收与发送代码分享一下 详细解释推荐大家看f1那篇 xff0c 都是一样的 xff0c stm32f1串口发送
  • STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流

    STM32使用串口空闲中断 xff08 IDLE xff09 和 DMA接收不定长数据 方法一 使用宏定义判断IDLE标志位 空闲的定义是总线上在一个字节的时间内没有再接收到数据 xff0c USART IT IDLE空闲中断是检测到有数据
  • 串口+DMA 数据收发编程实践

    更多交流欢迎关注作者抖音号 xff1a 81849645041 目标 了解DMA 的工作原理 xff0c 通过配置 STM32F407 芯片的DMA xff0c 实现串口 43 DMA数据收发 原理 基于USART的数据通讯中采用中断方式可
  • STM32F4 使用SPI读取气压计MS5611的数据并转化为大气压强

    ms5611是同时支持I2C和SPI通信协议的气压计芯片 已经很普遍的被用在飞行器控制板上作为高度传感器 开发过stm32的朋友都知道它的硬件I2C是由bug的 xff0c 一般使用的都是自己编写的软件I2C通信协议 但是其硬件SPI通信却
  • WS2812灯珠(二)-- STM32 SPI+DMA方式驱动

    通过硬件SPI的可以很巧妙的模拟出WS2812的通信时序 xff0c 用spi的8位数据模拟ws281x的一位数据 要将系统时钟设置为56M xff0c SPI分频数设置为8 xff0c 则SPI的通信频率为7M xff0c 1s 7M 1
  • FIFO和DMA

    FIFO SPI端口增加了FIFO xff0c 使得传输数据有了缓冲区间 FIFO存储器是一个先入先出的双口缓冲器 xff0c 即第一个进入其内的数据第一个被移出 xff0c 其中一个存储器的输入口 xff0c 另一个口是存储器的输出口 主
  • stm32串口DMA方式向上位机连续发送数据

    目录 一 认识DMA1 DMA框图2 什么是DMA xff1f 3 DMA传输方式4 DMA传输参数5 DMA数据传输的四个要素6 DMA的应用场景 二 串口DMA方式向上位机发送数据1 实验要求2 通过STMCube配置项目 1 设置RC
  • STM32F4中断(Interrupt)详解

    STM32F4中断 Interrupt 详解 文章目录 STM32F4中断 Interrupt 详解一 中断是什么 二 STM32的中断体系2 1 STM32的中断分类2 2 STM32中断优先级2 2 1 抢占优先级2 2 2 响应优先级
  • 轮询、中断和DMA三种方式的原理和联系

    CPU控制外部设备的方式 xff1a 中断 轮询 DMA 轮询中断DMA 由于外部设备的速度差异 xff0c CPU可以使用不同的方式控制外部设备的访问 常见的方式轮询 中断和DMA 轮询 轮询最简单 xff0c CPU通过不断地查询某个外
  • STM32F407的CAN通信实验CAN1CAN2的设置

    CAN2的初始化参照上一篇博文就OK了 需要修改的地方 配置过滤器 CAN FilterInitStructure CAN FilterNumber 14 那为什么要修改额 查datasheet 双 CAN CAN1 主 bxCAN 用于管
  • FreeRTOS例程4-串口DMA收发不定长数据

    FreeRTOS例程4 串口DMA收发不定长数据 知乎 zhihu com
  • Linux驱动程序DMA传输到PC作为主机的PCIe卡

    我正在开发一个 DMA 例程 将数据从 PC 传输到 PCIe 卡上的 FPGA 我阅读了 DMA API txt 和 LDD3 ch 15 详细信息 但是 我不知道如何从 PC 到 PCIe 卡上的一致 iomem 块进行 DMA 传输
  • STM32的HAL中实现单按、长按和双按功能

    我正在尝试实现单击 双击和长按功能来执行不同的功能 到目前为止 我已经理解了单击和长按的逻辑 但我不知道如何检测双击 至于代码 我使用计数器实现了单击和长按 但代码仅停留在第一个 if 条件上 bool single press false
  • PWM DMA 到整个 GPIO

    我有一个 STM32F4 我想对一个已与掩码进行 或 运算的 GPIO 端口进行 PWM 处理 所以 也许我们想要 PWM0b00100010一段时间为 200khz 但随后 10khz 后 我们现在想要 PWM0b00010001 然后

随机推荐

  • 利用CSS浮动制作一个简易导航栏

    初学CSS 利用CSS浮动和无序列表制作一个简易导航栏 xff1a lt DOCTYPE html gt lt html lang 61 34 en 34 gt lt head gt lt meta charset 61 34 UTF 8
  • ERROR Error: Cannot find module ‘vue-loader-v16/package.json‘ vue3.0安装时的错误

    vue3 0 出来一段时间了 xff0c 一直没机会学 xff0c 现在按照网上的教程安装时居然有报错 xff01 我的解决是 直接把项目里面的node modules和package lock json文件删除了 xff0c 然后在重新执
  • php 树形菜单数据获取

    php 树形菜单数据获取 public function generateTree data condition 61 array if empty condition foreach data as k 61 gt v if in arr
  • go语言编译前端静态文件到可执行文件 -statik

    statik 安装 go get github com rakyll statik statik 使用 1 把安装好的包 里面的 statik go 文件编译好 然后运行 2 编译好的statik go文件 src 61 你的静态文件路径
  • goLand全局修改时区

    两种方法 第一种 xff1a loc 61 time FixedZone 34 UTC 34 8 3600 time Local 61 loc 第二种 loc err 61 time LoadLocation 34 America Atka
  • python之Pyperclip模块

    python之Pyperclip模块 下面介绍一下 xff0c python中的Pyperclip模块 xff0c 它的简单又实用 xff0c 主要用法就2点 xff1a 1 用于复制剪贴板里的内容 2 向剪贴板写入内容 一 Pypercl
  • 利用栈判断一个字符串是否是回文

    利用栈判断一个字符串是否是回文 问题描述 编写一个程序 xff0c 判断一个字符串是否为回文 xff08 顺读和倒读都一样的字符串称为回文 xff09 输入形式 长度小于100的任意字符串 输出形式 如果输入字符串是回文 xff0c 则输出
  • Java把String转换成Date类型(Date转换成String类型)

    1 String转换成Date类型 span class token class name SimpleDateFormat span ft span class token operator 61 span span class toke
  • 微信小程序开发自学笔记 —— 七、性能优化

    性能优化 启动 在小程序启动时 xff0c 微信会为小程序展示一个固定的启动界面 xff0c 界面内包含小程序的图标 名称和加载提示图标 此时 xff0c 微信会在背后完成几项工作 xff1a 下载小程序代码包 加载小程序代码包 初始化小程
  • Error: failed to unmarshal json. invalid character “*”looking for beginning of value解决方案

    IPFS config时出现 Error failed to unmarshal json invalid character looking for beginning of value 在Win10 命令行执行ipfs config命令
  • Jsp的四种作用域范围

    首先要声明一点 xff0c 所谓 34 作用域 34 就是 34 信息共享的范围 34 xff0c 也就是说一个信息能够在多大的范围内有效 JSP的四种范围 xff0c 分别为page request session application
  • go 调用shell命令 两种方式(有无返回值)

    阻塞方式 需要执行结果 适用于执行普通非阻塞shell命令 xff0c 且需要shell标准输出的需要对shell标准输出的逐行实时进行处理的 非阻塞方式 不需要执行结果 官网的标准中文库 阻塞方式 需要执行结果 主要用于执行shell命令
  • linux内核链表应用--笔记

    Windows 应用linux内核链表 一 从网上现在linux kernel代码 linux内核版本有2种 稳定版 次版本为偶数 xff0c 开发版 次版本为奇数 版本号 主版本 次版本 释出版本 修改版本 内核下载连接网站 xff1a
  • STM32单片机产生PWM信号

    STM32单片机产生PWM信号 1 开发环境 目标单片机 STM32F407VET6芯片 xff0c 系统时钟高达168Mhz 开发平台 xff1a KEIL 5 编写程序借助ST公司的标准函数库 xff0c 不过现在已经不更新这个写函数库
  • 应用linux内核链表

    一 STM32应用linux内核链表 在此之前 xff0c 已经对Linux内核链表已经移植过一次 不过是针对Windows平台 xff0c 下面是链接 xff1a https blog csdn net qq 36883460 artic
  • 数据结构与算法 ---- C/C++

    数据结构与算法 C C 43 43 学习数据结构的目的 xff1a 针对不同的情况使用不同数据结构 xff0c 去解决不同的问题 一 线性表 线性表一般有几个函数 xff08 宏定义 xff09 xff1a 初始化线性表 List Init
  • 单片机低功耗

    单片机低功耗 如何减低整个系统功耗 xff1f 从两个方面下手 xff1a 软件 xff1f 硬件 xff1f 软件 xff1a 减少外设使用 xff08 不需要的就关掉 xff09 xff0c 减低时钟频率 xff0c 尽量选择低功耗模式
  • RS485通讯---Modbus数据链路层与应用层(二)

    前言 RS485通讯 Modbus物理层 xff1a https blog csdn net qq 36883460 article details 105630712 Modbus RTU通讯协议中OSI模型 xff0c 数据链路层和应用
  • 【笔记】MySQL 5+ 相同用户的数据,取最新登记日期的那条

    需求 xff1a MySQL5 43 处理 xff0c 下面表中 xff0c 用户名相同的数据 xff0c 取最新登记日期的登记号码 表名 xff1a userinfo 用户名 username 登记号码 regis num 登记时间 re
  • STM32F4应用DMA——串口收发不定长数据

    STM32F4应用DMA 串口收发不定长数据 使用STM32自带DMA传输数据 xff0c 可以减轻CPU负担 xff0c 只需设置一些参数即可发送想要发送的数据 xff0c 以下是STM32F407VE芯片测试过的部分代码 xff0c 可