1. 程式人生 > >第30章 ADC—電壓採集—零死角玩轉STM32-F429系列

第30章 ADC—電壓採集—零死角玩轉STM32-F429系列

第30章     ADC—電壓採集

全套200集視訊教程和1000PDF教程請到秉火論壇下載:www.firebbs.cn

野火視訊教程優酷觀看網址:http://i.youku.com/firege

本章參考資料:《STM32F4xx中文參考手冊》ADC章節。

學習本章時,配合《STM32F4xx中文參考手冊》ADC章節一起閱讀,效果會更佳,特別是涉及到暫存器說明的部分。

30.1 ADC簡介

STM32F429IGT63ADC,每個ADC12位、10位、8位和6位可選,每個ADC16個外部通道。另外還有兩個內部ADC源和VBAT通道掛在ADC1上。ADC具有獨立模式、雙重模式和三重模式,對於不同AD轉換要求幾乎都有合適的模式可選。

ADC功能非常強大,具體的我們在功能框圖中分析每個部分的功能。

30.2 ADC功能框圖剖析

01 單個ADC功能框圖

掌握了ADC的功能框圖,就可以對ADC有一個整體的把握,在程式設計的時候可以做到了然如胸,不會一知半解。框圖講解採用從左到右的方式,跟ADC採集資料,轉換資料,傳輸資料的方向大概一致。

1.    ①電壓輸入範圍

ADC輸入範圍為:VREF- VIN VREF+。由VREF-VREF+ VDDA VSSA、這四個外部引腳決定。

我們在設計原理圖的時候一般把VSSAVREF-接地,把VREF+VDDA 3V3,得到ADC的輸入電壓範圍為:0~3.3V

如果我們想讓輸入的電壓範圍變寬,去到可以測試負電壓或者更高的正電壓,我們可以在外部加一個電壓調理電路,把需要轉換的電壓擡升或者降壓到0~3.3V,這樣ADC就可以測量了。

2.    ②輸入通道

我們確定好ADC輸入電壓之後,那麼電壓怎麼輸入到ADC?這裡我們引入通道的概念,STM32ADC多達19個通道,其中外部的16個通道就是框圖中的ADCx_IN0ADCx_IN1...ADCx_IN5。這16個通道對應著不同的IO口,具體是哪一個IO口可以從手冊查詢到。其中ADC1/2/3還有內部通道: ADC1的通道ADC1_IN16連線到內部的VSS,通道ADC1_IN17連線到了內部參考電壓

VREFINT 連線,通道ADC1_IN18連線到了晶片內部的溫度感測器或者備用電源VBATADC2ADC3的通道161718全部連線到了內部的VSS

02 STM32F429IGT6 ADC 通道

外部的16個通道在轉換的時候又分為規則通道和注入通道,其中規則通道最多有16路,注入通道最多有4路。那這兩個通道有什麼區別?在什麼時候使用?

規則通道

規則通道:顧名思意,規則通道就是很規矩的意思,我們平時一般使用的就是這個通道,或者應該說我們用到的都是這個通道,沒有什麼特別要注意的可講。

注入通道

注入,可以理解為插入,插隊的意思,是一種不安分的通道。它是一種在規則通道轉換的時候強行插入要轉換的一種。如果在規則通道轉換過程中,有注入通道插隊,那麼就要先轉換完注入通道,等注入通道轉換完成後,再回到規則通道的轉換流程。這點跟中斷程式很像,都是不安分的主。所以,注入通道只有在規則通道存在時才會出現。

3.    ③轉換順序
規則序列

規則序列暫存器有3個,分別為SQR3SQR2SQR1SQR3控制著規則序列中的第一個到第六個轉換,對應的位為:SQ1[4:0]~SQ6[4:0],第一次轉換的是位4:0 SQ1[4:0],如果通道16想第一次轉換,那麼在SQ1[4:0]16即可。SQR2控制著規則序列中的第7到第12個轉換,對應的位為:SQ7[4:0]~SQ12[4:0],如果通道1想第8個轉換,則SQ8[4:0]1即可。SQR1控制著規則序列中的第13到第16個轉換,對應位為:SQ13[4:0]~SQ16[4:0],如果通道6想第10個轉換,則SQ10[4:0]6即可。具體使用多少個通道,由SQR1的位L[3:0]決定,最多16個通道。

