【微控制器基礎】ADC0809模數轉換器詳解

2021-09-27 10:01:31

ADC0809更詳細的引數說明可以檢視它的晶片手冊(英文原版)
連結:https://share.weiyun.com/wyAdZUPP
密碼:eteeec

1、ADC0809模數轉換器主要特性

  1. ADC0809是8通道8位元,以逐次逼近原理進行模——數轉換的器件
  2. 內部有一個8通道模擬多路開關,它可以根據地址碼鎖存譯碼後的訊號,只選擇8路模擬輸入訊號中的一個進行A/D轉換。
  3. A/D轉換後的資料由三態鎖存器輸出
  4. 片內沒有時鐘,需外接時鐘訊號
  5. 具有轉換啟停控制端
  6. 轉換時間為100us
  7. 單個+5V電源供電
  8. 模擬輸入電壓範圍0~+5V,不設零點和滿刻度校驗
  9. 工作溫度範圍為-40~+85攝氏度
    請新增圖片描述

2、ADC0809外部特徵

ADC0809晶片有28條引腳,採用雙列直插式封裝
請新增圖片描述

  • IN0~IN7: 8路模擬量輸入端
  • D0~D8: 8路數位量輸出端
  • ADDA、ADDB、ADDC: 3位地址輸入線,用於選通8路模擬輸入中的一路。
  • CLK: 時鐘脈衝輸入端
  • REF(+)、REF(-): 基準電壓
  • VCC: 電源,單一+5V
  • GND: 電源地
  • ALE: 地址鎖存允許訊號,輸入高電平有效
  • START: A/D轉換啟動脈衝輸入端,輸入一個正脈衝(至少100ns寬)使其啟動(脈衝上升沿使0809復位,下降沿啟動A/D轉換)
  • EOC: A/D轉換結束訊號,輸出,當A/D轉換結束時,此端輸出一個高電平(轉換期間一直為低電平)
  • OE: 資料輸出允許訊號,輸入,高電平有效。當A/D轉換結束時,此端輸入一個高電平,才能開啟輸出三態門,輸出數位量

模擬量輸入選擇通道表
請新增圖片描述

2、ADC0809工作時序

請新增圖片描述
ADC0809完成一次轉換需100us左右,可對0~5V訊號進行交換。

ALE: 正脈衝鎖存ADDA、ADDB、ADDC通道
START: 正脈衝的上升沿清除內部暫存器資料,下降沿啟動AD轉換
AD啟動後EOC由高變低;
AD轉換期間EOC保持低電平;
AD轉換結束後EOC由低變高電平。
OE: 輸入正脈衝,開啟三態門輸出

3、ADC與微控制器介面方式

A/D轉換後得到的是數位量的資料,這些資料資料傳給微控制器進行處理,資料的關鍵問題是如何確定A/D轉換完成,因為只有完成後才能進行傳送,為此可採用等待延時方式、查詢方式和中斷方式。

(1)等待延時方式——定時傳送方式(不推薦)

對於一種A/D轉換器來說,轉換時間作為一項指標是已知和固定的。例如,若ADC0809轉換時間為128us,相當於12MHZ的51微控制器共128個機器週期。可根據此設計一個延時子程式,A/D轉換啟動後呼叫這個子程式,延時時間一到,轉換時間肯定完成,接著就可以進行資料傳送了。

(2)查詢方式

A/D轉換晶片有表明完成的狀態訊號,ADC0809的EOC端就是轉換結束指示腳。因此可以用查詢方式,軟體測試EOC的轉態,即可確定轉換是否完成,然後進行資料傳送。

(3)中斷方式

把表明轉換完成的轉態訊號EOC作為中斷訊號請求,將EOC訊號經過反相器返送到微控制器INT0或INT1,以中斷方式進行資料傳送。

4、參考例程(Proteus模擬方式)

附上之前畫好的類比電路
連結:https://share.weiyun.com/nYRBzGt0
密碼:q3mivs
請新增圖片描述

