精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

GD32開發實戰指南(基礎篇) 第12章 ADC

嵌入式大雜燴 ? 來源:嵌入式大雜燴 ? 作者:嵌入式大雜燴 ? 2023-05-16 09:03 ? 次閱讀

開發環境:

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架構如下圖所示。

C:\\Users\\BruceOu\\Desktop\\555.png

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 就可以測量。

16841590353932t6gh8ctyx

【注】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,將產生一個中斷。

1684159035737ajzroxl41s

  • 連續轉換模式

在該模式可以運行在規則組通道上。對ADC_CTL1寄存器的CTN位置1可以使能連續轉換模式。在此模式下, ADC執行由RSQ0[4:0]規定的轉換通道。當ADCON位被置1,一旦相應軟件觸發或者外部觸發產生, ADC就會采樣和轉換規定的通道。轉換數據保存在ADC_RDATA寄存器中。

1684159036052j2fbsdxi07

  • 掃描模式

掃描轉換模式可以通過將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,則在規則通道轉換完之后,這個轉換自動重新開始。

1684159036772ec3o3v8zjl

  • 間斷模式

規則組

對于規則組, ADC_CTL0 寄存器的 DISRC 位置 1 使能間斷轉換模式。該模式下可以執行一次n 個通道的短序列轉換(n<=8),此轉換是 ADC_RSQ0RSQ2 寄存器所選擇的轉換序列的一部分。數值 n 由 ADC_CTL0 寄存器的 DISCNUM[2:0]位給出。當相應的軟件觸發或外部觸發發生, ADC 就會采樣和轉換在 ADC_RSQ0RSQ2 寄存器所選擇通道中接下來的 n 個通道,直到規則序列中所有的通道轉換完成。每個規則組轉換周期結束后, EOC位將被置1。如果EOCIE位被置 1 將產生一個中斷。

1684159037077odq0vilw2h

舉例: 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 將產生一個中斷。

16841590373553ltfvje8fs

例子: 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的各位描述如下圖所示。

16841590376575ejqnl21sh

這里我們不再詳細介紹每個位,而是抽出幾個我們本章要用到的位進行針對性的介紹,詳細的說明及介紹,請參考《GD32 參考手冊》。

ADC_CTL0的 SM位,該位用于設置掃描模式,由軟件設置和清除,如果設置為 1,則使用掃描模式,如果為 0,則關閉掃描模式。在掃描模式下,由ADC_RSQx或ADC_ISQ寄存器選中的通道被轉換。如果設置了 EOCIE 或 EOICIE,只在最后一個通道轉換完畢后才會產生 EOC 或 EOIC中斷。

ADC_CTL0 [19: 16]用于設置 ADC 的操作模式,詳細的對應關系如下圖所示。

1684159038060rndw8y1kg2

1684159038501heaxvn0yp5

本章我們要使用的是獨立模式,所以設置這幾位為 0 就可以了。接著我們介紹 ADC_CTL1,該寄存器的各位描述如下圖所示。

1684159038845ignvgq4ko0

該寄存器我們也只針對性的介紹一些位: ADCON 位用于開關 AD 轉換器。而 CTN位用于設置是否進行連續轉換,我們使用單次轉換,所以 CTN位必須為 0。 CLB和 RSTCLB用于ADC 校準。

ADC_CTL1寄存器中的DAL位選擇轉換后數據儲存的對齊方式。數據可以左對齊或右對齊,如下圖所示。

16841590391851x48rz79s2

注入組通道轉換的數據值已經減去了在 ADC_IOFFx 寄存器中定義的偏移量,因此結果可能是一個負值。符號值是一個擴展值。對于規則組通道,不需減去偏移值,因此只有12個位有效。

ETSRC [2: 0]用于選擇啟動規則轉換組轉換的外部事件,詳細的設置關系如下圖所示。

16841590395486wtit2t1oi

我們這里使用的是軟件觸發,所以設置這 3 個位為 111。 ADC_CTL1的SWRCST位用于開始規則通道的轉換,我們每次轉換(單次轉換模式下)都需要向該位寫 1。TSVREN為用于使能溫度傳感器和 Vrefint。GD32內部的溫度傳感器我們將在后文介紹。

