基於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("\nadcx[%d]:%4fV\t\n",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("\nvalue[%d]:%4fV\t\n",i,value[i]);//在串口調試助手中打印出來
            memset(value,0,M);
            
        }
        LED0=!LED0;
        delay_ms(500);
    }
 }
 
        

 





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM