STM32-DMA数据转运

本文详细描述了STM32F10x平台中DMA(直接内存访问)用于存储器到存储器的数据传输过程,以及ADC多通道数据采集与DMA协助的传输策略,包括DMA初始化、配置和数据处理方法。
摘要由CSDN通过智能技术生成

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述DMA进行转运的条件

  • 1:开关控制,DMA_CMD必须使能
  • 2:传输计数器必须大于0
  • 3:触发源必须有触发的信号

在这里插入图片描述在这里插入图片描述DMA存储器到存储器的数据转运
接线图
在这里插入图片描述

在这里插入图片描述

#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;
uint8_t Dma_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size){
	     MyDMA_Size = Size;
                                                                             // 1: 启rcc时钟,DMA是AHB总线上的外设时钟控制所以要使用的是AHD总线控制
	     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	     DMA_InitTypeDef DMAStruture;
	                                                                            // 引出DMA的结构体,外设站点的起始地址,数据宽度是否自增
	     DMAStruture.DMA_PeripheralBaseAddr = AddrA;                            // 外设站点的基础地址,要编写的是一个32位的地址
	     DMAStruture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;      // 设置的是地址,也就是按照字节的方式自增
	     DMAStruture.DMA_PeripheralInc = DMA_PeripheralInc_Enable;              // 指定参数的地址是不是自增,根据上面的内容数组之间的数据转运地址是要自己增加的
	                                                                            // 存储器站点的,起始地址,数据宽度是否自增
	     DMAStruture.DMA_MemoryBaseAddr = AddrB;                                // 存储器 站点的基地址
	     DMAStruture.DMA_MemoryDataSize =DMA_MemoryDataSize_Byte;               // 以字节的方式传输
	     DMAStruture.DMA_MemoryInc = DMA_MemoryInc_Enable;                      // 自己增加
	     
                                                                              // 传输方向
	     DMAStruture.DMA_DIR = DMA_DIR_PeripheralSRC;                           // 指定是源地址还是起始地址
	                                                                            // 缓存区大小,也就是传输计数器,也就是传输的次数
	     DMAStruture.DMA_BufferSize = Size;
	                                                                            // DMA的传输模式,也就是是否使用自动重装
       DMAStruture.DMA_Mode = DMA_Mode_Normal;
																																							// DMA的,选择触发方式,也就是软件触发还是硬件触发,
	     DMAStruture.DMA_M2M =DMA_M2M_Enable; 
																																							// DMA的优先级
       DMAStruture.DMA_Priority = DMA_Priority_Medium;
																																							// 存储器到存储器的数据转运,使用的是软件的触发方式,所以通道的选择可以使用任意的通道
	     DMA_Init(DMA1_Channel1, &DMAStruture);
			 
			 // 使能DMA,也就是DMA数据转运
			 DMA_Cmd(DMA1_Channel1,DISABLE);
			 
}
// 启动DMA转运
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); 
}

DMA.h
在这里插入图片描述

`#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
// 初始化DMA让DMA里面的数据转运到DMB中去

uint8_t DataA[] = {0x01,0x02,0x03,0x04};
uint8_t DataB[] = {0,0,0,0};


int main(void)
{
  // 初始化oled
	OLED_Init();
	// 后面的是转运后的数据,在这个过程中源端数据DATAA的数据是不会变化的
  Dma_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);
		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);
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10,DataB[3], 2);
		Delay_ms(1000);
		MyDMA_Transfer();
		OLED_ShowHexNum(2, 1, DataA[0], 2);
		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);
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10,DataB[3], 2);
		Delay_ms(1000);
	}
}
`

DMA+ADC多通道数据转运

接线图
在这里插入图片描述

在这里插入图片描述AD.C部分的代码

#include "stm32f10x.h"                  // Device header
		//	  1: 开启RCC时钟,包括ADC和GPIO的时钟
		//    2:配置GPIO将GPIO配置为模拟输入模式
		//	  3:配置多路开关将左边的通道接入到规则组中
		//    4:配置ADC转换器,单次转换,连续转换,扫描还是非扫描
		//	  5:开关控制调用ADC_COM参数ADC配置完成就能正常工作
uint16_t AD_Value[4];