03 規則序列暫存器

注入序列

注入序列暫存器JSQR只有一個,最多支援4個通道,具體多少個由JSQRJL[2:0]決定。如果JL值小於4的話,則JSQRSQR決定轉換順序的設定不一樣,第一次轉換的不是JSQR1[4:0],而是JCQRx[4:0] x = 4-JL),跟SQR剛好相反。如果JL=001個轉換),那麼轉換的順序是從JSQR4[4:0]開始,而不是從JSQR1[4:0]開始,這個要注意,程式設計的時候不要搞錯。當JL等於4時,跟SQR一樣。

04 注入序列暫存器

4.    ④觸發源

通道選好了,轉換的順序也設定好了,那接下來就該開始轉換了。ADC轉換可以由ADC控制暫存器2: ADC_CR2ADON這個位來控制,寫1的時候開始轉換,寫0的時候停止轉換,這個是最簡單也是最好理解的開啟ADC轉換的控制方式,理解起來沒啥技術含量。

除了這種庶民式的控制方法,ADC還支援外部事件觸發轉換,這個觸發包括內部定時器觸發和外部IO觸發。觸發源有很多,具體選擇哪一種觸發源,由ADC控制暫存器2:ADC_CR2EXTSEL[2:0]JEXTSEL[2:0]位來控制。EXTSEL[2:0]用於選擇規則通道的觸發源,JEXTSEL[2:0]用於選擇注入通道的觸發源。選定好觸發源之後,觸發源是否要啟用,則由ADC控制暫存器2:ADC_CR2EXTTRIGJEXTTRIG這兩位來啟用。

如果使能了外部觸發事件,我們還可以通過設定ADC控制暫存器2:ADC_CR2EXTEN[1:0]JEXTEN[1:0]來控制觸發極性,可以有4種狀態,分別是:禁止觸發檢測、上升沿檢測、下降沿檢測以及上升沿和下降沿均檢測。

5.    ⑤轉換時間
ADC時鐘

ADC輸入時鐘ADC_CLKPCLK2經過分頻產生,最大值是36MHz,典型值為30MHz,分頻因子由ADC通用控制暫存器ADC_CCRADCPRE[1:0]設定,可設定的分頻係數有2468,注意這裡沒有1分頻。對於STM32F429IGT6我們一般設定PCLK2=HCLK/2=90MHz。所以程式一般使用4分頻或者6分頻。

取樣時間

ADC需要若干個ADC_CLK週期完成對輸入的電壓進行取樣,取樣的週期數可通過ADC 取樣時間暫存器ADC_SMPR1ADC_SMPR2中的SMP[2:0]位設定,ADC_SMPR2控制的是通道0~9ADC_SMPR1控制的是通道10~17。每個通道可以分別用不同的時間取樣。其中取樣週期最小是3個,即如果我們要達到最快的取樣,那麼應該設定取樣週期為3個週期,這裡說的週期就是1/ADC_CLK

ADC的總轉換時間跟ADC的輸入時鐘和取樣時間有關,公式為:

Tconv = 取樣時間 + 12個週期

ADCCLK = 30MHz,即PCLK260MHzADC時鐘為2分頻,取樣時間設定為3個週期,那麼總的轉換時為:Tconv = 3 + 12 = 15個週期 =0.5us

一般我們設定PCLK2=90MHz,經過ADC預分頻器能分頻到最大的時鐘只能是22.5M,取樣週期設定為3個週期,算出最短的轉換時間為0.6667us,這個才是最常用的。

6.    ⑥資料暫存器

