開發環境:
MDK:Keil 5.30
開發板:GD32F207I-EVAL
MCU:GD32F207IK
1 ADC工作原理
GD32F2系列有 3 個逐次逼近型的ADC,精度為 12 位,有18個多路復用通道,可以轉換來自16個外部通道和2個內部通道的模擬信號。其中ADC0 和 ADC1都有 16 個外部通道, ADC2 根據 CPU 引腳的不同通道數也不同,一般都有8 個外部通道。各通道的A/D轉換可以__單次、連續、掃描或間斷模式__執行。ADC的結果可以__左對齊或右對齊方式__存儲在16位數據寄存器中。模擬看門狗特性允許應用程序檢測輸入電壓是否超出用戶定義的高/低閥值。ADC 的輸入時鐘不得超過28MHz,它是由PCLK2經分頻產生。
1.1 ADC架構
ADC架構如下圖所示。
1.電壓輸入范圍
ADC 輸入范圍為:VREFN ≤ VIN ≤ VREFP。由VREFN、VREFP、VDDA、VSSA這四個外部引腳決定。
我們在設計原理圖的時候一般把 VSSA 和 VREFN接地,把VREFP和 VDDA 接 3V3,得到ADC 的輸入電壓范圍為: 0~3.3V。在 64 腳以下的 CPU 中,沒有VREFN和 VREFP這兩個引腳,
ADC 電壓輸入范圍直接由 VDDA和 VSSA決定。如果我們想讓輸入的電壓范圍變寬,去到可以測試負電壓或者更高的正電壓,我們可以在外部加一個電壓調理電路,把需要轉換的電壓抬升或者降壓到 0~3.3V,這樣 ADC 就可以測量。
【注】VDDA和VSSA必須分別連接到VDD和VSS。
2.輸入通道
我們確定好ADC輸入電壓之后,那么電壓怎么輸入到 ADC?這里我們引入通道的概念,GD32 的ADC多達18個通道,其中外部的16個通道就是框圖中的 ADCx_IN0、ADCx_IN1...ADCx_IN5。這16個通道對應著不同的 IO 口,具體是哪一個 IO 口可以從手冊查詢到。其中 ADC0/1/2還有內部通道: ADC0 的通道 16 連接到了芯片內部的溫度傳感器, Vrefint 連接到了通道 17。 ADC1 的模擬通道 16 和 17 連接到了內部的 VSS。ADC2 的模擬通道 9、 14、 15、 16 和 17 連接到了內部的 VSS。
外部的 16 個通道在轉換的時候又分為規則通道和注入通道,其中規則通道最多有 16路,注入通道最多有 4 路。那這兩個通道有什么區別?在什么時候使用?
規則通道
規則通道:規則通道就是很規矩的意思,我們平時一般使用的就是這個通道,或者應該說我們用到的都是這個通道,沒有什么特別要注意的可講。
注入通道
注入,可以理解為插入,插隊的意思,是一種不安分的通道。它是一種在規則通道轉換的時候強行插入要轉換的一種。如果在規則通道轉換過程中,有注入通道插隊,那么就要先轉換完注入通道,等注入通道轉換完成后,再回到規則通道的轉換流程。這點跟中斷程序很像,都是不安分的主。所以,注入通道只有在規則通道存在時才會出現。
3.轉換順序
ADC支持18個多路通道,可以把轉換組織成兩組:一個規則組通道和一個注入組通道。
規則組,可以按照特定的序列組織成多達16個轉換的序列。ADC_RSQ0~ADC_RSQ2寄存器規定了規則組的通道選擇。ADC_RSQ0寄存器的RL[3:0]位規定了整個規則組轉換序列的長度。
注入組,可以按照特定的序列組織成多達4個轉換的序列。ADC_ISQ寄存器規定了注入組的通道選擇。ADC_ISQ寄存器的IL[1:0]位規定了整個注入組轉換序列的長度。
4.觸發源
通道選好了,轉換的順序也設置好了,那接下來就該開始轉換了。 ADC 轉換可以由ADC_CTL1的 ADON 這個位來控制,寫 1 的時候開始轉換,寫 0 的時候停止轉換,這個是最簡單也是最好理解的開啟 ADC 轉換的控制方式,理解起來沒啥技術含量。
除了這種庶民式的控制方法, ADC 還支持觸發轉換,這個觸發包括內部定時器觸發和外部 IO 觸發。觸發源有很多,具體選擇哪一種觸發源,由 ADC_CTL1的ETSRC[2:0]和ETSIC[2:0]位來控制。ETSRC[2:0]用于選擇規則通道的觸發源,ETSIC[2:0]用于選擇注入通道的觸發源。選定好觸發源之后,觸發源是否要激活,則由ADC_CTL1的 ETERC和 ETEIC這兩位來激活。
5.數據寄存器
一切準備就緒后,ADC 轉換后的數據根據轉換組的不同,規則組的數據放在ADC_RDATA寄存器,注入組的數據放在ADC_IDATAx。
6.中斷
轉換結束中斷
數據轉換結束后,可以產生中斷,中斷分為三種:規則通道轉換結束中斷,注入轉換通道轉換結束中斷,模擬看門狗中斷。其中轉換結束中斷很好理解,跟我們平時接觸的中斷一樣,有相應的中斷標志位和中斷使能位,我們還可以根據中斷類型寫相應配套的中斷服務程序。
模擬看門狗中斷
當被 ADC 轉換的模擬電壓低于低閾值或者高于高閾值時,就會產生中斷,前提是我們開啟了模擬看門狗中斷,其中低閾值和高閾值由 ADC_WDHT 和 ADC_WDLT置。例如我們設置高閾值是 2.5V,那么模擬電壓超過 2.5V 的時候,就會產生模擬看門狗中斷,反之低閾值也一樣。
DMA 請求
DMA 請求可以通過設置 ADC_CTL1 寄存器的 DMA 位來使能,它用于傳輸規則組多個通道的轉換結果。 ADC 在規則組一個通道轉換結束后產生一個 DMA 請求, DMA 接受到請求后可以將轉換的數據從 ADC_RDATA 寄存器傳輸到用戶指定的目的地址。
注意: 只有 ADC0 和 ADC2 有 DMA 功能, ADC1 轉換的數據可以在 ADC 同步模式下傳輸。
7.轉換時間
ADC 時鐘
ADC 輸入時鐘 ADCCLK由 PCLK2 經過分頻產生,最大是28M,分頻因子由 RCC 時鐘配置寄存器RCU_CFG0的位 15:14 ADCPSC[1:0]設置,可以是 2/4/6/8/12/16 分頻,注意這里沒有 1 分頻。一般我們設置 PCLK2=HCLK=120M。
采樣時間
ADC 使用若干個 ADCCLK 周期對輸入的電壓進行采樣,采樣的周期數可通過 ADC采樣時間寄存器ADC_SAMPT0 和 ADC_SAMPT1中的 SMP[2:0]位設置,ADC_SAMPT1控制的是通道 09, ADC_SAMPT0 控制的是通道 1017。每個通道可以分別用不同的時間采樣。其中采樣周期最小是 1.5 個,即如果我們要達到最快的采樣,那么應該設置采樣周期為 1.5個周期,這里說的周期就是 1/ADCCLK。
ADC 的轉換時間跟 ADC 的輸入時鐘和采樣時間有關,公式為:
Tconv = 采樣時間 +12.5 個周期。
例如,當 ADCLK = 14MHz,采樣時間設置為 1.5 周期(最快),那么總的轉換時間:
Tconv = 1.5 周期 + 12.5 周期 = 14 周期 = 1us。
8.電壓轉換
模擬電壓經過 ADC 轉換后,是一個 12 位的數字值,如果通過串口以 16 進制打印出來的話,可讀性比較差,那么有時候我們就需要把數字電壓轉換成模擬電壓,也可以跟實際的模擬電壓(用萬用表測)對比,看看轉換是否準確。
我們一般在設計原理圖的時候會把ADC 的輸入電壓范圍設定在: 0~3.3v,因為 ADC是 12 位的,那么 12 位滿量程對應的就是3.3V,12 位滿量程對應的數字值是: 2^12。數值0 對應的就是 0V。如果轉換后的數值為X, X對應的模擬電壓為 Y,那么會有這么一個等式成立: 2^12 / 3.3 = X / Y, => Y = (3.3 * X ) / 2^12。
1.2 ADC通道選擇
GD32 將 ADC 的轉換分為 2 個通道組: 規則通道組和注入通道組 。
規則通道相當于你正常運行的程序,而注入通道呢,就相當于中斷。在你程序正常執行的時候,中斷是可以打斷你的執行的。同這個類似,注入通道的轉換可以打斷規則通道的轉換, 在注入通道被轉換完成之后,規則通道才得以繼續轉換。
GD32 ADC IO通道分配 | ||||||
---|---|---|---|---|---|---|
ADC0 | IO | ADC1 | IO | ADC2 | IO | |
通道0 | PA0 | 通道0 | PA0 | 通道0 | PA0 | |
通道1 | PA1 | 通道1 | PA1 | 通道1 | PA1 | |
通道2 | PA2 | 通道2 | PA2 | 通道2 | PA2 | |
通道3 | PA3 | 通道3 | PA3 | 通道3 | PA3 | |
通道4 | PA4 | 通道4 | PA4 | 通道4 | PF6 | |
通道5 | PA5 | 通道5 | PA5 | 通道5 | PF7 | |
通道6 | PA6 | 通道6 | PA6 | 通道6 | PF8 | |
通道7 | PA7 | 通道7 | PA7 | 通道7 | PF9 | |
通道8 | PB0 | 通道8 | PB0 | 通道8 | PF10 | |
通道9 | PB1 | 通道9 | PB1 | 通道9 | 內部VSS | |
通道10 | PC0 | 通道10 | PC0 | 通道10 | PC0 | |
通道11 | PC1 | 通道11 | PC1 | 通道11 | PC1 | |
通道12 | PC2 | 通道12 | PC2 | 通道12 | PC2 | |
通道13 | PC3 | 通道13 | PC3 | 通道13 | PC3 | |
通道14 | PC4 | 通道14 | PC4 | 通道14 | 內部VSS | |
通道15 | PC5 | 通道15 | PC5 | 通道15 | 內部VSS | |
通道16 | 內部溫度傳感器 | 通道16 | 內部VSS | 通道16 | 內部VSS | |
通道17 | 內部Vrefint | 通道17 | 內部VSS | 通道17 | 內部VSS |
上面的例子因為速度較慢,不能完全體現這樣區分(規則通道組和注入通道組)的好處,但在工業應用領域中有很多檢測和監視探頭需要較快地處理,這樣對 AD 轉換的分組將簡化事件處理的程序并提高事件處理的速度。
GD32 其 ADC 的規則通道組最多包含 16 個轉換,而注入通道組最多包含 4 個通道。關于這兩個通道組的詳細介紹,請參考《GD32參考手冊》,我們這里就不在一一列舉了。
溫度傳感器和通道ADC0_IN16相連接,內部參照電壓VREFINT和ADC0_IN17相連接。可以按注入或規則通道對這兩個內部通道進行轉換。
【注意】溫度傳感器和VREFINT只能出現在主ADC0 中。
1.3 ADC轉換模式
- 單次轉換模式
該模式能夠運行在規則組和注入組。單次轉換模式下, ADC_RSQ2寄存器的RSQ0[4:0]位或者ADC_ISQ寄存器的ISQ3[4:0]位規定了ADC的轉換通道。當ADCON位被置1,一旦相應軟件觸發或者外部觸發發生, ADC就會采樣和轉換一個通道。
規則通道單次轉換結束后,轉換數據將被存放于ADC_RDATA寄存器中, EOC將會置1。如果EOCIE位被置1,將產生一個中斷。
注入通道單次轉換結束后,轉換數據將被存放于ADC_IDATA0寄存器中, EOC和EOIC位將會置1。如果EOCIE或EOICIE位被置1,將產生一個中斷。
- 連續轉換模式
在該模式可以運行在規則組通道上。對ADC_CTL1寄存器的CTN位置1可以使能連續轉換模式。在此模式下, ADC執行由RSQ0[4:0]規定的轉換通道。當ADCON位被置1,一旦相應軟件觸發或者外部觸發產生, ADC就會采樣和轉換規定的通道。轉換數據保存在ADC_RDATA寄存器中。
- 掃描模式
掃描轉換模式可以通過將ADC_CTL0寄存器的SM位置1來使能。在此模式下, ADC掃描轉換所有被ADC_RSQ0~ADC_RSQ2寄存器或ADC_ISQ寄存器選中的所有通道。一旦ADCON位被置1,當相應軟件觸發或者外部觸發產生, ADC就會一個接一個的采樣和轉換規則組或注入組通道。轉換數據存儲在ADC_RDATA或ADC_IDATAx寄存器中。規則組或注入組轉換結束后,EOC或者EOIC位將被置1。如果EOCIE或EOICIE位被置1,將產生中斷。當規則組通道工作在掃描模式下時, ADC_CTL1寄存器的DMA位必須設置為1。
如果ADC_CTL1寄存器的CTN位也被置1,則在規則通道轉換完之后,這個轉換自動重新開始。
- 間斷模式
規則組
對于規則組, ADC_CTL0 寄存器的 DISRC 位置 1 使能間斷轉換模式。該模式下可以執行一次n 個通道的短序列轉換(n<=8),此轉換是 ADC_RSQ0RSQ2 寄存器所選擇的轉換序列的一部分。數值 n 由 ADC_CTL0 寄存器的 DISCNUM[2:0]位給出。當相應的軟件觸發或外部觸發發生, ADC 就會采樣和轉換在 ADC_RSQ0RSQ2 寄存器所選擇通道中接下來的 n 個通道,直到規則序列中所有的通道轉換完成。每個規則組轉換周期結束后, EOC位將被置1。如果EOCIE位被置 1 將產生一個中斷。
舉例: n=3,被轉換的通道 = 0 、1、2、3、6、7、9、10
第一次觸發:轉換的序列為 0 、1、2
第二次觸發:轉換的序列為 3 、6、7
第三次觸發:轉換的序列為 9 、10,并產生EOC事件
第四次觸發:轉換的序列 0 、1、2
注意:
1.當以間斷模式轉換一個規則組時,轉換序列結束后不自動從頭開始。
2.當所有子組被轉換完成,下一次觸發啟動第一個子組的轉換。在上面的例子中,第四次觸發重新轉換第一子組的通道 0 、1和2。
__注入組 __
對于注入組,ADC_CTL0 寄存器的 DISIC 位置 1 使能間斷轉換模式。該模式下可以執行ADC_ISQ 寄存器所選擇的轉換序列的一個通道進行轉換。當相應的軟件觸發或外部觸發發生,ADC 就會采樣和轉換 ADC_ISQ 寄存器中所選擇通道的下一個通道,直到注入組序列中所有通道轉換完成。每個注入組通道轉換周期結束后, EOIC 位將被置 1。如果 EOICIE 位被置 1 將產生一個中斷。
例子: n=1,被轉換的通道 = 1 、2、3
第一次觸發:通道1被轉換
第二次觸發:通道2被轉換
第三次觸發:通道3被轉換,并且產生EOC和JEOC事件
第四次觸發:通道1被轉換
【注意】
1.當完成所有注入通道轉換,下個觸發啟動第1個注入通道的轉換。在上述例子中,第四個觸發重新轉換第1個注入通道1。
2.不能同時使用自動注入和間斷模式。
3.必須避免同時為規則和注入組設置間斷模式。間斷模式只能作用于一組轉換。
規則組和注入組不能同時工作在間斷模式,同一時刻只能有一組被設置成間斷模式
2 ADC寄存器描述
我們介紹一下我們執行規則通道的單次轉換,需要用到的 ADC 寄存器。第一個要介紹的是 ADC 控制寄存器(ADC_CTL0和 ADC_CTL1)。ADC_CTL0的各位描述如下圖所示。
這里我們不再詳細介紹每個位,而是抽出幾個我們本章要用到的位進行針對性的介紹,詳細的說明及介紹,請參考《GD32 參考手冊》。
ADC_CTL0的 SM位,該位用于設置掃描模式,由軟件設置和清除,如果設置為 1,則使用掃描模式,如果為 0,則關閉掃描模式。在掃描模式下,由ADC_RSQx或ADC_ISQ寄存器選中的通道被轉換。如果設置了 EOCIE 或 EOICIE,只在最后一個通道轉換完畢后才會產生 EOC 或 EOIC中斷。
ADC_CTL0 [19: 16]用于設置 ADC 的操作模式,詳細的對應關系如下圖所示。
本章我們要使用的是獨立模式,所以設置這幾位為 0 就可以了。接著我們介紹 ADC_CTL1,該寄存器的各位描述如下圖所示。
該寄存器我們也只針對性的介紹一些位: ADCON 位用于開關 AD 轉換器。而 CTN位用于設置是否進行連續轉換,我們使用單次轉換,所以 CTN位必須為 0。 CLB和 RSTCLB用于ADC 校準。
ADC_CTL1寄存器中的DAL位選擇轉換后數據儲存的對齊方式。數據可以左對齊或右對齊,如下圖所示。
注入組通道轉換的數據值已經減去了在 ADC_IOFFx 寄存器中定義的偏移量,因此結果可能是一個負值。符號值是一個擴展值。對于規則組通道,不需減去偏移值,因此只有12個位有效。
ETSRC [2: 0]用于選擇啟動規則轉換組轉換的外部事件,詳細的設置關系如下圖所示。
我們這里使用的是軟件觸發,所以設置這 3 個位為 111。 ADC_CTL1的SWRCST位用于開始規則通道的轉換,我們每次轉換(單次轉換模式下)都需要向該位寫 1。TSVREN為用于使能溫度傳感器和 Vrefint。GD32內部的溫度傳感器我們將在后文介紹。
第二個要介紹的是 ADC 采樣事件寄存器(ADC_SAMPT0和 ADC_SAMPT1),這兩個寄存器用于設置通道 0~17 的采樣時間,每個通道占用 3 個位。 ADC_SAMPT0的各位描述如下圖。
ADC_SAMPT1和ADC_SAMPT0差不多,只是該寄存器用于配置通道0 ~ 通道9。
對于每個要轉換的通道,采樣時間建議盡量長一點,以獲得較高的準確度,但是這樣會降低 ADC 的轉換速率。ADC的轉換時間可以由以下公式計算:
Tcovn=采樣時間+12.5 個周期
其中: Tcovn 為總轉換時間,采樣時間是根據每個通道的SPT 位的設置來決定的。例如,當 ADCCLK=14Mhz 的時候,并設置 1.5 個周期的采樣時間,則得到: Tcovn=1.5+12.5=14 個周期=1us。
常見的周期有:1.5周期、7.5周期、13.5周期、28.5周期、41.5周期、55.5周期、71.5周期、239.5周期。
第三個要介紹的是 ADC 規則序列寄存器(ADC_RSQ0~2) ,該寄存器總共有 3 個,這幾個寄存器的功能都差不多,這里我們僅介紹一下ADC_RSQ0,該寄存器的各位描述如下圖所示。
RL[3:0]用于存儲規則序列的長度,我們這里只用了 1 個,所以設置這幾個位的值為 0。其他的 RSQ12~ 15則存儲了規則序列中第 12~ 15 個通道的編號(0~17)。另外兩個規則序列寄存器同 ADC_RSQ0大同小異,我們這里就不再介紹了,要說明一點的是:我們選擇的是單次轉換,所以只有一個通道在規則序列里面,這個序列就是 RSQ0,通過 ADC_RSQ2的最低 5 位(也就是 RSQ0)設置。
第四個要介紹的是 ADC 規則數據寄存器(ADC_RDATA)。規則序列中的 ADC 轉化結果都將被存在這個寄存器里面,而注入通道的轉換結果被保存在ADC_IOFFx 里面。ADC_RDATA的各位描述如下圖。
這里要提醒一點的是,該寄存器的數據可以通過ADC_CTL1的DAL位設置左對齊還是右對齊。在讀取數據的時候要注意。
最后一個要介紹的 ADC 寄存器為 ADC 狀態寄存器(ADC_STAT),該寄存器保存了 ADC 轉換時的各種狀態。該寄存器的各位描述如下圖。
這里我們要用到的是 EOC 位,我們通過判斷該位來決定是否此次規則通道的 ADC 轉換已經完成,如果完成我們就從 ADC_RDATA 中讀取轉換結果,否則等待轉換完成。
3 ADC具體代碼實現
接下來筆者將通過三種方式實現ADC單通道電壓數據采集,先看看筆者使用的開發板的硬件電路,其中PC3外接了一個滑動電阻。
3.1 ADC單通道電壓采集查詢方式實現
ADC參數設置的詳細步驟:
1)開啟 PC 口時鐘和 ADC0 時鐘,設置 PC3為模擬輸入。
GD32F207的ADC 通道 13在 PC3上,所以,我們先要使能 PC 的時鐘和 ADC0時鐘,然后設置PC0為模擬輸入。 使能 GPIOC 和 ADC 時鐘,設置 PC3的輸入方式。
2)復位 ADC0,同時設置 ADC0分頻因子。
開啟 ADC0 時鐘之后,我們要復位 ADC0, 將 ADC1 的全部寄存器重設為缺省值之后我們就可以通過RCU_CFG0設置 ADC的分頻因子。分頻因子要確保 ADC的時鐘(ADCCLK)不要超過 28Mhz。這個我們設置分頻因子位 8, 時鐘為 120/8=15MHz,庫函數的實現方法是:
void rcu_adc_clock_config(uint32_t adc_psc);
輸入參數范圍:
/* ADC prescaler selection */
#define RCU_CKADC_CKAPB2_DIV2 ((uint32_t)0x00000000U) /*!< ADC prescaler select CK_APB2/2 */
#define RCU_CKADC_CKAPB2_DIV4 ((uint32_t)0x00000001U) /*!< ADC prescaler select CK_APB2/4 */
#define RCU_CKADC_CKAPB2_DIV6 ((uint32_t)0x00000002U) /*!< ADC prescaler select CK_APB2/6 */
#define RCU_CKADC_CKAPB2_DIV8 ((uint32_t)0x00000003U) /*!< ADC prescaler select CK_APB2/8 */
#define RCU_CKADC_CKAPB2_DIV12 ((uint32_t)0x00000005U) /*!< ADC prescaler select CK_APB2/12 */
#define RCU_CKADC_CKAPB2_DIV16 ((uint32_t)0x00000007U) /*!< ADC prescaler select CK_APB2/16 */
GD32F2的ADC最大的轉換速率為2Mhz,也就是轉換時間為0.5us(在ADCCLK=28M,采樣周期為1.5個ADC時鐘下得到),不要讓ADC的時鐘超過28M,否則將導致結果準確度下降。
3) 初始化 ADC0參數,設置 ADC0 的工作模式以及規則序列的相關信息。
在設置完分頻因子之后,我們就可以開始 ADC0的模式配置了,設置單次轉換模式、觸發方式選擇、數據對齊方式等都在這一步實現。 同時,我們還要設置 ADC0規則序列的相關信息,我們這里只有一個通道,并且是單次轉換的,所以設置規則序列中通道數為 1。
/* ADC mode config */
adc_mode_config(ADC_MODE_FREE);
/* ADC data alignment config */
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
/* ADC channel length config */
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);
/* ADC regular channel config */
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_13, ADC_SAMPLETIME_1POINT5);
/* ADC trigger config */
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
/* ADC external trigger enable */
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_mode_config()用來設置ADC模式,這里只使用一個ADC,因此設置為獨立模式。
adc_data_alignment_config()用來設置 ADC 數據對齊方式是左對齊還是右對齊,這里我們選擇右對齊方式。
adc_channel_length_config()用來設置規則序列的長度,這里我們是單次轉換,所以值為 1 即可。
adc_regular_channel_config()用來設置ADC通道轉換順序,這里設置采樣時間為1.5個時鐘周期。
adc_special_function_config()函數用來設置是否開啟連續轉換模式,因為是單次轉換模式,所以我們選擇不開啟連續轉換模式,DISABLE 即可。
adc_external_trigger_source_config()用來設置啟動規則轉換組轉換的外部事件,這里我們選擇軟件觸發,選擇值為ADC0_1_2_EXTTRIG_REGULAR_NONE即可。
adc_external_trigger_config()用于使能外部觸發。
4)使能 ADC 并校準。
在設置完了以上信息后, 我們就使能 ADC 轉換器,執行復位校準和 ADC校準,注意這兩步是必須的!不校準將導致結果很不準確。
使能指定的 ADC 的方法是:
adc_enable(ADC0);
執行 ADC 校準的方法是:
adc_calibration_enable(ADC0);
ADC有一個內置自校準模式。校準可大幅減小因內部電容器組的變化而造成的準精度誤差。在校準期間,在每個電容器上都會計算出一個誤差修正碼(數字值),這個碼用于消除在隨后的轉換中每個電容器上產生的誤差。
通過設置ADC_CTL1寄存器的CLB位啟動校準。一旦校準結束,CLB位被硬件復位,可以開始正常轉換。建議在上電時執行一次ADC校準。
【注意】
1.建議在每次上電后執行一次校準。
2.啟動校準前,ADC必須處于關電狀態(ADON=’0’)超過至少兩個ADC時鐘周期。
5)讀取 ADC 值。
在上面的校準完成之后, ADC 就算準備好了。接下來啟動 ADC 轉換。在轉換結束后,讀取 ADC 轉換結果值就是了。
軟件開啟 ADC 轉換的方法是:
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
開啟轉換之后,就可以獲取轉換 ADC 轉換結果數據, 方法是:
adc_regular_data_read(ADC0);//ADC轉換結果
同時在 AD 轉換中,我們還要根據狀態寄存器的標志位來獲取 AD 轉換的各個狀態信息。 庫函數獲取 AD 轉換的狀態信息的函數是:
FlagStatus adc_flag_get(uint32_t adc_periph, uint32_t adc_flag)
比如我們要判斷 ADC的轉換是否結束,方法是:
while(!adc_flag_get(ADC0,ADC_FLAG_EOC));//檢查轉換標志
接下來看看ADC完整的配置。
/*
brief Configure the ADC peripheral
param[in] adc_typedef_enum adc_id
param[out] none
retval none
*/
void adc_init(adc_typedef_enum adc_id)
{
/* enable GPIOC clock */
rcu_periph_clock_enable(ADC_GPIO_CLK[adc_id]);
/* enable ADC0 clock */
rcu_periph_clock_enable(ADC_CLK[adc_id]);
/* config ADC clock */
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
/* config the GPIO as analog mode */
gpio_init(ADC_GPIO_PORT[adc_id], GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, ADC_GPIO_PIN[adc_id]);
/* ADC mode config */
adc_mode_config(ADC_MODE_FREE);
/* ADC continuous mode function disable */
adc_special_function_config(ADC_PERIPH[adc_id], ADC_CONTINUOUS_MODE, DISABLE);
/* ADC data alignment config */
adc_data_alignment_config(ADC_PERIPH[adc_id], ADC_DATAALIGN_RIGHT);
/* ADC channel length config */
adc_channel_length_config(ADC_PERIPH[adc_id], ADC_REGULAR_CHANNEL, 1);
/* ADC regular channel config */
adc_regular_channel_config(ADC_PERIPH[adc_id], 0, ADC_CHANNEL[adc_id], ADC_SAMPLETIME_1POINT5);
/* ADC trigger config */
adc_external_trigger_source_config(ADC_PERIPH[adc_id], ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
/* ADC external trigger enable */
adc_external_trigger_config(ADC_PERIPH[adc_id], ADC_REGULAR_CHANNEL, ENABLE);
/* enable ADC interface */
adc_enable(ADC_PERIPH[adc_id]);
delay_ms(1);
/* ADC calibration and reset calibration */
adc_calibration_enable(ADC_PERIPH[adc_id]);
}
主函數如下所示。
/*
brief main function
param[in] none
param[out] none
retval none
*/
int main(void)
{
float adc_convertedValueLocal;
uint32_t adc_convertedValue;
//systick init
sysTick_init();
//usart init 115200 8-N-1
com_init(COM1, 115200, 0, 1);
//adc init
adc_init(A0);
while(1)
{
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
while(!adc_flag_get(ADC0,ADC_FLAG_EOC));//檢查轉換標志
adc_flag_clear(ADC0, ADC_FLAG_EOC); // 清除結束標志
adc_convertedValue=adc_regular_data_read(ADC0);//ADC轉換結果
adc_convertedValueLocal =(float) adc_convertedValue/4096*3.3; // 讀取轉換的AD值
printf("The current AD value = 0x%04X \\r\\n", adc_convertedValue);
printf("The current AD value = %f V \\r\\n\\r\\n",adc_convertedValueLocal); //實際電壓值
delay_ms(1000);
}
}
如果想開啟連續轉換,只需將ADC_CONTINUOUS_MODE配置為ENABLE即可。
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
然后只需打開啟動一次ADC轉換。
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
最后看看ADC采樣時間的計算。
ADCCLK(ADC的時鐘頻率 ) = 120MHZ(系統時鐘頻率) / 8 (ADC分頻因子) = 15MHZ。
一個ADC周期占用的時間 = 1 / 時鐘頻率 = 1 / 15MHz = 0.066666 uS
一次采樣總的時間 = 采樣時間 + 12.5個周期 = 1.5周期 + 12.5周期 = 14 * 0.066666 = 0.933333 us
3.2 ADC單通道電壓采集中斷方式實現
中斷方式和查詢方式不同的地方在于需要開啟ADC中斷服務,配置中斷優先級和中斷服務函數。筆者接下來之講與查詢方式不同的地方。
1.需要在ADC配置函數中開啟ADC中斷
adc_interrupt_enable(ADC0, ADC_INT_EOC);
2. NVIC配置
因為我們是在轉換完成后利用中斷,在中斷函數中讀取數據,所以要首先配置中斷函數的優先級。
nvic_irq_enable(ADC0_1_IRQn, 0, 0);
3.中斷服務函數
在中斷函數中進行讀取數據,將數據存放在變量adc_convertedValue中。需要注意的是,此處使用關鍵字extern聲明,代表變量adc_convertedValue已經在其他文件中定義。
/*!
\\brief this function handles ADC0 and ADC1 interrupt
\\param[in] none
\\param[out] none
\\retval none
*/
void ADC0_1_IRQHandler(void)
{
if(adc_interrupt_flag_get(ADC0, ADC_INT_FLAG_EOC))
{
adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOC); // 清除ADC規則組轉換結束中斷標志
adc_convertedValue = adc_regular_data_read(ADC0); // 讀取ADC數據
}
}
4.主函數
主函數負責接收轉換的值,并將其轉換為電壓值,然后通過串口打印出來,便于查看ADC轉換值。
/*
brief main function
param[in] none
param[out] none
retval none
*/
int main(void)
{
float adc_convertedValueLocal;
//systick init
sysTick_init();
//usart init 115200 8-N-1
com_init(COM1, 115200, 0, 1);
//adc init
adc_init(A0, 1, 0);
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
while(1)
{
adc_convertedValueLocal =(float) adc_convertedValue/4096*3.3; // 讀取轉換的AD值
printf("The current AD value = 0x%04X \\r\\n", adc_convertedValue);
printf("The current AD value = %f V \\r\\n\\r\\n",adc_convertedValueLocal); //實際電壓值
delay_ms(1000);
}
}
我們還可以通過定時器方式來實現,關于定時器參看前面的章節。如果開啟定時器1,定時時間為1s,則可將以下函數的內容替換main()函數的循環體的內容。這樣可空出主循環干其他事情了。
3.3 ADC單通道電壓采集DMA方式實現
DMA方式實現的代碼結構和查詢方式差不多,主要新增DMA配置不同。
/*
brief configure the DMA peripheral
param[in] none
param[out] none
retval none
*/
void dma_config(void)
{
/* ADC_DMA_channel configuration */
dma_parameter_struct dma_data_parameter;
/* enable DMA clock */
rcu_periph_clock_enable(RCU_DMA0);
/* ADC_DMA_channel deinit */
dma_deinit(DMA0, DMA_CH0);
/* initialize DMA single data mode */
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_data_parameter.memory_addr = (uint32_t)(&adc_convertedValue);
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_DISABLE;
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_32BIT;
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_data_parameter.number = 1;
dma_data_parameter.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH0, &dma_data_parameter);
dma_circulation_enable(DMA0, DMA_CH0);
/* enable DMA channel */
dma_channel_enable(DMA0, DMA_CH0);
}
然后使能ADC的DMA。
adc_dma_mode_enable(ADC0);
代碼的注釋已經很詳細了,我不再贅述了。
這里還需要說明一下 ADC 的參考電壓,我的開發板使用的是 GD32F207,
該芯片有外部參考電壓: Vref-和 Vref+,其中 Vref-必須和 VSSA 連接在一起, 而 Vref+的輸入范圍為: 2.4~VDDA。需要設置 Vref-和 Vref+設置參考電壓,默認的我們是通過跳線帽將 Vref-接到 GND, Vref+接到 VDDA,參考電壓就是 3.3V。如果大家想自己設置其他參考電壓,將你的參考電壓接在 Vref-和 Vref+上就 OK 了。本章我們的參考電壓設置的是 3.3V。一般的開發板已經設置好了,不在需要單獨去設置。
通過以上幾個步驟的設置,我們就能正常的使用 GD32 的 ADC0來執行 AD 轉換操作了。
4 實驗現象
將程序編譯好后下載到板子中,打開串口助手可以看到如下現象,當然了,普通方式、中斷方式和DMA方式都是一樣的現象。
-
adc
+關注
關注
98文章
6430瀏覽量
544078 -
串口
+關注
關注
14文章
1543瀏覽量
76187 -
開發板
+關注
關注
25文章
4943瀏覽量
97188 -
Cortex-M
+關注
關注
2文章
227瀏覽量
29726 -
GD32
+關注
關注
7文章
403瀏覽量
24218
發布評論請先 登錄
相關推薦
評論