void AD_Init(void){
	  
	  // 开启ADC的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	  // 开启GPIOA的时钟
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	  // 配置ADC_CLK,72MHz/6 = 12MHz
	  RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	  // 配置GPIO
	  GPIO_InitTypeDef GPIO_InitStructre;
	  // 将GPIO的模式引用出来
	  GPIO_InitStructre.GPIO_Mode =GPIO_Mode_AIN;       //  选择GPIO的模式,设置为AN模拟输入的模式在AIN模式下GPIO口是没有效果的
	  GPIO_InitStructre.GPIO_Pin = 	GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3;         //  选择GPIO的输出模式,选择输出的管脚
	  GPIO_InitStructre.GPIO_Speed = GPIO_Speed_50MHz;  //  选择GPIO的时钟频率
	  GPIO_Init(GPIOA,&GPIO_InitStructre);              //  初始化GPIO
	
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); 
	  ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5); 
	  ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_55Cycles5); 
	  ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_55Cycles5); 
		
	  // 第三步 ---> 使用结构体初始化ADC
	  ADC_InitTypeDef ADC_InitStructure;
	  // 引出结构体成员
	  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;                 //  配置扫描的模式
		ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;              //  配置ADC的数据对齐方式
		ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //  配置ADC的外部触发转换选择:这里使用内部软件触发的方式
		ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                  //  配置ADC的工作模式为独立模式
		ADC_InitStructure.ADC_NbrOfChannel = 4;                            //  配置通道数目
		ADC_InitStructure.ADC_ScanConvMode = ENABLE;                       //  配置扫描的模式
	  ADC_Init(ADC1,&ADC_InitStructure);
	
	  // 以下还可以配置中断和模拟看门狗,根据自己的需求进行配置
		
		
	
	     DMA_InitTypeDef DMAStruture;
	                                                                            // 引出DMA的结构体,外设站点的起始地址,数据宽度是否自增
	     DMAStruture.DMA_PeripheralBaseAddr =(uint32_t)&ADC1->DR;                            // 外设站点的基础地址,要编写的是一个32位的地址
	     DMAStruture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;      // 设置的是地址,也就是按照字节的方式自增
	     DMAStruture.DMA_PeripheralInc = DMA_PeripheralInc_Disable;              // 指定参数的地址是不是自增,根据上面的内容数组之间的数据转运地址是要自己增加的
	                                                                            // 存储器站点的,起始地址,数据宽度是否自增
	     DMAStruture.DMA_MemoryBaseAddr = (uint32_t)AD_Value;                                // 存储器 站点的基地址
	     DMAStruture.DMA_MemoryDataSize =DMA_MemoryDataSize_HalfWord;               // 以字节的方式传输
	     DMAStruture.DMA_MemoryInc = DMA_MemoryInc_Enable;                      // 自己增加
	     
                                                                              // 传输方向
	     DMAStruture.DMA_DIR = DMA_DIR_PeripheralSRC;                           // 指定是源地址还是起始地址
	                                                                            // 缓存区大小,也就是传输计数器,也就是传输的次数
	     DMAStruture.DMA_BufferSize = 4;
	                                                                            // DMA的传输模式,也就是是否使用自动重装
       DMAStruture.DMA_Mode = DMA_Mode_Normal;
																																							// DMA的,选择触发方式,也就是软件触发还是硬件触发,
	     DMAStruture.DMA_M2M =DMA_M2M_Disable; 
																																							// DMA的优先级
       DMAStruture.DMA_Priority = DMA_Priority_Medium;
																																							// 存储器到存储器的数据转运,使用的是软件的触发方式,所以通道的选择可以使用任意的通道
	     DMA_Init(DMA1_Channel1, &DMAStruture);
			 
			 // 使能DMA,也就是DMA数据转运
			 DMA_Cmd(DMA1_Channel1,ENABLE);
			 // 开启DMA触发信号
			 ADC_DMACmd(ADC1,ENABLE);
			 
		
		// 开启ADC设置,第一个ENABLE第二个开启ADC的电源
		ADC_Cmd(ADC1,ENABLE); 
	  // 对ADC进行校准,这里分别有四个函数可以进行配置
		ADC_ResetCalibration(ADC1);  						              // 复位校准
		while(ADC_GetResetCalibrationStatus(ADC1) == SET);    // 返回复位校准的状态
	  ADC_StartCalibration(ADC1);                           // 启动校准
		while(ADC_GetCalibrationStatus(ADC1) == SET);         // 获取校准后的状态        
	  // 1: 软件触发转换 2:等待触发完成也就是等待EOC标志位设置为1,3:读取ADC数据寄存器
	  ADC_SoftwareStartConvCmd(ADC1,ENABLE);

}
uint16_t AD_GetValue(void){
	   // 重新个传输计数器赋值,在重新个传输计数器赋值的同时要给传输计数器失能
     DMA_Cmd(DMA1_Channel1,DISABLE);
	   DMA_SetCurrDataCounter(DMA1_Channel1,4);  
	   DMA_Cmd(DMA1_Channel1,ENABLE);
     // 单次触发模式,在DMA触发之前要重新写入传输计数器
	   ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	   // 等待DMA、转换完成的代码
	   while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
     DMA_ClearFlag(DMA1_FLAG_TC1); 
}

