文章目錄

  • 前言
  • 一、ADC
  • 什麼是ADC?
  • ADC主要特性有那些?
  • ADC的功能説明
  • 二、實驗步驟
  • 代碼部分講解
  • 代碼示例
  • 總結

前言

本文參考了網上的博文,並加以歸納總結,幫助新手 從入門到放棄


提示:以下是本篇文章正文內容

一、ADC


什麼是ADC?

ADC:Analog-to-Digital Converter的縮寫。指模/數轉換器或者模擬/數字轉換器。是指將連續變量的模擬信號轉換為離散的數字信號的器件。典型的模擬數字轉換器將模擬信號轉換為表示一定比例電壓值的數字信號。

12 位 ADC 是逐次趨近型模數轉換器。它具有多達 19 個複用通道,可測量來自 16 個外部源、兩個內部源和 V BAT 通道的信號。這些通道的 A/D 轉換可在單次、連續、掃描或不連續採樣模式下進行。ADC 的結果存儲在一個左對齊右對齊的 16 位數據寄存器中。ADC 具有模擬看門狗特性,允許應用檢測輸入電壓是否超過了用户自定義的閾值上限或下限。

STM32f103系列有3個ADC,精度為12位,每個ADC最多有16個外部通道。其中ADC1和ADC2都有16個外部通道,ADC3一般有8個外部通道,各通道的A/D轉換可以單次、連續、掃描或間斷執行,ADC轉換的結果可以左對齊或右對齊儲存在16位數據寄存器中。ADC的輸入時鐘不得超過14MHz,其時鐘頻率由PCLK2分頻產生。

ADC主要特性有那些?

①12位逐次逼近型的模擬數字轉換器;
②最多帶3個ADC控制器,可以單獨使用,也可以使用雙重模式提高採樣率;
③最多支持23個通道,可最多測量21個外部和2個內部信號源;
④支持單次和連續轉換模式;
⑤轉換結束,注入轉換結束,和發生模擬看門狗事件時產生中斷;
⑥通道0到通道n的自動掃描模式;
⑦自動校準;
⑧採樣間隔可以按通道編程;
⑨規則通道和注入通道均有外部觸發選項;
⑩轉換結果支持左對齊或右對齊方式存儲在16位數據寄存器;
⑪ADC轉換時間:最大轉換速率 1us(最大轉換速度為1MHz,在ADCCLK=14M,採樣週期為1.5ADC時鐘下得到);
⑫ADC供電要求:2.4V-3.6V;

ADC輸入範圍:VREF- ≤ VIN ≤ VREF+。

ADC的功能説明

1.電壓輸入
我們先看一下下面這張圖:

ADC所能測量的電壓範圍就是VREF- ≤ VIN ≤ VREF+,把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的輸入電壓範圍為: 0~3.3V。
2.輸入通道
從上面截取下來的部分:
ADC的信號輸入就是通過通道來實現的,信號通過通道輸入到單片機中,單片機經過轉換後,將模擬信號輸出為數字信號。

STM32F103ZET6的ADC通道和引腳對應關係圖:

可以看出,STM32F103ZET6帶3個ADC控制器,一共支持23個通道,包括21個外部和2個內部信號源;但是每個ADC控制器最多隻可以有18個通道,包括16個外部和2個內部信號源。

關於通道,在轉換時我們又分為規則通道注入通道

規則通道: 平時的ADC轉換都是用規則通道實現,最多可以安排16個通道,如果有列外處理的話就可以啓用注入通道的轉換。

注入通道: 注入通道可以在規則通道轉換時,強行插入轉換,相當於一個“中斷通道”。

一個不太恰當的比喻是:規則通道組的轉換好比是程序的正常執行,而注入通道組的轉換則好比是程序正常執行之外的一箇中斷處理程序。

關於通道的選擇
有 16 條複用通道。可以將轉換分為兩組:規則轉換和注入轉換。每個組包含一個轉換序列,該序列可按任意順序在任意通道上完成。例如,可按以下順序對序列進行轉換:ADC_IN3、ADC_IN8、ADC_IN2、ADC_IN2、ADC_IN0、ADC_IN2、ADC_IN2、ADC_IN15。

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

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

