單片機是一個典型的數字系統,數字系統只能呢個對輸入的數字信號進行處理,其輸出信號也是數字的。但工業或者生活中的很多量都是模擬量,這些模擬量可以通過傳感器變成與之對應的電壓、電流等模擬量。為了實現數字系統對這些電模擬量的測量,運算和控制,就需要一個模擬量和數字量之間的相互轉化的過程。
A/D和D/A的基本概念
A/D是模擬量到數字量的轉換,依靠的是模數轉換器(Analog to Digital Converter),簡稱ADC。D/A是數字量到模擬量的轉換,依靠的是數模轉換器(Digital to Analog Converter),簡稱DAC。它們的道理是完全一樣的,只是轉換方向不同,因此我們講解過程主要以A/D為例來講解。
什么是模擬量?就是指變量在一定范圍內連續變化的量,總之,任何兩個數字之間都有無限個中間值,所以稱之為連續變化的量,也就是模擬量。ADC就是起到把連續的信號用離散的數字表達出來的作用
A/D的主要指標
我們在選取和使用A/D的時候,依靠什么指標很重要。由於AD的種類很多,分為積分型,逐次逼近型,並行/串行比較型,Σ-Δ型等多種類型。同時指標也比較多,並且有的指標還有輕微差別。
1.ADC的位數
一個n位的ADC表示這個ADC共有2的n次方個刻度。8位ADC,輸出的是從0-255的256個數字,也就是2的8次方的一個數據刻度。
2.基准源
基准源也叫基准電壓,是ADC的1個重要標准,要想把輸入ADC的信號測量准確,那么基准源首先要准,基准源的偏差會導致轉換結果的偏差比如一根米尺,總長度本應該是1米,假定這根米尺被火烤了一下,實際變成了1.2米,再用這根米尺測物體長度的話自然就有了較大的偏差。假如我們的基准源應該是5.10V,但是實際上提供的卻是4.5V,這樣誤把4.5V當成了5.10V來處理的話,偏差也會比較大。
3.分辨率
分辨率是數字量變化的一個最小刻度時,模擬信號的變化量,定義為滿刻度量程與2n-1的比值。假定5.10V的電壓系統,使用8位的ADC進行測量,那么相當於0-255一共256個刻度把5.10V划分成255份,那么分辨率就是5.10/255=0.02V.
INL(積分非線性度)和DNL(差分非線性度)
一般容易混淆兩個概念就是分辨率和精度,認為分辨率越高,則精度越高,而實際上,兩者並沒有必然的聯系。分辨率是用來描述刻度划分的,而精度是用來描述准確程度。同樣一根米尺,刻度數相同,分辨率就相當,但是精度卻可以相差很大,ADC精度關系重大的兩個指標是INL(Integral NonLiner)和DNL(Differencial NonLiner)。
INL指的是ADC器件在所有的數值上對應的模擬值,和真實值之間的誤差最大的哪一個點的誤差值,是ADC最重要的一個精度標准,單位是LSB。LSB是最低有效位的意思,那么實際上對應的就是ADC的分辨率。一個基准為5.10V的8位ADC,它的分辨率就是0.02V,用它去測量一個電壓信號,得到的實際結果是100,就是表示它測到的電壓值是100*0.02=2V,假定它的INL是1LSB,就是表示這個電壓信號真實的准確值是在1.98V——2.02之間的,按理想情況對應的數字因該是99-101,測量誤差是一個最低的有效位,即1LBS。
DNL表示的是ADC相鄰兩個刻度之間最大的差異,單位也是LSB。一把分辨率是1毫米的尺子,相鄰的刻度之間並不都剛好是一毫米,而總是會存在或大或小的誤差。同理,一個ADC的兩個刻度線之間也不總是准確的等於分辨率,也是存在誤差,這個就是DNL。一個基准為5.10V的8位ADC,假定DNL是0.5LSB,那么當它的轉換結果從100增加到101時,理想情況下實際電壓應該增加0.02V,但DNL為0.5LSB的情況下實際電壓為0.01——0.03V之間,值得一提的是DNL並非一定小於1LSB,很多時候它會等於或者大於1LSB,這在相當一定程度上刻度紊亂,當實際電壓保持不變時,ADC得出的結果可能會在幾個數值之間跳動,很大程度上由於這個原因(但並不完全是,因為還有很多干擾)。
5.轉換速率
轉換速率,是指ADC每秒能進行采樣轉換的最大次數,單位是sps(或s/s、sa/s,即samples per second),它與ADC完成一次從模擬到數字的轉換所需要的時間互為倒數關系。ADC的種類比較多,其中積分型的ADC轉換時間是毫秒級的,屬於低速ADC;逐次逼近型ADC轉換時間是微妙級的,屬於中速ADC;並行/串行的ADC的轉換時間可達到納秒級,屬於高速ADC。
ADC的這幾個主要指標大家先熟悉一下,對於其它的,作為一個入門級別的選手來說,先不着急深入理解。以后使用過程中遇到了,再查找相關資料深入學習,當前重點是在頭腦中建立一個ADC的基本概念。
PCF8591
PCF8591是一個單電源低功耗的8位CMOS數據采集器件,具有4路模擬輸入,1路模擬輸出和一個串行I2C總線接口用來與單片機通信。引腳1、2、3、4是4路模擬輸入,引腳5、6、7是I2C總線的硬件地址,8腳是數字地GND,9腳和10腳是I2C總線的SDA和SCL。12腳是時鍾選擇引腳,如果接高電平表示用外部時鍾輸入,接低電平則用內部時鍾,我們這套電路用的是內部時鍾,因此12腳直接接GND,同時11腳懸空。13腳是模擬地AGND,在實際開發中,如果有比較復雜的模擬電路,那么AGND部分在布局布線上要特別處理,而且和GND的連接也有多種方式,這里大家先了解即可。在我們板子上沒有復雜的模擬部分電路,所以我們把AGND和GND接到一起。14腳是基准源,15腳是DAC的模擬輸出,16腳是供電電源VCC。
PCF8591的ADC是逐次逼近型的,轉換速率算是中速,但是它的速度瓶頸在I2C通信上。由於I2C通信速度較慢,所以最終的PCF8591的轉換速度,直接取決於I2C的通信速率。由於I2C速度的限制,所以PCF8591得算是個低速的AD和DA的集成,主要應用在一些轉換速度要求不高,希望成本較低的場合,比如電池供電設備,測量電池的供電電壓,電壓低於某一個值,報警提示更換電池等類似場合。
軟件編程
PCF8591的通信接口是I2C,那么編程肯定是要符合這個協議的。單片機對PCF8591進行初始化,一共發送三個字節即可。第一個字節,和EEPROM類似,是器件地址字節,其中7位代表地址,1位代表讀寫方向。地址高4位固定是0b1001,低三位是A2,A1,A0,這三位我們電路上都接了GND,因此也就是0b000,發送到PCF8591的第二個字節將被存儲在控制寄存器,用於控制PCF8591的功能。其中第3位和第7位是固定的0,另外6位各自有各自的作用。
控制字節的第6位是DA使能位,這一位置1表示DA輸出引腳使能,會產生模擬電壓輸出功能。第4位和第5位可以實現把PCF8591的4路模擬輸入配置成單端模式和差分模式,單端模式和差分模式的區別,我們在17.5節有介紹,這里大家只需要知道這兩位是配置AD輸入方式的控制位即可
控制字節的第2位是自動增量控制位,自動增量的意思就是,比如我們一共有4個通道,當我們全部使用的時候,讀完了通道0,下一次再讀,會自動進入通道1進行讀取,不需要我們指定下一個通道,由於A/D每次讀到的數據,都是上一次的轉換結果,所以同學們在使用自動增量功能的時候,要特別注意,當前讀到的是上一個通道的值。為了保持程序的通用性,我們的代碼沒有使用這個功能,直接做了一個通用的程序。
控制字節的第0位和第1位就是通道選擇位了,00、01、10、11代表了從0到3的一共4個通道選擇。
發送給PCF8591的第三個字節D/A數據寄存器,表示D/A模擬輸出的電壓值。D/A模擬我們一會介紹,大家知道這個字節的作用即可。我們如果僅僅使用A/D功能的話,就可以不發送第三個字節。
/* 讀取當前的ADC轉換值,chn-ADC通道號0~3 */
unsigned char GetADCValue(unsigned char chn)
{
unsigned char val;
I2CStart();
if (!I2CWrite(0x48<<1)) //尋址PCF8591,如未應答,則停止操作並返回0
{
I2CStop();
return 0;
}
I2CWrite(0x40|chn); //寫入控制字節,選擇轉換通道
I2CStart();
I2CWrite((0x48<<1)|0x01); //尋址PCF8591,指定后續為讀操作
I2CReadACK(); //先空讀一個字節,提供采樣轉換時間
val = I2CReadNAK(); //讀取剛剛轉換完的值
I2CStop();
return val;
}
DAC的輸出
此處輸入代碼/* 設置DAC輸出值,val-設定值 */
void SetDACOut(unsigned char val)
{
I2CStart();
if (!I2CWrite(0x48<<1)) //尋址PCF8591,如未應答,則停止操作並返回
{
I2CStop();
return;
}
I2CWrite(0x40); //寫入控制字節
I2CWrite(val); //寫入DA值
I2CStop();
}