第二個要介紹的是 ADC 采樣事件寄存器(ADC_SAMPT0和 ADC_SAMPT1),這兩個寄存器用于設置通道 0~17 的采樣時間,每個通道占用 3 個位。 ADC_SAMPT0的各位描述如下圖。

16841590399173bbd9w0mrc

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周期。

1684159040252g20vcan5iw

第三個要介紹的是 ADC 規則序列寄存器(ADC_RSQ0~2) ,該寄存器總共有 3 個,這幾個寄存器的功能都差不多,這里我們僅介紹一下ADC_RSQ0,該寄存器的各位描述如下圖所示。

1684159040586k3h8qi33du

16841590408610l6623orbg

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的各位描述如下圖。

16841590411709l9v6bcbmf

1684159041480en1grj6zt7

這里要提醒一點的是,該寄存器的數據可以通過ADC_CTL1的DAL位設置左對齊還是右對齊。在讀取數據的時候要注意。

最后一個要介紹的 ADC 寄存器為 ADC 狀態寄存器(ADC_STAT),該寄存器保存了 ADC 轉換時的各種狀態。該寄存器的各位描述如下圖。

1684159041840ihas77fwbt

這里我們要用到的是 EOC 位,我們通過判斷該位來決定是否此次規則通道的 ADC 轉換已經完成,如果完成我們就從 ADC_RDATA 中讀取轉換結果,否則等待轉換完成。

3 ADC具體代碼實現

接下來筆者將通過三種方式實現ADC單通道電壓數據采集,先看看筆者使用的開發板的硬件電路,其中PC3外接了一個滑動電阻

1684159042173jky6ojvna4

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方式都是一樣的現象。

1684159042478wr2ypduy1z

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • adc
    adc
    +關注

    關注

    98

    文章

    6430

    瀏覽量

    544078
  • 串口
    +關注

    關注

    14

    文章

    1543

    瀏覽量

    76187
  • 開發板
    +關注

    關注

    25

    文章

    4943

    瀏覽量

    97188
  • Cortex-M
    +關注

    關注

    2

    文章

    227

    瀏覽量

    29726
  • GD32
    +關注

    關注

    7

    文章

    403

    瀏覽量

    24218
收藏 人收藏

    評論

    相關推薦

    GD32開發實戰指南(基礎) 1 開發環境搭建

    開發環境: MDK:Keil 5.30 開發板:GD32F207I-EVAL MCU:GD32F207IK 1 GD32F207I-EVAL
    的頭像 發表于 05-07 23:35 ?1.1w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>1<b class='flag-5'>章</b> <b class='flag-5'>開發</b>環境搭建

    GD32開發實戰指南(基礎) 4 GD32啟動流程詳解(Keil版)

    ,所有的一切都需要由開發者來設置,這里處理器是沒有堆棧,沒有中斷,更沒有外圍設備,這些工作是需要軟件來指定的,而且不同的CPU類型、不同大小的內存和不同種類的外設,其初始化工作都是不同的。本文將以GD32F207IK (基于Cortex-M3)為例進行講解。
    的頭像 發表于 05-10 09:00 ?1.6w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>4<b class='flag-5'>章</b> <b class='flag-5'>GD32</b>啟動流程詳解(Keil版)

    GD32開發實戰指南(基礎) 7 定時器

    系統滴答定時器一般用來提供“心跳”作用,而GD32定時器最基本功能也是定時,可以設置不同時間長度的定時。定時器除了最基本的定時功能外,定時器與GPIO有掛鉤使得它可以發揮強大的作用,比如可以輸出
    的頭像 發表于 05-11 09:00 ?1.2w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>7<b class='flag-5'>章</b> 定時器

    GD32開發實戰指南(基礎) 8 定時器

    開發環境: MDK:Keil 5.30 開發板:GD32F207I-EVAL MCU:GD32F207IK 1 PWM輸出的工作原理 脈沖寬度調制(PWM) ,是英文“Pulse Wi
    的頭像 發表于 05-12 22:14 ?7837次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>8<b class='flag-5'>章</b> 定時器

    GD32開發實戰指南(基礎) 13 DAC

    數字/模擬轉換模塊(DAC)是12位數字輸入,電壓輸出的數字/模擬轉換器。DAC可以配置為8位或12位模式,也可以與DMA控制器配合使用。DAC工作在12位模式時,數據可以設置成左對齊或右對齊
    的頭像 發表于 05-17 08:57 ?5557次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>13<b class='flag-5'>章</b> DAC

    GD32開發實戰指南(基礎) 14 內部溫度傳感器

    GD32 有一個內部的溫度傳感器,可以用來測量 CPU 及周圍的溫度(TA)。該溫度傳感器在內部和 ADCx_IN16 輸入通道相連接,此通道把傳感器輸出的電壓轉換成數字值。溫度傳感器模擬輸入
    的頭像 發表于 05-17 08:58 ?5246次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>14<b class='flag-5'>章</b> 內部溫度傳感器

    GD32開發實戰指南(基礎) 15 低功耗

    GD32的工作電壓(VDD)為2.0~3.6V。通過內置的電壓調節器提供所需的1.8V電源。當主電源VDD掉電后,通過VBAT腳為實時時鐘(RTC)和備份寄存器提供電源。
    的頭像 發表于 05-17 08:59 ?7965次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>15<b class='flag-5'>章</b> 低功耗

    GD32開發實戰指南(基礎) 16 RTC

    開發環境: MDK:Keil 5.30 開發板:GD32F207I-EVAL MCU:GD32F207IK 1 RTC工作原理 1.1 RTC簡介
    的頭像 發表于 05-18 22:14 ?7030次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>16<b class='flag-5'>章</b> RTC

    GD32開發實戰指南(基礎) 17 看門狗

    開發環境: MDK:Keil 5.30 開發板:GD32F207I-EVAL MCU:GD32F207IK GD32 有兩個看門狗, 一個是
    的頭像 發表于 06-03 16:00 ?1.1w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>17<b class='flag-5'>章</b> 看門狗

    GD32開發實戰指南(基礎) 22 SPI

    摩托羅拉公司提出,當前最新的為 V04.01—2004 版。它被廣泛地使用在ADC、LCD 等設備與 MCU 間通信的場合。SPI接口主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有
    的頭像 發表于 05-24 09:04 ?9242次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>22<b class='flag-5'>章</b> SPI

    【圖書分享】《STM32庫開發實戰指南

    庫函數開發小結 10 DMA——為CPU減負 11 ADC實驗(DMA方式) 
    發表于 03-13 17:01

    GD32 MCU原理及固件庫開發指南》 + 初讀感悟

    GD32 MCU原理固件庫開發指南這本書內容豐富,囊括了GD32中的所有外設,書中首先介紹了如何使用MDK或IAR軟件搭建GD32工程環境,讓初學者能快速基于工程上手編程。書中主要對
    發表于 03-31 22:11

    GD32 MCU原理及固件庫開發指南》+讀后感

    2介紹GD32 MCU快速入門與開發平臺搭建的方法,包括對軟硬件開發平臺、調試工具、GD32
    發表于 06-06 21:52

    GD32開發實戰指南(基礎) 19 程序加密

    GD32通過讀取芯片唯一ID號來實現程序的保護,防止被抄襲。96位的產品唯一身份標識所提供的參考號碼對任意一個GD32微控制器
    的頭像 發表于 05-20 09:10 ?4093次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>19<b class='flag-5'>章</b> 程序加密

    GD32 ADC轉換時間如何計算?

    GD32 MCU當前產品的ADC都是SAR ADC,它有著轉換速度快,精度高的優點,剛好適合在一些需要快速ADC轉換的場合使用,比如電機應用。那么小伙伴們知道如何計算
    的頭像 發表于 03-04 09:33 ?2260次閱讀
    <b class='flag-5'>GD32</b> <b class='flag-5'>ADC</b>轉換時間如何計算?