一切準備就緒後,ADC轉換後的資料根據轉換組的不同,規則組的資料放在ADC_DR暫存器,注入組的資料放在JDRx。如果是使用雙重或者三重模式那規矩組的資料是存放在通用規矩暫存器ADC_CDR內的。

規則資料暫存器ADC_DR

ADC規則組資料暫存器ADC_DR只有一個,是一個32位的暫存器,只有低16位有效並且只是用於獨立模式存放轉換完成資料。因為ADC的最大精度是12位,ADC_DR16位有效,這樣允許ADC存放資料時候選擇左對齊或者右對齊,具體是以哪一種方式存放,由ADC_CR211ALIGN設定。假如設定ADC精度為12位,如果設定資料為左對齊,那AD轉換完成資料存放在ADC_DR暫存器的[4:15]位內;如果為右對齊,則存放在ADC_DR暫存器的[0:11]位內。

規則通道可以有16個這麼多,可規則資料暫存器只有一個,如果使用多通道轉換,那轉換的資料就全部都擠在了DR裡面,前一個時間點轉換的通道資料,就會被下一個時間點的另外一個通道轉換的資料覆蓋掉,所以當通道轉換完成後就應該把資料取走,或者開啟DMA模式,把資料傳輸到記憶體裡面,不然就會造成資料的覆蓋。最常用的做法就是開啟DMA傳輸。

如果沒有使用DMA傳輸,我們一般都需要使用ADC狀態暫存器ADC_SR獲取當前ADC轉換的進度狀態,進而進行程式控制。

注入資料暫存器ADC_JDRx

ADC注入組最多有4個通道,剛好注入資料暫存器也有4個,每個通道對應著自己的暫存器,不會跟規則暫存器那樣產生資料覆蓋的問題。ADC_JDRx32位的,低16位有效,高16位保留,資料同樣分為左對齊和右對齊,具體是以哪一種方式存放,由ADC_CR211ALIGN設定。

通用規則資料暫存器ADC_CDR

規則資料暫存器ADC_DR是僅適用於獨立模式的,而通用規則資料暫存器ADC_CDR是適用於雙重和三重模式的。獨立模式就是僅僅適用三個ADC的其中一個,雙重模式就是同時使用ADC1ADC2,而三重模式就是三個ADC同時使用。在雙重或者三重模式下一般需要配合DMA資料傳輸使用。

7.    ⑦中斷
轉換結束中斷

資料轉換結束後,可以產生中斷,中斷分為四種:規則通道轉換結束中斷,注入轉換通道轉換結束中斷,模擬看門狗中斷和溢位中斷。其中轉換結束中斷很好理解,跟我們平時接觸的中斷一樣,有相應的中斷標誌位和中斷使能位,我們還可以根據中斷型別寫相應配套的中斷服務程式。

模擬看門狗中斷

當被ADC轉換的模擬電壓低於低閾值或者高於高閾值時,就會產生中斷,前提是我們開啟了模擬看門狗中斷,其中低閾值和高閾值由ADC_LTRADC_HTR設定。例如我們設定高閾值是2.5V,那麼模擬電壓超過2.5V的時候,就會產生模擬看門狗中斷,反之低閾值也一樣。

溢位中斷

如果發生DMA傳輸資料丟失,會置位ADC狀態暫存器ADC_SROVR位,如果同時使能了溢位中斷,那在轉換結束後會產生一個溢位中斷。

DMA請求

規則和注入通道轉換結束後,除了產生中斷外,還可以產生DMA請求,把轉換好的資料直接儲存在記憶體裡面。對於獨立模式的多通道AD轉換使用DMA傳輸非常有必須要,程式程式設計簡化了很多。對於雙重或三重模式使用DMA傳輸幾乎可以說是必要的。有關DMA請求需要配合《STM32F4xx中文參考手冊》DMA控制器這一章節來學習。一般我們在使用ADC的時候都會開啟DMA傳輸。

8.    ⑧電壓轉換

模擬電壓經過ADC轉換後,是一個相對精度的數字值,如果通過串列埠以16進位制打印出來的話,可讀性比較差,那麼有時候我們就需要把數字電壓轉換成模擬電壓,也可以跟實際的模擬電壓(用萬用表測)對比,看看轉換是否準確。

