cortex_m3_stm32嵌入式學習筆記(十六):ADC實驗(模數轉換)

ARM 724瀏覽

之前沒學過數模電,對A/D D/A轉換一竅不通,也百度了很多資料大都深奧難懂。。算了,先自以為是一下吧,等以后學了專業課再說。。(寒假回家一定要學。。恩 就這么決定了)看了那么多資料,感覺 A/D轉換就是將電壓(或者是其他模擬量:如 壓力,圖像等)轉換為數字,D/A就是反過來,而ADC就是A/D轉換器,他可以采集外部電壓轉化為數字。本節實驗通過ADC采集外部電壓轉換為數字顯示在屏幕上。


STM32 擁有 1~3 個 ADC( STM32F101/102 系列只有 1 個 ADC),這些 ADC 可以獨立使用,也可以使用雙重模式(提高采樣率)。 STM32 的 ADC 是 12 位逐次逼近型的模擬數字轉換器它有 18
個通道,可測量 16 個外部和 2 個內部信號源。各通道的 A/D 轉換可以單次、連續、掃描或間斷模式執行。 ADC 的結果可以左對齊或右對齊方式存儲在 16 位數據寄存器中。 模擬看門狗特性允許應用程序檢測輸入電壓是否超出用戶定義的高/低閥值。


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


通過一個形象的例子可以說明: 假如你在家里的院子內放了 5 個溫度探頭,室內放了3個溫度探頭;
你需要時刻監視室外溫度即可,但偶爾你想看看室內的溫度;因此你可以使用規則
通道組循環掃描室外的 5 個探頭并顯示 AD 轉換結果,當你想看室內溫度時,通過一個按鈕啟動注入轉換組(3 個室內探頭)并暫時顯示室內溫度,當你放開這個按鈕后,系統又會回到規則通道組繼續檢測室外溫度。從系統設計上,測量并顯示室內溫度的過程中斷了測量并顯示室外溫度的過程,但程序設計上可以在初始化階段分別設置好不同的轉換組,系統運行中不必再變更循環轉換的配置,從而達到兩個任務互不干擾和快速切換的結果。可以設想一下,如果沒有規則組和注入組的劃分,當你按下按鈕后,需要從新配置
AD 循環掃描的通道,然后在釋放按鈕
后需再次配置 AD 循環掃描的通道。

但本節只用到規則通道,因為是單次轉換模式。。大概可以理解為我們現在只測量一個地方的電壓值。。

配置ADC步驟如下:

1) 開啟
PA 口和
ADC1 時鐘,設置
PA1 為模擬輸入。
STM32F103RCT6 的 ADC 通道 1 在 PA1 上,所以,我們先要使能 PORTA 的時鐘,然后設置 PA1 為模擬輸入。 使能 GPIOA 和 ADC 時鐘用 RCC_APB2PeriphClockCmd 函數,設置 PA1的輸入方式,使用 GPIO_Init 函數即可。這里我們列出 STM32 的 ADC 通道與 GPIO
對應表:

? ?
2)復位
ADC1,同時設置
ADC1 分頻因子。
3)初始化
ADC1 參數,設置
ADC1 的工作模式以及規則序列的相關信息。
4)使能
ADC 并校準。
5)讀取
ADC 值。

配置ADC的文件adc.c

