這一章節(jié)的內(nèi)容比較簡(jiǎn)單,大家反復(fù)細(xì)讀學(xué)習(xí)《手把手教你學(xué)51單片機(jī)》文檔第17章就會(huì)了解透徹這部分的知識(shí)。
1.簡(jiǎn)要解析概念和代碼
我們知道PCF8591有4個(gè)通道是可以用來(lái)讀取輸入的電壓值的。
那么宋老師寫的“unsigned char GetADCValue(unsigned char chn)”就是選擇讀取其中一個(gè)通道輸入的測(cè)量電壓是多少V,受基準(zhǔn)源影響,測(cè)量范圍是0V~2.5V,函數(shù)返回值代表讀出的電壓值為0.01V的n倍左右,也就是返回值為150時(shí),測(cè)得的輸入電壓大概為1.5V。大于2.5V的輸入電壓,返回值最高為255,也就是測(cè)量范圍只支持到最高為2.5V。這個(gè)概念叫做A/D。
D/A則是選擇通道需要輸出的電壓值,范圍同樣是只能輸出0V~2.5V之間,宋老師寫的“void SetDACOut(unsigned char val)”,參數(shù)val就是決定輸出多少V的電壓,參數(shù)為100時(shí),PCF8591的AOUT引腳輸出電壓就是1.0V。
函數(shù)“void ValueToString(unsigned char *str, unsigned char val)”也比較實(shí)用,
我們一起把這三個(gè)函數(shù)封裝成單獨(dú)的文件,創(chuàng)建好“adc.c”和“adc.h”文件。
2.adc.c的代碼
#include <reg52.h> #include <iic.h> /* 讀取當(dāng)前的ADC轉(zhuǎn)換值,chn-ADC通道號(hào)0~3 */ unsigned char GetADCValue(unsigned char chn) { unsigned char val; I2CStart(); if (!I2CWrite(0x48<<1)) //尋址PCF8591,如未應(yīng)答,則停止操作并返回0 { I2CStop(); return 0; } I2CWrite(0x40|chn); //寫入控制字節(jié),選擇轉(zhuǎn)換通道 I2CStart(); I2CWrite((0x48<<1)|0x01); //尋址PCF8591,指定后續(xù)為讀操作 I2CReadNAK_OR_ACK(0); //先空讀一個(gè)字節(jié),提供采樣轉(zhuǎn)換時(shí)間 val = I2CReadNAK_OR_ACK(1); //讀取剛剛轉(zhuǎn)換完的值 I2CStop(); return val; } /* 設(shè)置DAC輸出值,val-設(shè)定值 */ void SetDACOut(unsigned char val) { I2CStart(); if (!I2CWrite(0x48<<1)) //尋址PCF8591,如未應(yīng)答,則停止操作并返回 { I2CStop(); return; } I2CWrite(0x40); //寫入控制字節(jié) I2CWrite(val); //寫入DA值 I2CStop(); } /* ADC轉(zhuǎn)換值轉(zhuǎn)為實(shí)際電壓值的字符串形式,str-字符串指針,val-AD轉(zhuǎn)換值 */ void ValueToString(unsigned char *str, unsigned char val) { //電壓值=轉(zhuǎn)換結(jié)果*2.5V/255,式中的25隱含了一位十進(jìn)制小數(shù) val = (val*25) / 255; str[0] = (val/10) + '0'; //整數(shù)位字符 str[1] = '.'; //小數(shù)點(diǎn) str[2] = (val%10) + '0'; //小數(shù)位字符 str[3] = 'V'; //電壓?jiǎn)挝? str[4] = '\0'; //結(jié)束符 }
3.adc.h的代碼
#ifndef __ADC_H__ #define __ADC_H__ unsigned char GetADCValue(unsigned char chn); //讀取當(dāng)前的ADC轉(zhuǎn)換值,chn-ADC通道號(hào)0~3 void SetDACOut(unsigned char val); //設(shè)置DAC輸出值,val-設(shè)定值 void ValueToString(unsigned char *str, unsigned char val); //ADC轉(zhuǎn)換值轉(zhuǎn)為實(shí)際電壓值的字符串形式,str-字符串指針,val-AD轉(zhuǎn)換值 #endif
4.main.c測(cè)試代碼
#include <reg52.h> #include <function.h>//詳見第六章第8講 #include <timer.h> //詳見第八章第11講 #include <lcd.h> //詳見第十一章第3講 #include <adc.h> u8 flag300ms=0; void main() { u8 val,key,volt; u8 str[10]; EA = 1; KEY_Init(); //初始化按鍵模塊 InitLcd1602(); //初始化液晶屏 TIM0_Init(10000,11); //定時(shí)10ms,11是微調(diào)使定時(shí)更精確 LcdShowStr(6, 0, "AIN3"); while (1) { //請(qǐng)將開發(fā)板底部中間那里的位置插針將第一個(gè)和最后一個(gè)跳線帽拔出,把AOUT引腳與AIN3引腳用杜邦線連接起來(lái)測(cè)試代碼 if (flag300ms) { flag300ms = 0; val = GetADCValue(3); //獲取ADC通道3的轉(zhuǎn)換值 ValueToString(str, val); //轉(zhuǎn)為字符串格式的電壓值 LcdShowStr(6, 1, str); //顯示通道3的電壓 } //按鍵控制AOUT的輸出電壓值 key=KEY_Scan(0,500); if(key==4) { if (volt<25)volt++; SetDACOut(volt*255/25); //轉(zhuǎn)換為AD輸出值 } if(key==12) { if(volt>0)volt--; SetDACOut(volt*255/25); //轉(zhuǎn)換為AD輸出值 } } } void TIM0_IRQHandler() interrupt 1 { static u8 tmr300ms = 0; TH0 = T0RH; //重新加載重載值 TL0 = T0RL; tmr300ms++; if (tmr300ms >= 30) //定時(shí)300ms { tmr300ms = 0; flag300ms = 1; } }
我們就是通過(guò)K4,K12來(lái)控制PCF8591的AOUT引腳輸出不同的電壓,每按一次按鍵電壓改變0.1V,然后通過(guò)PCF8591的ADC通道3來(lái)檢測(cè)所讀到的電壓值顯示在液晶屏上,在測(cè)試之前我們還需在開發(fā)板上像如下一樣用杜邦線連接起來(lái)
因?yàn)镻CF8591的AOUT引腳輸出電壓有50mV的偏差,也就是有0.05V的偏差,所以我們通過(guò)按鍵把AOUT引腳輸出電壓調(diào)到最高時(shí),液晶屏上看到的只是2.4V,也就是說(shuō)由于偏差導(dǎo)致,PCF8591的ADC通道3讀到的電壓有可能是2.45V,然后液晶屏就只顯示成2.4V而已。
C語(yǔ)言網(wǎng)提供由在職研發(fā)工程師或ACM藍(lán)橋杯競(jìng)賽優(yōu)秀選手錄制的視頻教程,并配有習(xí)題和答疑,點(diǎn)擊了解:
一點(diǎn)編程也不會(huì)寫的:零基礎(chǔ)C語(yǔ)言學(xué)練課程
解決困擾你多年的C語(yǔ)言疑難雜癥特性的C語(yǔ)言進(jìn)階課程
從零到寫出一個(gè)爬蟲的Python編程課程
只會(huì)語(yǔ)法寫不出代碼?手把手帶你寫100個(gè)編程真題的編程百練課程
信息學(xué)奧賽或C++選手的 必學(xué)C++課程
藍(lán)橋杯ACM、信息學(xué)奧賽的必學(xué)課程:算法競(jìng)賽課入門課程
手把手講解近五年真題的藍(lán)橋杯輔導(dǎo)課程