開發(fā)環(huán)境:
MDK:Keil 5.30
開發(fā)板:GD32F207I-EVAL
MCU:GD32F207IK
1 ADC工作原理
GD32F2系列有 3 個逐次逼近型的ADC,精度為 12 位,有18個多路復用通道,可以轉換來自16個外部通道和2個內部通道的模擬信號。其中ADC0 和 ADC1都有 16 個外部通道, ADC2 根據(jù) CPU 引腳的不同通道數(shù)也不同,一般都有8 個外部通道。各通道的A/D轉換可以__單次、連續(xù)、掃描或間斷模式__執(zhí)行。ADC的結果可以__左對齊或右對齊方式__存儲在16位數(shù)據(jù)寄存器中。模擬看門狗特性允許應用程序檢測輸入電壓是否超出用戶定義的高/低閥值。ADC 的輸入時鐘不得超過28MHz,它是由PCLK2經(jīng)分頻產生。
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 個通道在轉換的時候又分為規(guī)則通道和注入通道,其中規(guī)則通道最多有 16路,注入通道最多有 4 路。那這兩個通道有什么區(qū)別?在什么時候使用?
規(guī)則通道
規(guī)則通道:規(guī)則通道就是很規(guī)矩的意思,我們平時一般使用的就是這個通道,或者應該說我們用到的都是這個通道,沒有什么特別要注意的可講。
注入通道
注入,可以理解為插入,插隊的意思,是一種不安分的通道。它是一種在規(guī)則通道轉換的時候強行插入要轉換的一種。如果在規(guī)則通道轉換過程中,有注入通道插隊,那么就要先轉換完注入通道,等注入通道轉換完成后,再回到規(guī)則通道的轉換流程。這點跟中斷程序很像,都是不安分的主。所以,注入通道只有在規(guī)則通道存在時才會出現(xiàn)。
3.轉換順序
ADC支持18個多路通道,可以把轉換組織成兩組:一個規(guī)則組通道和一個注入組通道。
規(guī)則組,可以按照特定的序列組織成多達16個轉換的序列。ADC_RSQ0~ADC_RSQ2寄存器規(guī)定了規(guī)則組的通道選擇。ADC_RSQ0寄存器的RL[3:0]位規(guī)定了整個規(guī)則組轉換序列的長度。
注入組,可以按照特定的序列組織成多達4個轉換的序列。ADC_ISQ寄存器規(guī)定了注入組的通道選擇。ADC_ISQ寄存器的IL[1:0]位規(guī)定了整個注入組轉換序列的長度。
4.觸發(fā)源
通道選好了,轉換的順序也設置好了,那接下來就該開始轉換了。 ADC 轉換可以由ADC_CTL1的 ADON 這個位來控制,寫 1 的時候開始轉換,寫 0 的時候停止轉換,這個是最簡單也是最好理解的開啟 ADC 轉換的控制方式,理解起來沒啥技術含量。
除了這種庶民式的控制方法, ADC 還支持觸發(fā)轉換,這個觸發(fā)包括內部定時器觸發(fā)和外部 IO 觸發(fā)。觸發(fā)源有很多,具體選擇哪一種觸發(fā)源,由 ADC_CTL1的ETSRC[2:0]和ETSIC[2:0]位來控制。ETSRC[2:0]用于選擇規(guī)則通道的觸發(fā)源,ETSIC[2:0]用于選擇注入通道的觸發(fā)源。選定好觸發(fā)源之后,觸發(fā)源是否要激活,則由ADC_CTL1的 ETERC和 ETEIC這兩位來激活。
5.數(shù)據(jù)寄存器
一切準備就緒后,ADC 轉換后的數(shù)據(jù)根據(jù)轉換組的不同,規(guī)則組的數(shù)據(jù)放在ADC_RDATA寄存器,注入組的數(shù)據(jù)放在ADC_IDATAx。
6.中斷
轉換結束中斷
數(shù)據(jù)轉換結束后,可以產生中斷,中斷分為三種:規(guī)則通道轉換結束中斷,注入轉換通道轉換結束中斷,模擬看門狗中斷。其中轉換結束中斷很好理解,跟我們平時接觸的中斷一樣,有相應的中斷標志位和中斷使能位,我們還可以根據(jù)中斷類型寫相應配套的中斷服務程序。
模擬看門狗中斷
當被 ADC 轉換的模擬電壓低于低閾值或者高于高閾值時,就會產生中斷,前提是我們開啟了模擬看門狗中斷,其中低閾值和高閾值由 ADC_WDHT 和 ADC_WDLT置。例如我們設置高閾值是 2.5V,那么模擬電壓超過 2.5V 的時候,就會產生模擬看門狗中斷,反之低閾值也一樣。
DMA 請求
DMA 請求可以通過設置 ADC_CTL1 寄存器的 DMA 位來使能,它用于傳輸規(guī)則組多個通道的轉換結果。 ADC 在規(guī)則組一個通道轉換結束后產生一個 DMA 請求, DMA 接受到請求后可以將轉換的數(shù)據(jù)從 ADC_RDATA 寄存器傳輸?shù)接脩糁付ǖ哪康牡刂贰?/p>
注意: 只有 ADC0 和 ADC2 有 DMA 功能, ADC1 轉換的數(shù)據(jù)可以在 ADC 同步模式下傳輸。
7.轉換時間
ADC 時鐘
ADC 輸入時鐘 ADCCLK由 PCLK2 經(jīng)過分頻產生,最大是28M,分頻因子由 RCC 時鐘配置寄存器RCU_CFG0的位 15:14 ADCPSC[1:0]設置,可以是 2/4/6/8/12/16 分頻,注意這里沒有 1 分頻。一般我們設置 PCLK2=HCLK=120M。
采樣時間
ADC 使用若干個 ADCCLK 周期對輸入的電壓進行采樣,采樣的周期數(shù)可通過 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.電壓轉換
模擬電壓經(jīng)過 ADC 轉換后,是一個 12 位的數(shù)字值,如果通過串口以 16 進制打印出來的話,可讀性比較差,那么有時候我們就需要把數(shù)字電壓轉換成模擬電壓,也可以跟實際的模擬電壓(用萬用表測)對比,看看轉換是否準確。
我們一般在設計原理圖的時候會把ADC 的輸入電壓范圍設定在: 0~3.3v,因為 ADC是 12 位的,那么 12 位滿量程對應的就是3.3V,12 位滿量程對應的數(shù)字值是: 2^12。數(shù)值0 對應的就是 0V。如果轉換后的數(shù)值為X, X對應的模擬電壓為 Y,那么會有這么一個等式成立: 2^12 / 3.3 = X / Y, => Y = (3.3 * X ) / 2^12。
1.2 ADC通道選擇
GD32 將 ADC 的轉換分為 2 個通道組: 規(guī)則通道組和注入通道組 。
規(guī)則通道相當于你正常運行的程序,而注入通道呢,就相當于中斷。在你程序正常執(zhí)行的時候,中斷是可以打斷你的執(zhí)行的。同這個類似,注入通道的轉換可以打斷規(guī)則通道的轉換, 在注入通道被轉換完成之后,規(guī)則通道才得以繼續(xù)轉換。
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 |
上面的例子因為速度較慢,不能完全體現(xiàn)這樣區(qū)分(規(guī)則通道組和注入通道組)的好處,但在工業(yè)應用領域中有很多檢測和監(jiān)視探頭需要較快地處理,這樣對 AD 轉換的分組將簡化事件處理的程序并提高事件處理的速度。
GD32 其 ADC 的規(guī)則通道組最多包含 16 個轉換,而注入通道組最多包含 4 個通道。關于這兩個通道組的詳細介紹,請參考《GD32參考手冊》,我們這里就不在一一列舉了。
溫度傳感器和通道ADC0_IN16相連接,內部參照電壓VREFINT和ADC0_IN17相連接。可以按注入或規(guī)則通道對這兩個內部通道進行轉換。
【注意】溫度傳感器和VREFINT只能出現(xiàn)在主ADC0 中。
1.3 ADC轉換模式
- 單次轉換模式
該模式能夠運行在規(guī)則組和注入組。單次轉換模式下, ADC_RSQ2寄存器的RSQ0[4:0]位或者ADC_ISQ寄存器的ISQ3[4:0]位規(guī)定了ADC的轉換通道。當ADCON位被置1,一旦相應軟件觸發(fā)或者外部觸發(fā)發(fā)生, ADC就會采樣和轉換一個通道。
規(guī)則通道單次轉換結束后,轉換數(shù)據(jù)將被存放于ADC_RDATA寄存器中, EOC將會置1。如果EOCIE位被置1,將產生一個中斷。
注入通道單次轉換結束后,轉換數(shù)據(jù)將被存放于ADC_IDATA0寄存器中, EOC和EOIC位將會置1。如果EOCIE或EOICIE位被置1,將產生一個中斷。
- 連續(xù)轉換模式
在該模式可以運行在規(guī)則組通道上。對ADC_CTL1寄存器的CTN位置1可以使能連續(xù)轉換模式。在此模式下, ADC執(zhí)行由RSQ0[4:0]規(guī)定的轉換通道。當ADCON位被置1,一旦相應軟件觸發(fā)或者外部觸發(fā)產生, ADC就會采樣和轉換規(guī)定的通道。轉換數(shù)據(jù)保存在ADC_RDATA寄存器中。
- 掃描模式
掃描轉換模式可以通過將ADC_CTL0寄存器的SM位置1來使能。在此模式下, ADC掃描轉換所有被ADC_RSQ0~ADC_RSQ2寄存器或ADC_ISQ寄存器選中的所有通道。一旦ADCON位被置1,當相應軟件觸發(fā)或者外部觸發(fā)產生, ADC就會一個接一個的采樣和轉換規(guī)則組或注入組通道。轉換數(shù)據(jù)存儲在ADC_RDATA或ADC_IDATAx寄存器中。規(guī)則組或注入組轉換結束后,EOC或者EOIC位將被置1。如果EOCIE或EOICIE位被置1,將產生中斷。當規(guī)則組通道工作在掃描模式下時, ADC_CTL1寄存器的DMA位必須設置為1。
如果ADC_CTL1寄存器的CTN位也被置1,則在規(guī)則通道轉換完之后,這個轉換自動重新開始。
- 間斷模式
規(guī)則組
對于規(guī)則組, ADC_CTL0 寄存器的 DISRC 位置 1 使能間斷轉換模式。該模式下可以執(zhí)行一次n 個通道的短序列轉換(n<=8),此轉換是 ADC_RSQ0RSQ2 寄存器所選擇的轉換序列的一部分。數(shù)值 n 由 ADC_CTL0 寄存器的 DISCNUM[2:0]位給出。當相應的軟件觸發(fā)或外部觸發(fā)發(fā)生, ADC 就會采樣和轉換在 ADC_RSQ0RSQ2 寄存器所選擇通道中接下來的 n 個通道,直到規(guī)則序列中所有的通道轉換完成。每個規(guī)則組轉換周期結束后, EOC位將被置1。如果EOCIE位被置 1 將產生一個中斷。
舉例: n=3,被轉換的通道 = 0 、1、2、3、6、7、9、10
第一次觸發(fā):轉換的序列為 0 、1、2
第二次觸發(fā):轉換的序列為 3 、6、7
第三次觸發(fā):轉換的序列為 9 、10,并產生EOC事件
第四次觸發(fā):轉換的序列 0 、1、2
注意:
1.當以間斷模式轉換一個規(guī)則組時,轉換序列結束后不自動從頭開始。
2.當所有子組被轉換完成,下一次觸發(fā)啟動第一個子組的轉換。在上面的例子中,第四次觸發(fā)重新轉換第一子組的通道 0 、1和2。
__注入組 __
對于注入組,ADC_CTL0 寄存器的 DISIC 位置 1 使能間斷轉換模式。該模式下可以執(zhí)行ADC_ISQ 寄存器所選擇的轉換序列的一個通道進行轉換。當相應的軟件觸發(fā)或外部觸發(fā)發(fā)生,ADC 就會采樣和轉換 ADC_ISQ 寄存器中所選擇通道的下一個通道,直到注入組序列中所有通道轉換完成。每個注入組通道轉換周期結束后, EOIC 位將被置 1。如果 EOICIE 位被置 1 將產生一個中斷。
例子: n=1,被轉換的通道 = 1 、2、3
第一次觸發(fā):通道1被轉換
第二次觸發(fā):通道2被轉換
第三次觸發(fā):通道3被轉換,并且產生EOC和JEOC事件
第四次觸發(fā):通道1被轉換
【注意】
1.當完成所有注入通道轉換,下個觸發(fā)啟動第1個注入通道的轉換。在上述例子中,第四個觸發(fā)重新轉換第1個注入通道1。
2.不能同時使用自動注入和間斷模式。
3.必須避免同時為規(guī)則和注入組設置間斷模式。間斷模式只能作用于一組轉換。
規(guī)則組和注入組不能同時工作在間斷模式,同一時刻只能有一組被設置成間斷模式
2 ADC寄存器描述
我們介紹一下我們執(zhí)行規(guī)則通道的單次轉換,需要用到的 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位用于設置是否進行連續(xù)轉換,我們使用單次轉換,所以 CTN位必須為 0。 CLB和 RSTCLB用于ADC 校準。
ADC_CTL1寄存器中的DAL位選擇轉換后數(shù)據(jù)儲存的對齊方式。數(shù)據(jù)可以左對齊或右對齊,如下圖所示。
注入組通道轉換的數(shù)據(jù)值已經(jīng)減去了在 ADC_IOFFx 寄存器中定義的偏移量,因此結果可能是一個負值。符號值是一個擴展值。對于規(guī)則組通道,不需減去偏移值,因此只有12個位有效。
ETSRC [2: 0]用于選擇啟動規(guī)則轉換組轉換的外部事件,詳細的設置關系如下圖所示。
我們這里使用的是軟件觸發(fā),所以設置這 3 個位為 111。 ADC_CTL1的SWRCST位用于開始規(guī)則通道的轉換,我們每次轉換(單次轉換模式下)都需要向該位寫 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 為總轉換時間,采樣時間是根據(jù)每個通道的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 規(guī)則序列寄存器(ADC_RSQ0~2) ,該寄存器總共有 3 個,這幾個寄存器的功能都差不多,這里我們僅介紹一下ADC_RSQ0,該寄存器的各位描述如下圖所示。
RL[3:0]用于存儲規(guī)則序列的長度,我們這里只用了 1 個,所以設置這幾個位的值為 0。其他的 RSQ12~ 15則存儲了規(guī)則序列中第 12~ 15 個通道的編號(0~17)。另外兩個規(guī)則序列寄存器同 ADC_RSQ0大同小異,我們這里就不再介紹了,要說明一點的是:我們選擇的是單次轉換,所以只有一個通道在規(guī)則序列里面,這個序列就是 RSQ0,通過 ADC_RSQ2的最低 5 位(也就是 RSQ0)設置。
第四個要介紹的是 ADC 規(guī)則數(shù)據(jù)寄存器(ADC_RDATA)。規(guī)則序列中的 ADC 轉化結果都將被存在這個寄存器里面,而注入通道的轉換結果被保存在ADC_IOFFx 里面。ADC_RDATA的各位描述如下圖。
這里要提醒一點的是,該寄存器的數(shù)據(jù)可以通過ADC_CTL1的DAL位設置左對齊還是右對齊。在讀取數(shù)據(jù)的時候要注意。
最后一個要介紹的 ADC 寄存器為 ADC 狀態(tài)寄存器(ADC_STAT),該寄存器保存了 ADC 轉換時的各種狀態(tài)。該寄存器的各位描述如下圖。
這里我們要用到的是 EOC 位,我們通過判斷該位來決定是否此次規(guī)則通道的 ADC 轉換已經(jīng)完成,如果完成我們就從 ADC_RDATA 中讀取轉換結果,否則等待轉換完成。
3 ADC具體代碼實現(xiàn)
接下來筆者將通過三種方式實現(xiàn)ADC單通道電壓數(shù)據(jù)采集,先看看筆者使用的開發(fā)板的硬件電路,其中PC3外接了一個滑動電阻。
3.1 ADC單通道電壓采集查詢方式實現(xiàn)
ADC參數(shù)設置的詳細步驟:
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,庫函數(shù)的實現(xiàn)方法是:
void rcu_adc_clock_config(uint32_t adc_psc);
輸入?yún)?shù)范圍:
/* 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參數(shù),設置 ADC0 的工作模式以及規(guī)則序列的相關信息。
在設置完分頻因子之后,我們就可以開始 ADC0的模式配置了,設置單次轉換模式、觸發(fā)方式選擇、數(shù)據(jù)對齊方式等都在這一步實現(xiàn)。 同時,我們還要設置 ADC0規(guī)則序列的相關信息,我們這里只有一個通道,并且是單次轉換的,所以設置規(guī)則序列中通道數(shù)為 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 數(shù)據(jù)對齊方式是左對齊還是右對齊,這里我們選擇右對齊方式。
adc_channel_length_config()用來設置規(guī)則序列的長度,這里我們是單次轉換,所以值為 1 即可。
adc_regular_channel_config()用來設置ADC通道轉換順序,這里設置采樣時間為1.5個時鐘周期。
adc_special_function_config()函數(shù)用來設置是否開啟連續(xù)轉換模式,因為是單次轉換模式,所以我們選擇不開啟連續(xù)轉換模式,DISABLE 即可。
adc_external_trigger_source_config()用來設置啟動規(guī)則轉換組轉換的外部事件,這里我們選擇軟件觸發(fā),選擇值為ADC0_1_2_EXTTRIG_REGULAR_NONE即可。
adc_external_trigger_config()用于使能外部觸發(fā)。
4)使能 ADC 并校準。
在設置完了以上信息后, 我們就使能 ADC 轉換器,執(zhí)行復位校準和 ADC校準,注意這兩步是必須的!不校準將導致結果很不準確。
使能指定的 ADC 的方法是:
adc_enable(ADC0);
執(zhí)行 ADC 校準的方法是:
adc_calibration_enable(ADC0);
ADC有一個內置自校準模式。校準可大幅減小因內部電容器組的變化而造成的準精度誤差。在校準期間,在每個電容器上都會計算出一個誤差修正碼(數(shù)字值),這個碼用于消除在隨后的轉換中每個電容器上產生的誤差。
通過設置ADC_CTL1寄存器的CLB位啟動校準。一旦校準結束,CLB位被硬件復位,可以開始正常轉換。建議在上電時執(zhí)行一次ADC校準。
【注意】
1.建議在每次上電后執(zhí)行一次校準。
2.啟動校準前,ADC必須處于關電狀態(tài)(ADON=’0’)超過至少兩個ADC時鐘周期。
5)讀取 ADC 值。
在上面的校準完成之后, ADC 就算準備好了。接下來啟動 ADC 轉換。在轉換結束后,讀取 ADC 轉換結果值就是了。
軟件開啟 ADC 轉換的方法是:
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
開啟轉換之后,就可以獲取轉換 ADC 轉換結果數(shù)據(jù), 方法是:
adc_regular_data_read(ADC0);//ADC轉換結果
同時在 AD 轉換中,我們還要根據(jù)狀態(tài)寄存器的標志位來獲取 AD 轉換的各個狀態(tài)信息。 庫函數(shù)獲取 AD 轉換的狀態(tài)信息的函數(shù)是:
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]);
}
主函數(shù)如下所示。
/*
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);
}
}
如果想開啟連續(xù)轉換,只需將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(系統(tǒng)時鐘頻率) / 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單通道電壓采集中斷方式實現(xiàn)
中斷方式和查詢方式不同的地方在于需要開啟ADC中斷服務,配置中斷優(yōu)先級和中斷服務函數(shù)。筆者接下來之講與查詢方式不同的地方。
1.需要在ADC配置函數(shù)中開啟ADC中斷
adc_interrupt_enable(ADC0, ADC_INT_EOC);
2. NVIC配置
因為我們是在轉換完成后利用中斷,在中斷函數(shù)中讀取數(shù)據(jù),所以要首先配置中斷函數(shù)的優(yōu)先級。
nvic_irq_enable(ADC0_1_IRQn, 0, 0);
3.中斷服務函數(shù)
在中斷函數(shù)中進行讀取數(shù)據(jù),將數(shù)據(jù)存放在變量adc_convertedValue中。需要注意的是,此處使用關鍵字extern聲明,代表變量adc_convertedValue已經(jīng)在其他文件中定義。
/*!
\\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規(guī)則組轉換結束中斷標志
adc_convertedValue = adc_regular_data_read(ADC0); // 讀取ADC數(shù)據(jù)
}
}
4.主函數(shù)
主函數(shù)負責接收轉換的值,并將其轉換為電壓值,然后通過串口打印出來,便于查看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);
}
}
我們還可以通過定時器方式來實現(xiàn),關于定時器參看前面的章節(jié)。如果開啟定時器1,定時時間為1s,則可將以下函數(shù)的內容替換main()函數(shù)的循環(huán)體的內容。這樣可空出主循環(huán)干其他事情了。
3.3 ADC單通道電壓采集DMA方式實現(xiàn)
DMA方式實現(xiàn)的代碼結構和查詢方式差不多,主要新增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);
代碼的注釋已經(jīng)很詳細了,我不再贅述了。
這里還需要說明一下 ADC 的參考電壓,我的開發(fā)板使用的是 GD32F207,
該芯片有外部參考電壓: Vref-和 Vref+,其中 Vref-必須和 VSSA 連接在一起, 而 Vref+的輸入范圍為: 2.4~VDDA。需要設置 Vref-和 Vref+設置參考電壓,默認的我們是通過跳線帽將 Vref-接到 GND, Vref+接到 VDDA,參考電壓就是 3.3V。如果大家想自己設置其他參考電壓,將你的參考電壓接在 Vref-和 Vref+上就 OK 了。本章我們的參考電壓設置的是 3.3V。一般的開發(fā)板已經(jīng)設置好了,不在需要單獨去設置。
通過以上幾個步驟的設置,我們就能正常的使用 GD32 的 ADC0來執(zhí)行 AD 轉換操作了。
4 實驗現(xiàn)象
將程序編譯好后下載到板子中,打開串口助手可以看到如下現(xiàn)象,當然了,普通方式、中斷方式和DMA方式都是一樣的現(xiàn)象。
-
adc
+關注
關注
99文章
6534瀏覽量
545772 -
串口
+關注
關注
14文章
1558瀏覽量
77072 -
開發(fā)板
+關注
關注
25文章
5121瀏覽量
98218 -
Cortex-M
+關注
關注
2文章
229瀏覽量
29842 -
GD32
+關注
關注
7文章
413瀏覽量
24478
發(fā)布評論請先 登錄
相關推薦
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第1章 開發(fā)環(huán)境搭建
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第4章 GD32啟動流程詳解(Keil版)
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第7章 定時器
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第8章 定時器
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第13章 DAC
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第14章 內部溫度傳感器
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第15章 低功耗
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第16章 RTC
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第17章 看門狗
【圖書分享】《STM32庫開發(fā)實戰(zhàn)指南》
《GD32 MCU原理及固件庫開發(fā)指南》 + 初讀感悟
《GD32 MCU原理及固件庫開發(fā)指南》+讀后感
【GD32】GD32讀取ADC數(shù)據(jù)
![【<b class='flag-5'>GD32</b>】<b class='flag-5'>GD32</b>讀取<b class='flag-5'>ADC</b>數(shù)據(jù)](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
GD32開發(fā)實戰(zhàn)指南(基礎篇) 第19章 程序加密
GD32 ADC轉換時間如何計算?
![<b class='flag-5'>GD32</b> <b class='flag-5'>ADC</b>轉換時間如何計算?](https://file1.elecfans.com/web2/M00/C2/75/wKgZomXlJOGANn8xAAA8pT3MMN4777.png)
評論