STM32学习第四篇 ---------- DMA

学习江科大 8-1 DMA直接存储器存取 至  8-2 DMA数据转运&DMA+AD多通道

1、DMA(Direct Memory Access)

        (1) DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU资源;

        (2)12个独立可配置的通道:DMA1 (7个通道) 、DMA2(5个通道);

        (3)每个通道都支持软件触发和特定的硬件触发;

        (4)STM32F103C8T6  DMA资源 :DMA1 (7个通道)

        (5)DMA框图

        其中包括用于访问各个存储器的DMA总线;内部的多个通道可以进行独立的数据转运;仲裁器用于调度各个通道,防止产生冲突;AHB从设备用于配置DMA参数;DMA请求用于硬件触发DMA的数据转运

        (6)DMA基本结构

        DMA进行转运的条件:

        a、开关控制,DMA_Cmd必须使能;

        b、传输计数器必须大于0;

        c、有触发信号,触发一次,转运一次,传输计数器自减一次。当传输计数器等于0,且没有自动重装时,此时就需要DMA_Cmd,给DISABLE,关闭DMA,再为传输计数器写入一个大于0的数,重新DMA_Cmd,从而开启DMA(写传输计数器时,必须要先关闭DMA,再进行)。

2、存储器映像

3、数据转运 + DMA

其中ADC扫描模式 + DMA

4、代码部分

(1)DMA数据转运

        a、MyDMA.c

#include "stm32f10x.h"


uint16_t  MyDMA_Size;  //定义全局变量

void MyDMA_Init( uint32_t AddrA , uint32_t AddrB , uint16_t Size)  //DMA初始化函数
{
    MyDMA_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_PeriphalDataSize_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_Priority_Medium; //优先级选择中等
    DMA_Init(DMA1_Channel1,&DMA_InitStructure); //将结构体交给函数DMA_Init


    /*使能DMA*/
    DMA_Cmd(DMA1_Channel1,DISABLE);  //运用转运函数来实现DMA的进一步控制

}


void MyDMA_Transfer(void)  //启动DMA数据转运
{
    DMA_Cmd(DMA1_Channel1,DISABLE); //DMA失能 (在写入传输计数器之前,需要DMA暂停工作)
    DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size); //写入传输计数器,指定将要转运的次数
    DMA_Cmd(DMA1_Channel1,ENABLE); //DMA使能,开始工作

    while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET) ; //等待DMA工作完成
    DMA_ClearFlag(DMA1_FLAG_TC1);   //清除工作完成标志位


}

        b、main.c

uint8_t DataA[] = { 0X01 , 0X02 , 0X03 , 0X04 };
uint8_t DataB[] = { 0 , 0 , 0 , 0 };

int main(void)
{
    /*模块初始化*/
    OLED_Init();   //OLED初始化
    MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);  //DMA初始化

   	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,观察转运后的现象
	}
}

(2)DMA+AD多通道

        a、AD.c

uint16_t AD_Value[4];

void AD_Init(void)  //AD初始化
{
    /*开启ADC1 GPIOA DMA1的时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

    /*设置ADC时钟*/
    RCC_ADCCLKConfig(RCC_PCLK2_Div2);  //选择6分频,ADCCLK = 12MHz 
    
    /*GPIO初始化*/
    GPIO_InitStructure GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN ;
    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 );   //将PA0--PA3引脚配置为模拟输入

    /*规则组通道配置*/
    ADC_RegularChannelConfig(ADC1 ,ADC_Channel0 ,1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1 ,ADC_Channel1 ,2, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1 ,ADC_Channel2 ,3, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1 ,ADC_Channel3 ,4, ADC_SampleTime_55Cycles5);

    /*ADC初始化*/
    ADC_InitTypeDef ADC_InitStructure ;
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent ; //选择独立模式
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right ;//数据右对齐
    ADC_InitStructure.ADC_ExternalTriConv = ADC_ExternalTrigConv_None ; //使用软件触发
    ADC_InitStructure.ADC_ContionuousConvMode = ENABLE ; //连续转换使能
    ADC_InitStructure.ADC_ScanConvMode = ENABLE ; //扫描模式
    ADC_InitStructure.ADC_NbrOfChannel = 4 ; //扫描通道组的前4个通道
    ADC_Init(ADC1,&ADC_InitStructure) 

    /*DMA初始化*/
    DMA_InitTypeDef DMA_InitStructure ; 
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR ;//外设基地址
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ;//半字
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable ;//外设地址自增
    DMA_InitStructure.MemoryBaseAddr = (uint32_t) AD_Value ;//存储器基地址
    DMA_InitStructure.MemoryDataSize = DMA_MemoryDataSize_HalfWord ; //半字
    DMA_InitStructure.MemoryInc = DMA_MemoryInc_Enable ; 存储器地址自增
    DMA_InitStructure.DIR = DMA_DIR_PeripheralSRC ; //选择由外设到存储器的方向
    DMA_InitStructure.BufferSize = 4 ;//转运次数为4
    DMA_InitStructure.Mode = DMA_Mode_Circular ; //选择循环模式
    DMA_InitStructure.M2M =DMA_M2M_Disable ; //存储器到存储器
    DMA_InitStructure.Priority = DMA_Priority_Medium ; //优先级选择中等
    DMA_Init(DMA_Channel1 ,&DMA_InitStructure); //配置DMA1的通道1


    /*使能DMA ADC*/
    DMA_Cmd(DMA1_Channel1 ,ENABLE);
    ADC_DMACmd(ADC1,ENABLE);  //ADC1触发DMA1的信号使能
    ADC_Cmd(ADC1,ENABLE);

    /*ADC校准*/
    ADC_ResetCalibration(ADC1); 
    while (ADC_GetResetCalibrationStatus(ADC1) == SET) ;
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1) == SET) ;


    /*ADC触发*/
    ADC_SoftwareStartConvCmd(ADC1,ENABLE); //软件触发ADC开始工作,且之后ADC就会一直连续不断地工作


}

        首先,开启ADC、DMA、GPIOA的时钟,配置ADCCLK的时钟为12MHz;   

        然后,配置PA0--PA3的引脚为模拟输入; 

        其次,配置规则组通道(根据引脚与输入通道的表,从而得到PA0--PA3对应的输入通道分别为ADC_Channel_0-----ADC_Channel_3,并将其设置在通道0-3) ;

        之后,进行ADC初始化,因为是多通道AD ,所以选择连续转换 扫描模式 ,并且将ADC转换设置为外部触发 ;

        再就是,进行DMA初始化,设置外设、存储器的基地址、数据长度、地址自增 ,以及转运次数、循环模式、是否是存储器到存储器、优先级  ;

        还有DMA、ADC的使能 ,不仅需要DMA_Cmd、ADC_Cmd函数 ,还需ADC_DMACmd(ADC1触发DMA1的信号使能);

        以及就是固定流程 ------ADC校准;

        最后就是ADC触发 ,通过软件触发ADC开始工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值