(1)查詢方式

使用ADC0809數模轉換器,採用查詢方式將電位上的模擬量(模擬電壓)轉換為數位量,把轉換結果送到數碼管顯示。

#include <reg52.h>

#define uchar unsigned char
#define uint unsigned int


/*ADC0809埠定義*/
#define OUTPUT_PORT P1;	//ADC輸出的匯流排介面
sbit EOC 	= P3^5;		//A/D轉換結束訊號,輸出,當A/D轉換結束時,此端輸出一個高電平(轉換期間一直為低電平)
sbit START 	= P3^6;		//A/D轉換啟動脈衝輸入端,輸入一個正脈衝(至少100ns寬)使其啟動(脈衝上升沿使0809復位,下降沿啟動A/D轉換)。
sbit CLOCK 	= P3^7;		//時鐘脈衝輸入端
sbit OE 	= P3^4;		//資料輸出允許訊號,輸入,高電平有效。當A/D轉換結束時,此端輸入一個高電平,才能開啟輸出三態門,輸出數位量
sbit ADD_A 	= P3^0;		//通道A
sbit ADD_B	= P3^1;		//通道B
sbit ADD_C	= P3^2;		//通道C

//數碼管位選定義
sbit smg1 = P2^0;
sbit smg2 = P2^1;
sbit smg3 = P2^2;
sbit smg4 = P2^3;


//數碼管碼錶,共陰極
unsigned char leddata[10]={ 
			0x3F,  //"0"
			0x06,  //"1"
			0x5B,  //"2"
			0x4F,  //"3"
			0x66,  //"4"
			0x6D,  //"5"
			0x7D,  //"6"
			0x07,  //"7"
			0x7F,  //"8"
			0x6F,  //"9"
                         };



/*1毫秒延時函數*/
void Delay1ms()		//@12.000MHz
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/*毫秒延時函數*/
void delay_ms(uint time)
{
	uint i;
	for(i=0; i<time; i++)
	{
		Delay1ms();
	}
}

//在數碼管顯示三位數位顯示函數,
//unsigned char i 輸入0-999以內的數位
void disply_three_digit(unsigned int i)
{
	unsigned int ge,shi,bai;
	
	bai = i / 100;
	shi = i % 100 / 10;
	ge  = i % 10;
	
	P0 = leddata[ge];
	smg4 = 0;
	delay_ms(1);
	smg4 = 1;
	
	P0 = leddata[shi];
	smg3 = 0;
	delay_ms(1);
	smg3 = 1;
	
	P0 = leddata[bai];
	smg2 = 0;
	delay_ms(1);
	smg2 = 1;
}


/*系統初始化*/
void system_Init(void)
{
	/*上電後P0、P1、P2全部置1*/
	P0 = 0xff;
	P1 = 0xff;
	P2 = 0xff;
}

/***********************************************
程式名稱:InitTimer1
程式功能:定時器1初始化
程式說明:使用定時器1,工作方式2來產生一個時鐘脈衝
程式輸入:無
程式返回:無
***********************************************/
void InitTimer1(void)
{
    TMOD = 0x20;	//定時器T1,工作方式2,自動重灌
    TH1 = 0x38;
    TL1 = 0x38;		//定時約200us
    EA = 1;			//開啟總中斷
    ET1 = 1;		//開定時中斷
    TR1 = 1;		//啟動定時器T1
}

