
圖21-1(截自stm32f103參考手冊)
圖21-1為ADC的功能框圖,下面將圍繞這個框圖進行解析。
電壓輸入范圍

圖21-2(截自stm32f103參考手冊)
ADC一般用於采集小電壓,其輸入值不能超過 ,即:
。相關的定義見圖21-2。一般把
和
接地,
和
接3V3,那么ADC的輸入范圍是0~3.3V。
如果想采集到超過閾值的模擬電壓,則可改善電路,使其輸入ADC的電壓在有效范圍內即可。
輸入通道
電壓經過輸入通道進入ADC。stm32的ADC共有18個通道,其中16個是外部通道,ADCx_IN0~ADCx_IN15。還有兩個內部通道。相關的引腳定義和描述可在開發板的數據手冊里找。
而外部的16個通道又分為規則通道(最多16路)和注入通道(最多4路)。一般情況下我們使用規則通道,而注入通道需要在規則通道轉換過程中強行插入的,一旦插入后得先等注入通道轉換完成再繼續規則通道的轉換。類似於中斷服務。
轉換順序
三個規則序列寄存器ADC_SQR1、ADC_SQR2、ADC_SQR3,分別定義着第13~16、第7~12、第1~6個轉換,哪個通道想要哪個轉換,即可賦值對應通道給相應的轉換位。例如,通道0想要第9個轉換,則在SQ9[4:0]寫0即可。而具體的轉換數目則由ADC_SQR1 的L[3:0]決定,最多16個轉換。
而注入序列寄存器只有ADC_JSQR一個,最多支持4個通道,具體轉換數目由JL[1:0]位決定。需要注意的是,如果JL位的值小於4(不含4),則轉換順序剛好相反,即第一次轉換的是JSQx[4:0](x=4-JL),而不是JSQ1[4:0]。
觸發源
除了可以用ADC_CR2寄存器的ADON位控制轉換的開始與停止,還可以支持觸發轉換。包括內部定時器觸發和外部IO觸發。具體的觸發源由ADC_CR2的EXTSEL[2:0]位(規則通道觸發源)和JEXTSEL[2:0]位(注入通道觸發源)控制。
轉換時間
ADC的輸入時鍾ADCCLK由PCLK2分頻產生,分頻因子由RCC_CFGR的ADCPRE[1:0]配置,可配置2/4/6/8分頻,分頻后的ADCCLK最大應不超過14MHz。
在進行輸入電壓的采樣時,可以配置ADC_SMPR1(通道0~9)、ADC_SMPR2(通道10~17)寄存器的SMP[2:0]位,來控制采樣周期。每個通道可以配置不同的采樣周期,最小周期是1.5個。那么,ADC的轉換時間為(參考手冊里有公式表述):
T = 采樣時間 + 12.5個周期,其中1周期為1/ADCCLK
例如,ADCCLK分頻后為12MHz(PCLK2為72MHz,6分頻),采樣時間為1.5個周期,則T=14個周期=1.17us。
ADC_DR和ADC_JDRx
ADC_DR和ADC_JDRx分別是規則數據寄存器和注入數據寄存器。ADC_DR只有一個,有32位,低16位在單ADC時使用,高16位在ADC1中雙模式下保存ADC2轉換的規則數據(雙模式就是ADC1和ADC2同時使用)。
規則通道多達16個,而ADC_DR只有一個,在多通道轉換的情況下,就需要將前一個時間點轉換的數據快速移出,否則會被下一個時間點轉換的數據覆蓋。這里用到DMA模式,可將數據傳輸到內存。如圖21-3是ADC_DR的寄存器描述。

圖21-3
注入通道最多只有4個,剛好ADC_JDRx也有4個,每個通道都有對應的寄存器,不會產生數據覆蓋問題。如圖21-4為ADC_JDRx的寄存器描述。

圖21-4
由於ADC精度為12位,所以無論在低16位或高16位,都無法完全放滿,這時由ADC_CR2的ALIGN位配置轉換結果的左對齊或右對齊。
中斷
分為三種:規則通道轉換結束中斷、注入通道轉換結束中斷和模擬看門狗中斷。前兩種轉換結束中斷就是普通的根據中斷標志位和使能位判斷執行。這里說明下模擬看門狗中斷。
使能模擬看門狗中斷后,當被ADC轉換的模擬電壓值低於低閾值或高於高閾值時,便會產生中斷。閾值的高低值由ADC_LTR和ADC_HTR配置。
DMA請求
規則和注入通道轉換結束后會產生DMA請求,用於將轉換好的數據傳輸到內存。需要注意的是,只有ADC1和ADC3可以產生DMA請求。(有關DMA請求可移步stm32:DMA數據傳輸)
電壓轉換
模擬電壓轉換后是一個12位的數字值,坦誠地講,這個值人類是看不懂的,所以需要再次轉換成可讀性較好的電壓值,也就是用萬用表量到的電壓值。
一般情況下,ADC的輸入電壓范圍在0~3.3v,所以12位滿量程對應的電壓值為3.3v,數字值為2^12。我們假設ADC轉換后的12位的值為x,其對應的電壓值為y,那么,
,即
![]()
所求得的y值便是我們需要的電壓值,也就是用萬用表測得的值。
ADC_InitTypeDef
至此,ADC的基礎部分大概清楚了,可以開始進行程序的說明了。先講講ADC_InitTypeDef結構體。

