一.简介
DMA(Direct Memory Access)直接存储器存取
DMA可以提供外设(外设寄存器、ADC的数据寄存器、串口的数据寄存器等)和存储器(运行内存SRAM和程序存储器Flash)或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发
STM32F103C8T6 DMA资源:DMA1(7个通道)
二.DMA基本结构
三.DMA触发部分
每个通道都支持软件触发和特定的硬件触发,选择硬件触发要去选择特定的通道的,例如通道1 的硬件触发是ADC1、TIM2_CH3、TIM4_CH1具体的选择对应外设去开启,如使用ADC1,库函数是ADC_DMACmd。
四.数据宽度与对齐
这个表的意思是把多出来的高位舍弃掉,小的数据转到大的里面去,高位就会补0,大的数据转到小的里面去,高位就会舍弃掉。
五 .具体例子
1.数据转运+DMA
2.ADC扫描模式+DMA
3.代码
uint16_t aa=0x66;//存在SRAM区
const uint16_t aa=0x66;//是常量了只能读不能写,存在Flash里面
OLED_ShowHexNum(2,1,(uint32_t)&ADC1->DR,8);//外设ADC1寄存器地址
//&ADC1->DR用结构体来访问内存
#define DMA_PeripheralDataSize_Byte //字节类型uint8_t
#define DMA_PeripheralDataSize_HalfWord //半字类型uint16_t
#define DMA_PeripheralDataSize_Word //字uint32_t
DMA_memory_to_memory
#define DMA_M2M_Enable //软件触发
#define DMA_M2M_Disable //硬件触发
#include "stm32f10x.h" // Device header
uint32_t MyDMA_size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
MyDMA_size=Size;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//DMA是AHB总线上的时钟
DMA_InitTypeDef DMA_InitStructure;
//外设站点的起始地址,数据宽度,是否自增
DMA_InitStructure.DMA_PeripheralBaseAddr=AddrA;//外设站点地址
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;//字节传输
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Enable; //是否自增
//存储器站点的起始地址,数据宽度,是否自增
DMA_InitStructure.DMA_MemoryBaseAddr=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_M2M=DMA_M2M_Enable;
DMA_InitStructure.DMA_BufferSize=Size;
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//是否重装
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
DMA_Init(DMA1_Channel1,&DMA_InitStructure);//DMA1通道1里面
DMA_Cmd(DMA1_Channel1,DISABLE);
}
//如果数据转运过一次还想去接着转运,重新赋值
void MyDMA_Transfer()//重新转运
{
DMA_Cmd(DMA1_Channel1,DISABLE);//转运前先给DMA失能
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_size);//将上面的Size赋值过来(重新赋值)
DMA_Cmd(DMA1_Channel1,ENABLE);//使能开始转运
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);//等待转运完成
DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运标志位
}
uint16_t AD_Value[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef 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);
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);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 4;
ADC_Init(ADC1, &ADC_InitStructure);
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.DMA_MemoryBaseAddr = (uint32_t)AD_Value;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 4;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA循环转运
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);//开启ADC-DMA
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发模式
}