/***********************************************
程式名稱:ADC0809_Result
程式功能:獲取ADC0809 ADC值
程式說明:因為ADC0809是並行輸出方式,所以輸出的
		  ADC結果反饋到微控制器的對應的OUTPUT_PORT引腳。
程式輸入:uchar adc_number(要獲取的ADC通道號1-7)
程式返回:unsigned char result 返回當前通道的ADC值
***********************************************/
uchar ADC0809_Result(uchar adc_number)
{
	uchar result = 0;	//定義一個臨時變數,用於存放轉換後的ADC值
	
	/*初始化ADC0809*/
	START = 0;
    OE = 0;
    START = 1;
	
	/*選擇相應輸入通道*/
	switch(adc_number)
	{
		case 1:
			ADD_C = 0;
			ADD_B = 0;
			ADD_A = 0;
			break;
		case 2:
			ADD_C = 0;
			ADD_B = 0;
			ADD_A = 1;
			break;
		case 3:
			ADD_C = 0;
			ADD_B = 1;
			ADD_A = 1;
			break;
		case 4:
			ADD_C = 0;
			ADD_B = 1;
			ADD_A = 1;
			break;
		case 5:
			ADD_C = 1;
			ADD_B = 0;
			ADD_A = 0;
			break;
		case 6:
			ADD_C = 1;
			ADD_B = 0;
			ADD_A = 1;
			break;
		case 7:
			ADD_C = 1;
			ADD_B = 1;
			ADD_A = 0;
			break;
		case 8:
			ADD_C = 1;
			ADD_B = 1;
			ADD_A = 1;
			break;
	}
	
	/*啟動ADC轉換*/
	START = 0;
	while(EOC == 0);	//等待EOC = 1
	OE = 1;				//開啟三態門輸出
	result = OUTPUT_PORT;
	
	return result;		//將結果返回輸出
}

void main()
{
	uchar temp = 0;
	
	system_Init();
	InitTimer1();
	while(1)
	{
		temp = ADC0809_Result(3);//讀取通道3的AD值
		disply_three_digit(temp);//將讀取到的ADC值送到數碼管
	}
}

void Timer1Interrupt(void) interrupt 3
{
	CLOCK = ~CLOCK;		//通過定時器T1提供時鐘脈衝
}

(2)中斷方式

(3)將轉換的ADC結果換算成電壓值

電壓值 = ADC值 * (參考電壓 / 解析度最大位) * 100(保留小數點後兩位)

參考例程

#include <reg52.h>

#define uchar unsigned char
#define uint unsigned int

/*ADC0809埠定義*/
#define OUTPUT_PORT P1;	//ADC輸出的匯流排介面
sbit EOC 	= P3^5;		//A/D轉換結束訊號,輸出,當A/D轉換結束時,此端輸出一個高電平(轉換期間一直為低電平)
sbit START 	= P3^6;		//A/D轉換啟動脈衝輸入端,輸入一個正脈衝(至少100ns寬)使其啟動(脈衝上升沿使0809復位,下降沿啟動A/D轉換)。
sbit CLOCK 	= P3^7;		//時鐘脈衝輸入端
sbit OE 	= P3^4;		//資料輸出允許訊號,輸入,高電平有效。當A/D轉換結束時,此端輸入一個高電平,才能開啟輸出三態門,輸出數位量
sbit ADD_A 	= P3^0;		//通道A
sbit ADD_B	= P3^1;		//通道B
sbit ADD_C	= P3^2;		//通道C

//數碼管位選定義
sbit smg1 = P2^0;
sbit smg2 = P2^1;
sbit smg3 = P2^2;
sbit smg4 = P2^3;


//數碼管碼錶,共陰極
unsigned char leddata[10]={ 
			0x3F,  //"0"
			0x06,  //"1"
			0x5B,  //"2"
			0x4F,  //"3"
			0x66,  //"4"
			0x6D,  //"5"
			0x7D,  //"6"
			0x07,  //"7"
			0x7F,  //"8"
			0x6F,  //"9"
                         };

unsigned char leddata2[10]={
		0xBF,//"0."
		0x86,//"1."
		0xDB,//"2."
		0xCF,//"3."
		0xE6,//"4."
		0xED,//"5."
		0xFD,//"6."
		0x87,//"7."
		0xFF,//"8."
		0xEF,//"9."
};



/*1毫秒延時函數*/
void Delay1ms()		//@12.000MHz
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/*毫秒延時函數*/
void delay_ms(uint time)
{
	uint i;
	for(i=0; i<time; i++)
	{
		Delay1ms();
	}
}