AD.H部分代码

#ifndef __AD_H
#define __AD_H


extern uint16_t AD_Value[4]; // 这个数组作为一个外部可以调用的数组
uint16_t AD_GetValue(void);
void AD_Init(void);


#endif

main.c部分的代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0,AD1,AD2,AD3;
float Votage;
int main(void)
{
  // 初始化oled
	OLED_Init();
	AD_Init();
	
	OLED_ShowString(1,1,"AD0:");
  OLED_ShowString(2,1,"AD1:");
	OLED_ShowString(3,1,"AD3:");
  OLED_ShowString(4,1,"AD4:");
	
	while (1)
	{
    AD_GetValue();
		OLED_ShowNum(1, 5, AD_Value[0], 4);
		OLED_ShowNum(2, 5, AD_Value[1], 4);
		OLED_ShowNum(3, 5, AD_Value[2], 4);
		OLED_ShowNum(4, 5, AD_Value[3], 4);
		Delay_ms(100);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基本的 STM32F4 DMA 数据传输的代码示例: ``` #include "stm32f4xx.h" #define BUFFER_SIZE 1024 uint32_t src_buffer[BUFFER_SIZE]; uint32_t dst_buffer[BUFFER_SIZE]; void init_DMA(void) { DMA_InitTypeDef dma_init; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); DMA_StructInit(&dma_init); dma_init.DMA_Channel = DMA_Channel_0; dma_init.DMA_PeripheralBaseAddr = (uint32_t)src_buffer; dma_init.DMA_Memory0BaseAddr = (uint32_t)dst_buffer; dma_init.DMA_DIR = DMA_DIR_PeripheralToMemory; dma_init.DMA_BufferSize = BUFFER_SIZE; dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Enable; dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; dma_init.DMA_Mode = DMA_Mode_Normal; dma_init.DMA_Priority = DMA_Priority_High; dma_init.DMA_FIFOMode = DMA_FIFOMode_Enable; dma_init.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; dma_init.DMA_MemoryBurst = DMA_MemoryBurst_Single; dma_init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &dma_init); } int main(void) { init_DMA(); // 启动 DMA 传输 DMA_Cmd(DMA2_Stream0, ENABLE); while (1) { // 循环等待 DMA 传输完成 while (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF0) == RESET) {} DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF0); // 处理数据 for (int i = 0; i < BUFFER_SIZE; i++) { // ... } // 重新启动 DMA 传输 DMA_Cmd(DMA2_Stream0, ENABLE); } } ``` 在这个代码中,我们首先定义了两个缓冲区 `src_buffer` 和 `dst_buffer`,它们的大小均为 `BUFFER_SIZE`。然后我们在 `init_DMA()` 函数中初始化了 DMA 控制器,并将 `src_buffer` 的地址作为外设地址,`dst_buffer` 的地址作为内存地址,设置了数据传输方向为外设到内存,以及传输数据的大小和增量等参数。 在 `main()` 函数中,我们启动了 DMA 的传输,并在一个无限循环中等待 DMA 的传输完成。一旦传输完成,我们处理数据,并再次启动 DMA 的传输。在这个无限循环中,DMA 将会不断地传输数据,直到程序结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值