標籤:

ADC模數轉換(一)——獨立模式單通道電壓採集實驗

ADC即模數轉換器,一般可用於採集電壓,將電壓的模擬信號轉換為數字信號,經過計算以獲得肉眼可讀的電壓值。

圖21-1(截自stm32f103參考手冊)

圖21-1為ADC的功能框圖,下面將圍繞這個框圖進行解析。

電壓輸入範圍

圖21-2(截自stm32f103參考手冊)

ADC一般用於採集小電壓,其輸入值不能超過 V_{DDA} ,即:V_{REF-} le Vin le V_{REFf+} 。相關的定義見圖21-2。一般把 V_{SSA}V_{REF-} 接地,V_{DDA}V_{REF+} 接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,那麼,

2^{12} / 3.3 = x / y ,即 y = (3.3*x) / 2^{12}

所求得的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結構體的成員,配置成項目需要的。直接貼出代碼,比較直觀。

/**n * @brief 配置ADC工作模式n * @param 無n * @retval 無n */nstatic void ADC_Mode_Config(void)n{n ADC_InitTypeDef ADC_InitStructure;tnn RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);nn ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 獨立模式n ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 這是單通道實驗,不使用掃描 n ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 連續轉換n ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 不使用外部觸發,使用軟體觸發n ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 轉化結果右對齊n ADC_InitStructure.ADC_NbrOfChannel = 1; // 通道數量,這是單通道,所以為1n ADC_Init(ADC1, &ADC_InitStructure);nn RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC時鐘為8分頻,即9MHzn // 轉換通道、轉換順序、採樣時間(這裡是55.5個周期)n ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5); n ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); // 轉換結束中斷nn ADC_Cmd(ADC1, ENABLE);nn ADC_ResetCalibration(ADC1); // 初始化ADC校準寄存器n while(ADC_GetResetCalibrationStatus(ADC1)); // 等待校準寄存器初始化完成n ADC_StartCalibration(ADC1); // ADC開始校準n while(ADC_GetCalibrationStatus(ADC1)); // 等待校準完成nn ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 軟體觸發ADC轉換n}n

相關的分析都在程序注釋里了,很容易讀懂。這裡只說明一個關鍵點。ADC_RegularChannelConfig()函數用來配置ADC的轉換順序和採樣時間等,關於採樣時間的周期選擇,周期越長,採樣精度越高,反之則反。

中斷配置

關於中斷的配置,在之前的文章也介紹了,可移步閱讀。這裡直接貼出配置代碼。

/**n * @brief ADC中斷配置n * @param 無n * @retval 無n */nstatic void ADC_NVIC_Config(void)n{n NVIC_InitTypeDef NVIC_InitStructure;nn NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);nn NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn; // ADC中斷源n NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 搶佔優先順序為1n NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子優先順序為1n NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;nn NVIC_Init(&NVIC_InitStructure);n}n

中斷服務函數

中斷函數一般定義在stm32f10x_it.c文件里,進入中斷服務函數後,在函數內直接讀取ADC轉換結果,並保存在ADC_ConvertedValue變數里,該變數是在main.c文件里定義的。

extern __IO uint16_t ADC_ConvertedValue; // 該變數在main.c文件定義,所以需添加extern關鍵字nnvoid ADC_IRQHandler(void)n{tn if (ADC_GetITStatus(ADC1, ADC_IT_EOC) == SET) n {ntADC_ConvertedValue = ADC_GetConversionValue(ADC1); // 讀取ADC的轉換值n }n ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);n}n

ADC_GetConversionValue()庫函數直接讀取ADC_DR寄存器的值。

最後在main()函數里執行電壓轉換的公式即可得出我們需要的電壓值了。

ADC_ConvertedValueLocal = (float)ADC_ConvertedValue / 4096 * 3.3;n

那麼ADC_ConvertedValueLocal 即為我們需要的值了,可以和萬用表測量到的值做對比。

-----------------------------------------------------------------------

文章首發於知乎專欄 - stm32,轉載請私信,並註明原文出處。


推薦閱讀:

ADC模數轉換(三)——雙重ADC同步規則模式電壓採集實驗
如何公平客觀的評價EDG ADC選手deft?
中高端局殺神馬可波羅攻略
下路榮耀行刑官德萊文+眾星之子索拉卡的組合怎麼打?

TAG:ADC |