//在數碼管顯示三位數位顯示函數,
//unsigned char i 輸入0-999以內的數位
void disply_three_digit(unsigned int i)
{
	unsigned int ge,shi,bai;
	
	bai = i / 100;
	shi = i % 100 / 10;
	ge  = i % 10;
	
	P0 = leddata[ge];
	smg4 = 0;
	delay_ms(1);
	smg4 = 1;
	
	P0 = leddata[shi];
	smg3 = 0;
	delay_ms(1);
	smg3 = 1;
	
	P0 = leddata2[bai];
	smg2 = 0;
	delay_ms(1);
	smg2 = 1;
}


/*系統初始化*/
void system_Init(void)
{
	/*上電後P0、P1、P2全部置1*/
	P0 = 0xff;
	P1 = 0xff;
	P2 = 0xff;
}

/***********************************************
程式名稱:InitTimer1
程式功能:定時器1初始化
程式說明:使用定時器1,工作方式2來產生一個時鐘脈衝
程式輸入:無
程式返回:無
***********************************************/
void InitTimer1(void)
{
    TMOD = 0x20;	//定時器T1,工作方式2,自動重灌
    TH1 = 0x38;
    TL1 = 0x38;		//定時約200us
    EA = 1;			//開啟總中斷
    ET1 = 1;		//開定時中斷
    TR1 = 1;		//啟動定時器T1
}

/***********************************************
程式名稱:ADC0809_Result
程式功能:獲取ADC0809 ADC值
程式說明:因為ADC0809是並行輸出方式,所以輸出的
		  ADC結果反饋到微控制器的對應的OUTPUT_PORT引腳。
程式輸入:uchar adc_number(要獲取的ADC通道號1-7)
程式返回:unsigned char result 返回當前通道的ADC值
***********************************************/
uchar ADC0809_Result(uchar adc_number)
{
	uchar result = 0;	//定義一個臨時變數,用於存放轉換後的ADC值
	
	/*初始化ADC0809*/
	START = 0;
    OE = 0;
    START = 1;
	
	/*選擇相應輸入通道*/
	switch(adc_number)
	{
		case 1:
			ADD_C = 0;
			ADD_B = 0;
			ADD_A = 0;
			break;
		case 2:
			ADD_C = 0;
			ADD_B = 0;
			ADD_A = 1;
			break;
		case 3:
			ADD_C = 0;
			ADD_B = 1;
			ADD_A = 1;
			break;
		case 4:
			ADD_C = 0;
			ADD_B = 1;
			ADD_A = 1;
			break;
		case 5:
			ADD_C = 1;
			ADD_B = 0;
			ADD_A = 0;
			break;
		case 6:
			ADD_C = 1;
			ADD_B = 0;
			ADD_A = 1;
			break;
		case 7:
			ADD_C = 1;
			ADD_B = 1;
			ADD_A = 0;
			break;
		case 8:
			ADD_C = 1;
			ADD_B = 1;
			ADD_A = 1;
			break;
	}
	
	/*啟動ADC轉換*/
	START = 0;
	while(EOC == 0);	//等待EOC = 1
	OE = 1;				//開啟三態門輸出
	result = OUTPUT_PORT;
	
	return result;		//將結果返回輸出
}

void main()
{
	uint temp = 0;
	
	system_Init();
	InitTimer1();
	while(1)
	{
		temp = ADC0809_Result(3);					//讀取通道3的AD值
		temp = temp * 0.0196078431372549 * 100;		//轉換電壓=轉換ADC值 * (參考電壓 / 解析度最大位) * 100(保留小數點後兩位)
		disply_three_digit(temp);					//將讀取到的ADC值送到數碼管
	}
}

void Timer1Interrupt(void) interrupt 3
{
	CLOCK = ~CLOCK;		//通過定時器T1提供時鐘脈衝
}