STM32 ADC結合DMA資料採樣與軟體濾波處理

來源:互聯網
上載者:User

本文原創於觀海聽濤,原作者著作權,轉載請註明出處。
作為一個偏向工控的晶片,ADC採樣是一個十分重要的外設。STM32整合三個12位精度18通道的內部ADC,最高速度1微秒,結合DMA可以解放CPU進行更好的處理。
ADC介面上的其它邏輯功能包括:
●同步的採樣和保持
●交叉的採樣和保持
●單次採樣
類比看門狗功能允許非常精準地監視一路、多路或所有選中的通道,當被監視的訊號超出預置的閥值時,將產生中斷。
由標準定時器(TIMx)和進階控制定時器(TIM1和TIM8)產生的事件,可以分別內部級聯到ADC的開始觸發和注入觸發,應用程式能使AD轉換與時鐘同步。

12位ADC是一種逐次逼近型類比數字數字轉換器。它有多達18個通道,可測量16個外部和2個內部訊號源。

ADC的輸入時鐘不得超過14MHZ,它是由PCLK2經分頻產生。

如果被ADC轉換的類比電壓低於低閥值或高於高閥值,AWD類比看門狗狀態位被設定。

關於ADC採樣與DMA關係,引用網上一段解釋:

STM32 的優點在哪裡?
除去宣傳環節,細細分析。
STM32 時鐘不算快,72MHZ,
也不能擴充大容量的RAM FLASH,
同樣沒有 DSP 那樣強大的指令集。
它的優勢在哪裡呢?
---就在快速採集資料,快速處理上。
ARM 的特點就是方便。
這個快速採集,高效能的ADC 就是一個很好的體現,
12 位精度,最快1uS 的轉換速度,通常具備2 個以上獨立的ADC 控制器,
這意味著,
STM32 可以同時對多個類比量進行快速採集,
這個特性不是一般的MCU具有的。
以上高效能的 ADC,配合相對比較塊的指令集和一些特色的演算法支援,
就構成了STM32 在電機控制上的強大特性。
好了,正題,怎末做一個簡單的ADC,注意是簡單的,
ADC 是個複雜的問題,涉及硬體設計,電源品質,參考電壓,訊號預先處理等等問題。
我們只就如何在MCU內完成一次ADC 作討論。
談到 ADC,我們還要第一次引入另外一個重要的裝置DMA.
DMA是什麼東西呢。
通常在 8 位單片機時代,很少有這個概念。
在外置資源越來越多以後,
我們把一個MCU內部分為主處理器和 外設兩個部分。
主處理器當然是執行我們指令的主要部分,
外設則是串口 I2C ADC 等等用來實現特定功能的裝置
回憶一下,8 位時代,我們的主處理器最常乾的事情是什嗎?
邏輯判斷?不是。那才幾個指令
計算演算法?不是。大部分時候演算法都很簡單。
事實上,主處理器就是作個搬運工,
把 USART 的資料接收下來,存起來
把 ADC 的資料接收下來,存起來
把要發送的資料,存起來,一個個的往USART 裡放。
…………
為瞭解決這個矛盾,
人們想到一個辦法,讓外設和記憶體間建立一個通道,
在主處理器允許下,
讓外設和記憶體直接讀寫,這樣就釋放了主處理器,
這個東西就是DMA。
打個比方:
一個MCU是個公司。
老闆就是主處理器
員工是外設
倉庫就是記憶體
從前 倉庫的東西都是老闆管的。
員工需要原料工作,就一個個報給老闆,老闆去倉庫裡一個一個拿。
員工作好的東西,一個個給老闆,老闆一個個放進倉庫裡。
老闆很累,雖然老闆是超人,也受不了越來越多的員工和單子。
最後老闆雇了一個倉庫保管員,它就是DMA
他專門負責入庫和出庫,
只需要把出庫和入庫計劃給老闆過目
老闆說 OK,就不管了。
後面的入庫和出庫過程,
員工只需要和這個倉庫保管員打交道就可以了。
--------閑話,馬七時常想,讓裝置與裝置之間開DMA,豈不更牛X
比喻完成。
ADC 是個高速裝置,前面提到。
而且 ADC 採集到的資料是不能直接用的。即使你再小心的設計外圍電路,測的離譜的資料總會出現。
那麼通常來說,是採集一批資料,然後進行處理,這個過程就是軟體濾波。
DMA用到這裡就很合適。讓ADC 高速採集,把資料填充到RAM 中,填充一定數量,比如32 個,64 個MCU再來使用。
-----多一句,也可以說,單次ADC 毫無意義。
下面我們來具體介紹,如何使用DMA來進行ADC 操作。
初始化函數包括兩部分,DMA 初始化和ADC 初始化
我們有多個管理員--DMA
一個管理員當然不止管一個DMA 操作。所以DMA有多個Channel

以下是程式分析:
程式基於STM32F103VET6,庫函數實現
RCC部分:(忽略系統時鐘配置)
   //啟動DMA時鐘 
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);   
    //啟動ADC1時鐘    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); 
GPIO部分:(ADC引腳參見上表)
   //ADC_CH10--> PC0    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//類比輸入   
    GPIO_Init(GPIOC, &GPIO_InitStructure); 
    // PC2
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;   
    GPIO_Init(GPIOC, &GPIO_InitStructure);   
