DMA的英文Direct memory access,意思就是传输将数据从一个地址空间复制到另一个地址空间,设置好后
自动传输而不需要处理器参与。STM32F030F4只有DMA1,DMA1有5个通道,要想使用必须进行一些设置。
下面以ADC多通道采集为例,大致分3步:
1. GPIO的配置。该配置主要设置ADC采集涉及的IO口,本例设置ADC连接9个IO口进行模拟量采集。
2. ADC的配置。设置多通道采集速率、连续转换模式、使能和DMA通道的连接等。注意该芯片只有ADC1。
3. DMA的配置。设置DMA的开启、源地址、目标地址等。该芯片只有DMA1,但有5个通道,本例用通道1。
下面是main.c代码:
#include "stm32f0xx.h"
#include "stm32f0xx_rcc.h"
#include "stm32f0xx_gpio.h"
#include "stm32f0xx_adc.h"
#include "stm32f0xx_dma.h"
// 原版创作,引用请注明出处:https://www.cnblogs.com/beiyhs/p/12061278.html 北有寒山
// 6 脚PA0 ADC_IN0
// 7 脚PA1 ADC_IN1
// 8 脚PA2 ADC_IN2
// 9 脚PA3 ADC_IN3
// 10脚PA4 ADC_IN4
// 11脚PA5 ADC_IN5
// 12脚PA6 ADC_IN6
// 13脚PA7 ADC_IN7
// 14脚PB1 ADC_IN9 注意:STM32F030F4芯片没有ADC_IN8通道哦!
#define N 50 //预定义每通道采50次
#define M 9 //预定义为9个通道
uint16_t ad_value[N][M]; //定义二维数组,用来存放ADC转换结果,也是DMA的目标地址
uint16_t ad_avg[M]; //9个ADC通道采集50次后平均值结果,M从0-8
float adc_data[M]; //9个ADC通道平均值转换电压值结果,M从0-8
void GPIO_cfg(void); // 1. GPIO的配置函数声明
void ADC1_cfg(void); // 2. ADC的配置函数声明
void DMA_cfg(void); // 3. DMA的配置函数声明
void delay_ms(void); // 延时函数
void filter(void); // 9通道,每通道50次求平均函数
//*************************************************************************************************
void GPIO_cfg(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //打开A口时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|
GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //PA0/1/2/3/4/5/6/7/ 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PB1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入引脚
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//***************************************************************************************************************
void ADC1_cfg(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //使能AHB预分频器到外设ADC1的开关
ADC_DeInit(ADC1); //ADC复位
RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4) ; //时钟分频48M/4=12M 最大时钟不超过14M
ADC_InitTypeDef ADC_InitStruct; //声明ADC结构变量,在stm32f0xx_adc.c中
ADC_InitStruct.ADC_Resolution=ADC_Resolution_12b; //采集设为12位精度即4095
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE; //转换工作在连续转换模式
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发转换关闭
ADC_InitStruct.ADC_ScanDirection = ADC_ScanDirection_Upward; //1-9升序,Backward降序
ADC_Init(ADC1,&ADC_InitStruct); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
ADC_ChannelConfig(ADC1,ADC_Channel_0,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_1,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_2,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_3,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_4,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_5,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_6,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_7,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_9,ADC_SampleTime_55_5Cycles);
//ADC_ChannelConfig(ADC1,ADC_Channel_0, ADC_SampleTime_239Cycles5 ); //等待更长采集周期
ADC_GetCalibrationFactor(ADC1); //校准ADC
ADC_DMACmd(ADC1, ENABLE); //使能ADC1的DMA通道,还需独立配置DMA通道等参数
ADC_Cmd(ADC1,ENABLE); //使能的ADC1
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)); //等待ADC准备好
ADC_StartOfConversion(ADC1); //启动转换
}
//****************************************************************************************************
void DMA_cfg(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //1 使能DMA传输,开启DMA时钟
DMA_InitTypeDef DMA_InitStructu; //2 声明DMA结构变量
DMA_DeInit(DMA1_Channel1); //3 将DMA的通道1寄存器复位
DMA_InitStructu.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //4 DMA外设ADC基地址 源地址
DMA_InitStructu.DMA_MemoryBaseAddr = (uint32_t)&ad_value; //5 DMA目标地址,内存AD_Value首地址
DMA_InitStructu.DMA_DIR = DMA_DIR_PeripheralSRC; //6 内存作为数据传输的目的地 传输方向
DMA_InitStructu.DMA_BufferSize = N*M; //7 DMA通道的DMA缓存的大小50*9 每次
DMA_InitStructu.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //8 外设地址寄存器不变 源地址不变
DMA_InitStructu.DMA_MemoryInc = DMA_MemoryInc_Enable; //9 内存地址寄存器递增 目标地址加1
DMA_InitStructu.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //10 ADC 数据宽度为16位
DMA_InitStructu.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //11 内存 数据宽度为16位
DMA_InitStructu.DMA_Mode = DMA_Mode_Circular; //12 工作在循环缓存模式/Normal正常转换
DMA_InitStructu.DMA_Priority = DMA_Priority_High; //13 DMA通道 高优先级 Medium 中优先
DMA_InitStructu.DMA_M2M = DMA_M2M_Disable; //14 DMA通道 关闭内存到内存传输
DMA_Init(DMA1_Channel1, &DMA_InitStructu); //15 据DMA_InitStruct指定参数初始化DMA通道1
DMA_Cmd(DMA1_Channel1, ENABLE); //16 启动DMA通道
}
//***********************************************************************************************
void filter(void)
{
int sum = 0; //每通道累加和变量
uint8_t count_M=0; //通道数变量
uint8_t count_N=0; //次数变量
for( count_M=0;count_M<M;count_M++) //M为9列,就是通道数,从0-9
{
for(count_N=0;count_N<N;count_N++) //N为50行,就是次数,从0-50
{
sum += ad_value[count_N][count_M]; //通道50次ADC值求和
}
ad_avg[count_M]=sum/N; //通道50次ADC值求平均
sum=0; //完成通道后清空和值,进入下一轮通道
}
}
//*******************************************************************************************
void delay_ms(void)
{
unsigned char i,j;
for(i=255;i>0;i--)
{
for(j=200;j>0;j--);
}
}
//**********************************************************************************************
//**********************************************************************************************
int main(void)
{
GPIO_cfg(); //初始化9个ADC的IO口
ADC1_cfg(); //初始ADC
DMA_cfg(); //初始DMA
while(1) //主循环
{
delay_ms(); //延时等待9通道50次采集结束
filter(); //ADC0-9通道50次采集求平均
adc_data[0]=(ad_avg[0]*3.263)/4095; //PA0 ADC通道0平均后转换电压值
adc_data[1]=(ad_avg[1]*3.263)/4095; //PA1 ADC通道1平均后转换电压值
adc_data[2]=(ad_avg[2]*3.263)/4095; //PA2 ADC通道2平均后转换电压值
adc_data[3]=(ad_avg[3]*3.263)/4095; //PA3 ADC通道3平均后转换电压值
adc_data[4]=(ad_avg[4]*3.263)/4095; //PA4 ADC通道4平均后转换电压值
adc_data[5]=(ad_avg[5]*3.263)/4095; //PA5 ADC通道5平均后转换电压值
adc_data[6]=(ad_avg[6]*3.245)/4095; //PA6 ADC通道6平均后转换电压值
adc_data[7]=(ad_avg[7]*3.263)/4095; //PA7 ADC通道7平均后转换电压值
adc_data[8]=(ad_avg[8]*3.263)/4095; //PB1 ADC通道9平均后转换电压值
}
说明:
1. 程序在uVision开发环境仿真运行,挂载的STM32F030F4实验板的PA0、PA1等9个通道给入模
拟电压。在程序adc_data[8]=(ad_avg[8]*3.263)/4095; 等处设断点,则可看到采集电压值。
2. 程序在GPIO_cfg()、ADC1_cfg()、DMA_cfg() 函数执行完后,ADC的采集已经开始并且DMA
通道已经打开。这时ADC执行通道0-9的连续循环采集,但采集值存放的地址不变只有一个,每
次采集完由DMA传送到内存的二维数组ad_value[N][M],每采集DMA传输一次,数组从首地址
开始加1,直到N*M=9*50个地址用完,DMA重新从ad_value[N][M]首地址再传,周而复始。
3. ADC通过DMA传输采集值到内存过程,不需要处理器干预,可以腾出资源处理别的事物。上例
只是在主环里面需要转换电压值时,从数组读取数据、求平均并转换,需要处理器计算。