我們一般在設計原理圖的時候會把ADC的輸入電壓範圍設定在:0~3.3v,如果設定ADC12位的,那麼12位滿量程對應的就是3.3V12位滿量程對應的數字值是:2^12。數值0對應的就是0V。如果轉換後的數值為  X X對應的模擬電壓為Y,那麼會有這麼一個等式成立:  2^12 / 3.3 = X / Y=> Y = (3.3 * X ) / 2^12

30.3 ADC初始化結構體詳解

標準庫函式對每個外設都建立了一個初始化結構體xxx_InitTypeDef(xxx為外設名稱),結構體成員用於設定外設工作引數,並由標準庫函式xxx_Init()呼叫這些設定引數進入設定外設相應的暫存器,達到配置外設工作環境的目的。

結構體xxx_InitTypeDef和庫函式xxx_Init配合使用是標準庫精髓所在,理解了結構體xxx_InitTypeDef每個成員意義基本上就可以對該外設運用自如了。結構體xxx_InitTypeDef定義在stm32f4xx_xxx.h檔案中,庫函式xxx_Init定義在stm32f4xx_xxx.c檔案中,程式設計時我們可以結合這兩個檔案內註釋使用。

ADC_InitTypeDef結構體

ADC_InitTypeDef結構體定義在stm32f4xx_adc.h檔案內,具體定義如下:

1 typedef struct {

2 uint32_t ADC_Resolution; //ADC解析度選

3 FunctionalState ADC_ScanConvMode; //ADC掃描選擇

4 FunctionalState ADC_ContinuousConvMode; //ADC連續轉換模式選擇

5 uint32_t ADC_ExternalTrigConvEdge; //ADC外部觸發極性

6 uint32_t ADC_ExternalTrigConv; //ADC外部觸發選擇

7 uint32_t ADC_DataAlign; //輸出資料對齊方式

8 uint8_t ADC_NbrOfChannel; //轉換通道數目

9 } ADC_InitTypeDef;

ADC_Resolution:配置ADC的解析度,可選的解析度有12位、10位、8位和6位。解析度越高,AD轉換資料精度越高,轉換時間也越長;解析度越低,AD轉換資料精度越低,轉換時間也越短。

ScanConvMode:可選引數為ENABLEDISABLE,配置是否使用掃描。如果是單通道AD轉換使用DISABLE,如果是多通道AD轉換使用ENABLE

ADC_ContinuousConvMode:可選引數為ENABLEDISABLE,配置是啟動自動連續轉換還是單次轉換。使用ENABLE配置為使能自動連續轉換;使用DISABLE配置為單次轉換,轉換一次後停止需要手動控制才重新啟動轉換。

ADC_ExternalTrigConvEdge:外部觸發極性選擇,如果使用外部觸發,可以選擇觸發的極性,可選有禁止觸發檢測、上升沿觸發檢測、下降沿觸發檢測以及上升沿和下降沿均可觸發檢測。

ADC_ExternalTrigConv:外部觸發選擇,圖 01中列舉了很多外部觸發條件,可根據專案需求配置觸發來源。實際上,我們一般使用軟體自動觸發。

ADC_DataAlign:轉換結果資料對齊模式,可選右對齊ADC_DataAlign_Right或者左對齊ADC_DataAlign_Left。一般我們選擇右對齊模式。

ADC_NbrOfChannelAD轉換通道數目。

ADC_CommonInitTypeDef結構體

ADC除了有ADC_InitTypeDef初始化結構體外,還有一個ADC_CommonInitTypeDef通用初始化結構體。ADC_CommonInitTypeDef結構體內容決定三個ADC共用的工作環境,比如模式選擇、ADC時鐘等等。

ADC_CommonInitTypeDef結構體也是定義在stm32_f4xx.h檔案中,具體定義如下:

1 typedef struct {

2 uint32_t ADC_Mode; //ADC模式選擇

3 uint32_t ADC_Prescaler; //ADC分頻係數

4 uint32_t ADC_DMAAccessMode; //DMA模式配置

5 uint32_t ADC_TwoSamplingDelay; //取樣延遲

6 } ADC_InitTypeDef;

ADC_ModeADC工作模式選擇,有獨立模式、雙重模式以及三重模式。

ADC_PrescalerADC時鐘分頻係數選擇,ADC時鐘是有PCLK2分頻而來,分頻係數決定ADC時鐘頻率,可選的分頻係數為2468ADC最大時鐘配置為36MHz

ADC_DMAAccessModeDMA模式設定,只有在雙重或者三重模式才需要設定,可以設定三種模式,具體可參考參考手冊說明。

ADC_TwoSamplingDelay:2個取樣階段之前的延遲,僅適用於雙重或三重交錯模式。

30.4 獨立模式單通道採集實驗

STM32ADC功能繁多,我們設計三個實驗儘量完整的展示ADC的功能。首先是比較基礎實用的單通道採集,實現開發板上電位器的動觸點輸出引腳電壓的採集並通過串列埠列印至PC端串列埠除錯助手。單通道採集適用AD轉換完成中斷,在中斷服務函式中讀取資料,不使用DMA傳輸,在多通道採集時才使用DMA傳輸。

30.4.1 硬體設計

開發闆闆載一個貼片滑動變阻器,電路設計見圖 05。

05 開發板電位器部分原理圖

貼片滑動變阻器的動觸點通過連線至STM32晶片的ADC通道引腳。當我們使用旋轉滑動變阻器調節旋鈕時,其動觸點電壓也會隨之改變,電壓變化範圍為0~3.3V,亦是開發板預設的ADC電壓採集範圍。

30.4.2 軟體設計

這裡只講解核心的部分程式碼,有些變數的設定,標頭檔案的包含等並沒有涉及到,完整的程式碼請參考本章配套的工程。

我們編寫兩個ADC驅動檔案,bsp_adc.h bsp_adc.c,用來存放ADC所用IO引腳的初始化函式以及ADC配置相關函式。

1.    程式設計要點

1)    初始化配置ADC目標引腳為模擬輸入模式;

2)    使能ADC時鐘;

3)    配置通用ADC為獨立模式,取樣4分頻;

4)    設定目標ADC為12位解析度,1通道的連續轉換,不需要外部觸發;

5)    設定ADC轉換通道順序及取樣時間;

6)    配置使能ADC轉換完成中斷,在中斷內讀取轉換完資料;

7)    啟動ADC轉換;

8)    使能軟體觸發ADC轉換。

ADC轉換結果資料使用中斷方式讀取,這裡沒有使用DMA進行資料傳輸。

2.    程式碼分析
ADC巨集定義

程式碼清單 01 ADC巨集定義

1 #define Rheostat_ADC_IRQ ADC_IRQn

2 #define Rheostat_ADC_INT_FUNCTION ADC_IRQHandler

3

4 #define RHEOSTAT_ADC_GPIO_PORT GPIOC

5 #define RHEOSTAT_ADC_GPIO_PIN GPIO_Pin_3

6 #define RHEOSTAT_ADC_GPIO_CLK RCC_AHB1Periph_GPIOC

7

8 #define RHEOSTAT_ADC ADC1

9 #define RHEOSTAT_ADC_CLK RCC_APB2Periph_ADC1

10 #define RHEOSTAT_ADC_CHANNEL ADC_Channel_13

使用巨集定義引腳資訊方便硬體電路改動時程式移植。

ADC GPIO初始化函式

程式碼清單 02 ADC GPIO初始化

1 static void Rheostat_ADC_GPIO_Config(void)

2 {

3 GPIO_InitTypeDef GPIO_InitStructure;

4

5 // 使能 GPIO 時鐘

6 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK, ENABLE);

7

8 // 配置 IO

9 GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN;

10 // 配置為模擬輸入

11 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;