如果在轉換期間修改 ADC_SQRx 或 ADC_JSQR 寄存器,將復位當前轉換並向 ADC 發送一個新的啓動脈衝,以轉換新選擇的組。

温度傳感器、V REFINT 和 V BAT 內部通道

對於 STM32F40x 和 STM32F41x 器件,温度傳感器內部連接到通道 ADC1_IN16。內部參考電壓 VREFINT 連接到
ADC1_IN17。 對於 STM23F42x 和 STM32F43x 器件,温度傳感器內部連接到與 VBAT
共用的通道ADC1_IN18。一次只能選擇一個轉換(温度傳感器或 VBAT)。同時設置了温度傳感器和 VBAT 轉換時,將只進行 VBAT轉換。

內部參考電壓 VREFINT 連接到 ADC1_IN17。
V BAT 通道連接到通道 ADC1_IN18。該通道也可轉換為注入通道或規則通道。
注意: 温度傳感器、 V REFINT 和 V BAT 通道只在主 ADC1 外設上可用。

轉換模式
規則通道中的轉換順序由三個寄存器控制:SQR1、 SQR2、 SQR3

注入通道轉換順序

只有一個JSQR寄存器來控制
只有當JL=4的時候,注入通道的轉換順序才會按照JSQ1、JSQ2、JSQ3、JSQ4的順序執行。當JL<4時,注入通道的轉換順序恰恰相反,也就是執行順序為:JSQ4、JSQ3、JSQ2、JSQ1。

ADC單次轉換模式

ADC 在單次轉換模式下,只執行一次轉換,該模式可以通過 ADC_CR2 寄存器的 ADON 位(只適用於規則通道)啓動,也可以通過外部觸發啓動(適用於規則通道和注入通道),這時 CONT 位為 0。

1.將ADC_CR2寄存器中的SWSTART位置1(僅適合於規則通道)
2.將JSWSTART位置1(適合於注入通道)
3.外部觸發(適用於規則通道或注入通道)

一旦所選擇的通道轉換完成
如果轉換了規則通道

— 轉換數據存儲在 16 位 ADC_DR 寄存器中
— EOC(轉換結束)標誌置 1
— EOCIE 位置 1 時將產生中斷

如果轉換了注入通道

— 轉換數據存儲在 16 位 ADC_JDR1 寄存器中
— JEOC(注入轉換結束)標誌置 1
— JEOCIE 位置 1 時將產生中斷

然後,ADC 停止。

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

ADC掃描模式(適用於掃描一組模擬通道)

通過將 ADC_CR1 寄存器中的 SCAN 位置 1 來選擇掃描模式。將此位置 1 後,ADC 會掃描在 ADC_SQRx 寄存器(對於規則通道)或 ADC_JSQR 寄存器(對於注入通道)中選擇的所有通道。為組中的每個通道都執行一次轉換。每次轉換結束後,會自動轉換該組中的下一個通道。如果將 CONT 位置 1,規則通道轉換不會在組中最後一個所選通道處停止,而是再次從第一個所選通道繼續轉換。

如果將 DMA 位置 1,則在每次規則通道轉換之後,均使用直接存儲器訪問 (DMA) 控制器將轉換自規則通道組的數據(存儲在 ADC_DR 寄存器中)傳輸到 SRAM。

在以下情況下,ADC_SR 寄存器中的 EOC 位置 1:
● 如果 EOCS 位清零,在每個規則組序列轉換結束時
● 如果 EOCS 位置 1,在每個規則通道轉換結束時
從注入通道轉換的數據始終存儲在 ADC_JDRx 寄存器中。

ADC的 中斷
當模擬看門狗狀態位和溢出狀態位分別置 1 時,規則組和注入組在轉換結束時可能會產生中 斷。可以使用單獨的中斷使能位以實現靈活性。
ADC_SR 寄存器中存在另外兩個標誌,但這兩個標誌不存在中斷相關性:
● JSTRT(開始轉換注入組的通道)
● STRT(開始轉換規則組的通道)

