STM32多通道AD采樣DMA傳輸的實現

在嵌入式產品中有時候需要實現對外部的模擬量進行采樣處理和記錄,而這就需要使用到ADC功能,將外部的模擬量轉換成數字量。而在復雜的嵌入式產品中,往往需要使用多路AD采樣,例如在智能家居產品,電池電量檢測,熱敏溫度傳感器,煙霧傳感器,氣敏傳感器等都是可以使用ADC來實現采樣的。在本文章,將會介紹如何通過意法的STM32 MCU實現用DMA完成多通道的AD采樣功能。

什么叫ADC

ADC即模擬數字轉換器(英語:Analog-to-digital converter)是用于將模擬形式的連續信號轉換為數字形式的離散信號的一類設備。一個模擬數字轉換器可以提供信號用于測量。與之相對的設備成為數字模擬轉換器。

影響AD采樣的因素有哪些

  • 分辨率

分辨率指的是ADC的位數,例如STM32F103MCU的內部ADC的分辨率是12位,那么它所采樣的結果就在0-4096之間。

  • 最小采樣單位值

根據基準電壓和參考電壓的不同,其值也是不同的,例如在基準電壓為3.3V,參考電壓最低為0V,最高為3.3V,采樣分辨率位12位的嵌入式系統中,則ADC的最小量程單位則為:3.3V/4096 = 0.00080566。

  • 量程

在無負電壓的嵌入式系統中,量程范圍0-基準電壓。

  • 電源噪音

電源質量直接影響了AD采樣的正確性和穩定性,如果條件滿足,建議使用線性穩壓源,若是使用開關電源的話,需要在VDDA模擬電源輸入和參考電壓輸入接一個線性穩壓管,同時要注意減小PCB板布局走線中結電容對采樣電路的影響。

STM32F103 ADC主要特性

  • 12-位分辨率
  • 轉換結束,注入轉換結束和發生模擬看門狗事件時產生中斷
  • 單次和連續轉換模式
  • 從通道0到通道n的自動掃描模式
  • 自校準
  • 帶內嵌數據一致的數據對齊
  • 通道之間采樣間隔可編程
  • 規則轉換和注入轉換均有外部觸發選項
  • 間斷模式
  • 雙重模式(帶2個或以上ADC的器件)
  • ADC轉換時間:─ STM32F103xx 增強型產品: ADC 時鐘為 56MHz 時為 1μs(ADC 時鐘為 72MHz 為 1.17μs)─ STM32F101xx 基本型產品: ADC 時鐘為 28MHz 時為 1μs(ADC 時鐘為 36MHz 為 1.55μs)─ STM32F102xxUSB 型產品: ADC 時鐘為 48MHz 時為 1.2μs
  • ADC供電要求: 2.4V到3.6V
  • ADC輸入范圍: VREF- ≤ VIN ≤ VREF+● 規則通道轉換期間有DMA請求產生。

DMA簡介

直接存儲器存取用來提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。無須CPU任何干預,通過DMA數據可以快速地移動。這就節省了CPU的資源來做其他操作。

STM32F103 DMA主要特性

  • 12個 獨立的可配置的通道(請求)DMA1有7個通道, DMA2有5個通道
  • 每個通道都直接連接專用的硬件DMA請求,每個通道都同樣支持軟件觸發。這些功能通過軟件來配置。
  • 在七個請求間的優先權可以通過軟件編程設置(共有四級:很高、高、中等和低),假如在相等優先權時由硬件決定(請求0優先于請求1,依此類推) 。
  • 獨立的源和目標數據區的傳輸寬度(字節、半字、全字),模擬打包和拆包的過程。源和目標地址必須按數據傳輸寬度對齊。
  • 支持循環的緩沖器管理
  • 每個通道都有3個事件標志(DMA 半傳輸, DMA傳輸完成和DMA傳輸出錯),這3個事件標志邏輯或成為一個單獨的中斷請求。
  • 存儲器和存儲器間的傳輸
  • 外設和存儲器,存儲器和外設的傳輸
  • 閃存、 SRAM、外設的SRAM、 APB1 APB2和AHB外設均可作為訪問的源和目標。
  • 可編程的數據傳輸數目:最大為65536

如何實現多通道AD采樣的DMA傳輸

軟件流程.JPG

ADC功能引腳配置

