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

2023-11-03

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

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

一、初始化部分

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

void uart2_init(uint32_t m_u32bound)
{
 		//01-GPIO配置
		GPIO_InitTypeDef GPIO_InitStruct = {0};
		
		//02-开启时钟
		__HAL_RCC_USART2_CLK_ENABLE();			//启动串口2时钟
		__HAL_RCC_GPIOA_CLK_ENABLE(); 			//启动GPIOA时钟
		
		//03-配置引脚
		GPIO_InitStruct.Pin = GPIO_PIN_2;				//USART1_TX   PA.2
    	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;			//推完复用模式
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速配置
   	 	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);			//PA2初始化
			
		GPIO_InitStruct.Pin = GPIO_PIN_3;				//USART1_RX	  PA.3
    	GPIO_InitStruct.Mode = GPIO_MODE_INPUT; 		//输入模式:浮空输入
		GPIO_InitStruct.Pull =	GPIO_NOPULL;			//没有上下拉
		HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);  		//PA3初始化

		//04-设置串口中断
		HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);  
		HAL_NVIC_EnableIRQ(USART2_IRQn);
		
		//05-USART 配置
		I_huart2.Instance = USART2;
		I_huart2.Init.BaudRate = m_u32bound;			//波特率
		I_huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;	//没有硬件控制流
		I_huart2.Init.Mode = UART_MODE_TX_RX;			//收发模式
		I_huart2.Init.StopBits = UART_STOPBITS_1; 		//停止位1位
		I_huart2.Init.Parity = UART_PARITY_NONE; 		//无奇偶校验位
		I_huart2.Init.OverSampling = UART_OVERSAMPLING_16; //
		I_huart2.Init.WordLength = UART_WORDLENGTH_8B;  //数据位:8个
		if(HAL_UART_Init(&I_huart2)!=HAL_OK)		 //初始化串口
			while(1);
		
		//04-设置中断优先级
		HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); 
		HAL_NVIC_EnableIRQ(USART2_IRQn);
		
		//05-启动DMA1时钟
		__HAL_RCC_DMA1_CLK_ENABLE();

		//07-DMA1  USART2_RX
	    hdma_usart2_tx.Instance = DMA1_Channel7;						//通道7
	    hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;			//外设到内存
	    hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;				//外设地址不增长
	    hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;					//内存地址增长模式
	    hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;  //字节对齐
	    hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;		//字节对齐
	    hdma_usart2_tx.Init.Mode = DMA_NORMAL;							//正常模式
	    hdma_usart2_tx.Init.Priority = DMA_PRIORITY_HIGH;				//传输等级:高
	    if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
			while(1);
		
		//06-DMA1  USART2_RX
		hdma_usart2_rx.Instance = DMA1_Channel6;  				//通道6
		hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; 	//内存到外设
		hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;  		//外设地址不增长
		hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;  			//内存地址增长
		hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//字节对齐
		hdma_usart2_rx.Init.MemDataAlignment = DMA_PDATAALIGN_BYTE;	 //字节对齐
		hdma_usart2_rx.Init.Mode = DMA_CIRCULAR;  				//循环模式
		hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;   		//传输等级:低
		if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
			while(1);
		
		//07-开启发送完成中断
		__HAL_UART_ENABLE_IT(&I_huart2, UART_IT_TC);	//发送完成中断
		
		//08-开启DMA接收
		HAL_DMA_Start(&hdma_usart2_rx,(uint32_t)&USART2->DR,(uint32_t)u8rxbuff,BUFFMAX);
		SET_BIT(I_huart2.Instance->CR3, USART_CR3_DMAR);//USART2请求  DMA启动 
				
		//09-开启DMA发送
		HAL_DMA_Start(&hdma_usart2_tx, (uint32_t)u8txbuff,(uint32_t)&USART2->DR, 0);
		SET_BIT(I_huart2.Instance->CR3, USART_CR3_DMAT);//USART2请求  DMA启动

}

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

这个需要开启串口发送完成中断,当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传送数据到串口已经完成发送。

//入口参数
//txlen : 发送数据长度
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;		//Ô¤·ÖƵϵÊý
  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
			}
		}

	}
}

五、测试