ADC的轉換時間

轉換時間=採樣時間+12.5個週期

12.5個週期是固定的,一般我們設置 PCLK2=72M,經過 ADC 預分頻器能分頻到最大的時鐘只能是 12M,採樣週期設置為 1.5 個週期,算出最短的轉換時間為 1.17us。

ADC的每一次信號轉換都要時間,這個時間就是轉換時間。轉換時間由輸入時鐘和採樣週期來決定。ADC在STM32中是掛載在APB2總線上的

ADC得時鐘是由PCLK2(72MHz)經過分頻得到的,分頻因子由 RCC 時鐘配置寄存器RCC_CFGR 的位 15:14ADCPRE[1:0]設置,可以是 2/4/6/8分頻,一般配置分頻因子為6,即6分頻得到ADC的輸入時鐘頻率為12MHz。(一般不超過14MHz)

採樣週期是確立在輸入時鐘上的,配置採樣週期可以確定使用多少個ADC時鐘週期來對電壓進行採樣採樣的週期數可通過 ADC採樣時間寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位設置,ADC_SMPR2 控制的是通道 0~9, ADC_SMPR1 控制的是通道 10~17。每個通道可以配置不同的採樣週期,但最小的採樣週期是1.5個週期,也就是説如果想最快時間採樣就設置採樣週期為1.5。

ADC的數據對齊
ADC_CR2 寄存器中的 ALIGN 位用於選擇轉換後存儲的數據的對齊方式。可選擇左對齊和右對齊兩種方式。

需要注意的是ADC轉換的精度是12位,而寄存器中有16個位來存放數據,所以要規定數據存放是左對齊還是右對齊。

ADC的時序圖


二、實驗步驟

代碼部分講解


需要的步驟有:

1. 開啓 PA 口時鐘和 ADC1 時鐘,設置 PA1 為模擬輸入。
2. 復位 ADC1,同時設置 ADC1 分頻因子。
3. 初始化 ADC1 參數, 設置 ADC1 的工作模式以及規則序列的相關信息。
4. 使能 ADC 並校準。
5. 讀取 ADC 值。

1. 開啓 PA 口時鐘和 ADC1 時鐘,設置 PA1 為模擬輸入。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); 
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模擬輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.1

2. 復位 ADC1,同時設置 ADC1 分頻因子。

RCC_ADCCLKConfig(RCC_PCLK2_Div6);//設置 ADC1 分頻因子為6
ADC_DeInit(ADC1);//復位 ADC1

3. 初始化 ADC1 參數, 設置 ADC1 的工作模式

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
/*下面是結構體裏的內容
typedef struct
{
uint32_t ADC_Mode;
FunctionalState ADC_ScanConvMode;
FunctionalState ADC_ContinuousConvMode;
uint32_t ADC_ExternalTrigConv;
uint32_t ADC_DataAlign;
uint8_t ADC_NbrOfChannel;
}ADC_InitTypeDef;
*/
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式:獨立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //AD 單通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //AD 單次轉換模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//轉換由軟件而不是外部觸發啓動
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC 數據右對齊
ADC_InitStructure.ADC_NbrOfChannel = 1; //順序進行規則轉換的 ADC 通道的數目 1
ADC_Init(ADC1, &ADC_InitStructure); //根據指定的參數初始化外設 ADCx

4. 使能 ADC 並校準

ADC_Cmd(ADC1, ENABLE); //使能指定的 ADC1
ADC_ResetCalibration(ADC1);//復位校準
ADC_StartCalibration(ADC1); //開始指定 ADC1 的校準狀態
while(ADC_GetResetCalibrationStatus(ADC1)); //等待復位校準結束
while(ADC_GetCalibrationStatus(ADC1)); //等待校 AD 準結束

5.讀取 ADC 值。

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel,
uint8_t Rank, uint8_t ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的 ADC1 的軟件轉換啓動功能
ADC_GetConversionValue(ADC1);//獲取轉換 ADC 轉換結果數據
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)//獲取 AD 轉換的狀態信息的函數
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待轉換結束

