ADC簡介
STM32F103系列有3個ADC,精度為12位,每個ADC最多有16個外部通道。其中ADC1和ADC2都有16個外部通道,ADC3一般有8個外部通道,各通道的A/D轉(zhuǎn)換可以單次、連續(xù)、掃描或間斷執(zhí)行,ADC轉(zhuǎn)換的結(jié)果可以左對齊或右對齊儲存在16位數(shù)據(jù)寄存器中。ADC的輸入時鐘不得超過14MHz,其時鐘頻率由PCLK2分頻產(chǎn)生。
ADC功能框圖講解
學(xué)習(xí)STM32開發(fā)板上的外設(shè)時首先要了解其外設(shè)的功能框圖,如下:
功能框圖可以大體分為7部分,下面一一講解:
電壓輸入范圍
ADC所能測量的電壓范圍就是VREF- ≤ VIN ≤ VREF+,把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的輸入電壓范圍為:0~3.3V。
輸入通道
ADC的信號輸入就是通過通道來實現(xiàn)的,信號通過通道輸入到單片機(jī)中,單片機(jī)經(jīng)過轉(zhuǎn)換后,將模擬信號輸出為數(shù)字信號。STM32中的ADC有著18個通道,其中外部的16個通道已經(jīng)在框圖中標(biāo)出,如下:
這16個通道對應(yīng)著不同的IO口,此外ADC1/2/3 還有內(nèi)部通道:ADC1 的通道 16 連接到了芯片內(nèi)部的溫度傳感器, Vrefint 連接到了通道 17。ADC2 的模擬通道 16 和 17 連接到了內(nèi)部的 VSS。
ADC的全部通道如下圖所示:
外部的16個通道在轉(zhuǎn)換時又分為規(guī)則通道和注入通道,其中規(guī)則通道最多有16路,注入通道最多有4路(注入通道貌似使用不多),下面簡單介紹一下兩種通道:
規(guī)則通道顧名思義就是,最平常的通道、也是最常用的通道,平時的ADC轉(zhuǎn)換都是用規(guī)則通道實現(xiàn)的。
注入通道是相對于規(guī)則通道的,注入通道可以在規(guī)則通道轉(zhuǎn)換時,強(qiáng)行插入轉(zhuǎn)換,相當(dāng)于一個“中斷通道”吧。當(dāng)有注入通道需要轉(zhuǎn)換時,規(guī)則通道的轉(zhuǎn)換會停止,優(yōu)先執(zhí)行注入通道的轉(zhuǎn)換,當(dāng)注入通道的轉(zhuǎn)換執(zhí)行完畢后,再回到之前規(guī)則通道進(jìn)行轉(zhuǎn)換。
轉(zhuǎn)換順序
知道了ADC的轉(zhuǎn)換通道后,如果ADC只使用一個通道來轉(zhuǎn)換,那就很簡單,但如果是使用多個通道進(jìn)行轉(zhuǎn)換就涉及到一個先后順序了,畢竟規(guī)則轉(zhuǎn)換通道只有一個數(shù)據(jù)寄存器。多個通道的使用順序分為倆種情況:規(guī)則通道的轉(zhuǎn)換順序和注入通道的轉(zhuǎn)換順序。
規(guī)則通道中的轉(zhuǎn)換順序由三個寄存器控制:SQR1、SQR2、SQR3,它們都是32位寄存器。SQR寄存器控制著轉(zhuǎn)換通道的數(shù)目和轉(zhuǎn)換順序,只要在對應(yīng)的寄存器位SQx中寫入相應(yīng)的通道,這個通道就是第x個轉(zhuǎn)換。具體的對應(yīng)關(guān)系如下:
通過SQR1寄存器就能了解其轉(zhuǎn)換順序在寄存器上的實現(xiàn)了:
和規(guī)則通道轉(zhuǎn)換順序的控制一樣,注入通道的轉(zhuǎn)換也是通過注入寄存器來控制,只不過只有一個JSQR寄存器來控制,控制關(guān)系如下:
需要注意的是,只有當(dāng)JL=4的時候,注入通道的轉(zhuǎn)換順序才會按照J(rèn)SQ1、JSQ2、JSQ3、JSQ4的順序執(zhí)行。當(dāng)JL<4時,注入通道的轉(zhuǎn)換順序恰恰相反,也就是執(zhí)行順序為:JSQ4、JSQ3、JSQ2、JSQ1。
配置轉(zhuǎn)換順序的函數(shù)如下代碼所示:
/** * @brief Configures for the selected ADC regular channel its corresponding * rank in the sequencer and its sample time. * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral. * @param ADC_Channel: the ADC channel to configure. * This parameter can be one of the following values: * @arg ADC_Channel_0: ADC Channel0 selected * @arg ADC_Channel_1: ADC Channel1 selected * @arg ADC_Channel_2: ADC Channel2 selected * @arg ADC_Channel_3: ADC Channel3 selected * @arg ADC_Channel_4: ADC Channel4 selected * @arg ADC_Channel_5: ADC Channel5 selected * @arg ADC_Channel_6: ADC Channel6 selected * @arg ADC_Channel_7: ADC Channel7 selected * @arg ADC_Channel_8: ADC Channel8 selected * @arg ADC_Channel_9: ADC Channel9 selected * @arg ADC_Channel_10: ADC Channel10 selected * @arg ADC_Channel_11: ADC Channel11 selected * @arg ADC_Channel_12: ADC Channel12 selected * @arg ADC_Channel_13: ADC Channel13 selected * @arg ADC_Channel_14: ADC Channel14 selected * @arg ADC_Channel_15: ADC Channel15 selected * @arg ADC_Channel_16: ADC Channel16 selected * @arg ADC_Channel_17: ADC Channel17 selected * @param Rank: The rank in the regular group sequencer. This parameter must be between 1 to 16. * @param ADC_SampleTime: The sample time value to be set for the selected channel. * This parameter can be one of the following values: * @arg ADC_SampleTime_1Cycles5: Sample time equal to 1.5 cycles * @arg ADC_SampleTime_7Cycles5: Sample time equal to 7.5 cycles * @arg ADC_SampleTime_13Cycles5: Sample time equal to 13.5 cycles * @arg ADC_SampleTime_28Cycles5: Sample time equal to 28.5 cycles * @arg ADC_SampleTime_41Cycles5: Sample time equal to 41.5 cycles * @arg ADC_SampleTime_55Cycles5: Sample time equal to 55.5 cycles * @arg ADC_SampleTime_71Cycles5: Sample time equal to 71.5 cycles * @arg ADC_SampleTime_239Cycles5: Sample time equal to 239.5 cycles * @retval None */ void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime) { 函數(shù)內(nèi)容略; }
觸發(fā)源
ADC轉(zhuǎn)換的輸入、通道、轉(zhuǎn)換順序都已經(jīng)說明了,但ADC轉(zhuǎn)換是怎么觸發(fā)的呢?就像通信協(xié)議一樣,都要規(guī)定一個起始信號才能傳輸信息,ADC也需要一個觸發(fā)信號來實行模/數(shù)轉(zhuǎn)換。
其一就是通過直接配置寄存器觸發(fā),通過配置控制寄存器CR2的ADON位,寫1時開始轉(zhuǎn)換,寫0時停止轉(zhuǎn)換。在程序運行過程中只要調(diào)用庫函數(shù),將CR2寄存器的ADON位置1就可以進(jìn)行轉(zhuǎn)換,比較好理解。
另外,還可以通過內(nèi)部定時器或者外部IO觸發(fā)轉(zhuǎn)換,也就是說可以利用內(nèi)部時鐘讓ADC進(jìn)行周期性的轉(zhuǎn)換,也可以利用外部IO使ADC在需要時轉(zhuǎn)換,具體的觸發(fā)由控制寄存器CR2決定。
在參考手冊中可以找到,ADC_CR2寄存器的詳情如下:
轉(zhuǎn)換時間
還有一點,就是轉(zhuǎn)換時間的問題,ADC的每一次信號轉(zhuǎn)換都要時間,這個時間就是轉(zhuǎn)換時間,轉(zhuǎn)換時間由輸入時鐘和采樣周期來決定。
由于ADC在STM32中是掛載在APB2總線上的,所以ADC的時鐘是由PCLK2(72MHz)經(jīng)過分頻得到的,分頻因子由 RCC 時鐘配置寄存器RCC_CFGR 的位 15:14 ADCPRE[1:0]設(shè)置,可以是 2/4/6/8 分頻,一般配置分頻因子為8,即8分頻得到ADC的輸入時鐘頻率為9MHz。
采樣周期是確立在輸入時鐘上的,配置采樣周期可以確定使用多少個ADC時鐘周期來對電壓進(jìn)行采樣,采樣的周期數(shù)可通過 ADC采樣時間寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位設(shè)置,ADC_SMPR2 控制的是通道 0~9, ADC_SMPR1 控制的是通道 10~17。每個通道可以配置不同的采樣周期,但最小的采樣周期是1.5個周期,也就是說如果想最快時間采樣就設(shè)置采樣周期為1.5.
轉(zhuǎn)換時間=采樣時間+12.5個周期
12.5個周期是固定的,一般我們設(shè)置 PCLK2=72M,經(jīng)過 ADC 預(yù)分頻器能分頻到最大的時鐘只能是 12M,采樣周期設(shè)置為 1.5 個周期,算出最短的轉(zhuǎn)換時間為 1.17us。
數(shù)據(jù)寄存器
轉(zhuǎn)換完成后的數(shù)據(jù)就存放在數(shù)據(jù)寄存器中,但數(shù)據(jù)的存放也分為規(guī)則通道轉(zhuǎn)換數(shù)據(jù)和注入通道轉(zhuǎn)換數(shù)據(jù)的。
規(guī)則數(shù)據(jù)寄存器負(fù)責(zé)存放規(guī)則通道轉(zhuǎn)換的數(shù)據(jù),通過32位寄存器ADC_DR來存放:
當(dāng)使用ADC獨立模式(也就是只使用一個ADC,可以使用多個通道)時,數(shù)據(jù)存放在低16位中,當(dāng)使用ADC多模式時高16位存放ADC2的數(shù)據(jù)。需要注意的是ADC轉(zhuǎn)換的精度是12位,而寄存器中有16個位來存放數(shù)據(jù),所以要規(guī)定數(shù)據(jù)存放是左對齊還是右對齊。
當(dāng)使用多個通道轉(zhuǎn)換數(shù)據(jù)時,會產(chǎn)生多個轉(zhuǎn)換數(shù)據(jù),然鵝數(shù)據(jù)寄存器只有一個,多個數(shù)據(jù)存放在一個寄存器中會覆蓋數(shù)據(jù)導(dǎo)致ADC轉(zhuǎn)換錯誤,所以我們經(jīng)常在一個通道轉(zhuǎn)換完成之后就立刻將數(shù)據(jù)取出來,方便下一個數(shù)據(jù)存放。一般開啟DMA模式將轉(zhuǎn)換的數(shù)據(jù),傳輸在一個數(shù)組中,程序?qū)?shù)組讀操作就可以得到轉(zhuǎn)換的結(jié)果。
DMA的使用之前介紹過:DMA介紹。
注入通道轉(zhuǎn)換的數(shù)據(jù)寄存器有4個,由于注入通道最多有4個,所以注入通道轉(zhuǎn)換的數(shù)據(jù)都有固定的存放位置,不會跟規(guī)則寄存器那樣產(chǎn)生數(shù)據(jù)覆蓋的問題。ADC_JDRx 是 32 位的,低 16 位有效,高 16 位保留,數(shù)據(jù)同樣分為左對齊和右對齊,具體是以哪一種方式存放,由ADC_CR2 的 11 位 ALIGN 設(shè)置。
中斷
????
從框圖中可以知道數(shù)據(jù)轉(zhuǎn)換完成之后可以產(chǎn)生中斷,有三種情況:
規(guī)則通道數(shù)據(jù)轉(zhuǎn)換完成之后,可以產(chǎn)生一個中斷,可以在中斷函數(shù)中讀取規(guī)則數(shù)據(jù)寄存器的值。這也是單通道時讀取數(shù)據(jù)的一種方法。
注入通道數(shù)據(jù)轉(zhuǎn)換完成之后,可以產(chǎn)生一個中斷,并且也可以在中斷中讀取注入數(shù)據(jù)寄存器的值,達(dá)到讀取數(shù)據(jù)的作用。
當(dāng)輸入的模擬量(電壓)不再閾值范圍內(nèi)就會產(chǎn)生看門狗事件,就是用來監(jiān)視輸入的模擬量是否正常。
以上中斷的配置都由ADC_SR寄存器決定:
當(dāng)然,在轉(zhuǎn)換完成之后也可以產(chǎn)生DMA請求,從而將轉(zhuǎn)換好的數(shù)據(jù)從數(shù)據(jù)寄存器中讀取到內(nèi)存中。
電壓轉(zhuǎn)換
要知道,轉(zhuǎn)換后的數(shù)據(jù)是一個12位的二進(jìn)制數(shù),我們需要把這個二進(jìn)制數(shù)代表的模擬量(電壓)用數(shù)字表示出來。比如測量的電壓范圍是0~3.3V,轉(zhuǎn)換后的二進(jìn)制數(shù)是x,因為12位ADC在轉(zhuǎn)換時將電壓的范圍大小(也就是3.3)分為4096(2^12)份,所以轉(zhuǎn)換后的二進(jìn)制數(shù)x代表的真實電壓的計算方法就是:
y=3.3* x / 4096
初始化結(jié)構(gòu)體
每個外設(shè)的核心就是其對應(yīng)的初始化結(jié)構(gòu)體了,ADC的初始化結(jié)構(gòu)體代碼如下:
typedef struct { uint32_t ADC_Mode; // ADC 工作模式選擇 FunctionalState ADC_ScanConvMode; // ADC 掃描(多通道)或者單次(單通道)模式選擇 FunctionalState ADC_ContinuousConvMode; // ADC 單次轉(zhuǎn)換或者連續(xù)轉(zhuǎn)換選擇 uint32_t ADC_ExternalTrigConv; // ADC 轉(zhuǎn)換觸發(fā)信號選擇 uint32_t ADC_DataAlign; // ADC 數(shù)據(jù)寄存器對齊格式 uint8_t ADC_NbrOfChannel; // ADC 采集通道數(shù) } ADC_InitTypeDef;
通過配置初始化結(jié)構(gòu)體來設(shè)置ADC的相關(guān)信息。
單通道電壓采集
用這個程序來簡單熟練一下ADC的單通道電壓采集吧,程序使用了ADC1的通道11,對應(yīng)的IO口是PC^1,因為博主的開發(fā)板上PC ^1引腳沒有任何復(fù)用,使用中斷,在中斷中讀取轉(zhuǎn)換的電壓。
頭文件
為了提高文件的可移植性,頭文件中定義了一些與ADC和中斷相關(guān)的量,在移植程序的時候只需要修改頭文件中的定義即可。
#ifndef __ADC_H #define__ADC_H #include"stm32f10x.h" /* 采用ADC1的通道11 引腳為PC^1 模式必須是模擬輸入*/ #define ADC_GPIO_RCC RCC_APB2Periph_GPIOC #define ADC_GPIO_PORT GPIOC #define ADC_GPIO_PIN GPIO_Pin_1 #defineADC_GPIO_MODEGPIO_Mode_AIN /* 配置與中斷有關(guān)的信息 */ #define ADC_IRQn ADC1_2_IRQn #defineADC_RCCRCC_APB2Periph_ADC1 /* 配置ADC初始化結(jié)構(gòu)體的宏定義 */ #define ADCx ADC1 #define ADCx_ContinuousConvMode ENABLE //連續(xù)轉(zhuǎn)換模式 #define ADCx_DataAlign ADC_DataAlign_Right //轉(zhuǎn)換結(jié)果右對齊 #define ADCx_ExternalTrigConv ADC_ExternalTrigConv_None //不使用外部觸發(fā)轉(zhuǎn)換,采用軟件觸發(fā) #define ADCx_Mode ADC_Mode_Independent //只使用一個ADC,獨立模式 #define ADCx_NbrOfChannel 1 //一個轉(zhuǎn)換通道 #defineADCx_ScanConvModeDISABLE//禁止掃描模式,多通道時使用 /* 通道信息和采樣周期 */ #define ADC_Channel ADC_Channel_11 #defineADC_SampleTimeADC_SampleTime_55Cycles5 /* 函數(shù)聲明 */ void ADC_COnfig(void); void ADC_NVIC_Config(void); void ADC_GPIO_Config(void); voidADCx_Init(void); #endif /* __ADC_H */
引腳配置函數(shù)
首先配置相應(yīng)的GPIO引腳,畢竟模擬信號是通過GPIO引腳傳輸?shù)介_發(fā)板的,注意的是,引腳的模式一定要是模擬輸入!
void ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(ADC_GPIO_RCC,ENABLE); GPIO_InitStruct.GPIO_Pin = ADC_GPIO_PIN ; GPIO_InitStruct.GPIO_Mode=ADC_GPIO_MODE; GPIO_Init(ADC_GPIO_PORT , &GPIO_InitStruct); }
配置引腳就是老套路:聲明結(jié)構(gòu)體變量、開啟時鐘、寫入結(jié)構(gòu)體、初始化GPIO。
NVIC配置函數(shù)
因為我們是在轉(zhuǎn)換完成后利用中斷,在中斷函數(shù)中讀取數(shù)據(jù),所以要首先配置中斷函數(shù)的優(yōu)先級,因為程序中只有這一個中斷,所以優(yōu)先級的配置就比較隨意。
void ADC_NVIC_Config(void) { NVIC_InitTypeDefNVIC_InitStruct; /* 配置中斷優(yōu)先級分組(設(shè)置搶占優(yōu)先級和子優(yōu)先級的分配),在函數(shù)在misc.c */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* 配置初始化結(jié)構(gòu)體 在misc.h中 */ /* 配置中斷源 在stm32f10x.h中 */ NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn ; /* 配置搶占優(yōu)先級 */ NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ; /* 配置子優(yōu)先級 */ NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ; /* 使能中斷通道 */ NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ; /* 調(diào)用初始化函數(shù) */ NVIC_Init(&NVIC_InitStruct) ; }
ADC配置函數(shù)
ADC的配置函數(shù)是ADC的精髓,在這個函數(shù)中包含的內(nèi)容有:ADC的初始化結(jié)構(gòu)體配置、配置了時鐘分頻、配置了通道轉(zhuǎn)換順序、打開轉(zhuǎn)換中斷、進(jìn)行校準(zhǔn)、軟件觸發(fā)ADC采集等。
函數(shù)中都有詳細(xì)的注釋:
void ADC_COnfig(void) { ADC_InitTypeDef ADC_InitStruct; RCC_APB2PeriphClockCmd(ADC_RCC,ENABLE); /* 配置初始化結(jié)構(gòu)體,詳情見頭文件 */ ADC_InitStruct.ADC_ContinuousConvMode = ADCx_ContinuousConvMode ; ADC_InitStruct.ADC_DataAlign = ADCx_DataAlign ; ADC_InitStruct.ADC_ExternalTrigConv = ADCx_ExternalTrigConv ; ADC_InitStruct.ADC_Mode = ADCx_Mode ; ADC_InitStruct.ADC_NbrOfChannel = ADCx_NbrOfChannel ; ADC_InitStruct.ADC_ScanConvMode=ADCx_ScanConvMode; ADC_Init(ADCx,&ADC_InitStruct); /* 配置ADC時鐘為8分頻,即9M */ RCC_ADCCLKConfig(RCC_PCLK2_Div8); /* 配置ADC通道轉(zhuǎn)換順序和時間 */ ADC_RegularChannelConfig(ADCx, ADC_Channel, 1, ADC_SampleTime ); /* 配置為轉(zhuǎn)換結(jié)束后產(chǎn)生中斷 在中斷中讀取信息 */ ADC_ITConfig(ADCx, ADC_IT_EOC,ENABLE); /* 開啟ADC,進(jìn)行轉(zhuǎn)換 */ ADC_Cmd(ADCx, ENABLE ); /* 重置ADC校準(zhǔn) */ ADC_ResetCalibration(ADCx); /* 等待初始化完成 */ while(ADC_GetResetCalibrationStatus( ADCx)) /* 開始校準(zhǔn) */ ADC_StartCalibration(ADCx); /* 等待校準(zhǔn)完成 */ while (ADC_GetCalibrationStatus(ADCx)); /* 軟件觸發(fā)ADC轉(zhuǎn)換 */ ADC_SoftwareStartConvCmd(ADCx,ENABLE); }
中斷函數(shù)
在中斷函數(shù)中進(jìn)行讀取數(shù)據(jù),將數(shù)據(jù)存放在變量result中,此處使用關(guān)鍵字extern聲明,代表變量result已經(jīng)在其他文件中定義,關(guān)于extern的介紹在之前發(fā)的文章中有extern關(guān)鍵字的介紹。
externuint16_tresurt; void ADC1_2_IRQHandler(void) { /* 判斷產(chǎn)生中斷請求 */ while(ADC_GetITStatus(ADCx, ADC_IT_EOC) == SET) resurt=ADC_GetConversionValue(ADCx); /* 清除中斷標(biāo)志 */ ADC_ClearITPendingBit(ADCx, ADC_IT_EOC); }
主函數(shù)
主函數(shù)負(fù)責(zé)接收轉(zhuǎn)換的值,并將其轉(zhuǎn)換為電壓值,然后通過串口打印在計算機(jī)上,便于調(diào)試。
變量result是主函數(shù)中的全局變量,注意最后的結(jié)果應(yīng)該轉(zhuǎn)換為浮點型。
#include "stm32f10x.h" #include "usart.h" #include"adc.h" uint16_tresult; void delay(void) { uint16_t k=0xffff; while(k--); } int main(void) { float voltage; /* 串口調(diào)試函數(shù) */ DEBUG_USART_Config(); /* 與ADC相關(guān)的函數(shù)打包在此函數(shù)中 */ ADCx_Init(); while(1) { /* 強(qiáng)制轉(zhuǎn)換為浮點型 */ voltage = (float) result/4096*3.3; printf(" 電壓值為:%f ",voltage); delay(); } }
審核編輯:湯梓紅
-
adc
+關(guān)注
關(guān)注
98文章
6438瀏覽量
544112 -
STM32
+關(guān)注
關(guān)注
2266文章
10873瀏覽量
354840 -
時鐘
+關(guān)注
關(guān)注
10文章
1720瀏覽量
131366 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
4949瀏覽量
97202 -
STM32F103
+關(guān)注
關(guān)注
33文章
476瀏覽量
63483
原文標(biāo)題:詳解STM32中的ADC
文章出處:【微信號:ARM與嵌入式,微信公眾號:ARM與嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論