32DMA数据转运

目录

一.简介

二.存储器映像

三.DMA结构框图

四.测试

五.相关函数 

五.代码实现

(1)存储器到存储器数据转运

(2)ADC单次扫描

(3)ADC连续扫描



一.简介

DMA外设是可以直接访问32内部的存储器的,包括运行内存SRAM,程序存储器Flash和寄存器等

外设的数据寄存器DR,Data Register 

存储器到存储器之间的转运需要软件触发

外设到存储器之间的转运需要硬件触发,对上外设数据的时机

二.存储器映像

主闪存的地址以0x800开头 ,串口下载的程序由STM32接收数据,然后刷新到0x0800位置

外设寄存器都是0x40开头

选项字节存储Flash的读保护,写保护还有看门狗等等的配置

SRAM也就是程序中定义变量,数组,结构体的地方

内核外设就是NVIC和SysTick,内核外设和其他外设不是一个公司设计的

寻址范围为32位,0xFFFF FFFF

Bootloader程序(类似于手机的刷机模式或电脑的PE系统,是辅助主程序进行自我更新的)也叫自举程序,Bootloader接收到USART1数据,刷新到程序存储器,这时主程序处于瘫痪状态,更新好之后再启动主程序,执行新程序

全片擦除后,所有的数据都是FF

选项字节

三.DMA结构框图

通过总线的形式,可以很好地将各种外设分离开来,可以独立各种外设来控制其使能与否,可以实现功耗的控制,以达到节能的目的

控制外设的使能与否就是控制其外设的时钟

 

四.测试
 

#include "stm32f10x.h"                  // Device header
#include "Delay.h"  
#include "OLED.h" 
uint8_t a = 0x66;
int main()
{
	OLED_Init();

	OLED_ShowHexNum(1,1,a,5);
	OLED_ShowHexNum(2,1,(uint32_t)&a,8);//只能显示数字,所以要进行强制类型转换


	while(1)
	{

		
	}	
}

#include "stm32f10x.h"                  // Device header
#include "Delay.h"  
#include "OLED.h" 
const uint8_t a = 0x66;
int main()
{
	OLED_Init();

	OLED_ShowHexNum(1,1,a,5);
	OLED_ShowHexNum(2,1,(uint32_t)&a,8);


	while(1)
	{

		
	}	
}

当我们程序中出现大量不需要修改的数据时 ,就可以定义为常量,节省SRAM的空间,比如查找表,字库数据等

对于变量或常量来说,其地址是由编译器决定的,不同的程序,地址可能不一样

对于外设寄存器来说,其地址是固定的

起始地址+偏移=寄存器的实际地址

五.相关函数 

互联型是STM32F105/107的型号 

五.代码实现

(1)存储器到存储器数据转运

DMA.c

#include "stm32f10x.h"                  // Device header
uint8_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
	DMA_InitTypeDef DMA_InitStructure;
	MyDMA_Size = Size;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	DMA_InitStructure.DMA_MemoryBaseAddr=AddrA;//存储器站点地址
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//地址自增
	DMA_InitStructure.DMA_PeripheralBaseAddr=AddrB;//外设站点地址
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Enable;
	DMA_InitStructure.DMA_BufferSize=Size;//传输计数器
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;//传输方向
	DMA_InitStructure.DMA_M2M=DMA_M2M_Enable;//使用软件触发
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//是否使用自动重装
	DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1,ENABLE);
}
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);//清除标志位
}

 main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"  
#include "OLED.h" 
#include "MyDMA.h" 
const uint8_t arr1[] = {1,2,3,4};//想要由Flash转运到存储器就加const
uint8_t arr2[] = {0,0,0,0};
int main()
{
	
	OLED_Init();
	OLED_ShowString(1,1,"arr1");
	OLED_ShowHexNum(1,6,(uint32_t)arr1,8);
	OLED_ShowString(3,1,"arr2");
	OLED_ShowHexNum(3,6,(uint32_t)arr2,8);
	
	OLED_ShowNum(2,1,arr1[0],2);
	OLED_ShowNum(2,4,arr1[1],2);
	OLED_ShowNum(2,7,arr1[2],2);
	OLED_ShowNum(2,10,arr1[3],2);
	             
	OLED_ShowNum(4,1,arr2[0],2);
	OLED_ShowNum(4,4,arr2[1],2);
	OLED_ShowNum(4,7,arr2[2],2);
	OLED_ShowNum(4,10,arr2[3],2);
	
	MyDMA_Init((uint32_t)arr1,(uint32_t)arr2,4);
	
	while(1)
	{
//		arr1[0]++;
//		arr1[1]++;
//		arr1[2]++;
//		arr1[3]++;
		Delay_ms(1000);
		MyDMA_Transfer();
		OLED_ShowNum(2,1,arr1[0],2);
		OLED_ShowNum(2,4,arr1[1],2);
		OLED_ShowNum(2,7,arr1[2],2);
		OLED_ShowNum(2,10,arr1[3],2);
					 
		OLED_ShowNum(4,1,arr2[0],2);
		OLED_ShowNum(4,4,arr2[1],2);
		OLED_ShowNum(4,7,arr2[2],2);
		OLED_ShowNum(4,10,arr2[3],2);
		Delay_ms(1000);	
	}	
}