這裏還需要説明一下 ADC 的參考電壓,戰艦 STM32 開發板使用的是 STM32F103ZET6,該芯片有外部參考電壓: Vref-和 Vref+,其中 Vref-必須和 VSSA 連接在一起, 而 Vref+的輸入範圍為:2.4~VDDA。戰艦 STM23 開發板通過 P7 端口,設置 Vref-和 Vref+設置參考電壓,默認的我們是通過跳線帽將 Vref-接到 GND, Vref+接到 VDDA,參考電壓就是 3.3V。如果大家想自己設置其他參考電壓,將你的參考電壓接在 Vref-和 Vref+上就 OK 了。本章我們的參考電壓設置的是 3.3V。

代碼示例

adc.c:

//初始化 ADC
//這裏我們僅以規則通道為例
//我們默認將開啓通道 0~3
void Adc_Init(void)
{ 
	ADC_InitTypeDef ADC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能 ADC1 通道時鐘
	RCC_ADCCLKConfig(RCC_PCLK2_Div6); //設置 ADC 分頻因子 6
	//72M/6=12,ADC 最大時間不能超過 14M
	//PA1 作為模擬通道輸入引腳
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模擬輸入
	GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.1
	ADC_DeInit(ADC1); //復位 ADC1,將外設 ADC1 的全部寄存器重設為缺省值
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 獨立模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE; //單通道模式
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //單次轉換模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//轉換由
	//軟件而不是外部觸發啓動
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC 數據右對齊
	ADC_InitStructure.ADC_NbrOfChannel = 1; //順序進行規則轉換的 ADC 通道的數目
	ADC_Init(ADC1, &ADC_InitStructure); //根據指定的參數初始化外設 ADCx
	ADC_Cmd(ADC1, ENABLE); //使能指定的 ADC1
	ADC_ResetCalibration(ADC1); //開啓復位校準
	while(ADC_GetResetCalibrationStatus(ADC1)); //等待復位校準結束
	ADC_StartCalibration(ADC1); //開啓 AD 校準
	while(ADC_GetCalibrationStatus(ADC1)); //等待校準結束
}

//獲得 ADC 值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
	//設置指定 ADC 的規則組通道,設置它們的轉化順序和採樣時間
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
	//通道 1,規則採樣順序值為 1,採樣時間為 239.5 週期
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能軟件轉換功能
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待轉換結束
	return ADC_GetConversionValue(ADC1); //返回最近一次 ADC1 規則組的轉換結果
}

u16 Get_Adc_Average(u8 ch,u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{ 
		temp_val+=Get_Adc(ch);
		delay_ms(5);
	}
	return temp_val/times;
}

main.c:

int main(void)
{
	u16 adcx;
	float temp;
	delay_init(); //延時函數初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置 NVIC 中斷分組 2
	uart_init(115200); //串口初始化波特率為 115200
	LED_Init(); //LED 端口初始化
	LCD_Init(); //LCD 初始化
	Adc_Init(); //ADC 初始化
	POINT_COLOR=RED; //設置字體為紅色
	LCD_ShowString(60,50,200,16,16,"WarShip STM32");
	LCD_ShowString(60,70,200,16,16,"ADC TEST");
	LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2015/1/14");
	//顯示提示信息
	POINT_COLOR=BLUE; //設置字體為藍色
	LCD_ShowString(60,130,200,16,16,"ADC_CH0_VAL:");
	LCD_ShowString(60,150,200,16,16,"ADC_CH0_VOL:0.000V");
	while(1)
	{
		adcx=Get_Adc_Average(ADC_Channel_1,10);
		LCD_ShowxNum(156,130,adcx,4,16,0);//顯示 ADC 的值
		temp=(float)adcx*(3.3/4096);
		adcx=temp;
		LCD_ShowxNum(156,150,adcx,1,16,0);//顯示電壓值
		temp-=adcx;
		temp*=1000;
		LCD_ShowxNum(172,150,temp,3,16,0X80);
		LED0=!LED0;
		delay_ms(250);
	}
}