1 ADC (HAL庫)
1.1 ADC工作原理
STM32f103 系列有 3 個 ADC侨颈,精度為 12 位蕴掏,每個 ADC 最多有 16 個外部通道抄邀。其中ADC1 和 ADC2 都有 16 個外部通道遣钳, ADC3 根據(jù) CPU 引腳的不同通道數(shù)也不同鄙币,一般都有8 個外部通道荐类。各通道的A/D轉(zhuǎn)換可以單次怖现、連續(xù)、掃描或間斷模式執(zhí)行玉罐。ADC的結(jié)果可以左對齊或右對齊方式存儲在16位數(shù)據(jù)寄存器中屈嗤。模擬看門狗特性允許應(yīng)用程序檢測輸入電壓是否超出用戶定義的高/低閥值。ADC 的輸入時鐘不得超過14MHz吊输,它是由PCLK2經(jīng)分頻產(chǎn)生饶号。
1.1.1 ADC架構(gòu)
1.電壓輸入范圍
ADC 輸入范圍為: VREF- ≤ VIN ≤ VREF+。由 VREF-季蚂、 VREF+ 茫船、 VDDA 、 VSSA扭屁、這四個外部引腳決定算谈。
我們在設(shè)計原理圖的時候一般把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3料滥,得到ADC 的輸入電壓范圍為: 0~3.3V然眼。在 64 腳以下的 CPU 中,沒有 VREF-和 VREF+這兩個引腳葵腹,
ADC 電壓輸入范圍直接由 VDDA和 VSSA決定高每。如果我們想讓輸入的電壓范圍變寬屿岂,去到可以測試負(fù)電壓或者更高的正電壓,我們可以在外部加一個電壓調(diào)理電路鲸匿,把需要轉(zhuǎn)換的電壓抬升或者降壓到 0~3.3V雁社,這樣 ADC 就可以測量。
【注】VDDA和VSSA應(yīng)該分別連接到VDD和VSS晒骇。
2.輸入通道
我們確定好 ADC 輸入電壓之后,那么電壓怎么輸入到 ADC磺浙?這里我們引入通道的概念洪囤,STM32 的 ADC 多達(dá) 18 個通道,其中外部的 16 個通道就是框圖中的 ADCx_IN0撕氧、ADCx_IN1...ADCx_IN5瘤缩。這 16 個通道對應(yīng)著不同的 IO 口, 具體是哪一個 IO 口可以從手冊查詢到伦泥。其中 ADC1/2/3 還有內(nèi)部通道: ADC1 的通道 16 連接到了芯片內(nèi)部的溫度傳感器剥啤, Vrefint 連接到了通道 17。 ADC2 的模擬通道 16 和 17 連接到了內(nèi)部的 VSS不脯。ADC3 的模擬通道 9府怯、 14、 15防楷、 16 和 17 連接到了內(nèi)部的 VSS牺丙。
外部的 16 個通道在轉(zhuǎn)換的時候又分為規(guī)則通道和注入通道,其中規(guī)則通道最多有 16路复局,注入通道最多有 4 路冲簿。
規(guī)則通道
規(guī)則通道:顧名思意,規(guī)則通道就是很規(guī)矩的意思亿昏,我們平時一般使用的就是這個通道峦剔,或者應(yīng)該說我們用到的都是這個通道,沒有什么特別要注意的可講角钩。
注入通道
注入吝沫,可以理解為插入,插隊的意思彤断,是一種不安分的通道野舶。它是一種在規(guī)則通道轉(zhuǎn)換的時候強(qiáng)行插入要轉(zhuǎn)換的一種。如果在規(guī)則通道轉(zhuǎn)換過程中宰衙,有注入通道插隊平道,那么就要先轉(zhuǎn)換完注入通道,等注入通道轉(zhuǎn)換完成后供炼,再回到規(guī)則通道的轉(zhuǎn)換流程一屋。這點跟中斷程序很像窘疮,都是不安分的主。所以冀墨,注入通道只有在規(guī)則通道存在時才會出現(xiàn)闸衫。
3.轉(zhuǎn)換順序
規(guī)則序列
規(guī)則序列寄存器有 3 個,分別為 SQR3诽嘉、 SQR2蔚出、 SQR1。 SQR3 控制著規(guī)則序列中的第一個到第六個轉(zhuǎn)換虫腋,對應(yīng)的位為: SQ1[4:0] ~ SQ6[4:0]骄酗,第一次轉(zhuǎn)換的是位 4:0 SQ1[4:0],如果通道 16 想第一次轉(zhuǎn)換悦冀,那么在 SQ1[4:0]寫 16 即可趋翻。 SQR2 控制著規(guī)則序列中的第 7 到第12 個轉(zhuǎn)換,對應(yīng)的位為: SQ7[4:0]~SQ12[4:0]盒蟆,如果通道 1 想第 8 個轉(zhuǎn)換踏烙,則 SQ8[4:0]寫 1即可。 SQR1 控制著規(guī)則序列中的第 13 到第 16 個轉(zhuǎn)換历等,對應(yīng)位為: SQ13[4:0]~SQ16[4:0]讨惩,如果通道 6 想第 10 個轉(zhuǎn)換,則 SQ10[4:0]寫 6 即可寒屯。具體使用多少個通道步脓,由 SQR1 的位L[3:0]決定,最多 16 個通道浩螺。
注入序列
注入序列寄存器 JSQR 只有一個靴患,最多支持 4 個通道,具體多少個由 JSQR 的 JL[2:0]決定要出。如果 JL 的 值小于 4 的話鸳君,則 JSQR 跟 SQR 決定轉(zhuǎn)換順序的設(shè)置不一樣,第一次轉(zhuǎn)換的不是 JSQR1[4:0]患蹂,而是 JCQRx[4:0] 或颊, x = (4-JL),跟 SQR 剛好相反传于。如果 JL=00(1個轉(zhuǎn)換)囱挑,那么轉(zhuǎn)換的順序是從 JSQR4[4:0]開始,而不是從 JSQR1[4:0]開始沼溜,這個要注意平挑,編程的時候不要搞錯。當(dāng) JL 等于 4 時,跟 SQR 一樣通熄。
4.觸發(fā)源
通道選好了唆涝,轉(zhuǎn)換的順序也設(shè)置好了,那接下來就該開始轉(zhuǎn)換了唇辨。 ADC 轉(zhuǎn)換可以由ADC 控制寄存器 2: ADC_CR2 的 ADON 這個位來控制廊酣,寫 1 的時候開始轉(zhuǎn)換,寫 0 的時候停止轉(zhuǎn)換赏枚,這個是最簡單也是最好理解的開啟 ADC 轉(zhuǎn)換的控制方式亡驰,理解起來沒啥技術(shù)含量。
除了這種庶民式的控制方法饿幅, ADC 還支持觸發(fā)轉(zhuǎn)換隐解,這個觸發(fā)包括內(nèi)部定時器觸發(fā)和外部 IO 觸發(fā)。觸發(fā)源有很多诫睬,具體選擇哪一種觸發(fā)源,由 ADC 控制寄存器 2:ADC_CR2 的EXTSEL[2:0]和 JEXTSEL[2:0]位來控制帕涌。 EXTSEL[2:0]用于選擇規(guī)則通道的觸發(fā)源摄凡,JEXTSEL[2:0]用于選擇注入通道的觸發(fā)源。選定好觸發(fā)源之后蚓曼,觸發(fā)源是否要激活亲澡,則由ADC 控制寄存器 2:ADC_CR2 的 EXTTRIG 和 JEXTTRIG 這兩位來激活。其中 ADC3 的規(guī)則轉(zhuǎn)換和注入轉(zhuǎn)換的觸發(fā)源與 ADC1/2 的有所不同纫版,在框圖上已經(jīng)表示出來床绪。
5.轉(zhuǎn)換時間
ADC 時鐘
ADC 輸入時鐘 ADC_CLK 由 PCLK2 經(jīng)過分頻產(chǎn)生,最大是 14M其弊,分頻因子由 RCC 時鐘配置寄存器 RCC_CFGR 的位 15:14 ADCPRE[1:0]設(shè)置癞己,可以是 2/4/6/8 分頻,注意這里沒有 1 分頻梭伐。一般我們設(shè)置 PCLK2=HCLK=72M痹雅。
采樣時間
ADC 使用若干個 ADC_CLK 周期對輸入的電壓進(jìn)行采樣,采樣的周期數(shù)可通過 ADC采樣時間寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位設(shè)置糊识, ADC_SMPR2 控制的是通道 0~9绩社, ADC_SMPR1 控制的是通道 10~17。每個通道可以分別用不同的時間采樣赂苗。其中采樣周期最小是 1.5 個愉耙,即如果我們要達(dá)到最快的采樣,那么應(yīng)該設(shè)置采樣周期為 1.5個周期拌滋,這里說的周期就是 1/ADC_CLK朴沿。
ADC 的轉(zhuǎn)換時間跟 ADC 的輸入時鐘和采樣時間有關(guān),公式為: Tconv = 采樣時間 +12.5 個周期败砂。 當(dāng) ADCLK = 14MHZ (最高)悯仙,采樣時間設(shè)置為 1.5 周期(最快)龄毡,那么總的轉(zhuǎn)換時間(最短) Tconv = 1.5 周期 + 12.5 周期 = 14 周期 = 1us。一般我們設(shè)置 PCLK2=72M锡垄,經(jīng)過 ADC 預(yù)分頻器能分頻到最大的時鐘只能是 12M沦零,采樣周期設(shè)置為 1.5 個周期,算出最短的轉(zhuǎn)換時間為 1.17us货岭,這個才是最常用的路操。
6.數(shù)據(jù)寄存器
一切準(zhǔn)備就緒后, ADC 轉(zhuǎn)換后的數(shù)據(jù)根據(jù)轉(zhuǎn)換組的不同千贯,規(guī)則組的數(shù)據(jù)放在 ADC_DR寄存器屯仗,注入組的數(shù)據(jù)放在 JDRx。
規(guī)則數(shù)據(jù)寄存器
ADC 規(guī)則組數(shù)據(jù)寄存器 ADC_DR 只有一個搔谴,是一個 32 位的寄存器魁袜,低 16 位在單 ADC時使用,高 16 位是在 ADC1 中雙模式下保存 ADC2 轉(zhuǎn)換的規(guī)則數(shù)據(jù)敦第,雙模式就是 ADC1 和ADC2 同時使用峰弹。在單模式下, ADC1/2/3 都不使用高 16 位芜果。因為 ADC 的精度是 12 位鞠呈,無論 ADC_DR 的高 16 或者低 16 位都放不滿,只能左對齊或者右對齊右钾,具體是以哪一種方式存放蚁吝,由 ADC_CR2 的 11 位 ALIGN 設(shè)置。
規(guī)則通道可以有 16 個這么多舀射,可規(guī)則數(shù)據(jù)寄存器只有一個窘茁, 如果使用多通道轉(zhuǎn)換, 那轉(zhuǎn)換的數(shù)據(jù)就全部都擠在了 DR 里面脆烟,前一個時間點轉(zhuǎn)換的通道數(shù)據(jù)庙曙,就會被下一個時間點的另外一個通道轉(zhuǎn)換的數(shù)據(jù)覆蓋掉,所以當(dāng)通道轉(zhuǎn)換完成后就應(yīng)該把數(shù)據(jù)取走浩淘,或者開啟 DMA 模式捌朴,把數(shù)據(jù)傳輸?shù)絻?nèi)存里面,不然就會造成數(shù)據(jù)的覆蓋张抄。 最常用的做法就是開啟 DMA 傳輸砂蔽。
注入數(shù)據(jù)寄存器
ADC 注入組最多有 4 個通道,剛好注入數(shù)據(jù)寄存器也有 4 個署惯,每個通道對應(yīng)著自己的寄存器左驾,不會跟規(guī)則寄存器那樣產(chǎn)生數(shù)據(jù)覆蓋的問題。 ADC_JDRx 是 32 位的,低 16 位有效诡右,高 16 位保留安岂,數(shù)據(jù)同樣分為左對齊和右對齊,具體是以哪一種方式存放帆吻,由ADC_CR2 的 11 位 ALIGN 設(shè)置域那。
7.中斷
轉(zhuǎn)換結(jié)束中斷
數(shù)據(jù)轉(zhuǎn)換結(jié)束后,可以產(chǎn)生中斷猜煮,中斷分為三種:規(guī)則通道轉(zhuǎn)換結(jié)束中斷次员,注入轉(zhuǎn)換通道轉(zhuǎn)換結(jié)束中斷,模擬看門狗中斷王带。其中轉(zhuǎn)換結(jié)束中斷很好理解淑蔚,跟我們平時接觸的中斷一樣,有相應(yīng)的中斷標(biāo)志位和中斷使能位愕撰,我們還可以根據(jù)中斷類型寫相應(yīng)配套的中斷服務(wù)程序刹衫。
模擬看門狗中斷
當(dāng)被 ADC 轉(zhuǎn)換的模擬電壓低于低閾值或者高于高閾值時,就會產(chǎn)生中斷搞挣,前提是我們開啟了模擬看門狗中斷带迟,其中低閾值和高閾值由 ADC_LTR 和 ADC_HTR 設(shè)置。例如我們設(shè)置高閾值是 2.5V柿究,那么模擬電壓超過 2.5V 的時候,就會產(chǎn)生模擬看門狗中斷黄选,反之低閾值也一樣蝇摸。
DMA 請求
規(guī)則和注入通道轉(zhuǎn)換結(jié)束后,除了產(chǎn)生中斷外办陷,還可以產(chǎn)生 DMA 請求貌夕,把轉(zhuǎn)換好的數(shù)據(jù)直接存儲在內(nèi)存里面。要注意的是只有 ADC1 和 ADC3 可以產(chǎn)生 DMA 請求民镜。有關(guān)DMA 請求需要配合《STM32F10X-中文參考手冊》 DMA 控制器這一章節(jié)來學(xué)習(xí)啡专。一般我們在使用 ADC 的時候都會開啟 DMA 傳輸。
8.電壓轉(zhuǎn)換
模擬電壓經(jīng)過 ADC 轉(zhuǎn)換后制圈,是一個 12 位的數(shù)字值们童,如果通過串口以 16 進(jìn)制打印出來的話,可讀性比較差鲸鹦,那么有時候我們就需要把數(shù)字電壓轉(zhuǎn)換成模擬電壓慧库,也可以跟實際的模擬電壓(用萬用表測)對比,看看轉(zhuǎn)換是否準(zhǔn)確馋嗜。
我們一般在設(shè)計原理圖的時候會把 ADC 的輸入電壓范圍設(shè)定在: 0~3.3v齐板,因為 ADC是 12 位的,那么 12 位滿量程對應(yīng)的就是 3.3V, 12 位滿量程對應(yīng)的數(shù)字值是: 甘磨。數(shù)值0 對應(yīng)的就是 0V橡羞。 如果轉(zhuǎn)換后的數(shù)值為 X , X 對應(yīng)的模擬電壓為 Y济舆,那么會有這么一個等式成立:
。
1.1.2 ADC通道選擇
STM32 將 ADC 的轉(zhuǎn)換分為 2 個通道組:規(guī)則通道組和注入通道組吗冤。規(guī)則通道相當(dāng)于你正常運行的程序又厉,而注入通道呢,就相當(dāng)于中斷椎瘟。在你程序正常執(zhí)行的時候覆致,中斷是可以打斷你的執(zhí)行的。同這個類似肺蔚,注入通道的轉(zhuǎn)換可以打斷規(guī)則通道的轉(zhuǎn)換煌妈, 在注入通道被轉(zhuǎn)換完成之后,規(guī)則通道才得以繼續(xù)轉(zhuǎn)換宣羊。
通過一個形象的例子可以說明: 假如你在家里的院子內(nèi)放了 5 個溫度探頭璧诵,室內(nèi)放了 3 個溫度探頭; 你需要時刻監(jiān)視室外溫度即可仇冯,但偶爾你想看看室內(nèi)的溫度之宿;因此你可以使用規(guī)則通道組循環(huán)掃描室外的 5 個探頭并顯示 AD 轉(zhuǎn)換結(jié)果,當(dāng)你想看室內(nèi)溫度時苛坚,通過一個按鈕啟動注入轉(zhuǎn)換組(3個室內(nèi)探頭)并暫時顯示室內(nèi)溫度比被,當(dāng)你放開這個按鈕后,系統(tǒng)又會回到規(guī)則通道組繼續(xù)檢測室外溫度泼舱。從系統(tǒng)設(shè)計上等缀,測量并顯示室內(nèi)溫度的過程中斷了測量并顯示室外溫度的過程,但程序設(shè)計上可以在初始化階段分別設(shè)置好不同的轉(zhuǎn)換組娇昙,系統(tǒng)運行中不必再變更循環(huán)轉(zhuǎn)換的配置尺迂,從而達(dá)到兩個任務(wù)互不干擾和快速切換的結(jié)果∶罢疲可以設(shè)想一下噪裕,如果沒有規(guī)則組和注入組的劃分,當(dāng)你按下按鈕后股毫,需要從新配置 AD 循環(huán)掃描的通道州疾,然后在釋放按鈕后需再次配置 AD 循環(huán)掃描的通道。
上面的例子因為速度較慢皇拣,不能完全體現(xiàn)這樣區(qū)分(規(guī)則通道組和注入通道組)的好處严蓖,但在工業(yè)應(yīng)用領(lǐng)域中有很多檢測和監(jiān)視探頭需要較快地處理薄嫡,這樣對 AD 轉(zhuǎn)換的分組將簡化事件處理的程序并提高事件處理的速度。
STM32 其 ADC 的規(guī)則通道組最多包含 16 個轉(zhuǎn)換颗胡,而注入通道組最多包含 4 個通道毫深。關(guān)于這兩個通道組的詳細(xì)介紹,請參考《STM32 參考手冊的》毒姨,我們這里就不在一一列舉了哑蔫。
● 規(guī)則組由多達(dá)16個轉(zhuǎn)換組成。規(guī)則通道和它們的轉(zhuǎn)換順序在ADC_SQRx寄存器中選擇弧呐。規(guī)則組中轉(zhuǎn)換的總數(shù)應(yīng)寫入ADC_SQR1寄存器的L[3:0]位中闸迷。
● 注入組由多達(dá)4個轉(zhuǎn)換組成。注入通道和它們的轉(zhuǎn)換順序在ADC_JSQR寄存器中選擇俘枫。注入組里的轉(zhuǎn)換總數(shù)目應(yīng)寫入ADC_JSQR寄存器的L[1:0]位中腥沽。
如果ADC_SQRx或ADC_JSQR寄存器在轉(zhuǎn)換期間被更改,當(dāng)前的轉(zhuǎn)換被清除鸠蚪,一個新的啟動脈沖將發(fā)送到ADC 以轉(zhuǎn)換新選擇的組今阳。
溫度傳感器和通道ADC1_IN16相連接,內(nèi)部參照電壓VREFINT和ADC1_IN17相連接茅信《苌啵可以按注入或規(guī)則通道對這兩個內(nèi)部通道進(jìn)行轉(zhuǎn)換。
【注意】溫度傳感器和VREFINT只能出現(xiàn)在主ADC1 中蘸鲸。
1.1.3 ADC轉(zhuǎn)換模式
? 單次轉(zhuǎn)換模式
單次轉(zhuǎn)換模式下妖谴,ADC只執(zhí)行一次轉(zhuǎn)換。該模式既可通過設(shè)置ADC_CR2 寄存器的ADON位(只適用于規(guī)則通道)啟動也可通過外部觸發(fā)啟動(適用于規(guī)則通道或注入通道)酌摇,這時CONT位為0 膝舅。一旦選擇通道的轉(zhuǎn)換完成:
● 如果一個規(guī)則通道被轉(zhuǎn)換:
─ 轉(zhuǎn)換數(shù)據(jù)被儲存在16位ADC_DR寄存器中
─ EOC(轉(zhuǎn)換結(jié)束)標(biāo)志被設(shè)置
─ 如果設(shè)置了EOCIE,則產(chǎn)生中斷妙痹。
● 如果一個注入通道被轉(zhuǎn)換:
─ 轉(zhuǎn)換數(shù)據(jù)被儲存在16位的ADC_DRJ1寄存器中
─ JEOC(注入轉(zhuǎn)換結(jié)束)標(biāo)志被設(shè)置
─ 如果設(shè)置了JEOCIE位铸史,則產(chǎn)生中斷鼻疮。然后ADC停止怯伊。
? 連續(xù)轉(zhuǎn)換模式
在連續(xù)轉(zhuǎn)換模式中,當(dāng)前面ADC轉(zhuǎn)換一結(jié)束馬上就啟動另一次轉(zhuǎn)換判沟。此模式可通過外部觸發(fā)啟動或通過設(shè)置ADC_CR2寄存器上的ADON位啟動耿芹,此時CONT位是1。 每個轉(zhuǎn)換后:
● 如果一個規(guī)則通道被轉(zhuǎn)換:
─ 轉(zhuǎn)換數(shù)據(jù)被儲存在16位的ADC_DR寄存器中
─ EOC(轉(zhuǎn)換結(jié)束)標(biāo)志被設(shè)置
─ 如果設(shè)置了EOCIE挪哄,則產(chǎn)生中斷吧秕。
● 如果一個注入通道被轉(zhuǎn)換:
─ 轉(zhuǎn)換數(shù)據(jù)被儲存在16位的ADC_DRJ1寄存器中
─ JEOC(注入轉(zhuǎn)換結(jié)束)標(biāo)志被設(shè)置
─ 如果設(shè)置了JEOCIE位,則產(chǎn)生中斷迹炼。
? 掃描模式
此模式用來掃描一組模擬通道砸彬。
掃描模式可通過設(shè)置ADC_CR1寄存器的SCAN位來選擇颠毙。一旦這個位被設(shè)置,ADC掃描所有被ADC_SQRX 寄存器(對規(guī)則通道)或ADC_JSQR(對注入通道)選中的所有通道砂碉。在每個組的每個通道上執(zhí)行單次轉(zhuǎn)換蛀蜜。在每個轉(zhuǎn)換結(jié)束時,同一組的下一個通道被自動轉(zhuǎn)換增蹭。如果設(shè)置了CONT位滴某,轉(zhuǎn)換不會在選擇組的最后一個通道上停止,而是再次從選擇組的第一個通道繼續(xù)轉(zhuǎn)換滋迈。
如果設(shè)置了DMA位霎奢,在每次EOC后,DMA控制器把規(guī)則組通道的轉(zhuǎn)換數(shù)據(jù)傳輸?shù)絊RAM 中饼灿。而注入通道轉(zhuǎn)換的數(shù)據(jù)總是存儲在ADC_JDRx寄存器中幕侠。
? 間斷模式
規(guī)則組
此模式通過設(shè)置ADC_CR1 寄存器上的DISCEN位激活。它可以用來執(zhí)行一個短序列的n次轉(zhuǎn)換(n<=8)赔退,此轉(zhuǎn)換是ADC_SQRx寄存器所選擇的轉(zhuǎn)換序列的一部分橙依。數(shù)值n由ADC_CR1寄存器的DISCNUM[2:0]位給出。
一個外部觸發(fā)信號可以啟動ADC_SQRx 寄存器中描述的下一輪n次轉(zhuǎn)換硕旗,直到此序列所有的轉(zhuǎn)換完成為止窗骑。總的序列長度由ADC_SQR1寄存器的L[3:0]定義漆枚。
舉例: 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)換一個規(guī)則組時,轉(zhuǎn)換序列結(jié)束后不自動從頭開始爽待。
2.當(dāng)所有子組被轉(zhuǎn)換完成损同,下一次觸發(fā)啟動第一個子組的轉(zhuǎn)換翩腐。在上面的例子中,第四次觸發(fā)重新轉(zhuǎn)換第一子組的通道 0 膏燃、1和2栗菜。
注入組
此模式通過設(shè)置ADC_CR1 寄存器的JDISCEN位激活。在一個外部觸發(fā)事件后蹄梢,該模式按通道順序逐個轉(zhuǎn)換ADC_JSQR寄存器中選擇的序列疙筹。
一個外部觸發(fā)信號可以啟動ADC_JSQR寄存器選擇的下一個通道序列的轉(zhuǎn)換,直到序列中所有的轉(zhuǎn)換完成為止禁炒《兀總的序列長度由ADC_JSQR寄存器的JL[1:0]位定義。
例子: 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)換涯捻,下個觸發(fā)啟動第1個注入通道的轉(zhuǎn)換。在上述例子中望迎,第四個觸發(fā)重新轉(zhuǎn)換第1個注入通道1障癌。
2.不能同時使用自動注入和間斷模式。
3.必須避免同時為規(guī)則和注入組設(shè)置間斷模式辩尊。間斷模式只能作用于一組轉(zhuǎn)換涛浙。
1.2 STM32Cube新建工程
本文使用串口通信的工程進(jìn)行修改。打開工程摄欲。筆者只講解ADC的配置轿亮,關(guān)于工程搭建和串口配置請看筆者博客。
接下來筆者將通過三種方式實現(xiàn)ADC單通道電壓數(shù)據(jù)采集胸墙,先看看筆者使用的開發(fā)板的硬件電路我注,其中PC0外接了一個滑動電阻。
1.2.1 ADC單通道電壓采集查詢方式工程配置
打開工程迟隅,打開Analog選項但骨,配置ADC參數(shù)。
具體配置參數(shù)如下玻淑。
使能連續(xù)轉(zhuǎn)換模式(Continuous Conversion Mode)嗽冒。設(shè)置轉(zhuǎn)換周期呀伙。其他為默認(rèn)設(shè)置补履。
值得注意的是,ADC 的輸入時鐘不得超過14MHz剿另,它是由PCLK2經(jīng)分頻產(chǎn)生箫锤,需要調(diào)整ADC輸入的分頻系數(shù)贬蛙。
然后生成工程即可。
1.2.2 ADC單通道電壓采集中斷方式工程配置
ADC單通道電壓采集中斷方式工程配置只需要在上個工程的基礎(chǔ)上進(jìn)行配置即可谚攒,打開工程阳准,找到Analog選項,使能ADC中斷即可馏臭。
值得注意的是野蝇,本例程使用了串口中斷,因此還需要設(shè)置中斷優(yōu)先級括儒,進(jìn)入NVIC配置表绕沈,簡單配置下即可,如下圖所示帮寻。
好了乍狐,直接就生成工程即可。
1.2.3 ADC單通道電壓采集DMA方式工程配置
前面的章節(jié)也講過DMA固逗,如果不懂可先去看看浅蚪,這是簡單說下用DMA的好處:我們在前面使用了查詢方式和中斷方式進(jìn)行采樣,都需要在主程序中占用好多時間出來,不管是那種采樣都需要調(diào)用HAL_ADC_GetValue()這個函數(shù)獲取轉(zhuǎn)換后的值,中斷還好點宪彩,要是查詢的話誓禁,還有可能會丟失數(shù)據(jù),使用DMA就可以避免丟失數(shù)據(jù)洒疚,而且不需要消耗CPU資源,下面看看如何使用使用STM32cubeMX配置DMA。
我們還是在第二個工程的基礎(chǔ)上進(jìn)行修改浊伙,打開工程。根據(jù)DMA通道預(yù)覽可以知道长捧,我們用的ADC1對應(yīng)DMA1 的通道1嚣鄙。
點擊DMASettings 點擊 Add 添加通道,選擇ADC1傳輸速率設(shè)置為中速串结,DMA傳輸模式為正常模式哑子,DMA內(nèi)存地址自增,每次增加一個word(字)肌割。
值得注意的是卧蜓,STM32cubeMX默認(rèn)開啟了DMA的中斷。
還需要設(shè)置中斷優(yōu)先級把敞,進(jìn)入NVIC配置表弥奸,簡單配置下即可,如下圖所示奋早。
然后生成代碼即可盛霎。
1.3 ADC具體代碼實現(xiàn)與分析
1.3.1 ADC單通道電壓采集查詢方式實現(xiàn)
在講解代碼之前赠橙,先看ADC采集數(shù)據(jù)的編程流程:
1.硬件等初始化;
2.串口愤炸、ADC等參數(shù)配置期揪;
3.校準(zhǔn)ADC,處理ADC數(shù)據(jù)规个;
關(guān)于串口的部分凤薛,筆者就不講了,這里只講解ADC的部分诞仓。在講解之前枉侧,先看看ADC的結(jié)構(gòu)體,其原型如下狂芋。
typedef struct __ADC_HandleTypeDef
{
ADC_TypeDef *Instance; /*!< Register base address */
ADC_InitTypeDef Init; /*!< ADC required parameters */
DMA_HandleTypeDef *DMA_Handle; /*!< Pointer DMA Handler */
HAL_LockTypeDef Lock; /*!< ADC locking object */
__IO uint32_t State; /*!< ADC communication state (bitmap of ADC states) */
__IO uint32_t ErrorCode; /*!< ADC Error code */
}ADC_HandleTypeDef;
以上六個參數(shù)分別是寄存器基地址榨馁、ADC初始化參數(shù)的結(jié)構(gòu)體、指向DMA機(jī)構(gòu)體的指針帜矾、互斥鎖翼虫、狀態(tài)描述符、保存錯誤代碼的變量屡萤,其中Init是需要我們重點關(guān)注的珍剑,其原型如下所示。
typedef struct
{
uint32_t DataAlign; /*!< Specifies ADC data alignment to right (MSB on register bit 11 and LSB on register bit 0) (default setting)
or to left (if regular group: MSB on register bit 15 and LSB on register bit 4, if injected group (MSB kept as signed value due to potential negative value after offset application): MSB on register bit 14 and LSB on register bit 3).
This parameter can be a value of @ref ADC_Data_align */
uint32_t ScanConvMode; /*!< Configures the sequencer of regular and injected groups.
This parameter can be associated to parameter 'DiscontinuousConvMode' to have main sequence subdivided in successive parts.
If disabled: Conversion is performed in single mode (one channel converted, the one defined in rank 1).
Parameters 'NbrOfConversion' and 'InjectedNbrOfConversion' are discarded (equivalent to set to 1).
If enabled: Conversions are performed in sequence mode (multiple ranks defined by 'NbrOfConversion'/'InjectedNbrOfConversion' and each channel rank).
Scan direction is upward: from rank1 to rank 'n'.
This parameter can be a value of @ref ADC_Scan_mode
Note: For regular group, this parameter should be enabled in conversion either by polling (HAL_ADC_Start with Discontinuous mode and NbrOfDiscConversion=1)
or by DMA (HAL_ADC_Start_DMA), but not by interruption (HAL_ADC_Start_IT): in scan mode, interruption is triggered only on the
the last conversion of the sequence. All previous conversions would be overwritten by the last one.
Injected group used with scan mode has not this constraint: each rank has its own result register, no data is overwritten. */
FunctionalState ContinuousConvMode; /*!< Specifies whether the conversion is performed in single mode (one conversion) or continuous mode for regular group,
after the selected trigger occurred (software start or external trigger).
This parameter can be set to ENABLE or DISABLE. */
uint32_t NbrOfConversion; /*!< Specifies the number of ranks that will be converted within the regular group sequencer.
To use regular group sequencer and convert several ranks, parameter 'ScanConvMode' must be enabled.
This parameter must be a number between Min_Data = 1 and Max_Data = 16. */
FunctionalState DiscontinuousConvMode; /*!< Specifies whether the conversions sequence of regular group is performed in Complete-sequence/Discontinuous-sequence (main sequence subdivided in successive parts).
Discontinuous mode is used only if sequencer is enabled (parameter 'ScanConvMode'). If sequencer is disabled, this parameter is discarded.
Discontinuous mode can be enabled only if continuous mode is disabled. If continuous mode is enabled, this parameter setting is discarded.
This parameter can be set to ENABLE or DISABLE. */
uint32_t NbrOfDiscConversion; /*!< Specifies the number of discontinuous conversions in which the main sequence of regular group (parameter NbrOfConversion) will be subdivided.
If parameter 'DiscontinuousConvMode' is disabled, this parameter is discarded.
This parameter must be a number between Min_Data = 1 and Max_Data = 8. */
uint32_t ExternalTrigConv; /*!< Selects the external event used to trigger the conversion start of regular group.
If set to ADC_SOFTWARE_START, external triggers are disabled.
If set to external trigger source, triggering is on event rising edge.
This parameter can be a value of @ref ADC_External_trigger_source_Regular */
}ADC_InitTypeDef;
- DataAlign:數(shù)據(jù)對齊方式死陆,可選左對齊與右對齊招拙;
- ScanConvMode:是否使用掃描模式,只有一個轉(zhuǎn)換通道關(guān)閉即可措译,多可通道需要開啟别凤;
- ContinuousConvMode:單一/連續(xù)轉(zhuǎn)換模式,一般采用來連續(xù)轉(zhuǎn)換模式领虹;
- NbrOfConversion:ADC轉(zhuǎn)換通道數(shù)目规哪,有幾個寫就行;
- DiscontinuousConvMode:是否使用間斷模式塌衰,這個在工程應(yīng)用很有必要诉稍,也就是在某個事件觸發(fā)下,開啟轉(zhuǎn)換最疆,實際的產(chǎn)品是需要考慮功耗問題的杯巨,不僅僅是實現(xiàn);
- NbrOfDiscConversion:間斷模式的轉(zhuǎn)換數(shù)據(jù)努酸,如果沒有開啟間斷模式服爷,就不用設(shè)置。
- ExternalTrigConv:外部觸發(fā)選擇;這個有多個選擇层扶,一般采用軟件觸發(fā)方式;
還有一個結(jié)構(gòu)體ADC_ChannelConfTypeDef烙荷,原型如下镜会。
typedef struct
{
uint32_t Channel; /*!< Specifies the channel to configure into ADC regular group.
This parameter can be a value of @ref ADC_channels
Note: Depending on devices, some channels may not be available on package pins. Refer to device datasheet for channels availability.
Note: On STM32F1 devices with several ADC: Only ADC1 can access internal measurement channels (VrefInt/TempSensor)
Note: On STM32F10xx8 and STM32F10xxB devices: A low-amplitude voltage glitch may be generated (on ADC input 0) on the PA0 pin, when the ADC is converting with injection trigger.
It is advised to distribute the analog channels so that Channel 0 is configured as an injected channel.
Refer to errata sheet of these devices for more details. */
uint32_t Rank; /*!< Specifies the rank in the regular group sequencer
This parameter can be a value of @ref ADC_regular_rank
Note: In case of need to disable a channel or change order of conversion sequencer, rank containing a previous channel setting can be overwritten by the new channel setting (or parameter number of conversions can be adjusted) */
uint32_t SamplingTime; /*!< Sampling time value to be set for the selected channel.
Unit: ADC clock cycles
Conversion time is the addition of sampling time and processing time (12.5 ADC clock cycles at ADC resolution 12 bits).
This parameter can be a value of @ref ADC_sampling_times
Caution: This parameter updates the parameter property of the channel, that can be used into regular and/or injected groups.
If this same channel has been previously configured in the other group (regular/injected), it will be updated to last setting.
Note: In case of usage of internal measurement channels (VrefInt/TempSensor),
sampling time constraints must be respected (sampling time can be adjusted in function of ADC clock frequency and sampling time setting)
Refer to device datasheet for timings values, parameters TS_vrefint, TS_temp (values rough order: 5us to 17.1us min). */
}ADC_ChannelConfTypeDef;
這個結(jié)構(gòu)體用于配置通道的采樣時間以及通道排序。查詢方式采集ADC數(shù)據(jù)代碼實現(xiàn)很簡單终抽,在主函數(shù)中的循環(huán)體中添加以下代碼即可戳表。
HAL_ADCEx_Calibration_Start(&hadc1); //校準(zhǔn)
HAL_ADC_Start(&hadc1); //開啟
HAL_ADC_PollForConversion(&hadc1,50); //等待轉(zhuǎn)換完成
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
ADC_ConvertedValue = HAL_ADC_GetValue(&hadc1);
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); //實際電壓值
}
HAL_Delay(1000);
代碼很好理解,先進(jìn)行ADC校準(zhǔn)昼伴,然后開啟ADC采樣匾旭,等待轉(zhuǎn)換完成,最后獲取數(shù)據(jù)即可圃郊。不斷循環(huán)即可价涝。
完整代碼請參看附件。
1.3.2 ADC單通道電壓采集中斷方式實現(xiàn)
中斷方式和查詢方式不同的地方在于需要開啟ADC中斷服務(wù)持舆,配置中斷優(yōu)先級和中斷服務(wù)函數(shù)色瘩。筆者接下來之講與查詢方式不同的地方。
1.需要在ADC配置函數(shù)中開啟ADC中斷和ADC校準(zhǔn)
HAL_ADC_Start_IT(&hadc1); //開啟ADC中斷轉(zhuǎn)換
HAL_ADCEx_Calibration_Start(&hadc1); //AD校準(zhǔn)
2.中斷回調(diào)函數(shù)
在中斷回調(diào)函數(shù)中進(jìn)行讀取數(shù)據(jù)逸寓,將數(shù)據(jù)存放在變量ADC_ConvertedValue中居兆。
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) //ADC轉(zhuǎn)換完成回調(diào)
{
HAL_ADC_Stop_IT(hadc); //關(guān)閉ADC
ADC_ConvertedValue = HAL_ADC_GetValue(hadc);
}
有朋友會問為何要關(guān)閉ADC的中斷,因為ADC采樣頻率很高竹伸,我們每隔1S獲取數(shù)據(jù)泥栖,因此我們在獲取數(shù)據(jù)后就關(guān)閉ADC,這樣可以提高CPU利用率勋篓。
4.主函數(shù)
主函數(shù)負(fù)責(zé)接收轉(zhuǎn)換的值吧享,并將其轉(zhuǎn)換為電壓值,然后通過串口打印出來譬嚣,便于查看ADC轉(zhuǎn)換值耙蔑。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
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); //實際電壓值
HAL_Delay(1000);
HAL_ADC_Start_IT(&hadc1); //開啟ADC中斷轉(zhuǎn)換
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
完整代碼請參看附件。
我們還可以通過定時器方式來實現(xiàn)孤荣,關(guān)于定時器參看前面的章節(jié)甸陌。如果開啟定時器1,定時時間為1s盐股,則可將以下函數(shù)的內(nèi)容替換main()函數(shù)的循環(huán)體的內(nèi)容钱豁。這樣完就可空出主循環(huán)干其他事情了。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定時器中斷回調(diào)
{
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); //實際電壓值
HAL_ADC_Start_IT(&hadc1); //定時器中斷里面開啟ADC中斷轉(zhuǎn)換疯汁,1s開啟一次采集
}
1.3.3 ADC單通道電壓采集DMA方式實現(xiàn)
DMA方式實現(xiàn)的代碼結(jié)構(gòu)和查詢方式差不多牲尺,只需要開啟DMA轉(zhuǎn)換即可。然后即可獲取ADC的數(shù)據(jù)。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 1);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_ConvertedValue, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
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); //實際電壓值
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
代碼的注釋已經(jīng)很詳細(xì)了谤碳,我不再贅述了溃卡。完整代碼請參看附件。
當(dāng)然蜒简,也可開啟定時器瘸羡,將主循環(huán)中的代碼放入定時器的回調(diào)函數(shù)中。
這里還需要說明一下 ADC 的參考電壓搓茬,我的開發(fā)板使用的是 STM32F103ZET6犹赖,
該芯片有外部參考電壓: Vref-和 Vref+,其中 Vref-必須和 VSSA 連接在一起卷仑, 而 Vref+的輸入范圍為: 2.4~VDDA峻村。需要設(shè)置 Vref-和 Vref+設(shè)置參考電壓,默認(rèn)的我們是通過跳線帽將 Vref-接到 GND锡凝, Vref+接到 VDDA粘昨,參考電壓就是 3.3V。如果大家想自己設(shè)置其他參考電壓窜锯,將你的參考電壓接在 Vref-和 Vref+上就 OK 了雾棺。本章我們的參考電壓設(shè)置的是 3.3V。一般的開發(fā)板已經(jīng)設(shè)置好了衬浑,不在需要單獨去設(shè)置捌浩。
通過以上幾個步驟的設(shè)置,我們就能正常的使用 STM32 的 ADC1 來執(zhí)行 AD 轉(zhuǎn)換操作了工秩。
1.4實驗現(xiàn)象
將程序編譯好后下載到板子中尸饺,打開串口助手可以看到如下現(xiàn)象,當(dāng)然了助币,普通方式浪听、中斷方式和DMA方式都是一樣的現(xiàn)象。
代碼獲取方式
1.關(guān)注公眾號[嵌入式實驗樓]
2.在公眾號回復(fù)關(guān)鍵詞[STM32F1]獲取資料
歡迎訪問我的網(wǎng)站:
BruceOu的嗶哩嗶哩
BruceOu的主頁
BruceOu的博客
BruceOu的CSDN博客
BruceOu的簡書