#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(使用前将#替换为@)

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

  • 当数据大小较小时,内存到内存 DMA 传输是否需要权衡?

    我正在学习 STM32 F4 微控制器 我正在尝试找出使用 DMA 的限制 根据我的理解和研究 我知道如果数据量较小 即设备使用DMA生成或消耗少量数据 则开销会增加 因为DMA传输需要DMA控制器执行操作 从而不必要地增加系统成本 我做了
  • 140-基于stm32单片机智能晾衣杆控制系统Proteus仿真+源程序

    资料编号 140 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 ds1302时钟 光敏传感器 蜂鸣器 LED灯 制作一个基于stm32单片机智能晾衣杆控制系统Proteus仿真 2 通过光敏传感器
  • rt-thread studio中新建5.0不能用

    文章目录 一 版本对比 二 文件和文件夹打斜杠 在使用RT Thread studio创建新工程5 0版本的时候 结果发现新建完成之后程序不能正常运行 但是创建4 10版本的时候却能运行 那肯定是新版本出现了BUG 一 版本对比 首先对比了
  • STM32F4 通过软复位跳转到引导加载程序,无需 BOOT0 和 BOOT1 引脚

    我问这个问题是因为可以在这里找到类似问题的答案 通过应用程序跳转到 STM32 中的引导加载程序 即从用户闪存在引导模式下使用引导 0 和引导 1 引脚 用户 JF002 JF002回答 当我想跳转到引导加载程序时 我在其中一个备份寄存器中
  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • 毕业设计 江科大STM32的智能温室控制蓝牙声光报警APP系统设计

    基于STM32的智能温室控制蓝牙声光报警APP系统设计 1 项目简介 1 1 系统构成 1 2 系统功能 2 部分电路设计 2 1 stm32f103c8t6单片机最小系统电路设计 2 2 LCD1602液晶显示电路设计 2 2 风
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n
  • 擦除后无法写入闪存

    所以我不能在擦除后直接写入内部闪存 如果写操作之前没有擦除操作 那么我可以 有什么想法吗 编程函数返回 成功写入 值 但查看内存时 没有写入任何数据 这是代码 uint32 t pageAddress 0x08008000 uint16 t
  • 串口通讯第一次发送数据多了一字节

    先初始化IO再初始化串口 导致第一次发送时 多出一个字节数据 优化方案 先初始化串口再初始化IO 即可正常通讯
  • STM32 暂停调试器时冻结外设

    当到达断点或用户暂停代码执行时 调试器可以停止 Cortex 中代码的执行 但是 当皮质停止在暂停状态下执行代码时 调试器是否会冻结其他外设 例如 DMA UART 和定时器 您只能保留时间 r 取决于外围设备 我在进入主函数时调用以下代码
  • STM32F207 I2C 测试失败

    我正在使用 STM32F207 微控制器在 STM3220G EVAL 板上学习嵌入式开发 我尝试通过连接同一芯片上的两个 I2C2 和 I2C3 模块并发送 接收字符来测试 I2C 接口 这是我当前编写的代码 使用 mdk arm 5 i
  • 无法使用 OpenOCD 找到脚本文件

    我正在尝试按照本教程将 OpenOCD 与我的 ST 发现板一起使用 https japaric github io discovery README html https japaric github io discovery READM
  • 为什么在 Linux 中使用 kmalloc 和 GFP_DMA 时会得到高地址?

    我正在 Linux 中为 DMA 设备编写设备驱动程序 在Linux 设备驱动程序 第 15 章 http lwn net images pdf LDD3 ch15 pdf 它说 对于有这种限制的设备 应该分配内存 通过将 GFP DMA
  • 如何从cdev获取设备

    我正在编写一个内核模块 它将分配一些一致的内存并返回相应的虚拟和物理地址 我正在将模块注册为cdev 分配空间dma alloc coherent 我想使用 mmap 它dma common mmap dma common mmap 需要一
  • STM32 上的 ADC 单次转换

    我正在研究 STM32 F103x 上的 ADC 编程 并从最简单的情况 单次转换开始 测量内部温度传感器 连接到 ADC1 的值 并使用 USART 将其发送到 COM 端口 目标似乎很明确 但是当我尝试将源代码下载到闪存时 它不会向 C
  • 在 Contiki 程序中使用 malloc

    考虑以下 Contiki 程序 include
  • Linux 内核中的 DMA 映射和 DMA 引擎是什么?

    Linux 内核中的 DMA 映射和 DMA 引擎是什么 DMA映射API和DMA引擎API何时可以在Linux设备驱动程序中使用 任何真正的 Linux 设备驱动程序示例作为参考都会很棒 Linux 内核中的 DMA 映射和 DMA 引擎
  • 环形缓冲区和 DMA

    我试图了解从数据包到达网卡到目标应用程序收到数据包之间发生的所有事情 假设 缓冲区足够大 可以容纳整个数据包 我知道情况并非总是如此 但我不想介绍太多技术细节 一种选择是 1 Packet reaches the NIC 2 Interru
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE

随机推荐

  • spring-bean的生命周期和怎么配置spring-bean的后置处理器

    前言 本章是spring基于XML 配置bean系类中第6篇讲解spring bean的生命周期和怎么配置spring bean的后置处理器 个人主页 尘觉主页 个人简介 大家好 我是尘觉 希望我的文章可以帮助到大家 您的满意是我的动力 在
  • 计算PI值到一亿位的算法 (转)

    计算PI值到一亿位的算法 转 more 我大体上考虑了一下用Delphi计算PI值到一亿位的算法 得到一个大体的算法 也好用来交流一下 这是一个构造一种新的长四则运算的算法 所谓长四则运算 是指用数据库的字段来作一个小数 用一个记录来作一个
  • 基于python的毕业设计仓库库存管理系统

    更多项目资源 最下方联系我们 目录 Python项目介绍 资料获取 Python项目介绍 计算机毕业设计python毕设项目之python仓库库存管理系统 IT实战课堂 哔哩哔哩 bilibili计算机毕业设计python毕设项目之pyth
  • 微信浏览器清理缓存的方法

    项目场景 项目包含电脑浏览器和手机的微信公众号两个部分 现在需要在微信端对项目进行测试 问题描述 在微信端打开项目的网页 发现某些部分的功能不如预期 退出微信并在服务器端进行修改 修改完成再次打开该网页 跟修改前的表现一样没有任何变化 原因
  • 企业u盘系统服务器,服务器u盘装系统

    服务器u盘装系统 内容精选 换一换 如果Linux操作系统云服务器未安装密码重置插件 可以参见本节内容重新设置密码 本节操作重置的是root用户的密码 您可以重置完root密码后登录云服务器后再更换秘钥或重置非root用户的密码 Windo
  • Group conv vs. Depthwise separable conv

    本王有话说 这俩属于是做轻量化绕不开的经典工作 盘踞武林好多年 我们的目标学会并企图超越它 分组卷积 Group conv paper 原理 分组卷积 即ResNeXt的亮点 受Inception和AlexNet的启发产生 Inceptio
  • 通过css样式定义span标签实现文本输入框功能

    span style width 200px height 24px line height 24px font size 14px padding 5px 8px border 1px solid ddd 我是文本输入框 span
  • TensorFlow学习过程记录 -- 问题解决

    在运行过程中 输出总是会产生两行警告信息 WARNING tensorflow From D python35 lib site packages tensorflow python util tf should use py 118 in
  • 机器学习中概率论知识复习

    机器学习先验知识概率论部分 发现看Machine Learning Andrew Ng 课程的时候中间有推导过程不是很明白 遂针对性复习 知识内容组织结构 参考 Probability Theory Review for Machine L
  • 使用PHP生成Excel文件并通过邮件发送

    需求 每周一自动检测一个月内即将过期的用户 生成excel2007文件 xlsx文件 并发送给指定的人员 做成一个脚本 使用定时任务即可解决 脚本分解为两步 生成Excel 发送邮件 一 生成Excel 使用简单的更改文件头 header
  • Java入门(6)——集合、基本数据类型和引用数据类型的相互转换

    集合 1 HashMap gt 类 概述 通过key可以找到value key就是键 values就是值 俗称键值对 特点 无序的 值可以重复 键不可以重复的 如果重复了 值就会覆盖 回顾 10 int num 10 jack String
  • Python基础内容:适合刚入门的朋友看的教程

    1 基本概念 1 1 四种类型 python中数有四种类型 整数 长整数 浮点数和复数 整数 如 1 长整数 是比较大的整数 浮点数 如 1 23 3E 2 复数 如 1 2 j 1 1 2 2j 1 2 字符串 字符串 字符的序列 pyt
  • ResNet50模型学习笔记

    ResNet的各种网络结构图如下图所示 ResNet的层级结构 Layer gt Block gt Stage gt Network Layer是最小的单位 ResNet50代表有50层 Block由两层或者三层conv层叠加而成 50层以
  • JavaWeb-form传值(从一个jsp页面传数据到另一个jsp页面)

    第一个页面 login jsp
  • OkHttpClient获取文件并下载

    需要调用第三方接口获取文件 本地通过网页直接下载 public Result doExcelExport String repoId HttpServletResponse response try if StringUtils isBla
  • nginx配置指南

    nginx conf配置 找到Nginx的安装目录下的nginx conf文件 该文件负责Nginx的基础功能配置 配置文件概述 Nginx的主配置文件 conf nginx conf 按以下结构组织 配置块 功能描述 全局块 与Nginx
  • 行为型设计模式之策略模式【设计模式系列】

    系列文章目录 C 技能系列 Linux通信架构系列 C 高性能优化编程系列 深入理解软件架构设计系列 高级C 并发线程编程 设计模式系列 期待你的关注哦 现在的一切都是为将来的梦想编织翅膀 让梦想在现实中展翅高飞 Now everythin
  • C++基础---递归函数

    1 递归函数 1 1 递归函数的定义 递归函数 即在函数体中出现调用自身的函数 即函数Func Type a 直接或间接调用函数本身 递归函数 在数学上 关于递归函数的定义如下 对于某一函数f x 其定义域是集合A 那么若对于A集合中的某一
  • centos安装常见软件

    安装tar yum install y tar 安装zip yum install unzip y 安装上传 yum y install lrzsz y 安装git 方式一 yum install git y 方式二 开发会用的软件 yum
  • STM32F1应用DMA——串口收发不定长数据

    STM32F1应用DMA 串口收发不定长数据 使用STM32自带DMA传输数据 可以减轻CPU负担 只需设置一些参数即可发送想要发送的数据 以下是STM32F1系列芯片测试过的部分代码 可实现DMA串口收发数据 下图来自STM32官网的手册