(2)ADC单次扫描

AD.C

#include "stm32f10x.h"                  // Device header
uint16_t SRAM_arr[4]={0,0,0,0};
void AD_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;//必须要写在程序的开头位置
	ADC_InitTypeDef ADC_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//ADC都是APB2上的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	//在AIN模式下GPIO是无效的,专门为ADC服务
	//断开GPIO,防止GPIO的输入和输出对模拟电压造成干扰
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分频设置ADC时钟,ADCCLK=12MHz
	
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
	
	
	//若想要在多个序列填充多个通道,可以复制19行进行填充,配置不同的采样时间
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//数据右对齐
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//配置ADC独立模式
	ADC_InitStructure.ADC_ScanConvMode=ENABLE;//是否为扫描模式
	ADC_InitStructure.ADC_NbrOfChannel=4;//扫描模式下会用到的通道数目
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//配置成单次转换
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//不使用外部触发源
	ADC_Init(ADC1,&ADC_InitStructure);
	
	
	
	DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR;//此处ADC要加上&,外设站点地址
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SRAM_arr;//存储器站点地址
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//地址自增
	DMA_InitStructure.DMA_BufferSize=4;//传输计数器
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//传输方向
	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//使用软件触发
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//是否使用自动重装
	DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1,ENABLE);
	ADC_DMACmd(ADC1,ENABLE);
	ADC_Cmd(ADC1,ENABLE);//开启ADC电源
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);//复位校准完成后硬件自动置0
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1)== SET);//校准完成后硬件自动置0
}
void ADC_GetValue(void)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,4);//写入缓存
	DMA_Cmd(DMA1_Channel1,ENABLE);
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//ADC为单次模式要用软件先进行触发一次
	
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);//等待转运完成
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除标志位
}

  main.c

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

int main()
{
	OLED_Init();
	AD_Init();
	OLED_ShowString(1,1,"AD1:");
	OLED_ShowString(2,1,"AD2:");
	OLED_ShowString(3,1,"AD3:");
	OLED_ShowString(4,1,"AD4:");
	

	while(1)
	{
		ADC_GetValue();
		OLED_ShowNum(1,5,SRAM_arr[0],5);
		OLED_ShowNum(2,5,SRAM_arr[1],5);
		OLED_ShowNum(3,5,SRAM_arr[2],5);
		OLED_ShowNum(4,5,SRAM_arr[3],5);
		Delay_ms(100);

	}	
}
	

(3)ADC连续扫描

AD.C

#include "stm32f10x.h"                  // Device header
uint16_t SRAM_arr[4]={0,0,0,0};
void AD_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;//必须要写在程序的开头位置
	ADC_InitTypeDef ADC_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//ADC都是APB2上的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	//在AIN模式下GPIO是无效的,专门为ADC服务
	//断开GPIO,防止GPIO的输入和输出对模拟电压造成干扰
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分频设置ADC时钟,ADCCLK=12MHz
	
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
	
	
	//若想要在多个序列填充多个通道,可以复制19行进行填充,配置不同的采样时间
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//数据右对齐
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//配置ADC独立模式
	ADC_InitStructure.ADC_ScanConvMode=ENABLE;//是否为扫描模式
	ADC_InitStructure.ADC_NbrOfChannel=4;//扫描模式下会用到的通道数目
	ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//配置成连续转换
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//不使用外部触发源
	ADC_Init(ADC1,&ADC_InitStructure);
	
	
	
	DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR;//此处ADC要加上&,外设站点地址
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SRAM_arr;//存储器站点地址
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//地址自增
	DMA_InitStructure.DMA_BufferSize=4;//传输计数器
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//传输方向
	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//使用软件触发
	DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//使用自动重装
	DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1,ENABLE);
	ADC_DMACmd(ADC1,ENABLE);
	ADC_Cmd(ADC1,ENABLE);//开启ADC电源
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);//复位校准完成后硬件自动置0
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1)== SET);//校准完成后硬件自动置0

	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//ADC连续扫描,初始化结尾直接触发
}

 main.c

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

int main()
{
	OLED_Init();
	AD_Init();
	OLED_ShowString(1,1,"AD1:");
	OLED_ShowString(2,1,"AD2:");
	OLED_ShowString(3,1,"AD3:");
	OLED_ShowString(4,1,"AD4:");
	

	while(1)
	{
		OLED_ShowNum(1,5,SRAM_arr[0],5);
		OLED_ShowNum(2,5,SRAM_arr[1],5);
		OLED_ShowNum(3,5,SRAM_arr[2],5);
		OLED_ShowNum(4,5,SRAM_arr[3],5);
		Delay_ms(100);
	}	
}

极大地减轻了软件的负担,提高外设性能

还可以使用定时器定时触发ADC,ADC触发DMA

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值