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

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

  • 相关阅读:
    在线课程的总结
    数据库相关整理
    两个栈实现队列&两个栈实现队列
    Django中间件的5种自定义方法
    Python Web开发之路
    内置函数——format
    Django组件拾忆
    支付宝支付流程
    消息队列之RabbitMQ
    WebSocket
  • 原文地址:https://www.cnblogs.com/beiyhs/p/12061278.html
Copyright © 2011-2022 走看看