ADC多路采樣與穩定顯示

LED數碼管或者LCD12864等顯示器在顯示溫度采樣值時,需要的是穩定的能反映溫度實際變化的顯示效果。ADC采樣頻率快,直接采樣輸出的值往往有不小的跳變,這時候需要對其數據做一些數字濾波處理。常用的方法有:平均值法、多次采樣用冒泡法取中間一段數值法、平滑平均線法。冒泡法處理時間太長,這里主要用了平均值和平滑平均線兩種方法一起處理。

一、算法部分


平滑平均線法可解決一般均值法的欠靈敏和滯后性,同時濾除毛刺。用此法對ADC采集的數據進行ADC濾波后,在LCD12864上顯示穩定。

具體做法如下:

  1. 在一個ADC通道上采集多次,獲得采樣平均值a,用a初始化含有假定100個數據的數組b[100]中所有的數據(這樣b[]數組的平均值就是a);
  2. 后續每次運行一次ADC采樣都會得到一個新的采樣平均值A,同時把數組b的數據左移(b[0]=b[1]~~~b[98]=b[99]),然后把A的值賦值給b[99];
  3. 如此每次運行一次采樣就會平滑的更新一次數組b的平均值B,也就得到了數字濾波后的平滑平均值B,平均值B就可以拿去做顯示,效果穩定;
  4. 實際程序處理中是:第一次得到的采樣平均值A0賦值給b[0],如此順序,第100次采樣得到的平均值A99賦值給b[99],之后又重新依次更新b[0]到b[99]的值,如此循環,這樣做可以減少運算量,同時達到左移的效果

平滑平均值代碼

/**
  * @brief  ADC采樣所得到的溫度
  * @param None
  * @retval : None
  */
void ADC1_Tempera(void)
{
    u32 i,k,m,n;
    static u32 ValueCount;
    for(i=0;i<ADC_ChannelCount;i++)
    {
        /*
        *@平均值濾波
        */
        ValueCount=0;
        for(k=0;k<ADC1_Count;k++)//取中間的100次值(ADC1_Count)
        {
            ValueCount+=ADGetValue[k][i];
            ADC_Filter[i]=(ValueCount/ADC1_Count);//求取一個ADC通道的平均值
        }
        if(Tflag==0)
        {
            for(n=0;n<SmoothNum;n++)
            {
                ADC_SmoothFilter[i][n]=ADC_Filter[i];//獲得第一次平滑值
            }
            ADC_SmoothCount[i]=ADC_Filter[i];//獲取第一次平滑平均值線
        }
        /*
        *@平滑濾波
        */
        if(Tflag==1)
        {
            ADC_SmoothFilter[i][Smoth_Num[i]]=ADC_Filter[i];//把數值循環放置
            Smoth_Num[i]++;
            if(Smoth_Num[i]==SmoothNum)
                Smoth_Num[i]=0;
            for(n=0;n<SmoothNum;n++)//平滑濾波
            {
                ADC_SmoothCount[i] += ADC_SmoothFilter[i][n];
            }
            ADC_SmoothCount[i]=(ADC_SmoothCount[i]/SmoothNum);//獲取平滑平均值線
        }
    }
    for(m=0;m<3;m++)
    {
        TMonitor[m]=(((2361*ADC_SmoothCount[2*m])/(ADC_SmoothCount[2*m+1])-2597)+TemperaSet[m+4]);//已經擴大10倍的值轉化為溫度數據,低溫,高溫,環境溫度 TemperaSet[m+4]為溫度補償系數
        if(Tflag==0)
            TMonitorDis[m]=TMonitor[m];
        /*
        *@顯示處理緩慢變化
        */
        if(TMonitorDis[m]<(TMonitor[m]-1))
            TMonitorDis[m] += 1;
        if(TMonitorDis[m]>(TMonitor[m]+1))
            TMonitorDis[m] -= 1;
    }
    Tflag=1;
}

代碼注解

  • ADC_ChannelCount為采樣通道數,其值為6,表示ADC1有6個采樣通道。
  • ADC1_Count為每一個通道在執行一次采樣函數中需要采樣的次數,此值為100。
  • ADGetValue[ADC1_Count][ADC_ChannelCount]數組內存中存儲的是6個通道中每個通道的100個數據,即每執行一次函數void ADC1_Tempera(void)就更新一次這段內存的所有數據。
  • 批量采樣工作過程是:定時器觸發一次后依次采樣通道(1-2-3-4-5-6),第二次定時器觸發后依次采樣通道(1-2-3-4-5-6),如此連續轉換,循環掃描;所以要想每個通道都采樣100次,那么就需要觸發轉換100次,這樣定義一段內存存儲數據的數組就應該是ADGetValue[100][6],這樣在i=0~99情況下100個數據ADGetValue[i][0]表示為通道1在采樣了100次后所得到的數據。

ADC通道配置


DMA模式配置要點

  • STM32的ADC1作為DMA的外設源,其地址為16位,如果變量值不是16位那么所得到的數據會混亂,但是DMA配置里是32位,所以需要強制轉換為32位。
