STM32-ADC模數轉換

C~Tian發表於2020-09-27

本文基於STM32F407ZGT6,適用於絕大部分M3/M4核心的STM32晶片。
——————————————————————————————
ADC需要的引腳:在這裡插入圖片描述
STM32F4xx 系列般都有 3 個 ADC,這些 ADC 可以獨立使用,也可以使用雙重/三重模
式(提高取樣率)。
STM32-ADC具有多達 19 個複用通道,可測量來自 16 個外部源、兩個內部源和 VBAT 通道的訊號。
16個外部源:在這裡插入圖片描述
在這裡插入圖片描述

兩個內部源:
對於 STM32F40x 和 STM32F41x 器件,
溫度感測器內部連線到通道 ADC1_IN16。
內部參考電壓 VREFINT 連線到 ADC1_IN17。

VBAT 通道:
VBAT 通道連線到通道 ADC1_IN18。該通道也可轉換為注入通道或規則通道。

注意:對於兩個內部源和VBAT 通道是晶片出廠時自帶的,我們無法改變也不用搭建什麼外圍電路便可直接使用。

一、ADC 時鐘

ADC 具有兩個時鐘方案:

  1. ● 用於類比電路的時鐘:ADCCLK,所有 ADC 共用 此時鐘來自於經可程式設計預分頻器分頻的 APB2 時鐘,該預分頻器允許 ADC
    在 fPCLK2/2、 /4、/6 或 /8 下工作。有關 ADCCLK 的最大值,請參見資料手冊。

在這裡插入圖片描述
注意:ADCCLK的最大工作頻率與供電電壓有關,當供電電壓減低的時候必須降低取樣頻率,否則會造成取樣資料誤差率較大。

  1. ● 用於數字介面的時鐘(用於暫存器讀/寫訪問) 此時鐘等效於 APB2 時鐘。可以通過 RCC APB2 外設時鐘使能暫存器
    (RCC_APB2ENR) 分別為每個 ADC 使能/禁止數字介面時鐘。

二、通道組

STM32F4 將 ADC 的轉換分為 2 個通道組:規則通道組和注入通道組。
規則通道相當於你正常執行的程式,而注入通道呢,就相當於中斷。在你程式正常執行的時候,中斷是可以打斷你的執行的。同這個類似,注入通道的轉換可以打斷規則通道的轉換, 在注入通道被轉換完成之後,規則通道才得以繼續轉換。

  • ● 一個規則轉換組最多由 16 個轉換構成。必須在 ADC_SQRx 暫存器中選擇轉換序列的規
    則通道及其順序。規則轉換組中的轉換總數必須寫入 ADC_SQR1 暫存器中的 L[3:0] 位。
  • ● 一個注入轉換組最多由 4 個轉換構成。必須在 ADC_JSQR 暫存器中選擇轉換序列的注入
    通道及其順序。注入轉換組中的轉換總數必須寫入 ADC_JSQR 暫存器中的 L[1:0] 位。

注意:關於通道組,以規則組為例:我們可以理解為1個規則組裡面有16個或16個以下ADC通道需要轉換,我們可以給它們排一個轉換順序,然後一次就可以轉換完成多個通道的轉換。
比如用ADC1進行6個通道規則組轉換需要的操作步驟如下:

ADC_InitStructure.ADC_NbrOfConversion = 6;//6個轉換在規則序列中 也就是有6個通道需要轉換

ADC_NbrOfConversion設定為6,表示有6個ADC要進行轉換。

// 配置ADC 通道的轉換順序和取樣時間
	ADC_RegularChannelConfig(ADC1, ADC_CHANNEL1, 1, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_CHANNEL2, 2, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_CHANNEL3, 3, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_CHANNEL4, 4, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_CHANNEL5, 5, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_CHANNEL6, 6, ADC_SampleTime_55Cycles5);

下面分析一下這個函式:

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)

函式的四個引數:

  1. ADCx:就是ADC1、ADC2、ADC3三個ADC其中之一
  2. ADC_Channel:取值ADC_Channel_0~18,表示ADC19個通道
  3. Rank:轉換順序,規則轉換組最多由 16 個轉換構成,取值1~16
  4. ADC_SampleTime:ADC的取樣週期

三、轉換模式

ADC的轉換模式有很多種,這裡只是分析一下單次轉換模式和連續轉換模式。
單次轉換模式
在單次轉換模式下,ADC 執行一次轉換。CONT 位為 0 時,可通過以下方式啟動此模式:
● 將 ADC_CR2 暫存器中的 SWSTART 位置 1(僅適用於規則通道)
● 將 JSWSTART 位置 1(適用於注入通道)
● 外部觸發(適用於規則通道或注入通道)
完成所選通道的轉換之後:
● 如果轉換了規則通道:
— 轉換資料儲存在 16 位 ADC_DR 暫存器中
— EOC(轉換結束)標誌置 1
— EOCIE 位置 1 時將產生中斷
● 如果轉換了注入通道:
— 轉換資料儲存在 16 位 ADC_JDR1 暫存器中
— JEOC(注入轉換結束)標誌置 1
— JEOCIE 位置 1 時將產生中斷
然後,ADC 停止

**注意:**單次轉換模式轉換模式轉換結束以後ADC停止了,沒有值跟新了。如果需要更新採集資料必須從軟體上進行ADC再次採集。

連續轉換模式
在連續轉換模式下,ADC 結束一個轉換後立即啟動一個新的轉換。CONT 位為 1 時,可通過
外部觸發或將 ADC_CR2 暫存器中的 SWSTRT 位置 1 來啟動此模式(僅適用於規則通道)。
每次轉換之後:
● 如果轉換了規則通道組:
— 上次轉換的資料儲存在 16 位 ADC_DR 暫存器中
— EOC(轉換結束)標誌置 1
— EOCIE 位置 1 時將產生中斷
注意: 無法連續轉換注入通道。連續模式下唯一的例外情況是,注入通道配置為在規則通道之後自動轉換(使用 JAUTO 位)。

**注意:**連續轉換在轉換結束後會馬上啟動新的轉換,不需要軟體程式碼的的重新寫入,那麼就有一個問題,新轉換的資料放在哪裡呢?所以這就需要DMA幫忙了,把資料搬運出去。所以開啟連續轉換模式時,同時要把DMA功能開啟。

四、資料對齊

資料對其有左對齊和右對齊兩種模式,一般是採用資料右對齊模式
因為十六進位制模式下資料是右對齊的所以採取右對齊模式可以把資料拿出來直接使用。

五、取樣時間

ADC 會在數個 ADCCLK 週期內對輸入電壓進行取樣,可使用 ADC_SMPR1 和ADC_SMPR2 暫存器中的 SMP[2:0] 位修改週期數。每個通道均可以使用不同的取樣時間進行取樣。
總轉換時間的計算公式如下:
Tconv = 取樣時間 + 12 個週期
示例:
ADCCLK = 30 MHz 且取樣時間 = 3 個週期時:
Tconv = 3 + 12 = 15 個週期 = 0.5 μs
具體計算:Tconv = (3 + 12 )*1/30 = 0.5 μs
ADC的快速轉換模式 :
可通過降低 ADC 解析度來執行快速轉換。RES 位用於選擇資料暫存器中可用的位數。每種
解析度的最小轉換時間如下:
● 12 位:3 + 12 = 15 ADCCLK 週期
● 10 位:3 + 10 = 13 ADCCLK 週期
● 8 位:3 + 8 = 11 ADCCLK 週期
● 6 位:3 + 6 = 9 ADCCLK 週期

注意:每次取樣時間最小為3個週期,但是不建議使用那麼短的取樣時間;適當加長取樣時間可以提高取樣資料的準確性。

ADC轉換結果計算公式:
計算公式是 ADC 通用的:

實際電壓值 = ADC 轉換值*LSB

如果設定STM32 的 ADC 的精度為 12 位,而Vref+接的參考電壓值為3.3v ,那麼LSB =3.3/2^12

六、外部觸發轉換和觸發極性

在這裡插入圖片描述
更多的內容請看《STM32F4xx中文參考手冊》
下面引用以下原子哥的例程:
該例程是ADC1規則通道組只採用通道5不採用DMA模式的單次轉換模式資料右對齊的採集例程。
(好好理解這句話,太多知識點在裡面了)
ADC初始化:

//初始化ADC															   
void  Adc_Init(void)
{    
  	GPIO_InitTypeDef      GPIO_InitStructure;
	ADC_CommonInitTypeDef ADC_CommonInitStructure;
	ADC_InitTypeDef       ADC_InitStructure;
	
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA時鐘
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1時鐘

  //先初始化ADC1通道5 IO口
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道5
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模擬輸入
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不帶上下拉
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化  
 
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);	  //ADC1復位
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);	//復位結束	 
 
	
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//獨立模式
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;//兩個取樣階段之間的延遲5個時鐘
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA失能
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//預分頻4分頻。ADCCLK=PCLK2/4=84/4=21Mhz,ADC時鐘不要超過36Mhz 
  ADC_CommonInit(&ADC_CommonInitStructure);//初始化
	
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非掃描模式	
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//關閉連續轉換
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//禁止觸發檢測,使用軟體觸發
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右對齊	
  ADC_InitStructure.ADC_NbrOfConversion = 1;//1個轉換在規則序列中 也就是有1個通道需要轉換
  ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
	
 
	ADC_Cmd(ADC1, ENABLE);//開啟AD轉換器	

}				  

需要注意的是,ADC的GPIO口直接設定為模擬輸入即可,不需要複用功能。

獲取ADC的值:

//獲得ADC值
//ch: @ref ADC_channels 
//通道值 0~16取值範圍為:ADC_Channel_0~ADC_Channel_16
//返回值:轉換結果
u16 Get_Adc(u8 ch)   
{
	  	//設定指定ADC的規則組通道,一個序列,取樣時間
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles );	//ADC1,ADC通道,480個週期,提高取樣時間可以提高精確度			    
  
	ADC_SoftwareStartConv(ADC1);		//使能指定的ADC1的軟體轉換啟動功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待轉換結束

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1規則組的轉換結果
}

這裡使用的是軟體轉換ADC1,通過檢視ADC_FLAG_EOC標誌位是否置位來判斷轉換是否完成。

最後看一下轉換公式:

	temp=(float)adcx*(3.3/4096);          //獲取計算後的帶小數的實際電壓值,比如3.1111

現在採用12位ADC精度,2^12=4096,可以理解為把3.3V分成了4096份,然後再去逐次逼近型地去比較。

相關文章