一.ADC 模數(shù)轉(zhuǎn)換器
1.1 ADC、DAC、PWM
ADC(Analog-Digital Converter),意即模擬-數(shù)字轉(zhuǎn)換器,簡(jiǎn)稱模數(shù)轉(zhuǎn)換器。ADC可以將引腳上連續(xù)變化的模擬電壓轉(zhuǎn)換為內(nèi)存中存儲(chǔ)的數(shù)字變量,建立模擬電路到數(shù)字電路的橋梁。
DAC:數(shù)字到模擬的橋梁(PWM控制燈的亮度和電機(jī)旋轉(zhuǎn)的速度,DAC的使用只要是在信號(hào)發(fā)生器、音頻解碼芯片等
PWM:數(shù)字到模擬的橋梁,例如PWM控制燈的亮度和電機(jī)旋轉(zhuǎn)的速度,PWM只有完全導(dǎo)通和完全斷開兩種狀態(tài),在這兩種狀態(tài)都沒有功率損耗,故直流電機(jī)調(diào)速這種大功率的應(yīng)用場(chǎng)景,使用PWM來(lái)等效模擬量,是比DAC更好的選擇,PWM電路更簡(jiǎn)單,更常用。
1.2 12位逐次逼近型ADC,1us轉(zhuǎn)換時(shí)間
- STM32中的ADC是一個(gè)12位的逐次逼近型的ADC,最快轉(zhuǎn)換時(shí)間1us?!爸鸫伪平笔茿DC的一種工作模式;“12位”指ADC的分辨率,12位AD值的表示范圍即0 ~ 212? 1 ,量化值為0 ~ 4095 ,位數(shù)越高,量化結(jié)果越精細(xì),對(duì)應(yīng)ADC的分辨率就越高;轉(zhuǎn)換時(shí)間1us對(duì)應(yīng)的頻率就是1MHz,這就是STM32中ADC的最快轉(zhuǎn)換頻率。
- STM32中的ADC的輸入電壓范圍為0 ~ 3.3V,對(duì)應(yīng)的轉(zhuǎn)換結(jié)果就是0 ~ 4095。ADC的輸入電壓一般要求都是要在芯片供電的正負(fù)極之間變化且電壓和轉(zhuǎn)換結(jié)果都是一一對(duì)應(yīng)的線性關(guān)系。
1.3 ADC有18個(gè)輸入通道
- 可以測(cè)量16個(gè)外部信號(hào)和2個(gè)內(nèi)部信號(hào)源。外部的16個(gè)信號(hào)源即16個(gè)GPIO口(可能來(lái)自不同的GPIOx,具體要參考引腳定義),在引腳上直接輸入模擬信號(hào)即可,不需要額外的測(cè)量電路;
- 2個(gè)內(nèi)部信號(hào)源分別是內(nèi)部溫度傳感器和內(nèi)部參考電壓,溫度傳感器可以測(cè)量CPU的溫度,內(nèi)部參考電壓是一個(gè)1.2V左右的內(nèi)部基準(zhǔn)電壓,且這個(gè)內(nèi)部基準(zhǔn)電壓是不隨外部供電電壓變化的。當(dāng)芯片的供電電壓不是準(zhǔn)確的3.3V時(shí),就可以通過這個(gè)內(nèi)部的基準(zhǔn)電壓進(jìn)行校準(zhǔn)來(lái)得到正確的電壓值。
- 采用STM32C8T6只有 ADC1、ADC2,10個(gè)外部輸入通道,即最多只能測(cè)量10個(gè)外部引腳的模擬信號(hào)。
1.4 規(guī)則組和注入組兩個(gè)轉(zhuǎn)換單元
普通的AD轉(zhuǎn)換流程為:先啟動(dòng)一次轉(zhuǎn)換,之后讀值,依次循環(huán)。
STM32的ADC可以將要轉(zhuǎn)換的通道列為一組,每一次連續(xù)轉(zhuǎn)換多個(gè)值。
-
規(guī)則組用于常規(guī)使用,注入組一般用于突發(fā)事件。
-
一般可以用于測(cè)量光線強(qiáng)度、溫度這些值
如果光線高于某個(gè)預(yù)值、低于某個(gè)預(yù)值,或者溫度高于某個(gè)預(yù)值、低于某個(gè)預(yù)值時(shí),就會(huì)執(zhí)行一些操作 -
模擬開門狗可以監(jiān)測(cè)指定的某些通道,當(dāng)AD值高于他設(shè)定的上域值,或者低于下域值時(shí)。就會(huì)申請(qǐng)中斷,然后就可以在中斷函數(shù)里,執(zhí)行相應(yīng)的操作。
1.5 逐次逼近型ADC
下圖所示的即為逐次逼近型ADC的工作原理圖(ADC0809)。STM32中的ADC的結(jié)構(gòu)與此類似
- IN0~IN7是八個(gè)輸入通道
通過配置ADDA~ADDC可以選擇一個(gè)通道作為信號(hào)輸入
通過比較器, DAC逐漸逼近輸入信號(hào), DAC的值最終與輸入信號(hào)十分接近 - 結(jié)構(gòu)圖上方的EOC(End Of Convert)是轉(zhuǎn)換結(jié)束信號(hào)。該芯片通過START端口控制轉(zhuǎn)換開始,CLOCK控制ADC內(nèi)部的轉(zhuǎn)換工作頻率。VREF(+) 和VREF(-)是DAC的參考電壓,定義數(shù)據(jù)對(duì)應(yīng)的電壓范圍(255對(duì)應(yīng)3.3V或5V)。通常芯片的工作電壓正極VCC 和VREF 相同,接在一起;通常芯片的工作電壓負(fù)極GND和VREF(-) 相同,接在一起。
1.6 STM32中的ADC框圖
- 普通的ADC多路開關(guān)一般只選中一個(gè),STM32的ADC可以同時(shí)選中多個(gè)通道進(jìn)行轉(zhuǎn)換,規(guī)則組最多同時(shí)選中16個(gè)通道,注入組一次最多可以選中4個(gè)通道。(以餐廳點(diǎn)菜模型為例,普通模式為每次點(diǎn)一個(gè)菜,做好菜后上菜;STM32可以做到每次列出一個(gè)菜單,規(guī)則組一次最多可以列16個(gè)菜,注入組一次最多可以列4個(gè)菜,做好后依次上菜)
- STM32中的ADC的轉(zhuǎn)換結(jié)果會(huì)被存儲(chǔ)在對(duì)應(yīng)的數(shù)據(jù)寄存器中。對(duì)于規(guī)則組通道,其只有一個(gè)數(shù)據(jù)寄存器(餐桌上只能擺一個(gè)菜),后轉(zhuǎn)換的數(shù)據(jù)會(huì)將之前轉(zhuǎn)換的數(shù)據(jù)覆蓋,之前轉(zhuǎn)換的數(shù)據(jù)就會(huì)丟失。對(duì)于規(guī)則組通道,要想實(shí)現(xiàn)同時(shí)轉(zhuǎn)換的功能,最好配合DMA來(lái)將轉(zhuǎn)換后的數(shù)據(jù)及時(shí)轉(zhuǎn)運(yùn),就可以保證轉(zhuǎn)換的數(shù)據(jù)不會(huì)丟失了。對(duì)于注入組通道,它擁有4個(gè)數(shù)據(jù)寄存器(餐廳的VIP坐席,餐桌上一次可以擺四個(gè)菜)。對(duì)于注入組而言,就不用擔(dān)心數(shù)據(jù)覆蓋的問題了。一般情況下,使用規(guī)則組和DMA就可以滿足大部分的使用需求。(這里只講解規(guī)則組使用,注入組自行了解即可)
- 框圖的左下角為觸發(fā)轉(zhuǎn)換信號(hào),對(duì)應(yīng)ADC0809的START信號(hào)。STM32的觸發(fā)轉(zhuǎn)換信號(hào)來(lái)源有兩種:軟件觸發(fā)和硬件觸發(fā)。硬件觸發(fā)信號(hào)可以來(lái)自于定時(shí)器的各個(gè)通道、定時(shí)器TRGO主模式的輸出,外部中斷EXTI。下表列出了ADC1和ADC2的觸發(fā)源。(其中EXTI線11/TIM8_YRGO事件的選擇需要使用AFIO端口重映射來(lái)配置)
- 這里ADC的時(shí)鐘ADCCLK是來(lái)自于RCC的APB2時(shí)鐘。由原理圖可得,ADCCLK最大為14MHz,所以ADC預(yù)分頻器只能選擇6分頻(得到12MHz)和8分頻(得到9MHz)兩個(gè)值
- ADC可以通過DMA請(qǐng)求信號(hào)觸發(fā)DMA轉(zhuǎn)運(yùn)數(shù)據(jù)
- 模擬看門狗的功能是監(jiān)測(cè)指定的通道??梢栽O(shè)置模擬看門狗的閾值高限(12位)、閾值底限(12位)和指定“看門”的通道。只要通道的電壓值超過閾值范圍,模擬看門狗就會(huì)“亂叫”,申請(qǐng)一個(gè)模擬看門狗的中斷,之后通向NVIC。
- 規(guī)則組和注入組在轉(zhuǎn)換完成后會(huì)生成一個(gè)轉(zhuǎn)換完成的信號(hào)。EOC為規(guī)則組轉(zhuǎn)換完成的信號(hào),JEOC為注入組轉(zhuǎn)換完成的信號(hào)。這兩個(gè)信號(hào)會(huì)在狀態(tài)寄存器中置一個(gè)標(biāo)志位,我們通過讀取狀態(tài)寄存器,就可以知道轉(zhuǎn)換是否完成了。同時(shí)這兩個(gè)標(biāo)志位也可以通過配置通向NVIC申請(qǐng)中斷。
1.7 ADC基本結(jié)構(gòu)
- 左邊是輸入通道16個(gè)GPIO口外加2個(gè)內(nèi)部的通道
- AD數(shù)據(jù)寄存器:規(guī)則組只有1個(gè)數(shù)據(jù)計(jì)算器,注入組有4個(gè)
- 觸發(fā)控制:提供開始轉(zhuǎn)換START信號(hào),觸發(fā)控制可以選擇軟件觸發(fā)和硬件觸發(fā),硬件觸發(fā)主要是來(lái)自于定時(shí)器,也可以選擇外部中斷的硬件
- RCC的ADC時(shí)鐘CLOCK:推動(dòng)ADC逐次比較的過程
- 模擬看門狗:監(jiān)測(cè)轉(zhuǎn)換結(jié)果的范圍,如果超出設(shè)定的預(yù)值就通過中斷輸出控制,向NVIC申請(qǐng)中斷
1.8 輸入通道
通道 | AD1 | AD2 | AD3 |
---|---|---|---|
通道0 | PA0 | PA0 | PA0 |
通道1 | PA1 | PA1 | PA1 |
通道2 | PA2 | PA2 | PA2 |
通道3 | PA3 | PA3 | PA3 |
通道4 | PA4 | PA4 | PF6 |
通道5 | PA5 | PA5 | PF7 |
通道6 | PA6 | PA6 | PF8 |
通道7 | PA7 | PA7 | PF9 |
通道8 | PB0 | PB0 | PF10 |
通道9 | PB1 | PB1 | |
通道10 | PC0 | PC0 | PC0 |
通道11 | PC1 | PC1 | PC1 |
通道12 | PC2 | PC2 | PC2 |
通道13 | PC3 | PC3 | PC3 |
通道14 | PC4 | PC4 | |
通道15 | PC5 | PC5 | |
通道16 | 溫度傳感器 | ||
通道17 | 內(nèi)部參考電壓 |
本節(jié)課程使用的STM32F103C8T6沒有PC0到PC5的引腳,故也就不存在通道10到通道15。
1.9 ADC的四種轉(zhuǎn)換模式
單次轉(zhuǎn)換非掃描模式、連續(xù)轉(zhuǎn)換非掃描模式、單次轉(zhuǎn)換掃描模式、連續(xù)轉(zhuǎn)換掃描模式
1.9.1單次轉(zhuǎn)換非掃描模式
- 非掃描的模式下,只有第一個(gè)序列1的位置有效
- 在序列1的位置,我們可以指定想轉(zhuǎn)換的通道,比如通道2,寫到序列1的位置然后觸發(fā)轉(zhuǎn)換,ADC就會(huì)對(duì)這個(gè)通道2進(jìn)行模數(shù)轉(zhuǎn)換,轉(zhuǎn)換完成后,轉(zhuǎn)換結(jié)果放在數(shù)據(jù)計(jì)算器里,同時(shí)給EOC標(biāo)志位置1,轉(zhuǎn)換結(jié)束
- 通過判斷這個(gè)EOC標(biāo)志位是否轉(zhuǎn)換完成,若完成,就可以在數(shù)據(jù)寄存器里讀結(jié)果
- 若想再啟動(dòng)一次轉(zhuǎn)換,需要再觸發(fā)一次,轉(zhuǎn)換結(jié)束至EOC標(biāo)志位讀結(jié)果
- 若想換一個(gè)通道轉(zhuǎn)換,在轉(zhuǎn)換之前,把第一個(gè)位置的通道2改成其他通道,然后再啟動(dòng)轉(zhuǎn)換就行了
1.9.2 連續(xù)轉(zhuǎn)換非掃描模式
- 非掃描模式:菜單列表就只用第一個(gè)
- 連續(xù)轉(zhuǎn)換:在一次轉(zhuǎn)換結(jié)束后不會(huì)停止,而是立刻開始下一輪的轉(zhuǎn)換,然后一直持續(xù)下去,只需要最開始觸發(fā)一次,就可以一直轉(zhuǎn)換
- 讀取的時(shí)候不用判斷是否結(jié)束,直接從數(shù)據(jù)寄存器讀即可
1.9.3 單次轉(zhuǎn)換掃描模式
- 單次轉(zhuǎn)換:每觸發(fā)一次,轉(zhuǎn)換結(jié)束后就會(huì)停下來(lái),下次轉(zhuǎn)換需要再觸發(fā)
- 掃描模式:會(huì)用到這個(gè)菜單列表了,通道幾(相當(dāng)于菜)可以任意指定,且可以重復(fù),初始化結(jié)構(gòu)體有個(gè)參數(shù)表示通道數(shù)目(比如7個(gè)),說明只需要用多少序列
- 為了防止數(shù)據(jù)被覆蓋,用DMA及時(shí)將數(shù)據(jù)挪走,7個(gè)通道轉(zhuǎn)換完成之后產(chǎn)生EOC信號(hào),轉(zhuǎn)換結(jié)束
- 然后再觸發(fā),重新開始
1.9.4 連續(xù)轉(zhuǎn)換掃描模式
一次轉(zhuǎn)換完成后,立刻開始下一次的轉(zhuǎn)換
在掃描模式下還可有一種模式——間斷模式,作用是在掃描過程中每隔幾個(gè)轉(zhuǎn)換,就暫停一次,需要再次觸發(fā)才能繼續(xù)
1.10 細(xì)節(jié)之處
1.10.1 觸發(fā)控制
此表為規(guī)則組的觸發(fā)源,也就是上圖第2部分,有來(lái)自定時(shí)器的信號(hào)、引腳或定時(shí)器的信號(hào)(具體是引腳和定時(shí)器,需要用AFIO重映射來(lái)確定)最后的軟件控制位就是軟件觸發(fā)。這些觸發(fā)信號(hào)的選擇,可以通過上圖1設(shè)置表右邊的寄存器完成,使用庫(kù)函數(shù)的話只需一個(gè)函數(shù)給個(gè)參數(shù)即可。
1.10.2 數(shù)據(jù)對(duì)齊
數(shù)據(jù)右對(duì)齊,即作為轉(zhuǎn)換結(jié)果的12位數(shù)據(jù)向右靠,高位補(bǔ)0;
數(shù)據(jù)左對(duì)齊,即作為轉(zhuǎn)換結(jié)果的12位數(shù)據(jù)向左靠,低位補(bǔ)0。
在使用時(shí)通常使用數(shù)據(jù)右對(duì)齊,這樣在讀取時(shí)直接讀取寄存器即可。
如果選擇左對(duì)齊直接讀取,得到的數(shù)據(jù)會(huì)比實(shí)際的數(shù)據(jù)大16倍。
當(dāng)對(duì)分辨率的要求不高時(shí)(對(duì)電壓僅作大概的判斷即可)可以采用左對(duì)齊,將數(shù)據(jù)寄存器的高8位取出,就相當(dāng)于舍棄了轉(zhuǎn)換結(jié)果的4位的精度,12位的ADC退化位為8位的ADC
1.10.3 轉(zhuǎn)換時(shí)間(AD轉(zhuǎn)換很快,一般忽略)
- AD轉(zhuǎn)換的步驟: 采樣, 保持, 量化, 編碼
- STM32 ADC的總轉(zhuǎn)換時(shí)間為:
Tconv = 采樣時(shí)間 + 12.5個(gè)ADC周期
(花費(fèi)12個(gè)ADC周期進(jìn)行量化和編碼,多余的0.5個(gè)周期完成了其他的工作) -
最短的轉(zhuǎn)換時(shí)間:
例如:當(dāng)ADCCLK = 14MHz, 采樣時(shí)間為1.5個(gè)ADC周期
? Tconv = 1.5 + 12.5 = 14個(gè)ADC周期 = 1us - 采樣時(shí)間越大,越能避免一些毛刺信號(hào)的干擾,不過轉(zhuǎn)換時(shí)間也會(huì)相應(yīng)延長(zhǎng)
1.10.4 校準(zhǔn)
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)生的誤差
建議在每次上電后執(zhí)行一次校準(zhǔn)啟動(dòng)校準(zhǔn)前, ADC必須處于關(guān)電狀態(tài)超過至少兩個(gè)ADC時(shí)鐘周期由于校準(zhǔn)過程是固定的,對(duì)于使用者而言只需要在初始化后加上幾條代碼即可!
二. 硬件電路
ADC外圍電路的設(shè)計(jì)給出以下三個(gè)電路圖:
圖1:電位器產(chǎn)生一個(gè)可調(diào)的電壓的電路
中間的滑動(dòng)端可以輸出一個(gè)0~3.3伏可調(diào)的電壓輸出來(lái),上滑時(shí)電壓增大,下滑時(shí)電壓減小,若阻值太小,電阻就會(huì)比較費(fèi)電
圖2:分壓方法輸出傳感器組織的電路
- 傳感器輸出電壓的電路,例如光敏電阻、熱敏電阻、紅外接頭管、麥克風(fēng)等都可以等效為一個(gè)可變電阻
- 那電阻阻值得通過和一個(gè)固定電阻串聯(lián)分壓,當(dāng)傳感器阻值變大時(shí),下拉作用變?nèi)?,輸出端受上拉電阻的作用,電壓就?huì)升高
- 固定電阻建議選擇和傳感器阻值相近的電阻,才可以得到一個(gè)位于中間電壓區(qū)域比較好的輸出
- 此處傳感器和固定電阻的位置也調(diào)換,輸出電壓的極性就反過來(lái)了
圖3:簡(jiǎn)單的電壓轉(zhuǎn)換電路
想測(cè)一個(gè)0-5V的VIN電壓,到那時(shí)ADC只能接收0~3.3V的電壓,就可以搭建此類電路。使用電阻分壓,上面阻值17K,下面阻值33K,加一起50K,中間的電壓就是VIN/50K*33K,得到的電壓范圍就是0-3.3伏,就可以進(jìn)入ADC轉(zhuǎn)換了。想要其他范圍(如5V、10V)的VIN電壓可類似操作,如果電壓過高就不建議使用這種電路了,可能比較危險(xiǎn),高電壓采集最好使用專用芯片,比如隔離放大器等,做到高低電壓隔離保證電路安全。
三. ADC 常用庫(kù)函數(shù)
3.1 ADC的RCC時(shí)鐘配置函數(shù)
該配置函數(shù)定義存放在stm32f10x_rcc.h文件中,用來(lái)配置ADCCLK分頻器。它可以對(duì)APB2的72MHz時(shí)鐘選擇2、4、6、8分頻,輸出到ADCCLK。
1.void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
3.2 ADC設(shè)置
// 恢復(fù)ADC缺省配置
void ADC_DeInit(ADC_TypeDef* ADCx);
// ADC初始化
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
// ADC配置結(jié)構(gòu)體初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
// ADC上電工作函數(shù),即開關(guān)控制函數(shù)
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC開啟DMA輸出信號(hào)
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC中斷輸出控制函數(shù)
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
// 下面4個(gè)函數(shù)用于ADC工作前的校準(zhǔn)操作,在ADC初始化完成后依次調(diào)用即可
// ADC復(fù)位校準(zhǔn)
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
// ADC獲取復(fù)位校準(zhǔn)狀態(tài)
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
// ADC開始校準(zhǔn)
void ADC_StartCalibration(ADC_TypeDef* ADCx);
// ADC獲取開始校準(zhǔn)狀態(tài)
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
// ADC軟件觸發(fā)轉(zhuǎn)換,給CR2的SWSTART置1(開始轉(zhuǎn)換后立即自動(dòng)清0)
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC獲取軟件觸發(fā)狀態(tài),獲取CR2的SWSTART(開始轉(zhuǎn)換規(guī)則通道)位
// 不能用它判斷轉(zhuǎn)換是否結(jié)束,一般不用,了解即可
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
// ADC規(guī)則組通道配置,給轉(zhuǎn)換序列的每個(gè)位置填寫指定的通道
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
// ADC外部觸發(fā)轉(zhuǎn)換控制(是否允許外部觸發(fā)轉(zhuǎn)換)
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC獲取轉(zhuǎn)換值,獲取AD轉(zhuǎn)換的數(shù)據(jù)寄存器
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
// ADC獲取雙模式轉(zhuǎn)換值,讀取雙ADC模式下ADC的轉(zhuǎn)換結(jié)果
uint32_t ADC_GetDualModeConversionValue(void);
// ADC溫度傳感器、內(nèi)部參考電壓控制,開啟內(nèi)部的兩個(gè)轉(zhuǎn)換通道
void ADC_TempSensorVrefintCmd(FunctionalState NewState);
// 下面的函數(shù)與操作標(biāo)志位寄存器狀態(tài)有關(guān)
// ADC獲取標(biāo)志位狀態(tài),可通過獲取EOC標(biāo)志位判斷轉(zhuǎn)換是否結(jié)束
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
// 清除標(biāo)志位
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
// 獲取中斷標(biāo)志位
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
// 清除中斷掛起位
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);
3.3 配置的參數(shù)
(1)雙ADC工作模式選擇,ADC_Mode,其中分別為:
#define ADC_Mode_Independent ((uint32_t)0x00000000)//獨(dú)立模式
#define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000)//同步規(guī)則和同步注入模式
#define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000)//同步規(guī)則和交替觸發(fā)模式
#define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000)//同步注入和快速交叉模式
#define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000)//同步注入和慢速交叉模式
#define ADC_Mode_InjecSimult ((uint32_t)0x00050000)//同步注入模式
#define ADC_Mode_RegSimult ((uint32_t)0x00060000)//同步規(guī)則模式
#define ADC_Mode_FastInterl ((uint32_t)0x00070000)//快速交叉模式
#define ADC_Mode_SlowInterl ((uint32_t)0x00080000)//慢速交叉模式
#define ADC_Mode_AlterTrig ((uint32_t)0x00090000)//交替觸發(fā)模式
(2)掃描模式選擇,ADC_ScanConvMode,ENABLE表示多通道掃描模式,否則為單通道。
(3)連續(xù)轉(zhuǎn)換模式選擇,ADC_ContinuousConvMode,ENABLE表示連續(xù)轉(zhuǎn)換模式,否則為單次轉(zhuǎn)換模式。
(4)ADC轉(zhuǎn)換觸發(fā)方式選擇,ADC_ContinuousConvMode,其中ADC_ExternalTrigConv_None為不使用外部觸發(fā),即使用軟件觸發(fā)方式。
(5)ADC轉(zhuǎn)換數(shù)據(jù)對(duì)齊方式選擇,ADC_DataAlign,可以左對(duì)齊或右對(duì)齊
(6) 順序進(jìn)行規(guī)則轉(zhuǎn)換的ADC通道的數(shù)目設(shè)置,ADC_NbrOfChannel,可以設(shè)置1~16個(gè)通道
3.4 ADC間斷模式配置
// 下面兩個(gè)函數(shù)用來(lái)配置STM32中ADC的間斷模式
// 配置每隔幾個(gè)通道間斷依次
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
// 開啟間斷模式
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
四. 程序示例
4.1 ADC單通道轉(zhuǎn)換
1.AD.c(單次轉(zhuǎn)換非掃描)
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置ADCCLK分頻器,對(duì)APB2的72MHz時(shí)鐘選擇2、4、6、8分頻,輸入到ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分頻后等于72MHz/6=12MHz
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模擬輸入(ADC的專屬模式)在AIN,GPIO口無(wú)效,斷開GPIO口,防止GPIO口的輸入輸出對(duì)模擬電壓造成干擾
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//ADC規(guī)則組通道配置,給序列的每個(gè)位置填寫指定的通道,就是填寫點(diǎn)菜菜單的過程
//第一個(gè)參數(shù)是ADCx,第二個(gè)是ADC指定的通道(通道0-17)
//第三個(gè)是寫在序列幾的位置,然后第四個(gè)指定通道的采樣時(shí)間
//ADC_SampleTime_55Cycles5表示55.5個(gè)ADCCLK的周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
//ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_55Cycles5);
//ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_55Cycles5);
//通道可以重復(fù),序列不要重復(fù),需要的話可以多寫幾個(gè),這是填充菜單的方法
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//工作模式,獨(dú)立模式:ADC1和ADC2各轉(zhuǎn)各的
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右對(duì)齊
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部觸發(fā)轉(zhuǎn)換,即軟件觸發(fā)
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//單次 //ENABLE:連續(xù)轉(zhuǎn)換
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非掃描
ADC_InitStructure.ADC_NbrOfChannel = 1;//總共需要掃描多少個(gè)通道
ADC_Init(ADC1, &ADC_InitStructure);
//中斷和看門狗如果需要可以在此處定義
//ADC上電
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);//復(fù)位校準(zhǔn)
//為1時(shí),開始復(fù)位校準(zhǔn),復(fù)位校準(zhǔn)完后,該位就會(huì)由硬件自動(dòng)清0
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//等待復(fù)位校準(zhǔn)完成
ADC_StartCalibration(ADC1);//開始校準(zhǔn)
while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校準(zhǔn)完成
/*ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟件觸發(fā)(連續(xù)轉(zhuǎn)換只需要初始化一次即可,所以軟件觸發(fā)的函數(shù)可以挪到初始化函數(shù)) */
}
uint16_t AD_GetValue(void)
{
// 1. 軟件觸發(fā)開啟轉(zhuǎn)換
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟件觸發(fā)
// 2. 等待轉(zhuǎn)換完成(獲取標(biāo)志位狀態(tài),等待EOC標(biāo)志位置1)/*連續(xù)轉(zhuǎn)換非掃描:不需要判斷標(biāo)志位while這句可刪掉
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//轉(zhuǎn)換結(jié)束,EOC置1// 轉(zhuǎn)換未完成則等待(55.5T + 12.5T = 68T,結(jié)果大概為5.6us)
// 3. 讀取ADC數(shù)據(jù)寄存器并返回
return ADC_GetConversionValue(ADC1);// 讀取之后會(huì)自動(dòng)清除EOC標(biāo)志位
}
2、改為連續(xù)轉(zhuǎn)換非掃描
好處:無(wú)需不斷觸發(fā),不需要等待轉(zhuǎn)換完成
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//連續(xù)
- 連續(xù)轉(zhuǎn)換只需要初始化一次即可,所以軟件觸發(fā)的函數(shù)可以挪到初始化函數(shù)最后ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟件觸發(fā) 在初始化完成后觸發(fā)一次即可
- 且在AD_GetValue函數(shù)中,不需要判斷標(biāo)志位
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//轉(zhuǎn)換結(jié)束,EOC置1 這一句可以刪除
程序如上圖所示
3、main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
/*
電位器即滑動(dòng)變阻器,用電位器產(chǎn)生0~3.3V連續(xù)變化的模擬電壓信號(hào),然后接到STM32的PA0口上,
之后用STM32內(nèi)部的ADC讀取電壓數(shù)據(jù),顯示在屏幕上
屏幕第一行:模擬數(shù)據(jù)
屏幕第二行:處理過后顯示的電壓值
往左擰電位器,AD值減小,對(duì)應(yīng)的電壓減小,反之則反
ADC是12位的,AD結(jié)果最大值是4095,也就是2^12-1,對(duì)應(yīng)的電壓是3.3V
GPIO只能讀取高低電平 ,而ADC可以對(duì)高低電平之間的任意電壓進(jìn)行量化,最終用一個(gè)變量表示
*/
uint16_t ADValue;
float Voltage;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "ADValue:");
OLED_ShowString(2, 1, "Volatge:0.00V");
while(1)
{
ADValue = AD_GetValue();
Voltage = (float)ADValue/4095*3.3;
//數(shù)字與電壓映射關(guān)系,ADValue為uint16類型除以4095會(huì)舍棄小數(shù)部分,先強(qiáng)轉(zhuǎn)為float
OLED_ShowNum(1,9,ADValue,4);
//目前的OLED沒有顯示浮點(diǎn)數(shù)的功能,但可用顯示整數(shù)的借用
OLED_ShowNum(2,9,Voltage,1);//顯示整數(shù)部分
OLED_ShowNum(2,11,(uint16_t)(Voltage*100)%100,2);//顯示小數(shù)部分,浮點(diǎn)數(shù)不可%取余所以先強(qiáng)轉(zhuǎn)為整型
Delay_ms(100);//限制OLED刷新速度
}
}
4.2 ADC多通道轉(zhuǎn)換
1、思路
在每次觸發(fā)轉(zhuǎn)換之前,手動(dòng)更改一下列表第一個(gè)位置的通道
比如第一次轉(zhuǎn)換,在序列1先寫入通道0,之后觸發(fā)、等待、讀值
第二次轉(zhuǎn)換,在序列1把通道0改成通道1,之后觸發(fā)、等待、讀值
第三次轉(zhuǎn)換,在序列1改成通道2等等
2、AD.c文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-756942.html
#include "stm32f10x.h" // Device header
//(單次轉(zhuǎn)換非掃描)
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置ADCCLK分頻器,對(duì)APB2的72MHz時(shí)鐘選擇2、4、6、8分頻,輸入到ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分頻后等于72MHz/6=12MHz
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模擬輸入(ADC的專屬模式)在AIN,GPIO口無(wú)效,斷開GPIO口,防止GPIO口的輸入輸出對(duì)模擬電壓造成干擾
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//工作模式,獨(dú)立模式:ADC1和ADC2各轉(zhuǎn)各的
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右對(duì)齊
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部觸發(fā)轉(zhuǎn)換,即軟件觸發(fā)
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//單次 //ENABLE:連續(xù)轉(zhuǎn)換
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非掃描
ADC_InitStructure.ADC_NbrOfChannel = 1;//總共需要掃描多少個(gè)通道
ADC_Init(ADC1, &ADC_InitStructure);
//中斷和看門狗如果需要可以在此處定義
//ADC上電
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);//復(fù)位校準(zhǔn)
//為1時(shí),開始復(fù)位校準(zhǔn),復(fù)位校準(zhǔn)完后,該位就會(huì)由硬件自動(dòng)清0
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//等待復(fù)位校準(zhǔn)完成
ADC_StartCalibration(ADC1);//開始校準(zhǔn)
while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校準(zhǔn)完成
/*ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟件觸發(fā)(連續(xù)轉(zhuǎn)換只需要初始化一次即可,所以軟件觸發(fā)的函數(shù)可以挪到初始化函數(shù)) */
}
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1,ADC_Channel, 1, ADC_SampleTime_55Cycles5);
// 把通道作為參數(shù)填入序列1中,通道的采樣周期是55.5個(gè)ADCCLK的周期
// 1. 軟件觸發(fā)開啟轉(zhuǎn)換
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟件觸發(fā)
// 2. 等待轉(zhuǎn)換完成(獲取標(biāo)志位狀態(tài),等待EOC標(biāo)志位置1)/*連續(xù)轉(zhuǎn)換非掃描:不需要判斷標(biāo)志位while這句可刪掉
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//轉(zhuǎn)換結(jié)束,EOC置1// 轉(zhuǎn)換未完成則等待(55.5T + 12.5T = 68T,結(jié)果大概為5.6us)
// 3. 讀取ADC數(shù)據(jù)寄存器并返回
return ADC_GetConversionValue(ADC1);// 讀取之后會(huì)自動(dòng)清除EOC標(biāo)志位
}
3、main.c文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-756942.html
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
/*
DO是數(shù)字輸出
AO是模擬量輸出
*/
uint16_t AD0,AD1,AD2,AD3;//表示四個(gè)ADC輸入通道的轉(zhuǎn)換結(jié)果的接收變量
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
while(1)
{
AD0 = AD_GetValue(ADC_Channel_0);
AD1 = AD_GetValue(ADC_Channel_1);
//根據(jù)需要也可設(shè)置映射關(guān)系,再進(jìn)行顯示
OLED_ShowNum(1,5,AD0,4);
OLED_ShowNum(2,5,AD1,4);
Delay_ms(100);//限制OLED刷新速度
}
}
到了這里,關(guān)于STM32筆記(1)———ADC模數(shù)轉(zhuǎn)換器原理及單、雙通道轉(zhuǎn)換的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!