二十五、STM32的DMA(数据转运)

前言:在嵌入式系统中,DMA(Direct Memory Access)可以把数据在外设与内存之间(或内存与内存之间)搬运,而无需 CPU 每次拷贝,极大降低 CPU 负担、提高实时性。下一章节将结合ADC。

目录

一、接线图

二、DMA-API介绍

三、代码实现

四、程序现象

五、DMA+ADC思路


一、接线图

二、DMA-API介绍

1.void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);

作用:把指定 DMA 通道的寄存器恢复到复位默认值。

2.void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);

作用 :把 DMA_InitTypeDef 结构体中描述的配置写入指定通道寄存器(源地址、目的地址、传输方向、数据宽度、递增模式、传输计数、模式、优先级等)。

3.void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);

作用 :把 DMA_InitTypeDef 结构体填充为库定义的默认值。

4.void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

作用:使能或失能指定 DMA 通道(开启或停止 DMA 工作)。

5.void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);

作用:配置并使能/禁用 DMA 通道的中断(如传输完成 TC、中途半传 HT、传输错误 TE)。

6.void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);

作用:设置当前传输计数器(要搬运的数据单元数量)。此计数在 DMA 工作时会递减。

7.uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

作用:读取当前剩余的传输计数(可以用于监测传输进展或实现超时)。

8.FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);

作用 :读取 DMA 的全局/通道标志(例如传输完成 DMA1_FLAG_TC1、传输错误等)。

9.void DMA_ClearFlag(uint32_t DMAy_FLAG);

作用:清除对应的 DMA 标志位(常在处理中或中断服务中使用)。

10.ITStatus DMA_GetITStatus(uint32_t DMAy_IT);

作用:检查某中断源(TC/HT/TE)是否挂起。常在中断向量中判断具体中断原因。

11.void DMA_ClearITPendingBit(uint32_t DMAy_IT);

作用:清除 DMA 中断挂起位,结束中断处理。

三、代码实现

1.DMA初始化

复制代码
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	MyDMA_Size = Size;					//将Size写入到全局变量,记住参数Size
	
	/*开启时钟*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//开启DMA的时钟
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;										//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度,选择字节
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//外设地址自增,选择使能
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;							//存储器基地址,给定形参AddrB
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;			//存储器数据宽度,选择字节
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					//存储器地址自增,选择使能
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;						//数据传输方向,选择由外设到存储器
	DMA_InitStructure.DMA_BufferSize = Size;								//转运的数据大小(转运次数)
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;							//模式,选择正常模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;								//存储器到存储器,选择使能
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;					//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);							//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	/*DMA使能*/
	DMA_Cmd(DMA1_Channel1, DISABLE);	
}

2.转运接口

复制代码
void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);					
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);	
	DMA_Cmd(DMA1_Channel1, ENABLE);						
	
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);	
	DMA_ClearFlag(DMA1_FLAG_TC1);						
}

3.主函数

复制代码
int main(void)
{
	
	OLED_Init();				
	
	MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);	
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "DataA");
	OLED_ShowString(3, 1, "DataB");
	
	/*显示数组的首地址*/
	OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);
	OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);
		
	while (1)
	{
		DataA[0] ++;		//变换测试数据
		DataA[1] ++;
		DataA[2] ++;
		DataA[3] ++;
		
		OLED_ShowHexNum(2, 1, DataA[0], 2);		//显示数组DataA
		OLED_ShowHexNum(2, 4, DataA[1], 2);
		OLED_ShowHexNum(2, 7, DataA[2], 2);
		OLED_ShowHexNum(2, 10, DataA[3], 2);
		OLED_ShowHexNum(4, 1, DataB[0], 2);		//显示数组DataB
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10, DataB[3], 2);
		
		Delay_ms(1000);		//延时1s,观察转运前的现象
		
		MyDMA_Transfer();	//使用DMA转运数组,从DataA转运到DataB
		
		OLED_ShowHexNum(2, 1, DataA[0], 2);		//显示数组DataA
		OLED_ShowHexNum(2, 4, DataA[1], 2);
		OLED_ShowHexNum(2, 7, DataA[2], 2);
		OLED_ShowHexNum(2, 10, DataA[3], 2);
		OLED_ShowHexNum(4, 1, DataB[0], 2);		//显示数组DataB
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10, DataB[3], 2);

		Delay_ms(1000);		//延时1s,观察转运后的现象
	}
}

四、程序现象

  • 初始化 OLED 与 DMA,用 MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4) 把两个数组地址传入,并显示两者的首地址与数组内容。

  • 主循环先修改 DataA[](自增),显示 DataA 与 DataB。

  • 调用 MyDMA_Transfer() 将 DataA 的内容拷贝到 DataB(通过 DMA)。

  • 通过 OLED 在调用前后对比两组值,可以直观观察到 DMA 完成后 DataB 被更新为 DataA 的当前值。

五、DMA+ADC思路

  1. 准备缓冲区

  2. 配置 ADC

  3. 配置 DMA(外设→内存)

  4. 启动 DMA。

  5. 处理数据

相关推荐
创界工坊工作室21 分钟前
30-基于STM32的小麦烘干塔温度控制系统设计
stm32·单片机·嵌入式硬件·proteus
Zwawa1 小时前
先把轮子转稳,才不会烧心:编码器速度环 PID 怎么接回 PWM
c语言·单片机·嵌入式
单片机仿真设计2 小时前
【proteus仿真设计】基于 STM32 的开荒机器人遥控控制系统设计(仿真图+程序)
stm32·嵌入式硬件·机器人·proteus
smartpi_ai2 小时前
51 单片机写好了,大模型语音怎么接?17T 全驱动的现成方案、双板串口分工的边界与画板前的三项硬件确认
单片机·嵌入式硬件·学习
我先去打把游戏先3 小时前
NimBLE是什么-ESP32蓝牙协议栈详解
单片机·嵌入式硬件·ubuntu
木子n13 小时前
车载以太网与SOME/IP服务化通信实战-02.状态机与订阅:SOME/IP-SD的三态迁移、重传账与上电风暴
嵌入式硬件·网络协议·车载系统·车载以太网·some/ip
Gongsion3 小时前
30V输入耐压|1.5μA超低静态电流|200mA输出,CSM52系列:线性稳压器LDO,12V母线转3.3V/5V传感器、MCU弱电支路可靠稳压方案
单片机·嵌入式硬件·车载系统·无人机·智能家居
Steelenwang4 小时前
基于XT960的泵站监控方案
嵌入式硬件·物联网·硬件工程·智慧城市·智能硬件
国科安芯4 小时前
面向低轨卫星互联网载荷控制的抗辐射MCU技术综述
嵌入式硬件·mcu·卫星·低轨卫星·商业航天·抗辐射·微型互联网
2401_862880826 小时前
input 子系统框架 、platform 驱动框架
linux·驱动开发·嵌入式硬件