開(kāi)發(fā)環(huán)境:
MDK:Keil 5.30
開(kāi)發(fā)板:GD32F207I-EVAL
MCU:GD32F207IK
1 ADC工作原理
GD32F2系列有 3 個(gè)逐次逼近型的ADC,精度為 12 位,有18個(gè)多路復(fù)用通道,可以轉(zhuǎn)換來(lái)自16個(gè)外部通道和2個(gè)內(nèi)部通道的模擬信號(hào)。其中ADC0 和 ADC1都有 16 個(gè)外部通道, ADC2 根據(jù) CPU 引腳的不同通道數(shù)也不同,一般都有8 個(gè)外部通道。各通道的A/D轉(zhuǎn)換可以__單次、連續(xù)、掃描或間斷模式__執(zhí)行。ADC的結(jié)果可以__左對(duì)齊或右對(duì)齊方式__存儲(chǔ)在16位數(shù)據(jù)寄存器中。模擬看門狗特性允許應(yīng)用程序檢測(cè)輸入電壓是否超出用戶定義的高/低閥值。ADC 的輸入時(shí)鐘不得超過(guò)28MHz,它是由PCLK2經(jīng)分頻產(chǎn)生。
1.1 ADC架構(gòu)
ADC架構(gòu)如下圖所示。
1.電壓輸入范圍
ADC 輸入范圍為:VREFN ≤ VIN ≤ VREFP。由VREFN、VREFP、VDDA、VSSA這四個(gè)外部引腳決定。
我們?cè)谠O(shè)計(jì)原理圖的時(shí)候一般把 VSSA 和 VREFN接地,把VREFP和 VDDA 接 3V3,得到ADC 的輸入電壓范圍為: 0~3.3V。在 64 腳以下的 CPU 中,沒(méi)有VREFN和 VREFP這兩個(gè)引腳,
ADC 電壓輸入范圍直接由 VDDA和 VSSA決定。如果我們想讓輸入的電壓范圍變寬,去到可以測(cè)試負(fù)電壓或者更高的正電壓,我們可以在外部加一個(gè)電壓調(diào)理電路,把需要轉(zhuǎn)換的電壓抬升或者降壓到 0~3.3V,這樣 ADC 就可以測(cè)量。
【注】VDDA和VSSA必須分別連接到VDD和VSS。
2.輸入通道
我們確定好ADC輸入電壓之后,那么電壓怎么輸入到 ADC?這里我們引入通道的概念,GD32 的ADC多達(dá)18個(gè)通道,其中外部的16個(gè)通道就是框圖中的 ADCx_IN0、ADCx_IN1...ADCx_IN5。這16個(gè)通道對(duì)應(yīng)著不同的 IO 口,具體是哪一個(gè) IO 口可以從手冊(cè)查詢到。其中 ADC0/1/2還有內(nèi)部通道: ADC0 的通道 16 連接到了芯片內(nèi)部的溫度傳感器, Vrefint 連接到了通道 17。 ADC1 的模擬通道 16 和 17 連接到了內(nèi)部的 VSS。ADC2 的模擬通道 9、 14、 15、 16 和 17 連接到了內(nèi)部的 VSS。
外部的 16 個(gè)通道在轉(zhuǎn)換的時(shí)候又分為規(guī)則通道和注入通道,其中規(guī)則通道最多有 16路,注入通道最多有 4 路。那這兩個(gè)通道有什么區(qū)別?在什么時(shí)候使用?
規(guī)則通道
規(guī)則通道:規(guī)則通道就是很規(guī)矩的意思,我們平時(shí)一般使用的就是這個(gè)通道,或者應(yīng)該說(shuō)我們用到的都是這個(gè)通道,沒(méi)有什么特別要注意的可講。
注入通道
注入,可以理解為插入,插隊(duì)的意思,是一種不安分的通道。它是一種在規(guī)則通道轉(zhuǎn)換的時(shí)候強(qiáng)行插入要轉(zhuǎn)換的一種。如果在規(guī)則通道轉(zhuǎn)換過(guò)程中,有注入通道插隊(duì),那么就要先轉(zhuǎn)換完注入通道,等注入通道轉(zhuǎn)換完成后,再回到規(guī)則通道的轉(zhuǎn)換流程。這點(diǎn)跟中斷程序很像,都是不安分的主。所以,注入通道只有在規(guī)則通道存在時(shí)才會(huì)出現(xiàn)。
3.轉(zhuǎn)換順序
ADC支持18個(gè)多路通道,可以把轉(zhuǎn)換組織成兩組:一個(gè)規(guī)則組通道和一個(gè)注入組通道。
規(guī)則組,可以按照特定的序列組織成多達(dá)16個(gè)轉(zhuǎn)換的序列。ADC_RSQ0~ADC_RSQ2寄存器規(guī)定了規(guī)則組的通道選擇。ADC_RSQ0寄存器的RL[3:0]位規(guī)定了整個(gè)規(guī)則組轉(zhuǎn)換序列的長(zhǎng)度。
注入組,可以按照特定的序列組織成多達(dá)4個(gè)轉(zhuǎn)換的序列。ADC_ISQ寄存器規(guī)定了注入組的通道選擇。ADC_ISQ寄存器的IL[1:0]位規(guī)定了整個(gè)注入組轉(zhuǎn)換序列的長(zhǎng)度。
4.觸發(fā)源
通道選好了,轉(zhuǎn)換的順序也設(shè)置好了,那接下來(lái)就該開(kāi)始轉(zhuǎn)換了。 ADC 轉(zhuǎn)換可以由ADC_CTL1的 ADON 這個(gè)位來(lái)控制,寫(xiě) 1 的時(shí)候開(kāi)始轉(zhuǎn)換,寫(xiě) 0 的時(shí)候停止轉(zhuǎn)換,這個(gè)是最簡(jiǎn)單也是最好理解的開(kāi)啟 ADC 轉(zhuǎn)換的控制方式,理解起來(lái)沒(méi)啥技術(shù)含量。
除了這種庶民式的控制方法, ADC 還支持觸發(fā)轉(zhuǎn)換,這個(gè)觸發(fā)包括內(nèi)部定時(shí)器觸發(fā)和外部 IO 觸發(fā)。觸發(fā)源有很多,具體選擇哪一種觸發(fā)源,由 ADC_CTL1的ETSRC[2:0]和ETSIC[2:0]位來(lái)控制。ETSRC[2:0]用于選擇規(guī)則通道的觸發(fā)源,ETSIC[2:0]用于選擇注入通道的觸發(fā)源。選定好觸發(fā)源之后,觸發(fā)源是否要激活,則由ADC_CTL1的 ETERC和 ETEIC這兩位來(lái)激活。
5.數(shù)據(jù)寄存器
一切準(zhǔn)備就緒后,ADC 轉(zhuǎn)換后的數(shù)據(jù)根據(jù)轉(zhuǎn)換組的不同,規(guī)則組的數(shù)據(jù)放在ADC_RDATA寄存器,注入組的數(shù)據(jù)放在ADC_IDATAx。
6.中斷
轉(zhuǎn)換結(jié)束中斷
數(shù)據(jù)轉(zhuǎn)換結(jié)束后,可以產(chǎn)生中斷,中斷分為三種:規(guī)則通道轉(zhuǎn)換結(jié)束中斷,注入轉(zhuǎn)換通道轉(zhuǎn)換結(jié)束中斷,模擬看門狗中斷。其中轉(zhuǎn)換結(jié)束中斷很好理解,跟我們平時(shí)接觸的中斷一樣,有相應(yīng)的中斷標(biāo)志位和中斷使能位,我們還可以根據(jù)中斷類型寫(xiě)相應(yīng)配套的中斷服務(wù)程序。
模擬看門狗中斷
當(dāng)被 ADC 轉(zhuǎn)換的模擬電壓低于低閾值或者高于高閾值時(shí),就會(huì)產(chǎn)生中斷,前提是我們開(kāi)啟了模擬看門狗中斷,其中低閾值和高閾值由 ADC_WDHT 和 ADC_WDLT置。例如我們?cè)O(shè)置高閾值是 2.5V,那么模擬電壓超過(guò) 2.5V 的時(shí)候,就會(huì)產(chǎn)生模擬看門狗中斷,反之低閾值也一樣。
DMA 請(qǐng)求
DMA 請(qǐng)求可以通過(guò)設(shè)置 ADC_CTL1 寄存器的 DMA 位來(lái)使能,它用于傳輸規(guī)則組多個(gè)通道的轉(zhuǎn)換結(jié)果。 ADC 在規(guī)則組一個(gè)通道轉(zhuǎn)換結(jié)束后產(chǎn)生一個(gè) DMA 請(qǐng)求, DMA 接受到請(qǐng)求后可以將轉(zhuǎn)換的數(shù)據(jù)從 ADC_RDATA 寄存器傳輸?shù)接脩糁付ǖ哪康牡刂贰?/p>
注意: 只有 ADC0 和 ADC2 有 DMA 功能, ADC1 轉(zhuǎn)換的數(shù)據(jù)可以在 ADC 同步模式下傳輸。
7.轉(zhuǎn)換時(shí)間
ADC 時(shí)鐘
ADC 輸入時(shí)鐘 ADCCLK由 PCLK2 經(jīng)過(guò)分頻產(chǎn)生,最大是28M,分頻因子由 RCC 時(shí)鐘配置寄存器RCU_CFG0的位 15:14 ADCPSC[1:0]設(shè)置,可以是 2/4/6/8/12/16 分頻,注意這里沒(méi)有 1 分頻。一般我們?cè)O(shè)置 PCLK2=HCLK=120M。
采樣時(shí)間
ADC 使用若干個(gè) ADCCLK 周期對(duì)輸入的電壓進(jìn)行采樣,采樣的周期數(shù)可通過(guò) ADC采樣時(shí)間寄存器ADC_SAMPT0 和 ADC_SAMPT1中的 SMP[2:0]位設(shè)置,ADC_SAMPT1控制的是通道 09, ADC_SAMPT0 控制的是通道 1017。每個(gè)通道可以分別用不同的時(shí)間采樣。其中采樣周期最小是 1.5 個(gè),即如果我們要達(dá)到最快的采樣,那么應(yīng)該設(shè)置采樣周期為 1.5個(gè)周期,這里說(shuō)的周期就是 1/ADCCLK。
ADC 的轉(zhuǎn)換時(shí)間跟 ADC 的輸入時(shí)鐘和采樣時(shí)間有關(guān),公式為:
Tconv = 采樣時(shí)間 +12.5 個(gè)周期。
例如,當(dāng) ADCLK = 14MHz,采樣時(shí)間設(shè)置為 1.5 周期(最快),那么總的轉(zhuǎn)換時(shí)間:
Tconv = 1.5 周期 + 12.5 周期 = 14 周期 = 1us。
8.電壓轉(zhuǎn)換
模擬電壓經(jīng)過(guò) ADC 轉(zhuǎn)換后,是一個(gè) 12 位的數(shù)字值,如果通過(guò)串口以 16 進(jìn)制打印出來(lái)的話,可讀性比較差,那么有時(shí)候我們就需要把數(shù)字電壓轉(zhuǎn)換成模擬電壓,也可以跟實(shí)際的模擬電壓(用萬(wàn)用表測(cè))對(duì)比,看看轉(zhuǎn)換是否準(zhǔn)確。
我們一般在設(shè)計(jì)原理圖的時(shí)候會(huì)把ADC 的輸入電壓范圍設(shè)定在: 0~3.3v,因?yàn)?ADC是 12 位的,那么 12 位滿量程對(duì)應(yīng)的就是3.3V,12 位滿量程對(duì)應(yīng)的數(shù)字值是: 2^12。數(shù)值0 對(duì)應(yīng)的就是 0V。如果轉(zhuǎn)換后的數(shù)值為X, X對(duì)應(yīng)的模擬電壓為 Y,那么會(huì)有這么一個(gè)等式成立: 2^12 / 3.3 = X / Y, => Y = (3.3 * X ) / 2^12。
1.2 ADC通道選擇
GD32 將 ADC 的轉(zhuǎn)換分為 2 個(gè)通道組: 規(guī)則通道組和注入通道組 。
規(guī)則通道相當(dāng)于你正常運(yùn)行的程序,而注入通道呢,就相當(dāng)于中斷。在你程序正常執(zhí)行的時(shí)候,中斷是可以打斷你的執(zhí)行的。同這個(gè)類似,注入通道的轉(zhuǎn)換可以打斷規(guī)則通道的轉(zhuǎn)換, 在注入通道被轉(zhuǎn)換完成之后,規(guī)則通道才得以繼續(xù)轉(zhuǎn)換。
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 | 內(nèi)部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 | 內(nèi)部VSS | |
通道15 | PC5 | 通道15 | PC5 | 通道15 | 內(nèi)部VSS | |
通道16 | 內(nèi)部溫度傳感器 | 通道16 | 內(nèi)部VSS | 通道16 | 內(nèi)部VSS | |
通道17 | 內(nèi)部Vrefint | 通道17 | 內(nèi)部VSS | 通道17 | 內(nèi)部VSS |
上面的例子因?yàn)樗俣容^慢,不能完全體現(xiàn)這樣區(qū)分(規(guī)則通道組和注入通道組)的好處,但在工業(yè)應(yīng)用領(lǐng)域中有很多檢測(cè)和監(jiān)視探頭需要較快地處理,這樣對(duì) AD 轉(zhuǎn)換的分組將簡(jiǎn)化事件處理的程序并提高事件處理的速度。
GD32 其 ADC 的規(guī)則通道組最多包含 16 個(gè)轉(zhuǎn)換,而注入通道組最多包含 4 個(gè)通道。關(guān)于這兩個(gè)通道組的詳細(xì)介紹,請(qǐng)參考《GD32參考手冊(cè)》,我們這里就不在一一列舉了。
溫度傳感器和通道ADC0_IN16相連接,內(nèi)部參照電壓VREFINT和ADC0_IN17相連接??梢园醋⑷牖蛞?guī)則通道對(duì)這兩個(gè)內(nèi)部通道進(jìn)行轉(zhuǎn)換。
【注意】溫度傳感器和VREFINT只能出現(xiàn)在主ADC0 中。
1.3 ADC轉(zhuǎn)換模式
- 單次轉(zhuǎn)換模式
該模式能夠運(yùn)行在規(guī)則組和注入組。單次轉(zhuǎn)換模式下, ADC_RSQ2寄存器的RSQ0[4:0]位或者ADC_ISQ寄存器的ISQ3[4:0]位規(guī)定了ADC的轉(zhuǎn)換通道。當(dāng)ADCON位被置1,一旦相應(yīng)軟件觸發(fā)或者外部觸發(fā)發(fā)生, ADC就會(huì)采樣和轉(zhuǎn)換一個(gè)通道。
規(guī)則通道單次轉(zhuǎn)換結(jié)束后,轉(zhuǎn)換數(shù)據(jù)將被存放于ADC_RDATA寄存器中, EOC將會(huì)置1。如果EOCIE位被置1,將產(chǎn)生一個(gè)中斷。
注入通道單次轉(zhuǎn)換結(jié)束后,轉(zhuǎn)換數(shù)據(jù)將被存放于ADC_IDATA0寄存器中, EOC和EOIC位將會(huì)置1。如果EOCIE或EOICIE位被置1,將產(chǎn)生一個(gè)中斷。
- 連續(xù)轉(zhuǎn)換模式
在該模式可以運(yùn)行在規(guī)則組通道上。對(duì)ADC_CTL1寄存器的CTN位置1可以使能連續(xù)轉(zhuǎn)換模式。在此模式下, ADC執(zhí)行由RSQ0[4:0]規(guī)定的轉(zhuǎn)換通道。當(dāng)ADCON位被置1,一旦相應(yīng)軟件觸發(fā)或者外部觸發(fā)產(chǎn)生, ADC就會(huì)采樣和轉(zhuǎn)換規(guī)定的通道。轉(zhuǎn)換數(shù)據(jù)保存在ADC_RDATA寄存器中。
- 掃描模式
掃描轉(zhuǎn)換模式可以通過(guò)將ADC_CTL0寄存器的SM位置1來(lái)使能。在此模式下, ADC掃描轉(zhuǎn)換所有被ADC_RSQ0~ADC_RSQ2寄存器或ADC_ISQ寄存器選中的所有通道。一旦ADCON位被置1,當(dāng)相應(yīng)軟件觸發(fā)或者外部觸發(fā)產(chǎn)生, ADC就會(huì)一個(gè)接一個(gè)的采樣和轉(zhuǎn)換規(guī)則組或注入組通道。轉(zhuǎn)換數(shù)據(jù)存儲(chǔ)在ADC_RDATA或ADC_IDATAx寄存器中。規(guī)則組或注入組轉(zhuǎn)換結(jié)束后,EOC或者EOIC位將被置1。如果EOCIE或EOICIE位被置1,將產(chǎn)生中斷。當(dāng)規(guī)則組通道工作在掃描模式下時(shí), ADC_CTL1寄存器的DMA位必須設(shè)置為1。
如果ADC_CTL1寄存器的CTN位也被置1,則在規(guī)則通道轉(zhuǎn)換完之后,這個(gè)轉(zhuǎn)換自動(dòng)重新開(kāi)始。
- 間斷模式
規(guī)則組
對(duì)于規(guī)則組, ADC_CTL0 寄存器的 DISRC 位置 1 使能間斷轉(zhuǎn)換模式。該模式下可以執(zhí)行一次n 個(gè)通道的短序列轉(zhuǎn)換(n<=8),此轉(zhuǎn)換是 ADC_RSQ0RSQ2 寄存器所選擇的轉(zhuǎn)換序列的一部分。數(shù)值 n 由 ADC_CTL0 寄存器的 DISCNUM[2:0]位給出。當(dāng)相應(yīng)的軟件觸發(fā)或外部觸發(fā)發(fā)生, ADC 就會(huì)采樣和轉(zhuǎn)換在 ADC_RSQ0RSQ2 寄存器所選擇通道中接下來(lái)的 n 個(gè)通道,直到規(guī)則序列中所有的通道轉(zhuǎn)換完成。每個(gè)規(guī)則組轉(zhuǎn)換周期結(jié)束后, EOC位將被置1。如果EOCIE位被置 1 將產(chǎn)生一個(gè)中斷。
舉例: n=3,被轉(zhuǎn)換的通道 = 0 、1、2、3、6、7、9、10
第一次觸發(fā):轉(zhuǎn)換的序列為 0 、1、2
第二次觸發(fā):轉(zhuǎn)換的序列為 3 、6、7
第三次觸發(fā):轉(zhuǎn)換的序列為 9 、10,并產(chǎn)生EOC事件
第四次觸發(fā):轉(zhuǎn)換的序列 0 、1、2
注意:
1.當(dāng)以間斷模式轉(zhuǎn)換一個(gè)規(guī)則組時(shí),轉(zhuǎn)換序列結(jié)束后不自動(dòng)從頭開(kāi)始。
2.當(dāng)所有子組被轉(zhuǎn)換完成,下一次觸發(fā)啟動(dòng)第一個(gè)子組的轉(zhuǎn)換。在上面的例子中,第四次觸發(fā)重新轉(zhuǎn)換第一子組的通道 0 、1和2。
__注入組 __
對(duì)于注入組,ADC_CTL0 寄存器的 DISIC 位置 1 使能間斷轉(zhuǎn)換模式。該模式下可以執(zhí)行ADC_ISQ 寄存器所選擇的轉(zhuǎn)換序列的一個(gè)通道進(jìn)行轉(zhuǎn)換。當(dāng)相應(yīng)的軟件觸發(fā)或外部觸發(fā)發(fā)生,ADC 就會(huì)采樣和轉(zhuǎn)換 ADC_ISQ 寄存器中所選擇通道的下一個(gè)通道,直到注入組序列中所有通道轉(zhuǎn)換完成。每個(gè)注入組通道轉(zhuǎn)換周期結(jié)束后, EOIC 位將被置 1。如果 EOICIE 位被置 1 將產(chǎn)生一個(gè)中斷。
例子: n=1,被轉(zhuǎn)換的通道 = 1 、2、3
第一次觸發(fā):通道1被轉(zhuǎn)換
第二次觸發(fā):通道2被轉(zhuǎn)換
第三次觸發(fā):通道3被轉(zhuǎn)換,并且產(chǎn)生EOC和JEOC事件
第四次觸發(fā):通道1被轉(zhuǎn)換
【注意】
1.當(dāng)完成所有注入通道轉(zhuǎn)換,下個(gè)觸發(fā)啟動(dòng)第1個(gè)注入通道的轉(zhuǎn)換。在上述例子中,第四個(gè)觸發(fā)重新轉(zhuǎn)換第1個(gè)注入通道1。
2.不能同時(shí)使用自動(dòng)注入和間斷模式。
3.必須避免同時(shí)為規(guī)則和注入組設(shè)置間斷模式。間斷模式只能作用于一組轉(zhuǎn)換。
規(guī)則組和注入組不能同時(shí)工作在間斷模式,同一時(shí)刻只能有一組被設(shè)置成間斷模式
2 ADC寄存器描述
我們介紹一下我們執(zhí)行規(guī)則通道的單次轉(zhuǎn)換,需要用到的 ADC 寄存器。第一個(gè)要介紹的是 ADC 控制寄存器(ADC_CTL0和 ADC_CTL1)。ADC_CTL0的各位描述如下圖所示。
這里我們不再詳細(xì)介紹每個(gè)位,而是抽出幾個(gè)我們本章要用到的位進(jìn)行針對(duì)性的介紹,詳細(xì)的說(shuō)明及介紹,請(qǐng)參考《GD32 參考手冊(cè)》。
ADC_CTL0的 SM位,該位用于設(shè)置掃描模式,由軟件設(shè)置和清除,如果設(shè)置為 1,則使用掃描模式,如果為 0,則關(guān)閉掃描模式。在掃描模式下,由ADC_RSQx或ADC_ISQ寄存器選中的通道被轉(zhuǎn)換。如果設(shè)置了 EOCIE 或 EOICIE,只在最后一個(gè)通道轉(zhuǎn)換完畢后才會(huì)產(chǎn)生 EOC 或 EOIC中斷。
ADC_CTL0 [19: 16]用于設(shè)置 ADC 的操作模式,詳細(xì)的對(duì)應(yīng)關(guān)系如下圖所示。
本章我們要使用的是獨(dú)立模式,所以設(shè)置這幾位為 0 就可以了。接著我們介紹 ADC_CTL1,該寄存器的各位描述如下圖所示。
該寄存器我們也只針對(duì)性的介紹一些位: ADCON 位用于開(kāi)關(guān) AD 轉(zhuǎn)換器。而 CTN位用于設(shè)置是否進(jìn)行連續(xù)轉(zhuǎn)換,我們使用單次轉(zhuǎn)換,所以 CTN位必須為 0。 CLB和 RSTCLB用于ADC 校準(zhǔn)。
ADC_CTL1寄存器中的DAL位選擇轉(zhuǎn)換后數(shù)據(jù)儲(chǔ)存的對(duì)齊方式。數(shù)據(jù)可以左對(duì)齊或右對(duì)齊,如下圖所示。
注入組通道轉(zhuǎn)換的數(shù)據(jù)值已經(jīng)減去了在 ADC_IOFFx 寄存器中定義的偏移量,因此結(jié)果可能是一個(gè)負(fù)值。符號(hào)值是一個(gè)擴(kuò)展值。對(duì)于規(guī)則組通道,不需減去偏移值,因此只有12個(gè)位有效。
ETSRC [2: 0]用于選擇啟動(dòng)規(guī)則轉(zhuǎn)換組轉(zhuǎn)換的外部事件,詳細(xì)的設(shè)置關(guān)系如下圖所示。
我們這里使用的是軟件觸發(fā),所以設(shè)置這 3 個(gè)位為 111。 ADC_CTL1的SWRCST位用于開(kāi)始規(guī)則通道的轉(zhuǎn)換,我們每次轉(zhuǎn)換(單次轉(zhuǎn)換模式下)都需要向該位寫(xiě) 1。TSVREN為用于使能溫度傳感器和 Vrefint。GD32內(nèi)部的溫度傳感器我們將在后文介紹。
第二個(gè)要介紹的是 ADC 采樣事件寄存器(ADC_SAMPT0和 ADC_SAMPT1),這兩個(gè)寄存器用于設(shè)置通道 0~17 的采樣時(shí)間,每個(gè)通道占用 3 個(gè)位。 ADC_SAMPT0的各位描述如下圖。
ADC_SAMPT1和ADC_SAMPT0差不多,只是該寄存器用于配置通道0 ~ 通道9。
對(duì)于每個(gè)要轉(zhuǎn)換的通道,采樣時(shí)間建議盡量長(zhǎng)一點(diǎn),以獲得較高的準(zhǔn)確度,但是這樣會(huì)降低 ADC 的轉(zhuǎn)換速率。ADC的轉(zhuǎn)換時(shí)間可以由以下公式計(jì)算:
Tcovn=采樣時(shí)間+12.5 個(gè)周期
其中: Tcovn 為總轉(zhuǎn)換時(shí)間,采樣時(shí)間是根據(jù)每個(gè)通道的SPT 位的設(shè)置來(lái)決定的。例如,當(dāng) ADCCLK=14Mhz 的時(shí)候,并設(shè)置 1.5 個(gè)周期的采樣時(shí)間,則得到: Tcovn=1.5+12.5=14 個(gè)周期=1us。
常見(jiàn)的周期有:1.5周期、7.5周期、13.5周期、28.5周期、41.5周期、55.5周期、71.5周期、239.5周期。
第三個(gè)要介紹的是 ADC 規(guī)則序列寄存器(ADC_RSQ0~2) ,該寄存器總共有 3 個(gè),這幾個(gè)寄存器的功能都差不多,這里我們僅介紹一下ADC_RSQ0,該寄存器的各位描述如下圖所示。
RL[3:0]用于存儲(chǔ)規(guī)則序列的長(zhǎng)度,我們這里只用了 1 個(gè),所以設(shè)置這幾個(gè)位的值為 0。其他的 RSQ12~ 15則存儲(chǔ)了規(guī)則序列中第 12~ 15 個(gè)通道的編號(hào)(0~17)。另外兩個(gè)規(guī)則序列寄存器同 ADC_RSQ0大同小異,我們這里就不再介紹了,要說(shuō)明一點(diǎn)的是:我們選擇的是單次轉(zhuǎn)換,所以只有一個(gè)通道在規(guī)則序列里面,這個(gè)序列就是 RSQ0,通過(guò) ADC_RSQ2的最低 5 位(也就是 RSQ0)設(shè)置。
第四個(gè)要介紹的是 ADC 規(guī)則數(shù)據(jù)寄存器(ADC_RDATA)。規(guī)則序列中的 ADC 轉(zhuǎn)化結(jié)果都將被存在這個(gè)寄存器里面,而注入通道的轉(zhuǎn)換結(jié)果被保存在ADC_IOFFx 里面。ADC_RDATA的各位描述如下圖。
這里要提醒一點(diǎn)的是,該寄存器的數(shù)據(jù)可以通過(guò)ADC_CTL1的DAL位設(shè)置左對(duì)齊還是右對(duì)齊。在讀取數(shù)據(jù)的時(shí)候要注意。
最后一個(gè)要介紹的 ADC 寄存器為 ADC 狀態(tài)寄存器(ADC_STAT),該寄存器保存了 ADC 轉(zhuǎn)換時(shí)的各種狀態(tài)。該寄存器的各位描述如下圖。
這里我們要用到的是 EOC 位,我們通過(guò)判斷該位來(lái)決定是否此次規(guī)則通道的 ADC 轉(zhuǎn)換已經(jīng)完成,如果完成我們就從 ADC_RDATA 中讀取轉(zhuǎn)換結(jié)果,否則等待轉(zhuǎn)換完成。
3 ADC具體代碼實(shí)現(xiàn)
接下來(lái)筆者將通過(guò)三種方式實(shí)現(xiàn)ADC單通道電壓數(shù)據(jù)采集,先看看筆者使用的開(kāi)發(fā)板的硬件電路,其中PC3外接了一個(gè)滑動(dòng)電阻。
3.1 ADC單通道電壓采集查詢方式實(shí)現(xiàn)
ADC參數(shù)設(shè)置的詳細(xì)步驟:
1)開(kāi)啟 PC 口時(shí)鐘和 ADC0 時(shí)鐘,設(shè)置 PC3為模擬輸入。
GD32F207的ADC 通道 13在 PC3上,所以,我們先要使能 PC 的時(shí)鐘和 ADC0時(shí)鐘,然后設(shè)置PC0為模擬輸入。 使能 GPIOC 和 ADC 時(shí)鐘,設(shè)置 PC3的輸入方式。
2)復(fù)位 ADC0,同時(shí)設(shè)置 ADC0分頻因子。
開(kāi)啟 ADC0 時(shí)鐘之后,我們要復(fù)位 ADC0, 將 ADC1 的全部寄存器重設(shè)為缺省值之后我們就可以通過(guò)RCU_CFG0設(shè)置 ADC的分頻因子。分頻因子要確保 ADC的時(shí)鐘(ADCCLK)不要超過(guò) 28Mhz。這個(gè)我們?cè)O(shè)置分頻因子位 8, 時(shí)鐘為 120/8=15MHz,庫(kù)函數(shù)的實(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最大的轉(zhuǎn)換速率為2Mhz,也就是轉(zhuǎn)換時(shí)間為0.5us(在ADCCLK=28M,采樣周期為1.5個(gè)ADC時(shí)鐘下得到),不要讓ADC的時(shí)鐘超過(guò)28M,否則將導(dǎo)致結(jié)果準(zhǔn)確度下降。
3) 初始化 ADC0參數(shù),設(shè)置 ADC0 的工作模式以及規(guī)則序列的相關(guān)信息。
在設(shè)置完分頻因子之后,我們就可以開(kāi)始 ADC0的模式配置了,設(shè)置單次轉(zhuǎn)換模式、觸發(fā)方式選擇、數(shù)據(jù)對(duì)齊方式等都在這一步實(shí)現(xiàn)。 同時(shí),我們還要設(shè)置 ADC0規(guī)則序列的相關(guān)信息,我們這里只有一個(gè)通道,并且是單次轉(zhuǎn)換的,所以設(shè)置規(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()用來(lái)設(shè)置ADC模式,這里只使用一個(gè)ADC,因此設(shè)置為獨(dú)立模式。
adc_data_alignment_config()用來(lái)設(shè)置 ADC 數(shù)據(jù)對(duì)齊方式是左對(duì)齊還是右對(duì)齊,這里我們選擇右對(duì)齊方式。
adc_channel_length_config()用來(lái)設(shè)置規(guī)則序列的長(zhǎng)度,這里我們是單次轉(zhuǎn)換,所以值為 1 即可。
adc_regular_channel_config()用來(lái)設(shè)置ADC通道轉(zhuǎn)換順序,這里設(shè)置采樣時(shí)間為1.5個(gè)時(shí)鐘周期。
adc_special_function_config()函數(shù)用來(lái)設(shè)置是否開(kāi)啟連續(xù)轉(zhuǎn)換模式,因?yàn)槭菃未无D(zhuǎn)換模式,所以我們選擇不開(kāi)啟連續(xù)轉(zhuǎn)換模式,DISABLE 即可。
adc_external_trigger_source_config()用來(lái)設(shè)置啟動(dòng)規(guī)則轉(zhuǎn)換組轉(zhuǎn)換的外部事件,這里我們選擇軟件觸發(fā),選擇值為ADC0_1_2_EXTTRIG_REGULAR_NONE即可。
adc_external_trigger_config()用于使能外部觸發(fā)。
4)使能 ADC 并校準(zhǔn)。
在設(shè)置完了以上信息后, 我們就使能 ADC 轉(zhuǎn)換器,執(zhí)行復(fù)位校準(zhǔn)和 ADC校準(zhǔn),注意這兩步是必須的!不校準(zhǔn)將導(dǎo)致結(jié)果很不準(zhǔn)確。
使能指定的 ADC 的方法是:
adc_enable(ADC0);
執(zhí)行 ADC 校準(zhǔn)的方法是:
adc_calibration_enable(ADC0);
ADC有一個(gè)內(nèi)置自校準(zhǔn)模式。校準(zhǔn)可大幅減小因內(nèi)部電容器組的變化而造成的準(zhǔn)精度誤差。在校準(zhǔn)期間,在每個(gè)電容器上都會(huì)計(jì)算出一個(gè)誤差修正碼(數(shù)字值),這個(gè)碼用于消除在隨后的轉(zhuǎn)換中每個(gè)電容器上產(chǎn)生的誤差。
通過(guò)設(shè)置ADC_CTL1寄存器的CLB位啟動(dòng)校準(zhǔn)。一旦校準(zhǔn)結(jié)束,CLB位被硬件復(fù)位,可以開(kāi)始正常轉(zhuǎn)換。建議在上電時(shí)執(zhí)行一次ADC校準(zhǔn)。
【注意】
1.建議在每次上電后執(zhí)行一次校準(zhǔn)。
2.啟動(dòng)校準(zhǔn)前,ADC必須處于關(guān)電狀態(tài)(ADON=’0’)超過(guò)至少兩個(gè)ADC時(shí)鐘周期。
5)讀取 ADC 值。
在上面的校準(zhǔn)完成之后, ADC 就算準(zhǔn)備好了。接下來(lái)啟動(dòng) ADC 轉(zhuǎn)換。在轉(zhuǎn)換結(jié)束后,讀取 ADC 轉(zhuǎn)換結(jié)果值就是了。
軟件開(kāi)啟 ADC 轉(zhuǎn)換的方法是:
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
開(kāi)啟轉(zhuǎn)換之后,就可以獲取轉(zhuǎn)換 ADC 轉(zhuǎn)換結(jié)果數(shù)據(jù), 方法是:
adc_regular_data_read(ADC0);//ADC轉(zhuǎn)換結(jié)果
同時(shí)在 AD 轉(zhuǎn)換中,我們還要根據(jù)狀態(tài)寄存器的標(biāo)志位來(lái)獲取 AD 轉(zhuǎn)換的各個(gè)狀態(tài)信息。 庫(kù)函數(shù)獲取 AD 轉(zhuǎn)換的狀態(tài)信息的函數(shù)是:
FlagStatus adc_flag_get(uint32_t adc_periph, uint32_t adc_flag)
比如我們要判斷 ADC的轉(zhuǎn)換是否結(jié)束,方法是:
while(!adc_flag_get(ADC0,ADC_FLAG_EOC));//檢查轉(zhuǎn)換標(biāo)志
接下來(lái)看看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));//檢查轉(zhuǎn)換標(biāo)志
adc_flag_clear(ADC0, ADC_FLAG_EOC); // 清除結(jié)束標(biāo)志
adc_convertedValue=adc_regular_data_read(ADC0);//ADC轉(zhuǎn)換結(jié)果
adc_convertedValueLocal =(float) adc_convertedValue/4096*3.3; // 讀取轉(zhuǎn)換的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); //實(shí)際電壓值
delay_ms(1000);
}
}
如果想開(kāi)啟連續(xù)轉(zhuǎn)換,只需將ADC_CONTINUOUS_MODE配置為ENABLE即可。
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
然后只需打開(kāi)啟動(dòng)一次ADC轉(zhuǎn)換。
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
最后看看ADC采樣時(shí)間的計(jì)算。
ADCCLK(ADC的時(shí)鐘頻率 ) = 120MHZ(系統(tǒng)時(shí)鐘頻率) / 8 (ADC分頻因子) = 15MHZ。
一個(gè)ADC周期占用的時(shí)間 = 1 / 時(shí)鐘頻率 = 1 / 15MHz = 0.066666 uS
一次采樣總的時(shí)間 = 采樣時(shí)間 + 12.5個(gè)周期 = 1.5周期 + 12.5周期 = 14 * 0.066666 = 0.933333 us
3.2 ADC單通道電壓采集中斷方式實(shí)現(xiàn)
中斷方式和查詢方式不同的地方在于需要開(kāi)啟ADC中斷服務(wù),配置中斷優(yōu)先級(jí)和中斷服務(wù)函數(shù)。筆者接下來(lái)之講與查詢方式不同的地方。
1.需要在ADC配置函數(shù)中開(kāi)啟ADC中斷
adc_interrupt_enable(ADC0, ADC_INT_EOC);
2. NVIC配置
因?yàn)槲覀兪窃谵D(zhuǎn)換完成后利用中斷,在中斷函數(shù)中讀取數(shù)據(jù),所以要首先配置中斷函數(shù)的優(yōu)先級(jí)。
nvic_irq_enable(ADC0_1_IRQn, 0, 0);
3.中斷服務(wù)函數(shù)
在中斷函數(shù)中進(jìn)行讀取數(shù)據(jù),將數(shù)據(jù)存放在變量adc_convertedValue中。需要注意的是,此處使用關(guān)鍵字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ī)則組轉(zhuǎn)換結(jié)束中斷標(biāo)志
adc_convertedValue = adc_regular_data_read(ADC0); // 讀取ADC數(shù)據(jù)
}
}
4.主函數(shù)
主函數(shù)負(fù)責(zé)接收轉(zhuǎn)換的值,并將其轉(zhuǎn)換為電壓值,然后通過(guò)串口打印出來(lái),便于查看ADC轉(zhuǎn)換值。
/*
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; // 讀取轉(zhuǎn)換的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); //實(shí)際電壓值
delay_ms(1000);
}
}
我們還可以通過(guò)定時(shí)器方式來(lái)實(shí)現(xiàn),關(guān)于定時(shí)器參看前面的章節(jié)。如果開(kāi)啟定時(shí)器1,定時(shí)時(shí)間為1s,則可將以下函數(shù)的內(nèi)容替換main()函數(shù)的循環(huán)體的內(nèi)容。這樣可空出主循環(huán)干其他事情了。
3.3 ADC單通道電壓采集DMA方式實(shí)現(xiàn)
DMA方式實(shí)現(xiàn)的代碼結(jié)構(gòu)和查詢方式差不多,主要新增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)很詳細(xì)了,我不再贅述了。
這里還需要說(shuō)明一下 ADC 的參考電壓,我的開(kāi)發(fā)板使用的是 GD32F207,
該芯片有外部參考電壓: Vref-和 Vref+,其中 Vref-必須和 VSSA 連接在一起, 而 Vref+的輸入范圍為: 2.4~VDDA。需要設(shè)置 Vref-和 Vref+設(shè)置參考電壓,默認(rèn)的我們是通過(guò)跳線帽將 Vref-接到 GND, Vref+接到 VDDA,參考電壓就是 3.3V。如果大家想自己設(shè)置其他參考電壓,將你的參考電壓接在 Vref-和 Vref+上就 OK 了。本章我們的參考電壓設(shè)置的是 3.3V。一般的開(kāi)發(fā)板已經(jīng)設(shè)置好了,不在需要單獨(dú)去設(shè)置。
通過(guò)以上幾個(gè)步驟的設(shè)置,我們就能正常的使用 GD32 的 ADC0來(lái)執(zhí)行 AD 轉(zhuǎn)換操作了。
4 實(shí)驗(yàn)現(xiàn)象
將程序編譯好后下載到板子中,打開(kāi)串口助手可以看到如下現(xiàn)象,當(dāng)然了,普通方式、中斷方式和DMA方式都是一樣的現(xiàn)象。
-
adc
+關(guān)注
關(guān)注
98文章
6495瀏覽量
544457 -
串口
+關(guān)注
關(guān)注
14文章
1551瀏覽量
76421 -
開(kāi)發(fā)板
+關(guān)注
關(guān)注
25文章
5032瀏覽量
97371 -
Cortex-M
+關(guān)注
關(guān)注
2文章
229瀏覽量
29752 -
GD32
+關(guān)注
關(guān)注
7文章
403瀏覽量
24326
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論