void ADC_GPIO_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    /* Enable DMA clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    /* Enable ADC1 and GPIOC clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    
    //配置模擬通道輸入引腳
    GPIO_InitStructure.GPIO_Pin = ADC1_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引腳為模擬輸入模式
    GPIO_Init(ADC1_GPIOX, &GPIO_InitStructure);             // 輸入時不用設置速率
    
    //配置模擬通道輸入引腳
    GPIO_InitStructure.GPIO_Pin = ADC2_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引腳為模擬輸入模式
    GPIO_Init(ADC2_GPIOX, &GPIO_InitStructure);             // 輸入時不用設置速率
    
    //配置模擬通道輸入引腳
    GPIO_InitStructure.GPIO_Pin = ADC3_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引腳為模擬輸入模式
    GPIO_Init(ADC3_GPIOX, &GPIO_InitStructure);             // 輸入時不用設置速率

    GPIO_InitStructure.GPIO_Pin = ADC4_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(ADC4_GPIOX,&GPIO_InitStructure);
    
}

配置多通道ADC功能

void ADC_Multichannel_Config(void)
{
    ADC_InitTypeDef  ADC_InitStructure; 
    
    ADC_DeInit(ADC1);  //將外設 ADC1 的全部寄存器重設為缺省值
    
    /* ADC1 configuration ------------------------------------------------------*/ 
    
    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 = M; //順序進行規則轉換的ADC通道的數目
    ADC_Init(ADC1, &ADC_InitStructure); //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器
    
    /* ADC1 regular channel11 configuration */ 
    //設置指定ADC的規則組通道,設置它們的轉化順序和采樣時間
    //ADC1,ADC通道x,規則采樣順序值為y,采樣時間為239.5周期
    
    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_4, 3, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 4, ADC_SampleTime_239Cycles5);
    // 開啟ADC的DMA支持(要實現DMA功能,還需獨立配置DMA通道等參數)     
    ADC_DMACmd(ADC1, ENABLE); 
    /* Enable ADC1 */    
    ADC_Cmd(ADC1, ENABLE);    //使能指定的ADC1
     /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //復位指定的ADC1的校準寄存器 
    
    /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //復位指定的ADC1的校準寄存器 
    /* Start ADC1 calibaration */
    ADC_StartCalibration(ADC1);  //開始指定ADC1的校準狀態 
    /* Check the end of ADC1 calibration */
    while(ADC_GetCalibrationStatus(ADC1)); 
    //獲取指定ADC1的校準程序,設置狀態則等待
}

配置DMA通道,使能ADC轉換結果從外設到內存

void DMA_Configuration(void) 
{
    /* ADC1  DMA1 Channel Config */  
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA傳輸
    DMA_DeInit(DMA1_Channel1);   //將DMA的通道1寄存器重設為缺省值
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (u32)&ADC1->DR;  //DMA外設ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value;  //DMA內存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //內存作為數據傳輸的目的地
    DMA_InitStructure.DMA_BufferSize = N*M;  //DMA通道的DMA緩存的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外設地址寄存器不變
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //內存地址寄存器遞增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //數據位寬度16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //數據寬度16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在循環緩存模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x擁有高優先級
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x沒有設置為內存到內存
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);  //根據DMA_InitStruct中指定的參數DMA通道
}

開始啟動ADC轉換功能

void ADC_Start(void)
{
    ADC_GPIO_Config();
    ADC_Multichannel_Config();
    DMA_Configuration();
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); //開始ADC轉換
    DMA_Cmd(DMA1_Channel1, ENABLE);  //啟動DMA通道
    
}

使用冒泡排序法,對各通道的連續五次采樣結果取平均值

u16 ADC1_AveragValue(u16 ADC_Value[N][M])
{
    u16 ADC1_Value[N];
    u8 i = 0;
    u8 j = 0;
    u16 temp = 0;
    u16 ADC1_Av = 0;
    for (i = 0;i < N;i++)
    {
              ADC1_Value[i] = ADC_Value[i][0];
    }
    /*冒泡排序*/
    for(i=0; i<N-1; i++)
    {
//內循環選擇要進行比較的數
        for(j=0; j<N-1-i; j++)
        {
             if(ADC1_Value[j]>ADC1_Value[j+1])
             {
                    temp=ADC1_Value[j];
                    ADC1_Value[j]=ADC1_Value[j+1];
                    ADC1_Value[j+1]=temp;
             }
        }
    }
    /*去掉最大值和最小值*/
    for (i = 0; i<N-2;i++)
    {
        ADC1_Av += ADC1_Value[i+1];
        
    }
    /*取平均值*/
    ADC1_Av = ADC1_Av/4;
    return ADC1_Av;
    
}

轉換采樣結果

(float)ADC1_AveragValue(ADC_Value)/4096*3.3)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容