1. 程式人生 > >STM32學習筆記之ADC轉換

STM32學習筆記之ADC轉換

1. ADC簡介

stm32f103最少有2個AD模數轉換器,每個ADC都有18個通道,可以測量16個外部和2個內部模擬量。最大轉換頻率為1Mhz,也就是轉換時間為1us(在 ADCCLK = 14Mhz,取樣週期為1.5個時鐘週期時)。最大時鐘超過14Mhz,將導致ADC轉換準確度降低。stm32的ADC是12位精度的。

stm32的ADC轉換有兩種通道,規則通道和注入通道,注入通道可以搶佔式地打斷規則通道的取樣,執行注入通道取樣後,再執行之前的規則通道取樣,和中斷類似。本例只使用規則通道實現獨立模式的中斷取樣,這裡不再贅述兩種通道區別。

stm32的ADC可以由外部事件觸發(例如定時器捕獲,EXTI線)和軟體觸發(即在配置相關暫存器時,直接開啟取樣)。

STM32的ADC在單次轉換模式下,只執行一次轉換,該模式可以通過ADC_CR2 暫存器的ADON 位(只適用於規則通道)啟動,也可以通過外部觸發啟動(適用於規則通道和注入通道),這是CONT 位為0 。 以規則通道為例,一旦所選擇的通道轉換完成,轉換結果將被存在ADC_DR 暫存器,EOC (轉換結束)標誌將被置位,如果設定了EOCIE ,則會產生中斷。然後ADC將停止,直到下次啟動。

2. 暫存器簡介

2.1. ADC控制暫存器(ADC_CR1和ADC_CR2)

ADC_CR1的SCAN 位,該位用於設定掃描模式,由軟體設定和清除,如果設定為1 ,則使用掃描模式,如果為 0,則關閉掃描模式。在掃描模式下,由 ADC_SQRx或ADC_JSQRx暫存器選中的通道被轉換。如果設定了 EOCIE 或JEOCIE,只在最後一個通道轉換完畢後才會產生EOC 或JEOC 中斷。

ADC_CR1[19:16]用於設定 ADC的操作模式

ADC_CR2

ADCON 位用於開關AD轉換器。而CONT 位用於設定是否進行連續轉換,我們使用單次轉換,所以CONT 位必須為0。CAL 和RSTCAL 用於AD校準。ALIGN用於設定資料對齊,我們使用右對齊,該位設定為0 。

EXTSEL[2:0]用於選擇啟動規則轉換組轉換的外部事件,詳細的設定關係如下:

這裡使用的是軟體觸發(SWSTART ),所以設定這3 個位為111 。ADC_CR2 的SWSTART 位用於開始規則通道的轉換,我們每次轉換(單次轉換模式下)都需要向該位寫 1 。AWDEN 為用於使能溫度感測器和Vrefint 。

2.2. ADC取樣事件暫存器(ADC_SMPR1 和ADC_SMPR2 )

這兩個暫存器用於設定通道0~17的取樣時間,每個通道佔用 3 個位。

ADC_SMPR2 的各位描述如下

對於每個要轉換的通道,取樣時間建議儘量長一點,以獲得較高的準確度,但是這樣會降低ADC的轉換速率。ADC的轉換時間可以由下式計算:

Tcovn= 取樣時間+12.5 個週期

其中:Tcovn 為總轉換時間,取樣時間是根據每個通道的SMP位的設定來決定的。例如,當ADCCLK=14Mhz 的時候,並設定 1.5個週期的取樣時間,則得到:Tcovn=1.5+12.5=14 個週期=1us 。

2.3. ADC規則序列暫存器(ADC_SQR1~3)

L[3:0] 用於儲存規則序列的長度,我們這裡只用了 1 個,所以設定這幾個位的值為 0 。其他的SQ13~16 則儲存了規則序列中第13~16 個通道的編號(0~17)。另外兩個規則序列暫存器同ADC_SQR1大同小異,我們這裡就不再介紹了,要說明一點的是:我們選擇的是單次轉換,所以只有一個通道在規則序列裡面,這個序列就是SQ0 ,通過ADC_SQR3的最低5 位設定。

2.4. ADC規則資料暫存器(ADC_DR)

這裡要提醒一點的是,該暫存器的資料可以通過ADC_CR2 的ALIGN位設定左對齊還是右對齊。在讀取資料的時候要注意。

2.5. ADC狀態暫存器(ADC_SR )

這裡我們要用到的是EOC 位,我們通過判斷該位來決定是否此次規則通道的AD轉換已經完成,如果完成我們就從ADC_DR 中讀取轉換結果,否則等待轉換完成。

3. 暫存器操作步驟

1 、開啟PA口時鐘,設定PA0 為模擬輸入。

STM32F103RBT6的ADC通道0 在PA 0 上,所以,我們先要使能 PORTA的時鐘,然後設定PA 0 為模擬輸入。

2 、使能ADC1 時鐘,並設定分頻因子。

