• 基于STM32的ADC应用实例(单通道、多通道、基于DMA)


    • 硬件:STM32F103ZET6
    • 开发工具:Keil uVision5
    • 下载调试工具:ARM仿真器(ST-Link)

     一、硬件部分

    所用的芯片内嵌3个12位的模拟/数字转换器(ADC),每个ADC共用多达16个外部通道,2个内部通道。如下图所示:

    ADC就是一个转换器,可以把模拟量和数字量进行互相转换,在这里演示的是把模拟量转化为数字量,就像一个重力秤,一个多重的人或者物件在上面都有一个对应重量的数值,ADC与重力秤差不多,不过它是把模拟量(温度、电压)转化为数字量,数字量可以是一个浮点型数据、整型数据、数组数据等。

    在这里,ADC怎么把电压转换为数字量呢?

    ADC内部可以采集0~3.3V的电压,采集的电压为0时,里面的刻度会显示是0,当采集的电压是3.3V时,里面的刻度会显示是4095(即满值)。

    模拟/数字转换器(ADC)的分辨率、采样精度:12位。先来看看二进制的12位可表示0-4095个数,也就是说转换器通过采集转换所得到的最大值是4095,如:“111111111111”=4095,那么我们怎么通过转换器转换出来的值得到实际的电压值呢?如果我们要转换的电压范围是0v-3.3v的话,转换器就会把0v-3.3v平均分成4096份。设转换器所得到的值为x,所求电压值为y。
    那么就有:

    讲完ADC采集数值为啥要(乘以3.3除以4096)之后,接下来将一下一个AD为啥可以分成多个通道同时C采集电压呢?

    16个外部通道:简单的说就是芯片上有16个引脚是可以接到模拟电压上进行电压值检测的。16个通道不是独立的分配给3个转换器(ADC1、ADC2、ADC3)使用,有些通道是被多个转换器共用的。一个ADC的多个通道同时采集时,会有内部的转换机制进行分配优先顺序以及转换时间。

    void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)
    ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_239Cycles5);

    其中第一行是STM公司提供的源代码,第2~第4行是示范。

    ADCx指的是哪个ADC(ADC1、ADC2、ADC3都可以)
    ADC_Channel指的是指定ADC的规则组通道(如示范中的ADC_Channel_0,ADC_Channel_1,ADC_Channel_2)
    Rank指的是转换顺序(可分为序列1~序列16,数字越大优先顺序越后)
    ADC_SampleTime指的是转换的时间(一般采用的是ADC_SampleTime_239Cycles5)
     

    接下来,就是很多人比较困惑的地方,为啥ADC可以有多个采集通道?

    因为一个ADC就是一个工具,对这个工具有使用权的都可以使用它,要看谁有使用权,就得看PCB原理图,如下所示。同时每个有使用权的通道要使用它,也不是同时使用的,因为一个ADC在同一时间只能有一个通道使用,优先级高的通道就可以优先使用它,具有同样优先权的通道,就由MCU进行自主分配。

    其实一般我们可以通过上述的ADC通道配置函数进行配置:

    void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)
    Rank指的是转换顺序(可分为序列1~序列16,数字越大优先级越低)

    ADC123_IN1:字母“ADC”不用多说,“123”代表它被3个(ADC1、ADC2、ADC3)转换器共用的引脚,
    “IN0”对应刚才那张宏定义图里面的ADC_Channel_
    0,这样就能找到每个通道对应的引脚了。


    二、软件代码

    普通模式的多通道
     #include "adc.h"
     #include "delay.h"
    
    //初始化ADC
    //这里我们仅以规则通道为例
    //我们默认将开启通道0~3                                                                       
    void  Adc_Init(void)
    {     
        ADC_InitTypeDef ADC_InitStructure; 
        GPIO_InitTypeDef GPIO_InitStructure;
    
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE );      //使能ADC1通道时钟
     
    
        RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    
        //PA1 作为模拟通道输入引脚                         
        GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        //模拟输入引脚
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    
        
    
        ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
    
        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //ADC工作模式:ADC1和ADC2工作在独立模式
        ADC_InitStructure.ADC_ScanConvMode = ENABLE;    //模数转换工作在单通道模式
        ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;    //模数转换工作在单次转换模式
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    //转换由软件而不是外部触发启动
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //ADC数据右对齐
        ADC_InitStructure.ADC_NbrOfChannel = 3;    //顺序进行规则转换的ADC通道的数目
        ADC_Init(ADC1, &ADC_InitStructure);    //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   
    
      
        ADC_Cmd(ADC1, ENABLE);    //使能指定的ADC1
        
        ADC_ResetCalibration(ADC1);    //使能复位校准  
         
        while(ADC_GetResetCalibrationStatus(ADC1));    //等待复位校准结束
        
        ADC_StartCalibration(ADC1);     //开启AD校准
     
        while(ADC_GetCalibrationStatus(ADC1));     //等待校准结束
     
    
    }                  
    //获得ADC值
    //ch:通道值 0~3
    u16 Get_Adc(u8 ch)   
    {
          //设置指定ADC的规则组通道,一个序列,采样时间
        ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );    //ADC1,ADC通道,采样时间为239.5周期                      
      
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);        //使能指定的ADC1的软件转换启动功能    
         
        while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
    
        return ADC_GetConversionValue(ADC1);    //返回最近一次ADC1规则组的转换结果
    }
    
    u16 Get_Adc_Average(u8 ch,u8 times)        //求多次采集数值的平均值
    {
        u32 temp_val=0;
        u8 t;
        for(t=0;t<times;t++)
        {
            temp_val+=Get_Adc(ch);
            delay_ms(5);
        }
        return temp_val/times;
    }     
    
    
    
    #include "led.h"
    #include "delay.h"
    #include "key.h"
    #include "sys.h"
    #include "lcd.h"
    #include "usart.h"     
    #include "adc.h"
    const u8 Adc_Channel[3]={ADC_Channel_0,ADC_Channel_1,ADC_Channel_2};
     
     int main(void)
     {     
        int i;
      float adcx[3];
        float temp;
        delay_init();             //延时函数初始化      
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
        uart_init(115200);         //串口初始化为115200
         LED_Init();                 //LED端口初始化
        Adc_Init();                  //ADC初始化       
        while(1)
        {
            
            for(i=0;i<3;i++)                //把多次采集的结果存放到adcx[3]数组中
            {
                adcx[i]=(Get_Adc_Average(Adc_Channel[i],10)*3.3/4096);    //把采集数值转换为电压
                printf("
    adcx[%d]:%4fV	
    ",i,adcx[i]);                                //在串口调试助手中打印出来
                
            }
            delay_ms(1000);
    
        }
     }
     
    DMA模式的多通道
    #include "adc.h"
    #include "delay.h"
    
    void Adc_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStrue;
        ADC_InitTypeDef ADC_InitStrue;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);//使能ADC以及模拟输入端子的时钟
        RCC_ADCCLKConfig(RCC_PCLK2_Div6);                                      //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
        
        GPIO_InitStrue.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;
        GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AIN;
        GPIO_InitStrue.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStrue);                                    //初始化模拟输入端子
        
        
        
        
        ADC_DeInit(ADC1);                                                                   //复位ADC模块
        ADC_InitStrue.ADC_Mode=ADC_Mode_Independent;                                       //ADC工作模式:ADC1和ADC2工作在独立模式
        ADC_InitStrue.ADC_ContinuousConvMode=ENABLE;                                        //模数(Analog Digtal)转换次数设置  单次
        ADC_InitStrue.ADC_ScanConvMode=ENABLE;                                              //模数转换通道设置   Disable==〉不浏览即单通道模式
        ADC_InitStrue.ADC_DataAlign=ADC_DataAlign_Right;                                    //数据对齐方式==〉右对齐                                
        ADC_InitStrue.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;                    //不通过外部中断触发启动而是软件转换
        ADC_InitStrue.ADC_NbrOfChannel=M;                                                    //ADC转换通道数量                        
        ADC_Init(ADC1,&ADC_InitStrue);                                                      //初始化ADC模块
        
        
        ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_239Cycles5);
        ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_239Cycles5);
        ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_239Cycles5);    //内部通道测参考电压
        // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
        ADC_DMACmd(ADC1, ENABLE);
        ADC_Cmd(ADC1,ENABLE);                                                                    //使能ADC模块
        
        ADC_ResetCalibration(ADC1);                                                              //使能复位校准
        while(ADC_GetResetCalibrationStatus(ADC1));
        ADC_StartCalibration(ADC1);                                                              //使能AD校准
        while(ADC_GetCalibrationStatus(ADC1));
    }    
    
    
    void filter(void)      //求得ADC多次采集数值的平均值,直接DMA传回内存 After_filter[i]
    { int sum=0; u8 count,i; for(i=0;i<M;i++) { for(count=0;count<N;count++) { sum=sum+AD_Value[count][i]; } After_filter[i]=(float)(sum/N); sum=0; } } float GetVolt(float adcvalue) { return (adcvalue*3.3/4096); }
    #include "dma.h"
    #include "sys.h"
    #include "adc.h"
    
    /* ADC以及DMA的定义  */
    #define N 10        //10次
    #define M 3            //3个ADC通道
    u16 AD_Value[N][M];
    float After_filter[M];
    
    
    void Dma_Init(void)      //DMA初始化
    {    
        DMA_InitTypeDef DMA_InitType;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
        
        DMA_DeInit(DMA1_Channel1);
        DMA_InitType.DMA_BufferSize=N*M;
        DMA_InitType.DMA_DIR=DMA_DIR_PeripheralSRC;
        DMA_InitType.DMA_M2M=DMA_M2M_Disable;        
        DMA_InitType.DMA_Mode=DMA_Mode_Circular;
        
        DMA_InitType.DMA_MemoryBaseAddr=(u32)&AD_Value[0];
        DMA_InitType.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
        DMA_InitType.DMA_MemoryInc=DMA_MemoryInc_Enable;
        
        DMA_InitType.DMA_PeripheralBaseAddr=(u32)&ADC1->DR;
        DMA_InitType.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
        DMA_InitType.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
        DMA_InitType.DMA_Priority=DMA_Priority_High;
        
        DMA_Init(DMA1_Channel1,&DMA_InitType);
        
    }
    
    
    #include "led.h"
    #include "delay.h"
    #include "key.h"
    #include "sys.h"
    #include "lcd.h"
    #include "usart.h"    
    #include "adc.h"
    #include "dma.h"
    
    /* ADC以及DMA的定义  */
    #define N 10        //10次
    #define M 3            //3个ADC通道
    extern u16 AD_Value[N][M];
    extern float After_filter[M];
    
    int main(void)
     {     
        float value[M];
        u16 adcx;
        u8 i=0;
        float temp;
        delay_init();             //延时函数初始化      
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
        uart_init(115200);         //串口初始化为115200
         LED_Init();                 //LED端口初始化         
        Adc_Init();
        Dma_Init();
        ADC_SoftwareStartConvCmd(ADC1,ENABLE);
        DMA_Cmd(DMA1_Channel1,ENABLE);
        while(1)
        {
            while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
            filter();
            for(i=0;i<M;i++)
            {
                value[i]=GetVolt(After_filter[i]);            //把多次采集的结果转换为电压值存放到value[3]数组中
                printf("
    value[%d]:%4fV	
    ",i,value[i]);//在串口调试助手中打印出来
                memset(value,0,M);
                
            }
            LED0=!LED0;
            delay_ms(500);
        }
     }
    
    
    



  • 相关阅读:
    mysql-community-server-5.7.24 & 5.7.31 (5.6.35 升级到 5.7.24)
    企业 数据 能力 中台 大数据平台 CRM
    Tomcat日志切割配置
    你不知道的 Blob
    细说websocket快速重连机制
    DNS反向查询
    从安全的角度看待DNS
    LVS 负载均衡集群
    Linux服务器配置DNS解析
    react修改state的值
  • 原文地址:https://www.cnblogs.com/18689400042qaz/p/13449556.html
Copyright © 2020-2023  润新知