STM32的DMA的配置使用(关于原理。在《计算机组成与系统结构》的输入输出系统中有一些DMA的原理介绍)

来源:互联网 发布:淘宝退货率高怎么办 编辑:程序博客网 时间:2024/06/13 07:38

DMA原理:(DMA原理我也没有仔细研究,就根据自己学习的,说说自己的一些理解。百度资料也可以查到很多)

DMA作用:用于搬运数据,用于解放CPU与外设之间频繁的数据交流,使CPU专注于运算功能。

方式:外设与DMA直接访问内存,就得和CPU互斥使用内存,而互斥方式是这3种。

1.停止CPU访内    2.周期挪用   3.DMA与CPU交替访内

STM32F10x的DMA介绍

1.在STM32F103ZE中,有DMA1,DMA2这2个DMA.   DMA1有7个通道,DMA2有5个通道。 一个仲裁器协调DMA通道间的优先权<STM32>

附:在计组中,就有关于仲裁器的详细介绍,可以了解一下原理。集中式仲裁:链式,计数器式,独立式3种仲裁。(链式+独立式是使用最广且好的)。分布式仲裁

2.每个通道都有直连专用硬件DMA请求,每个通道都同样支持软件触发,软件配置

3.同一DMA模块,硬件通道有默认优先级, 除非进行软件编程设置优先级.(其实你了解一下仲裁的原理,链式仲裁是按循序访问,也是具有默认优先级的)

4.每个通道都有3个事件标志(DMA半传输,DMA传输完成和DMA传输出错)

了解一个知识,一定要了解架构,原理。再来学会它的应用。不说闲话,由图可知DMA挂接在AHB总线上

了解DMA1请求映像<DMA2类似>

从外设[ TIMx(1,2,3,4),ADC1,SPI1,,SPI/I2S,I2Cx(1,2),USAERTx(1,2,3) ]产生的7个请求,通过逻辑“或”输入到DMA控制器。*但同时只有一个请求有效<DMA只能为一个通道工作>.

下面我贴的程序就是将通道1分配给ADC1的映射。(外设一个对应一个通道,但一个通道可以被多种外设请求。多个外设抢占一个通道,多个通道抢占一个DMA)。

关于DMA的大致配置简要说一下

1.数据传输目的地和来源

DMA_DIR_PeripheralDST  外设作为目的地

DMA_DIR_PeripheralSRC  外设作为来源

2.定义DMA通道的DMA缓存大小

3.外设(DMA)地址寄存器递增与否 (这里的外设就是DMA,相对内存而言),参数用0,1标志即可

DMA_PeripheralInc_Enable    外设地址寄存器递增  -> 作用:数据不会覆盖

DMA_PeripheralInc_Disable   外设地址寄存器不变  ->作用: 节省内存,但会覆盖

4.内存地址寄存器递增与否

DMA_PeripheralInc_Enable    外设地址寄存器递增  -> 作用:作用相同

DMA_PeripheralInc_Disable   外设地址寄存器不变  ->作用:

5.外设数据宽度+内存数据宽度

6.DMA工作模式

7.DMA通道的软件优先级。如果配置软件优先级相同,就使用硬件优先级

8.使能DMA通道的内存到内存的传输,STM32没有则选择关闭

贴代码分析一下(该驱动实现,使用DMA将ADC采集数据传到MCU中)

#include "dma.h"
u16 adc_data[50]; //外部变量,可以被使用

void dma_init()
{
GPIO_InitTypeDef GPIO_InitStructure;         /* 配置GPIO结构体 */
ADC_InitTypeDef ADC_InitStructure;           /*配置ADC结构体 */
DMA_InitTypeDef DMA_InitStructure;           /* 配置DMA结构体*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);  /*上面有张架构图,APB,AHB的挂接很清晰*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);               //DMA在AHB上

RCC_ADCCLKConfig(RCC_PCLK2_Div6);            //ADC分频,不得超过14Mhz

/* GPIO配置*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;          //ADC123_IN1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;      //ADC模拟量输入
GPIO_Init(GPIOA,&GPIO_InitStructure);          

/* ADC配置 */
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   //独立工作模式
ADC_InitStructure.ADC_ScanConvMode =DISABLE;       //单通道
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;   //多次采集
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None ;  //软件触发

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  //右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;                 //通道数目?
ADC_Init(ADC1, &ADC_InitStructure); 

//设置指定ADC的规则组通道,设置转换顺序和采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE);          

ADC_ResetCalibration(ADC1);       //重置ADC校准
while(ADC_GetResetCalibrationStatus(ADC1));  //判断重设是否完成,0完成

ADC_StartCalibration(ADC1);             //开始校准
while(ADC_GetCalibrationStatus(ADC1));  //判断校准是否完成



ADC_SoftwareStartConvCmd(ADC1, ENABLE);    //软件启动ADC

DMA_DeInit(DMA1_Channel1);                         //DMA1通道1初始化配置

/*DMA配置*/
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;         //DMA外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)adc_data;             //内存地址存放数据
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC ;               //DMA外设作为来源还是目的,这里作为来源。ADC数据采集输入到DMA, DMA搬运到内存
DMA_InitStructure.DMA_BufferSize = 50;                            //DMA缓存大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器是否递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;           //内存地址寄存器是否递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;   //外设数据宽度
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;           //内存数据宽度
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ;                              //DMA工作模式,这里设置循环转换
DMA_InitStructure.DMA_Priority = DMA_Priority_High ;                           //配置优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                  //内存到内存。disable
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

DMA_Cmd(DMA1_Channel1, ENABLE);                                                //使能DMA通道1

ADC_DMACmd(ADC1,ENABLE);                                                       //ADC1与DMA连接功能使能。
}

//主函数,DMA, ADC进行功能的实现

#include "public.h"
#include "Systick.h"
#include "printf.h"
#include "dma.h"

int main()
{
u8 i;
u32 ad=0;
printf_init();
 dma_init();
 while(1)
{
for(i=0;i<50;i++)
{
ad=ad+adc_data[i];           //DMA½«adcµÄÊý¾Ý°áµ½adc_data[]Àï
}
ad=ad/50;
printf("AD=%fV!\r\n",ad*3.3/4096);               //ת»»µçѹÏÔʾ
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
}
}