目录
一:什么是DMA??
1.DMA简介:
2.DMA数据流与通道:
3.工作原理 :
二:实验代码及实验效果
2.1? ADC基本配置:
2.2 DMA基本配置:
2.3 主函数:
2.4 实验结果——串口接收数据
?三:DMA代码详细解及注意事项
?3.1 ADC代码段:
3.2DMA代码段:
?
一:什么是DMA??
1.DMA简介:
?????? DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
????? STM32F407ZGT6 有 2 个 DMA 控制器(DMA1 和 DMA2),共 16 个数据流(每个控制器 8 个),每一个 DMA 控制器都用于管理一个或多个外设的存储器访问请求。每个数据流有 8个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理 DMA 请求间的优先级。
2.DMA数据流与通道:
????? 每个DMA有八个数据流,而8个数据流中的每一个都连接到专用硬件 DMA 通道(请求),DMA 数据流请求之间的优先级可用软件编程(4 个级别:非常高、高、中、低),在软件优先级相同的情况下可以通过硬件决定优先级(例如,请求 0 的优先级高于请求 1),每个数据流也支持通过软件触发存储器到存储器的传输(仅限 DMA2 控制器),可供每个数据流选择的通道请求多达 8 个。此选择可由软件配置,允许几个外设启动 DMA请求。
????? 在设置DMA数据流与通道的时候要按照数据手册当中的对应关系进行配置。
3.工作原理 :
?????? 在产生事件后,外设会向 DMA 控制器发送请求信号。DMA 控制器根据通道优先级处理该请求。只要 DMA 控制器访问外设,DMA 控制器就会向外设发送确认信号。外设获得 DMA 控制器的确认信号后,便会立即释放其请求。一旦外设使请求失效,DMA 控制器就会释放确认信号。如果有更多请求,外设可以启动下一个事务。?
二:实验代码及实验效果
2.1? ADC基本配置:
#include "adc.h"
#include "delay.h"
//初始化ADC
void Adc_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
/*使能 GPIOA ADC1 时钟*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/*先初始化ADC1通道5 IO口*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);
/*ADC基本配置*/
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA失能
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频
ADC_CommonInit(&ADC_CommonInitStructure);//初始化
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//关闭连续转换
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_NbrOfConversion = 1;//1个转换在规则序列中 也就是只转换规则序列1
ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
ADC_Cmd(ADC1, ENABLE);//开启AD转换器
/*ADC1,ADC通道,480个周期,提高采样时间可以提高精确度*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_480Cycles );
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
/*使能指定的ADC1的软件转换启动功能 */
ADC_SoftwareStartConv(ADC1);
}
2.2 DMA基本配置:
u32 ADC_ConvertedValue;
void Dma_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
/*外设基址为:ADC 数据寄存器地址*/
DMA_InitStructure.DMA_PeripheralBaseAddr = ((u32)ADC1+0x4c);
/*存储器地址,实际上就是一个内部SRAM的变量*/
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&ADC_ConvertedValue;
/ 数据传输方向为外设到存储器*/
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
/* 缓冲区大小为,指一次传输的数据量*/
DMA_InitStructure.DMA_BufferSize = 1;
/*外设寄存器只有一个,地址不用递增*/
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/*存储器地址固定*/
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
/*外设数据大小为半字,即两个字节*/
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
/*存储器数据大小也为半字,跟外设数据大小相同*/
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
/*循环传输模式*/
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
/*DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响*/
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
/*禁止DMA FIFO,使用直连模式*/
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
/*FIFO 大小,FIFO模式禁止时,这个不用配置*/
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/* 选择 DMA 通道,通道存在于流中*/
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
/*初始化DMA流,流相当于一个大的管道,管道里面有很多通道*/
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
/*使能DMA流*/
DMA_Cmd(DMA2_Stream0, ENABLE);
}
2.3 主函数:
int main(void)
{
extern u32 ADC_ConvertedValue;
float temp;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为115200
Adc_Init(); //初始化ADC
Dma_Init();
while(1)
{
printf ("V:%d\r\n",ADC_ConvertedValue);
delay_ms(500);
}
}
2.4 实验结果——串口接收数据
分别对应引脚悬空、连接GND、连接3v3时的输出数据
?
?三:DMA代码详细解及注意事项
?3.1 ADC代码段:
相比无DMA的ADC配置代码,这里要多配置以下代码:
/*开启ADC DMA 请求*/
ADC_DMACmd(ADC1, ENABLE);
/*启用或禁用上次传输后的ADC DMA请求*/
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
3.2DMA代码段:
?????? 首先要关注传输方向,这里我们需要将ADC转换完成的数据传递到cpu内部寄存器,所以为外设到寄存器。
/*数据传输方向为外设到存储器*/
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
?????? 其次要关注寄存器地址与外设地址,寄存器地址在这里为变量,所以取地址。
/*存储器地址,实际上就是一个内部SRAM的变量 */
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&ADC_ConvertedValue;
?????? 外设在这里有对应的寄存器,地址为基地址+偏移地址,如果要更改ADC通道,要修改相应的寄存器地址,可以在中文参考手册里查找。
/*外设基址为:ADC 数据寄存器地址*/
DMA_InitStructure.DMA_PeripheralBaseAddr = ((u32)ADC1+0x4c);
?????? 寄存器地址与外设地址,在这里我们只传递了ADC1的数据,单对单,所以两个地址都不需要递增
/*外设寄存器只有一个,地址不用递增*/
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/*存储器地址固定*/
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
|