vu16 ADGetValue[][];//AD采樣值,ADC_ChannelCount個通道,每個通道采樣ADC1_Count次
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADGetValue;//設置DMA內存地址,ADC轉換結果直接放入該地址
  • 使用定時器外部觸發轉換,ADC六個通道采樣,循環采樣模式、連續轉換。
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;      //使用獨立模式,掃描模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;            //循環掃描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;      //連續轉換模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部定時器觸發轉換

ADC配置代碼

/**
  * @brief  配置采樣通道端口 使能GPIO時鐘    設置ADC采樣PA0端口信號
  * @param None
  * @retval : None
  */
void ADC1_GPIO_Config(void)
{ 
    GPIO_InitTypeDef ADC_PORT;    
    RCC_APB2PeriphClockCmd(ADC_ClK, ENABLE);      
    ADC_PORT.GPIO_Pin = ADC_Pin;                //ADC引腳選擇,6個通道
    ADC_PORT.GPIO_Mode = GPIO_Mode_AIN;         //GPIO設置為模擬輸入
    GPIO_Init(ADC_CONTROL, &ADC_PORT);   
}
/**
  * @brief  配置ADC1的工作模式為DMA模式
  * @param None
  * @retval : None
  */
 void ADC1_Mode_Config(void)
 {
    ADC_InitTypeDef ADC_InitStructure;  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);    //使能ADC1時鐘 
    /* ADC1 configuration */
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;      //使用獨立模式,掃描模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;            //循環掃描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;      //連續轉換模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部定時器觸發轉換
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//數據右對齊
    ADC_InitStructure.ADC_NbrOfChannel = ADC_ChannelCount;  //有ADC_ChannelCount個轉換通道
    ADC_Init(ADC1, &ADC_InitStructure);
    /* 以下為配置通道數總共6路,分別是AD10,AD11,AD12,AD13,AD14,AD15 */
    /* ADC1 regular channel10 configuration */ 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5); //通道10,作為第1個采樣目標,采樣周期239.5個時鐘周期
    /* ADC1 regular channel11 configuration */ 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_55Cycles5); //通道11,作為第2個采樣目標,采樣周期55.5個時鐘周期
    /* ADC1 regular channel12 configuration */ 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 3, ADC_SampleTime_55Cycles5); //通道12,作為第3個采樣目標,采樣周期55.5個時鐘周期
    /* ADC1 regular channel13 configuration */ 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 4, ADC_SampleTime_55Cycles5); //通道13,作為第4個采樣目標,采樣周期55.5個時鐘周期
    /* ADC1 regular channel14 configuration */ 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 5, ADC_SampleTime_55Cycles5); //通道14,作為第5個采樣目標,采樣周期55.5個時鐘周期
    /* ADC1 regular channel15 configuration */ 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 6, ADC_SampleTime_55Cycles5); //通道15,作為第6個采樣目標,采樣周期55.5個時鐘周期

    /* Enable ADC1 DMA */
    ADC_DMACmd(ADC1, ENABLE);    //使能ADC的DMA
    /*使能ADC1外部觸發*/
    ADC_ExternalTrigConvCmd(ADC1,ENABLE);
    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE); //使能ADC1

    /* Enable ADC1 reset calibaration register */   
    ADC_ResetCalibration(ADC1);
    /* Check the end of ADC1 reset calibration register */
    while(ADC_GetResetCalibrationStatus(ADC1));

    /* Start ADC1 calibaration */
    ADC_StartCalibration(ADC1);
    /* Check the end of ADC1 calibration */
    while(ADC_GetCalibrationStatus(ADC1));
     
    /* Start ADC1 Software Conversion */ 
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);  //開始轉換
    
}
/**
  * @brief  配置DMA
  * @param None
  * @retval : None
  */
 void DMA_Config(void)
 {
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA1時鐘
    /* DMA1 channel1 configuration */
    DMA_DeInit(DMA1_Channel1);  //指定DMA1通道1
    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//設置DMA外設地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADGetValue;//設置DMA內存地址,ADC轉換結果直接放入該地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外設設置為數據傳輸的來源
    DMA_InitStructure.DMA_BufferSize = (ADC_ChannelCount* ADC1_Count);  //DMA緩沖區設置為ADC_ChannelCount* ADC1_Count,有6個通道;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設地址不增加
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//內存地址增加
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外設源的數據寬度半字,ADC1_DR的值16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//循環模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    /* Enable DMA channel1 */
    DMA_Cmd(DMA1_Channel1, ENABLE);  //使能DMA通道
    //DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); //使能DMA傳輸完成中斷 
}

/**
  * @brief  初始化ADC1
  * @param None
  * @retval : None
  */
void ADC1_Init(void)
{
    ADC1_GPIO_Config();
    ADC1_Mode_Config();
    DMA_Config();
    TIM1_Mode_Config();//開啟定時器1,產生PWN1外部觸發源
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容