STM32实验DMA数据搬运小助手

本次实验做的是将一个数组的内容利用DMA数据搬运小助手搬运到另外一个数组中去。

最后的实验结果:

可以看到第四行的数据就都不是0了,成功搬运了过来。

DMA实现搬运的步骤其实不是很复杂,复杂的是结构体参数:

整个步骤为:

第一步:RCC开启DMA的时钟

第二步:调用DMA_Init,初始化各个参数 (包括外设和存储器的起始地址,数据宽度,地址是否自增,方向,传输计数器,是否需要自动重装,选择触发源,通道优先级)

第三步:调用DMA_CMD,通道使能(要在对应的外设调用XXX_DMACmd开启一下触发信号的输出,如果需要DMA的中断,就调用DMA_ITConfig,开启中断输出,再在NVIC里,配置中断通道,最后写中断函数就行了)

下面介绍一下DMA相关的库函数:

void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);

//恢复缺省配置

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct); //DMA初始化

void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);

//DMA结构体初始化

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

//DMA使能/失能

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

//中断输出使能/失能

void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); //DMA设置当前数据寄存器

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

//DMA获取当前数据寄存器的值

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG); //获取标志位状态 void DMA_ClearFlag(uint32_t DMAy_FLAG); //清除状态标志位

ITStatus DMA_GetITStatus(uint32_t DMAy_IT); //获取中断标志位状态 void DMA_ClearITPendingBit(uint32_t DMAy_IT);//清除中断标志位状态

下面是MyDMA.c的文件:

cs 复制代码
#include "stm32f10x.h"                  // Device header


void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	//第一步:RCC开启DMA的时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	//第二步:调用DMA_Init,初始化各个参数(包括外设和存储器的起始地址,数据宽度,地址是否自增,方向,传输计数器,是否需要自动重装,选择触发源,通道优先级)
	DMA_InitTypeDef DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr = AddrA;
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;   //数据宽度,按字节的宽度搬运
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;    //启用地址++自增
	// 以上是外设站点(数据来源)的起始地址、数据宽度、是否自增。
	
	DMA_InitStruct.DMA_MemoryBaseAddr = AddrB;
	DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度,按字节的宽度粘贴
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;       //启用地址++自增
	//以上3条是存储器(目的地)的起始地址、数据宽度、是否自增。
	
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;    //传输方向
	DMA_InitStruct.DMA_BufferSize = Size;  //缓存区大小,其实就是传输计数器   取用传进来的参数
	DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;   //传输模式,其实就是是否启用自动重装     不自动重装
	DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;   //选择是硬件触发还是软件触发    软件触发
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;   // 优先级    选择中等优先级
	DMA_Init(DMA1_Channel1, &DMA_InitStruct);  //第一个参数选择了是哪个DMA到哪个DMA通道,第二个参数结构体
	
	//第三步:调用DMA_CMD,通道使能(要在对应的外设调用XXX_DMACmd开启一下触发信号的输出,如果需要DMA的中断,就调用DMA_ITConfig,开启中断输出,再在NVIC里,配置中断通道,最后写中断函数就行了)
	DMA_Cmd(DMA1_Channel1, ENABLE);
}

下面是MyDMA.h文件:

cs 复制代码
#ifndef __MYDMA_H
#define __MYDMA_H


void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);



#endif

下面是main.c文件:

cs 复制代码
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "MyDMA.h"

uint8_t dataA[]={0x01, 0x02, 0x03, 0x04};
uint8_t dataB[]={0,0,0,0};

int main(void)
{
	OLED_Init();       //oled  屏幕初始化
	
	OLED_ShowHexNum(1,1, dataA[0], 2);
	OLED_ShowHexNum(1,4, dataA[1], 2);
	OLED_ShowHexNum(1,7, dataA[2], 2);
	OLED_ShowHexNum(1,10, dataA[3], 2);
	
	OLED_ShowHexNum(2,1, dataB[0], 2);
	OLED_ShowHexNum(2,4, dataB[1], 2);
	OLED_ShowHexNum(2,7, dataB[2], 2);
	OLED_ShowHexNum(2,10, dataB[3], 2);
	
	MyDMA_Init((uint32_t)dataA, (uint32_t)dataB, 4);
	
	OLED_ShowHexNum(3,1, dataA[0], 2);
	OLED_ShowHexNum(3,4, dataA[1], 2);
	OLED_ShowHexNum(3,7, dataA[2], 2);
	OLED_ShowHexNum(3,10, dataA[3], 2);
	
	OLED_ShowHexNum(4,1, dataB[0], 2);
	OLED_ShowHexNum(4,4, dataB[1], 2);
	OLED_ShowHexNum(4,7, dataB[2], 2);
	OLED_ShowHexNum(4,10, dataB[3], 2);
	while(1)
	{
		
	}
}

这样就实现了最简单的DMA数据搬运的小功能了。看似没有什么用,其实这功能是在硬件上自动完成的,可以在不浪费软件资源的前提下,把外设上的数据搬运到内存中来,软件就能直接用了,还是非常的方便的。

相关推荐
LNN20221 小时前
STM32H7 + 迪文屏 DGUS 开发实战:从零构建工业级时间设置界面
stm32·单片机·嵌入式硬件
Z文的博客5 小时前
嵌入式MCU与迪文屏通信:DMA+环形FIFO+变长队列+状态机完整手册
stm32·单片机·串口·dma·中断·串口dma·嵌入式单片机
BackCatK Chen5 小时前
STM32保姆级入门教程|第7章:串口通信(USART)收发数据 + printf重定向打印调试(功能超详细+CubeIDE手把手)
stm32·串口通信·usart·stm32cubeide·printf重定向·嵌入式调试·中断接收
12.=0.5 小时前
【stm32_5】Systick嘀嗒定时器、解析时钟源、分析时钟树、应用Systick设计延时
c语言·stm32·单片机·嵌入式硬件
达不溜的日记6 小时前
CAN总线网络传输层CanTp详解
网络·stm32·嵌入式硬件·网络协议·网络安全·信息与通信·信号处理
森利威尔电子-7 小时前
森利威尔SL6129兼容 AL8805 / AL8806,输入电压 5.5V - 30V,最大输出电流 1.2A
单片机·嵌入式硬件·集成电路·芯片·电源芯片
FreakStudio7 小时前
嘉立创开源:应该是全网MicroPython教程最多的开发板
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy
qq_441685758 小时前
CC26xx开发 第一节 前期准备
嵌入式硬件
史蒂芬_丁8 小时前
TI F28P65 使用 ePWM 模块模拟 SPI 时钟的详细方法
单片机·嵌入式硬件·fpga开发
LinuxRos8 小时前
I2C子系统与驱动开发:从协议到实战
linux·人工智能·驱动开发·嵌入式硬件·物联网