要使用ADC1,第一步就是要使能 ADC1 的時鐘,在使能完時鐘之後,進行一次 ADC1 的復位。接著我們就可以通過RCC_CFGR設定ADC1 的分頻因子。分頻因子要確保 ADC1 的時鐘(ADCCLK)不要超過14Mhz 。

3 、設定ADC1 的工作模式。

在設定完分頻因子之後,我們就可以開始 ADC1 的模式配置了,設定單次轉換模式、觸發方式選擇、資料對齊方式等都在這一步實現。

4 、設定ADC1 規則序列的相關資訊。

接下來我們要設定規則序列的相關資訊,我們這裡只有一個通道,並且是單次轉換的,所以設定規則序列中通道數為1 ,然後設定通道 0 的取樣週期。

5 、開啟AD轉換器,並校準。

在設定完了以上資訊後,我們就開啟AD轉換器,執行復位校準和AD校準,注意這兩步是必須的!不校準將導致結果很不準確。

6 )讀取ADC值。

在上面的校準完成之後,ADC就算準備好了。接下來我們要做的就是設定規則序列 0 裡面的通道,然後啟動ADC轉換。在轉換結束後,讀取ADC1_DR 裡面的值就是了。

硬體設定:我們通過ADC1 的通道0 (PA 0 )來讀取外部電壓值。

注意:這裡不能接到板上5V電源上去測試,這可能會燒壞 ADC!

ADC.C

#include <stm32f10x_lib.h>
#include "adc.h"
//ADC 驅動程式碼			   	   
//初始化ADC
//這裡我們僅以規則通道為例
//我們預設將開啟通道0~3																	   
void  Adc_Init(void)
{    
	//先初始化IO口
 	RCC->APB2ENR|=1<<2;    //使能PORTA口時鐘 
	GPIOA->CRL&=0XFFFF0000;//PA0 1 2 3 anolog輸入
	//通道10/11設定			 
	RCC->APB2ENR|=1<<9;    //ADC1時鐘使能	  
	RCC->APB2RSTR|=1<<9;   //ADC1復位
	RCC->APB2RSTR&=~(1<<9);//復位結束	    
	RCC->CFGR&=~(3<<14);   //分頻因子清零	
	//SYSCLK/DIV2=12M ADC時鐘設定為12M,ADC最大時鐘不能超過14M!
	//否則將導致ADC準確度下降! 
	RCC->CFGR|=2<<14;      	 
 
	ADC1->CR1&=0XF0FFFF;   //工作模式清零
	ADC1->CR1|=0<<16;      //獨立工作模式  
	ADC1->CR1&=~(1<<8);    //非掃描模式	  
	ADC1->CR2&=~(1<<1);    //單次轉換模式
	ADC1->CR2&=~(7<<17);	   
	ADC1->CR2|=7<<17;	   //軟體控制轉換  
	ADC1->CR2|=1<<20;      //使用用外部觸發(SWSTART)!!!	必須使用一個事件來觸發
	ADC1->CR2&=~(1<<11);   //右對齊	 
	ADC1->SQR1&=~(0XF<<20);
	ADC1->SQR1&=0<<20;     //1個轉換在規則序列中 也就是隻轉換規則序列1 			   
	//設定通道0~3的取樣時間
	ADC1->SMPR2&=0XFFFFF000;//通道0,1,2,3取樣時間清空	  
	ADC1->SMPR2|=7<<9;      //通道3  239.5週期,提高取樣時間可以提高精確度	 
	ADC1->SMPR2|=7<<6;      //通道2  239.5週期,提高取樣時間可以提高精確度	 
	ADC1->SMPR2|=7<<3;      //通道1  239.5週期,提高取樣時間可以提高精確度	 
	ADC1->SMPR2|=7<<0;      //通道0  239.5週期,提高取樣時間可以提高精確度	 
 
	ADC1->CR2|=1<<0;	    //開啟AD轉換器	 
	ADC1->CR2|=1<<3;        //使能復位校準  
	while(ADC1->CR2&1<<3);  //等待校準結束 			 
    //該位由軟體設定並由硬體清除。在校準暫存器被初始化後該位將被清除。 		 
	ADC1->CR2|=1<<2;        //開啟AD校準	   
	while(ADC1->CR2&1<<2);  //等待校準結束
	//該位由軟體設定以開始校準,並在校準結束時由硬體清除  
}				  
//獲得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)   
{
	//設定轉換序列	  		 
	ADC1->SQR3&=0XFFFFFFE0;//規則序列1 通道ch
	ADC1->SQR3|=ch;		  			    
	ADC1->CR2|=1<<22;       //啟動規則轉換通道 
	while(!(ADC1->SR&1<<1));//等待轉換結束	 	   
	return ADC1->DR;		//返回adc值	
}

  1. #ifndef __ADC_H
  2. #define __ADC_H
  3. #define ADC_CH0 0//通道0
  4. #define ADC_CH1 1//通道1
  5. #define ADC_CH2 2//通道2
  6. #define ADC_CH3 3//通道3
  7. voidAdc_Init(void);
  8. u16 Get_Adc(u8 ch);
  9. #endif