ADC1配置:(兩外部輸入,另採樣內部溫度感應器)
void ADC1_Configuration(void)   
{   
    ADC_InitTypeDef ADC_InitStructure;   
   
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //轉換模式為獨立,還有交叉等非常多樣的選擇   
    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_InitStructure.ADC_NbrOfChannel = 3;     //設定轉換序列長度為3,三通道    
    ADC_Init(ADC1, &ADC_InitStructure);   
      
    //ADC內建溫度感應器使能(要使用片內溫度感應器,切忌要開啟它)    
    ADC_TempSensorVrefintCmd(ENABLE);   
      
    //常規轉換序列1:通道10    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5);   
    //常規轉換序列2:通道16(內部溫度感應器),採樣時間>2.2us,(239cycles)    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5); 
     ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 3, ADC_SampleTime_239Cycles5);  
     //輸入參數:ADC外設,ADC通道,轉換序列順序,採樣時間 
    // Enable ADC1    
    ADC_Cmd(ADC1, ENABLE);   
    // 開啟ADC的DMA支援(要實現DMA功能,還需獨立配置DMA通道等參數)    
    ADC_DMACmd(ADC1, ENABLE);   
      
    // 下面是ADC自動校準,開機後需執行一次,保證精度    
    // 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));   
    // ADC自動校準結束---------------    
     ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ADC啟動    
}  
DMA配置:(無軟體濾波)
void DMA_Configuration(void)   
{   
    DMA_InitTypeDef DMA_InitStructure;   
      
    DMA_DeInit(DMA1_Channel1);   
    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;   //DMA外設地址,在頭部定義
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value;         //記憶體位址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                 //外設至記憶體模式
    //BufferSize=2,因為ADC轉換序列有2個通道    
    //如此設定,使序列1結果放在AD_Value[0],序列2結果放在AD_Value[1]    
    DMA_InitStructure.DMA_BufferSize = 3;                                           //一次轉換三個
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   //接受一次後,裝置地址不後移
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;        //接受一次後,記憶體位址後移
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;   //每次傳輸半字
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;   
    //迴圈模式開啟,Buffer寫滿後,自動回到初始地址開始傳輸    
    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);   
    //配置完成後,啟動DMA通道    
    DMA_Cmd(DMA1_Channel1, ENABLE);   

此DMA常式用於單次ADC轉換,配合軟體濾波可做如下改動:
全域聲明:
vu16 AD_Value[30][3];   //AD採樣值
vu16 After_filter[3];   //AD濾波後
DMA部分:(帶中斷濾波)
void DMA_Configuration(void)   
{   
    DMA_InitTypeDef DMA_InitStructure;   
      
    DMA_DeInit(DMA1_Channel1);   
    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;   
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value;   
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;   
    //BufferSize=2,因為ADC轉換序列有2個通道    
    //如此設定,使序列1結果放在AD_Value[0],序列2結果放在AD_Value[1]    
    DMA_InitStructure.DMA_BufferSize = 90;   
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;   
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;   
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;   
    //迴圈模式開啟,Buffer寫滿後,自動回到初始地址開始傳輸    
    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);   
    //配置完成後,啟動DMA通道    
    DMA_Cmd(DMA1_Channel1, ENABLE);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); //使能DMA傳輸完成中斷 
   

NVIC部分:
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQChannel; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure);          // Enable the DMA Interrupt 
stm32f10x_it.c檔案:
void DMA1_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC1) != RESET)
{
  filter();
  DMA_ClearITPendingBit(DMA1_IT_TC1);
}
}
濾波部分:(均值濾波)
#define N 30
void filter(void)
{
   int  sum = 0;
   u8 count,i;
   for(i=0;i<2;i++)
   {
    for ( count=0;count<N;count++)
    {
       sum += AD_Value[count][i];
    }
    After_filter[i]=sum/N;
    sum=0;
   }
   
}
採樣資料與實際電壓/溫度轉換:
u16 GetTemp(u16 advalue)   
{   
    u32 Vtemp_sensor;   
    s32 Current_Temp;   
      
//    ADC轉換結束以後,讀取ADC_DR寄存器中的結果,轉換溫度值計算公式如下:    
//          V25 - VSENSE    
//  T(℃) = ------------  + 25    
//           Avg_Slope    
//   V25:  溫度感應器在25℃時 的輸出電壓,典型值1.43 V。    
//  VSENSE:溫度感應器的當前輸出電壓,與ADC_DR 寄存器中的結果ADC_ConvertedValue之間的轉換關係為:    
//            ADC_ConvertedValue * Vdd    
//  VSENSE = --------------------------    
//            Vdd_convert_value(0xFFF)    
//  Avg_Slope:溫度感應器輸出電壓和溫度的關聯參數,典型值4.3 mV/℃。    
   
    Vtemp_sensor = advalue * 330 / 4096;   
    Current_Temp = (s32)(143 - Vtemp_sensor)*10000/43 + 2500;   
    return (s16)Current_Temp;   
}    
    
u16 GetVolt(u16 advalue)   
{
   
    return (u16)(advalue * 330 / 4096);   

濾波部分思路為:ADC正常連續採樣三個通道,由DMA進行搬運,一次搬運90個資料,即為1-2-3-1-2-3迴圈,每個通道各30次,存在 AD_Value[30][3]中,30為每通道30個資料,3為三個通道,根據二維數組儲存方式此過程自動完成。而每當一次DMA過程結束後,觸發 DMA完成中斷,進入濾波函數將30個資料均值成一個, 存入After_filter[3]。整個過程濾波計算需要CPU參與,而在程式中採樣結果值隨時均為最新,儘力解決程式複雜性和CPU負載。 x=GetVolt(After_filter[0]);即可得到即時電壓值。
本文參考:STM32手冊,《ADC資料的軟體濾波方法及其樣本程式》,《下面來講一下STM32的ADC應用》均來源於互連網

本文轉載自 ╄→風、吹不散《STM32
ADC結合DMA資料採樣與軟體濾波處理》

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.