#include "adc.h"
void Adc_Init(void)
{
	ADC_InitTypeDef ADC_ist;
	GPIO_InitTypeDef GPIO_ist;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE );
	//72M/6=12,ADC 最大時間不能超過 14M
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//設置 ADC分頻因子6
	//PA1 作為模擬通道輸入引腳
	GPIO_ist.GPIO_Pin=GPIO_Pin_1;
	GPIO_ist.GPIO_Mode=GPIO_Mode_AIN;//模擬輸入
	GPIO_Init(GPIOA,&GPIO_ist);
	
	ADC_DeInit(ADC1);//復位 ADC1,將外設 ADC1 的全部寄存器重設為缺省值
	ADC_ist.ADC_Mode= ADC_Mode_Independent;//ADC 獨立模式
	ADC_ist.ADC_ScanConvMode=DISABLE;//單通道模式
	ADC_ist.ADC_ContinuousConvMode=DISABLE;//單次轉換模式
	//轉換由軟件而不是外部觸發啟動
	ADC_ist.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	ADC_ist.ADC_DataAlign=ADC_DataAlign_Right;//ADC 數據右對齊
	ADC_ist.ADC_NbrOfChannel=1;//順序進行規則轉換的 ADC 通道的數目
	ADC_Init(ADC1,&ADC_ist);
	
	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); 
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的 ADC1 的軟件轉換功能
	while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));//等待轉換結束
	return ADC_GetConversionValue(ADC1);//返回最近一次 ADC1 規則組的轉換結果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
	u32 tem_val=0;
	u8 i;
	for(i=0;i<times;i++)
	{
		tem_val+=Get_Adc(ch);
		delay_ms(5);
	}
	return tem_val/times;
}

adc.h

#ifndef _ADC_H
#define _ADC_H
#include "sys.h"
#include "delay.h"
void Adc_Init(void);
u16 Get_Adc(u8 ch);
u16 Get_Adc_Average(u8 ch,u8 times);
#endif

主函數

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "adc.h"
void init()
{
	delay_init();	    	 //延時函數初始化	  
	uart_init(9600);	 	//串口初始化為9600
	LED_Init();		  		//初始化與LED連接的硬件接口
 	LCD_Init();
	Adc_Init();
	POINT_COLOR=RED;//設置字體為紅色
	LCD_ShowString(60,40,200,24,24,"ADC Test ^-^");
	LCD_ShowString(60,70,200,16,16,"Medium difficulty");
	LCD_ShowString(60,90,200,16,16,"2015/1/24");
	LCD_ShowString(60,110,200,16,16,"By--Mr yh");
	//顯示提示信息
	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");
}
int main(void)
{
	u16 adcnum;
	float tem;
	init();
	while(1)
	{
		adcnum=Get_Adc_Average(ADC_Channel_1,10);
		LCD_ShowxNum(156,130,adcnum,4,16,0);//顯示ADC的值
		tem=(float)adcnum*(3.3/4096);
		adcnum=tem;
		LCD_ShowxNum(156,150,adcnum,1,16,0);//顯示電壓值的整數位
		tem-=adcnum;
		tem*=1000;
		LCD_ShowxNum(172,150,tem,3,16,0x80);//顯示ADC的值的小數位
		LED0=!LED0;
		delay_ms(250);
	}
}

獲得了ADC的值之后。。 再轉換成電壓值的公式就看不懂了。。orz

不過有一個地方需要注意 LCD_ShowxNum()的用法

再次翻出它的源碼

//顯示數字,高位為0,還是顯示
//x,y:起點坐標
//num:數值(0~999999999);	 
//len:長度(即要顯示的位數)
//size:字體大小
//mode:
//[7]:0,不填充;1,填充0.
//[6:1]:保留
//[0]:0,非疊加顯示;1,疊加顯示.
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode)
{  
	u8 t,temp;
	u8 enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/LCD_Pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				if(mode&0X80)LCD_ShowChar(x+(size/2)*t,y,'0',size,mode&0X01);  
				else LCD_ShowChar(x+(size/2)*t,y,' ',size,mode&0X01);  
 				continue;
			}else enshow=1; 
		 	 
		}
	 	LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,mode&0X01); 
	}
} 

看到最后一個參數的說明, mode 是一個8位的變量,第7位為0代表不填充,1代表填充。

一開始對填充這個概念沒什么理解,于是將兩種結果(填充和不填充)燒進去看了一下,發現顯示0.001 的時候,如果選不填充,它會顯示0. ?1(點和1之間有2個空格),如果是填充就會顯示0.001 (正常顯示)所以我對填充的理解是:假如一個數6, 你想顯示006,那么需要設置數的長度為3,填充模式

七星彩走势图2元网官网