圖21-5
圖21-5的結構體截圖自stm32f10x_adc.h文件,下面對其結構體成員逐一分析。
- ADC_Mode:ADC模式,使用一個ADC為獨立模式,使用兩個ADC為雙模式等等。由ADC_CR1寄存器的DUALMOD[3:0]位配置;
- ADC_ScanConvMode:配置是否使能掃描。如果是單通道AD轉換則DISABLE,如果是多通道AD轉換則ENABLE。具體由ADC_CR1寄存器的SCAN位配置;
- ADC_ContinuousConvMode:配置是否自動連續轉換。ENABLE為使能自動連續轉換,DISABLE為單次轉換(轉換一次后需要手動控制才能重新啟動轉換)。具體由ADC_CR2寄存器的CONT位配置;
- ADC_ExternalTrigConv:外部觸發轉換,圖21-1列出了很多外部觸發條件,可根據項目需求配置觸發來源。不過我們一般使用軟件觸發;
- ADC_DataAlign:轉換結果數據對齊模式,可選ADC_DataAlign_Right和ADC_DataAlign_Left,我們一般選擇右對齊模式;
- ADC_NbrOfChannel:AD轉換通道數量,根據項目實際配置即可。
獨立模式單通道采集實驗
這個實驗被用來實現電位器(滑動變阻器)電壓的采集,通過串口將采集到的電壓值打印到串口調試助手。這里使用AD轉換完成中斷,在中斷服務函數中讀取數據,不使用DMA傳輸,在多通道采集時才使用DMA傳輸。
ADC的GPIO配置
使能ADC外設的GPIO時鍾,將ADC的引腳配置為模擬輸入模式。
查閱數據手冊的引腳定義說明(Table 5. High-density STM32F103xx pin definitions),我們可以選擇PC1引腳,該引腳支持ADC1/2/3_IN11,后面的ADC配置程序會用到,如圖21-6。關於外設引腳模式的配置可查閱參考手冊的8.1.11外設的GPIO配置章節,如圖21-7。

圖21-6

圖21-7
ADC配置
其實就是配置ADC_InitTypeDef結構體的成員,配置成項目需要的。直接貼出代碼,比較直觀。
/** * @brief 配置ADC工作模式 * @param 無 * @retval 無 */ static void ADC_Mode_Config(void) { ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 獨立模式 ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 這是單通道實驗,不使用掃描 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 連續轉換 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 不使用外部觸發,使用軟件觸發 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 轉化結果右對齊 ADC_InitStructure.ADC_NbrOfChannel = 1; // 通道數量,這是單通道,所以為1 ADC_Init(ADC1, &ADC_InitStructure); RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC時鍾為8分頻,即9MHz // 轉換通道、轉換順序、采樣時間(這里是55.5個周期) ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5); ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); // 轉換結束中斷 ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); // 初始化ADC校准寄存器 while(ADC_GetResetCalibrationStatus(ADC1)); // 等待校准寄存器初始化完成 ADC_StartCalibration(ADC1); // ADC開始校准 while(ADC_GetCalibrationStatus(ADC1)); // 等待校准完成 ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 軟件觸發ADC轉換 }
相關的分析都在程序注釋里了,很容易讀懂。這里只說明一個關鍵點。ADC_RegularChannelConfig()函數用來配置ADC的轉換順序和采樣時間等,關於采樣時間的周期選擇,周期越長,采樣精度越高,反之則反。
中斷配置
關於中斷的配置,在之前的文章也介紹了,可移步閱讀。這里直接貼出配置代碼。
/** * @brief ADC中斷配置 * @param 無 * @retval 無 */ static void ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn; // ADC中斷源 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 搶占優先級為1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子優先級為1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
中斷服務函數
中斷函數一般定義在stm32f10x_it.c文件里,進入中斷服務函數后,在函數內直接讀取ADC轉換結果,並保存在ADC_ConvertedValue變量里,該變量是在main.c文件里定義的。
extern __IO uint16_t ADC_ConvertedValue; // 該變量在main.c文件定義,所以需添加extern關鍵字 void ADC_IRQHandler(void) { if (ADC_GetITStatus(ADC1, ADC_IT_EOC) == SET) { ADC_ConvertedValue = ADC_GetConversionValue(ADC1); // 讀取ADC的轉換值 } ADC_ClearITPendingBit(ADC1, ADC_IT_EOC); }
ADC_GetConversionValue()庫函數直接讀取ADC_DR寄存器的值。
最后在main()函數里執行電壓轉換的公式即可得出我們需要的電壓值了。
ADC_ConvertedValueLocal = (float)ADC_ConvertedValue / 4096 * 3.3;
那么ADC_ConvertedValueLocal 即為我們需要的值了,可以和萬用表測量到的值做對比。