一、DMA(Direct Memory Access)直接存储器存取
1、DMA可以提供外设(外设寄存器DR)和存储器(运行内存SRAM和程序存储器Flash)或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
2、12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
3、每个通道都支持软件触发和特定的硬件触发
4、STM32F103C8T6 DMA资源:DMA1(7个通道)
5、存储器映像
6、DMA框图
7、DMA基本结构
8、DMA请求
9、数据宽度与对齐
二、DMA数据转运
1、数据转运+DMA
2、按照以下接线方式连接,并将STLINK插到电脑上
3、DMA函数驱动模块
(1)DMA库函数的功能
(3)MyDMA.c
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
MyDMA_Size = Size;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //开启DMA1时钟
DMA_InitTypeDef DMA_InitStructure; //配置DMA参数
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_BufferSize=Size;
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M=DMA_M2M_Enable;
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,DISABLE); //使能
}
void MyDMA_Transfer(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE); //失能,给传输计数器赋值必须先给DMA失能
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
DMA_Cmd(DMA1_Channel1,ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); //等待转运完成
DMA_ClearFlag(DMA1_FLAG_TC1); //清除标志位
}
(4)MyDMA.h
#ifndef __MYDMA_H
#define __MYDMA_H
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size);
void MyDMA_Transfer(void);
#endif
4、编写main.c代码
按Alt键以方框的形式框选
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
//const uint8_t DataA[] = {0x01,0x02,0x03,0x04};
uint8_t DataA[] = {0x01,0x02,0x03,0x04};
uint8_t DataB[] = {0,0,0,0};
int main(void)
{
OLED_Init();
MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);
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);
OLED_ShowHexNum(2,1,DataA[0],2);
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);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
while(1)
{
DataA[0] ++;
DataA[1] ++;
DataA[2] ++;
DataA[3] ++;
OLED_ShowHexNum(2,1,DataA[0],2);
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);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
Delay_ms(1000);
MyDMA_Transfer();
OLED_ShowHexNum(2,1,DataA[0],2);
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);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
Delay_ms(1000);
}
}
5、实现效果
DMA数据转运
6、把Flash的数据转运到SRAM里
(1)修改main.c
(2)效果
三、DMA+AD多通道
1、ADC扫描模式+DMA
2、按照以下接线方式连接,并将STLINK插到电脑上
3、DMA函数驱动模块
(1)AD.c
#include "stm32f10x.h" // Device header
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); //配置ADCCLK 72MHz/6=12MHz
GPIO_InitTypeDef GPIO_InitStructure; //配置GPIO
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模拟输入,在AIN模式下,GPIO口是无效的,断开GPIO,防止GPIO口的输入输出对模拟电压造成干扰,AIN模式就是ADC的专属模式
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); //四个通道对应4个序列
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
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//连续转换模式
// ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//连续转换模式
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//数据对齐
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel=4;//通道数目,在扫描模式下会用到几个通道
ADC_InitStructure.ADC_ScanConvMode=ENABLE;//扫描转换模式
ADC_Init(ADC1,&ADC_InitStructure);
DMA_InitTypeDef DMA_InitStruture;
DMA_InitStruture.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //ADC_DR寄存器地址,ADC1的DR寄存器地址0x4001244C
DMA_InitStruture.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
DMA_InitStruture.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStruture.DMA_MemoryBaseAddr=(uint32_t)AD_Value;
DMA_InitStruture.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
DMA_InitStruture.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_InitStruture.DMA_DIR=DMA_DIR_PeripheralSRC;
DMA_InitStruture.DMA_BufferSize=4;
DMA_InitStruture.DMA_Mode=DMA_Mode_Normal;
DMA_InitStruture.DMA_M2M=DMA_M2M_Disable;
DMA_InitStruture.DMA_Priority=DMA_Priority_Medium;
DMA_Init(DMA1_Channel1,&DMA_InitStruture); //ADC1的硬件触发只能接在DMA1的通道1上
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1,ENABLE); //开启AC电源
ADC_ResetCalibration(ADC1); //复位校准 //ADC校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET); //返回复位校准的状态
ADC_StartCalibration(ADC1); //开始校准
while(ADC_GetCalibrationStatus(ADC1) == SET); //获取校准状态
// ADC_SoftwareStartConvCmd(ADC1,ENABLE); //软件触发
}
void AD_GetValue(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE); //DMA也是单次模式
DMA_SetCurrDataCounter(DMA1_Channel1,4);
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //ADC还是单次模式,需要软件触发一下
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); //等待DMA转运
DMA_ClearFlag(DMA1_FLAG_TC1);
}
(2)AD.h
#ifndef __AD_H
#define __AD_H
extern uint16_t AD_Value[4];
void AD_Init(void);
void AD_GetValue(void);
#endif
4、编写main.c代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD0:");
OLED_ShowString(2,1,"AD1:");
OLED_ShowString(3,1,"AD2:");
OLED_ShowString(4,1,"AD3:");
while(1)
{
AD_GetValue();
OLED_ShowNum(1,5,AD_Value[0],4);
OLED_ShowNum(2,5,AD_Value[1],4);
OLED_ShowNum(3,5,AD_Value[2],4);
OLED_ShowNum(4,5,AD_Value[3],4);
Delay_ms(100);
}
}
5、实现效果
DMA+AD多通道
6、ADC连续扫描+DMA循环转运模式
(1)修改AD.c
(2)修改AD.h
(3)修改main.c
运行效果和上述相同,不再展示