STM32 多通道ADC连续采集之数据到内存 DMA传输

      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传输采集值到内存过程,不需要处理器干预,可以腾出资源处理别的事物。上例

                 只是在主环里面需要转换电压值时,从数组读取数据、求平均并转换,需要处理器计算。

posted @ 2019-12-18 18:03  北有寒山  阅读(9278)  评论(0编辑  收藏  举报