久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 20659|回復(fù): 10
打印 上一主題 下一主題
收起左側(cè)

第17章 A/D和D/A的學(xué)習(xí)

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:1 發(fā)表于 2013-11-14 00:31 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
本教材現(xiàn)以連載的方式由網(wǎng)絡(luò)發(fā)布,并將于2014年由清華大學(xué)出版社出版最終完整版,版權(quán)歸作者和清華大學(xué)出版社所有。本著開(kāi)源、分享的理念,本教材可以自由傳播及學(xué)習(xí)使用,但是務(wù)必請(qǐng)注明出處來(lái)自金沙灘工作室

從我們學(xué)到的知識(shí)了解到,我們的單片機(jī)是一個(gè)典型的數(shù)字系統(tǒng)。數(shù)字系統(tǒng)只能對(duì)輸入的數(shù)字信號(hào)進(jìn)行處理,其輸出信號(hào)也是數(shù)字信號(hào)。但是在工業(yè)檢測(cè)系統(tǒng)和日常生活中的許多物理量都是模擬量,比如溫度、長(zhǎng)度、壓力、速度等等,這些模擬量可以通過(guò)傳感器變成與之對(duì)應(yīng)的電壓、電流等電模擬量。為了實(shí)現(xiàn)數(shù)字系統(tǒng)對(duì)這些電模擬量的檢測(cè)、運(yùn)算和控制,就需要一個(gè)模擬量和數(shù)字量之間相互轉(zhuǎn)換的過(guò)程。這節(jié)課我們就要學(xué)習(xí)這個(gè)相互轉(zhuǎn)換過(guò)程。
17.1 A/D和D/A的基本概念
A/D是模擬量到數(shù)字量的轉(zhuǎn)換,依靠的是模數(shù)轉(zhuǎn)換器(Analog to Digital Converter),簡(jiǎn)稱(chēng)ADCD/A是數(shù)字量到模擬量的轉(zhuǎn)換,依靠的是數(shù)模轉(zhuǎn)換器(Digital to Analog Converter),簡(jiǎn)稱(chēng)DAC。他們的道理是完全一樣的,只是轉(zhuǎn)換方向不同,因此我們講解過(guò)程主要以A/D為例來(lái)講解。
很多同學(xué)學(xué)到A/D這部分的時(shí)候,感覺(jué)是個(gè)難點(diǎn),概念掌握不清楚。我個(gè)人認(rèn)為主要原因不在于技術(shù)問(wèn)題,而是不太會(huì)感悟生活。我們生活中有很多很多A/D的例子,只是沒(méi)有在單片機(jī)領(lǐng)域里應(yīng)用而已,下面我?guī)е蠹乙黄鸶形蛞幌?/font>A/D的概念。
什么是模擬量?就是指變量在一定范圍內(nèi)連續(xù)變化的量,也就是在一定范圍內(nèi)可以取任意值。比如我們米尺,從01米之間,可以是任意值。什么是任意值,也就是可以是1cm,也可以是1.001cm,當(dāng)然也可以10.000......后邊有無(wú)限個(gè)小數(shù)。總之,任何兩個(gè)數(shù)字之間都有無(wú)限個(gè)中間值,所以稱(chēng)之為連續(xù)變化的量,也就是模擬量。
而我們用的米尺上被我們?nèi)藶榈淖錾狭丝潭确?hào),每?jī)蓚(gè)刻度之間的間隔是1mm,這個(gè)刻度實(shí)際上就是我們對(duì)模擬量的數(shù)字化,由于有一定的間隔,不是連續(xù)的,所以在專(zhuān)業(yè)領(lǐng)域里我們稱(chēng)之為離散的。我們的ADC就是起到把連續(xù)的信號(hào)用離散的數(shù)字表達(dá)出來(lái)的作用。那么我們就可以使用米尺這個(gè)“ADC”來(lái)測(cè)量連續(xù)的長(zhǎng)度或者高度這些模擬量。如圖17-1一個(gè)簡(jiǎn)單的米尺刻度示意圖。

17-1 米尺刻度示意圖
我們往杯子里倒水,水位會(huì)隨著倒入的水量的多少而變化。現(xiàn)在就用這個(gè)米尺來(lái)測(cè)量我們杯子里的水位的高度。水位變化是連續(xù)的,而我們只能通過(guò)尺子上的刻度來(lái)讀取水位的高度,獲取我們想得到的水位的數(shù)字量信息。這個(gè)過(guò)程,就可以簡(jiǎn)單理解為我們電路中的ADC采樣。

17.2 A/D的主要指標(biāo)
我們?cè)谶x取和使用A/D的時(shí)候,依靠什么指標(biāo)來(lái)判斷很重要。由于AD的種類(lèi)很多,分為積分型、逐次逼近型、并行/串行比較型、Σ-Δ型等多種類(lèi)型。同時(shí)指標(biāo)也比較多,并且有的指標(biāo)還有輕微差別,具體可上m.zg4o1577.cn查詢(xún).在這里我是以同學(xué)們便于理解的方法去講解,如果和某一確定類(lèi)型A/D概念和原理有差別,也不會(huì)影響實(shí)際應(yīng)用。
1、ADC的位數(shù)。
一個(gè)n位的ADC表示這個(gè)ADC共有2n次方個(gè)刻度。8位的ADC,輸出的是從0
255一共256個(gè)數(shù)字量,也就是28次方個(gè)數(shù)據(jù)刻度。
2、基準(zhǔn)源
基準(zhǔn)源,也叫基準(zhǔn)電壓,是ADC的一個(gè)重要指標(biāo),要想把輸入ADC的信號(hào)測(cè)量準(zhǔn)確,那么基準(zhǔn)源首先要準(zhǔn),基準(zhǔn)源的偏差會(huì)直接導(dǎo)致轉(zhuǎn)換結(jié)果的偏差。比如一根米尺,總長(zhǎng)度本應(yīng)該是1米,假定這根米尺被火烤了一下,實(shí)際變成了1.2米,再用這根米尺測(cè)物體長(zhǎng)度的話(huà)自然就有了較大的偏差。假如我們的基準(zhǔn)源應(yīng)該是5.10V,但是實(shí)際上提供的卻是4.5V,這樣誤把4.5V當(dāng)成了5.10V來(lái)處理的話(huà),偏差也會(huì)比較大。
3、分辨率
分辨率是數(shù)字量變化一個(gè)最小刻度時(shí),模擬信號(hào)的變化量,定義為滿(mǎn)刻度量程與2n-1的
比值。5.10V的電壓系統(tǒng),使用8位的ADC進(jìn)行測(cè)量,那么相當(dāng)于0255一共256個(gè)刻度,把5.10V平均分成了255份,那么分辨率就是5.10/255 = 0.02V
4、INL(積分非線性度)DNL(差分非線性度)
初學(xué)者最容易混淆的兩個(gè)概念就是“分辨率”和“精度”,認(rèn)為分辨率越高,則精度越高,而實(shí)際上,兩者之間是沒(méi)有必然聯(lián)系的。分辨率是用來(lái)描述刻度劃分的,而精度是用來(lái)描述準(zhǔn)確程度的。同樣一根米尺,刻度數(shù)相同,分辨率就相當(dāng),但是精度卻可以相差很大,如圖17-2所示。

17-2 米尺精度對(duì)比
17-2表示的精度一目了然,不需多說(shuō)。和ADC精度關(guān)系重大的兩個(gè)指標(biāo)是INL(Integral NonLiner)DNL(Differencial NonLiner)
INL指的是ADC器件在所有的數(shù)值上對(duì)應(yīng)的模擬值,和真實(shí)值之間誤差最大的那一個(gè)點(diǎn)的誤差值,是ADC最重要的一個(gè)精度指標(biāo),單位是LSBLSBLeast Significant Bit)是最低有效位的意思,那么它實(shí)際上對(duì)應(yīng)的就是ADC的分辨率。一個(gè)基準(zhǔn)為5.10V8ADC,它的分辨率就是0.02V,用它去測(cè)量一個(gè)電壓信號(hào),得到的結(jié)果是100,就表示它測(cè)到的電壓值是100*0.02V=2V,假定它的INL1LSB,就表示這個(gè)電壓信號(hào)真實(shí)的準(zhǔn)確值是在1.98V2.02V之間的,按理想情況對(duì)應(yīng)得到的數(shù)字應(yīng)該是99101,測(cè)量誤差是一個(gè)最低有效位,即1LSB
DNL表示的是ADC相鄰兩個(gè)刻度之間最大的差異,單位是LSB。一把分辨率是1毫米的尺子,相鄰的刻度之間并不都剛好是1毫米,而總是會(huì)存在或大或小的誤差。同理,一個(gè)ADC的兩個(gè)刻度線之間也不總是準(zhǔn)確的等于分辨率,也是存在誤差,這個(gè)誤差就是DNL。一個(gè)基準(zhǔn)為5.10V8ADC,假定它的DNL0.5LSB,那么當(dāng)它的轉(zhuǎn)換結(jié)果從100增加到101時(shí),理想情況下實(shí)際電壓應(yīng)該增加0.02V,但DNL0.5LSB的情況下實(shí)際電壓的增加值是在0.010.03之間。值得一提的是DNL并非一定小于1LSB,很多時(shí)候它會(huì)等于或大于1LSB,這就相當(dāng)于是一定程度上的刻度紊亂,當(dāng)實(shí)際電壓保持不變時(shí),ADC得出的結(jié)果可能會(huì)在幾個(gè)數(shù)值之間跳動(dòng),很大程度上就是由于這個(gè)原因(但并不完全是,因?yàn)檫有無(wú)時(shí)無(wú)處不在的干擾的影響)。
5、轉(zhuǎn)換速率
轉(zhuǎn)換速率,是指ADC每秒能進(jìn)行采樣轉(zhuǎn)換的最大次數(shù),單位是sps(或s/ssa/s,即samples per second),它與ADC完成一次從模擬到數(shù)字的轉(zhuǎn)換所需要的時(shí)間互為倒數(shù)關(guān)系。ADC的種類(lèi)比較多,其中積分型的ADC轉(zhuǎn)換時(shí)間是毫秒級(jí)的,屬于低速ADC;逐次逼近型ADC轉(zhuǎn)換時(shí)間是微妙級(jí)的,屬于中速ADC;并行/串行的ADC的轉(zhuǎn)換時(shí)間可達(dá)到納秒級(jí),屬于高速ADC
ADC的這幾個(gè)主要指標(biāo)大家先熟悉一下,對(duì)于其他的,作為一個(gè)入門(mén)級(jí)別的選手來(lái)說(shuō),先不著急深入理解。以后使用過(guò)程中遇到了,再查找相關(guān)資料深入學(xué)習(xí),當(dāng)前重點(diǎn)是在頭腦中建立一個(gè)ADC的基本概念。

17.3 PCF8591的硬件接口
PCF8591是一個(gè)單電源低功耗的8CMOS數(shù)據(jù)采集器件,具有4路模擬輸入,1路模擬輸出和一個(gè)串行I2C總線接口用來(lái)與MCU通信。3個(gè)地址引腳A0A1A2用于編程硬件地址,允許最多8個(gè)器件連接到I2C總線而不需要額外的片選電路。器件的地址、控制以及數(shù)據(jù)都是通過(guò)I2C總線來(lái)傳輸,我們先看一下PCF8591的原理圖,如圖17-3所示。

圖17-3 PCF8591原理圖
其中引腳12344路模擬輸入,引腳567I2C總線的硬件地址,8腳是數(shù)字GND9腳和10腳是I2C總線的SDASCL12腳是時(shí)鐘選擇引腳,如果接高電平表示用外部時(shí)鐘輸入,接低電平則用內(nèi)部時(shí)鐘,我們這套電路用的是內(nèi)部時(shí)鐘,因此12腳直接接GND,同時(shí)11腳懸空。13腳是模擬GND,在實(shí)際開(kāi)發(fā)中,如果有比較復(fù)雜的模擬電路,那么模擬GND部分在布局布線上要特別處理,而且和數(shù)字GND的連接也有多種方式,這里大家先了解即可。在我們板子上沒(méi)有復(fù)雜的模擬部分電路,所以我們把模擬的GND和數(shù)字GND接到一起即可。14腳是基準(zhǔn)源,15腳是DAC的模擬輸出,16腳是供電電源VCC
PCF8591ADC是逐次逼近型的,轉(zhuǎn)換速率算是中速,但是他的速度瓶頸在I2C通信上。由于I2C通信速度較慢,所以最終的PCF8591的轉(zhuǎn)換速度,直接取決于I2C的通信速率。由于I2C速度的限制,所以PCF8591的算是個(gè)低速的ADDA集成,主要應(yīng)用在一些轉(zhuǎn)換速度要求不高,希望成本較低的場(chǎng)合,比如電池供電設(shè)備,測(cè)量電池的供電電壓,電壓低于某一個(gè)值,報(bào)警提示更換電池等類(lèi)似場(chǎng)合。
Vref基準(zhǔn)電壓的提供,方法一是采用簡(jiǎn)易的原則,直接接到VCC上去。但是由于VCC會(huì)受到整個(gè)線路的用電功耗情況影響,一來(lái)不是準(zhǔn)確的5V,實(shí)測(cè)大多在4.8V左右,二來(lái)隨著整個(gè)系統(tǒng)負(fù)載情況的變化會(huì)產(chǎn)生波動(dòng),所以只能用在簡(jiǎn)易的、對(duì)精度要求不高的場(chǎng)合。方法二是使用專(zhuān)門(mén)的基準(zhǔn)電壓器件,比如TL431,它可以提供一個(gè)精度很高的2.5V的電壓基準(zhǔn),這是我們通常采用的方法。如圖17-4所示。

圖17-4 PCF8591電路圖
圖中J17是雙排插針,大家可以根據(jù)自己的需求選擇跳線帽短接還是使用杜邦線接其他外接電路,都是可以的。在這個(gè)地方,我們直接把J173腳和4腳用跳線帽短路起來(lái),那么現(xiàn)在Vref的基準(zhǔn)源就是2.5V了。分別把56789101112用跳線帽短接起來(lái)的話(huà),那么我們的AIN0實(shí)測(cè)的就是滑動(dòng)變阻器的分壓值,AIN1AIN2測(cè)的是GND的值,AIN3測(cè)的是+5V的值。這里需要注意的是,AIN3雖然測(cè)的是+5V的值,但是對(duì)于AD來(lái)說(shuō),只要輸入信號(hào)超過(guò)Vref基準(zhǔn)源,它得到的始終都是最大值,即255,也就是說(shuō)它實(shí)際上無(wú)法測(cè)量超過(guò)其Vref的電壓信號(hào)。需要注意的是,所有輸入信號(hào)的電壓值都不能超過(guò)VCC,即+5V,否則可能會(huì)損壞ADC芯片。

17.4 PCF8591的軟件編程
PCF8591的通信接口是I2C,那么編程肯定是符合這個(gè)協(xié)議的。單片機(jī)對(duì)PCF8591進(jìn)行初始化,一共發(fā)送三個(gè)字節(jié)即可。第一個(gè)字節(jié),和EEPROM類(lèi)似,第一個(gè)字節(jié)是地址字節(jié),其中7位代表地址,1位代表讀寫(xiě)方向。地址高4位固定是1001,低三位是A2A1A0,這三位我們電路上都接了GND,因此也就是000,如圖17-5所示。

17-5 PCF8591地址字節(jié)
發(fā)送到PCF8591的第二個(gè)字節(jié)將被存儲(chǔ)在控制寄存器,用于控制PCF8591的功能。其中第3位和第7位是固定的0,另外6位各自有各自的作用,如圖17-6所示,我逐一介紹。

圖17-6 PCF8591控制字節(jié)
控制字節(jié)的第6位是DA使能位,這一位置1表示DA輸出引腳使能,會(huì)產(chǎn)生模擬電壓輸出功能。第4位和第5位可以實(shí)現(xiàn)把PCF85914路模擬輸入配置成單端模式和差分模式,單端模式和差分模式的區(qū)別,我們17.4章節(jié)有介紹,這里大家只需要知道這兩位是配置AD輸入方式的控制位即可,如圖17-7所示。

圖17-7 PCF8591模擬輸入配置方式

控制字節(jié)的第2位是自動(dòng)增量控制位,自動(dòng)增量的意思就是,比如我們一共有4個(gè)通道,當(dāng)我們?nèi)渴褂玫臅r(shí)候,讀完了通道0,下一次再讀,會(huì)自動(dòng)進(jìn)入通道1進(jìn)行讀取,不需要我們指定下一個(gè)通道,由于A/D每次讀到的數(shù)據(jù),都是上一次的轉(zhuǎn)換結(jié)果,所以同學(xué)們?cè)谑褂米詣?dòng)增量功能的時(shí)候,要特別注意,當(dāng)前讀到的是上一個(gè)通道的值。為了保持程序的通用性,我們的代碼沒(méi)有使用這個(gè)功能,直接做了一個(gè)通用的程序。
控制字節(jié)的第0位和第1位就是通道選擇位了,00011011代表了從03的一共4個(gè)通道選擇。
發(fā)送給PCF8591的第三個(gè)字節(jié)D/A數(shù)據(jù)寄存器,表示D/A模擬輸出的電壓值。D/A模擬我們一會(huì)介紹,大家知道這個(gè)字節(jié)的作用即可。我們?nèi)绻麅H僅使用A/D功能的話(huà),就可以不發(fā)送第三個(gè)字節(jié)。
下面我們用一個(gè)程序,把AIN0AIN1AIN3測(cè)到的電壓值顯示在液晶上,同時(shí)大家可以轉(zhuǎn)動(dòng)電位器,會(huì)發(fā)現(xiàn)AIN0的值發(fā)生變化。
/***********************lcd1602.c文件程序源代碼*************************/
#include <reg52.h>

#define LCD1602_DB   P0

sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E  = P1^5;

void LcdWaitReady()  //等待液晶準(zhǔn)備好
{
    unsigned char sta;

    LCD1602_DB = 0xFF;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do
    {
        LCD1602_E = 1;
        sta = LCD1602_DB; //讀取狀態(tài)字
        LCD1602_E = 0;
    } while (sta & 0x80); //bit7等于1表示液晶正忙,重復(fù)檢測(cè)直到其等于0為止
}
void LcdWriteCmd(unsigned char cmd)  //寫(xiě)入命令函數(shù)
{
    LcdWaitReady();
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DB = cmd;
    LCD1602_E  = 1;
    LCD1602_E  = 0;
}
void LcdWriteDat(unsigned char dat)  //寫(xiě)入數(shù)據(jù)函數(shù)
{
    LcdWaitReady();
    LCD1602_RS = 1;
    LCD1602_RW = 0;
    LCD1602_DB = dat;
    LCD1602_E  = 1;
    LCD1602_E  = 0;
}
void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str)  //顯示字符串,屏幕起始坐標(biāo)(x,y),字符串指針str
{
    unsigned char addr;

    //由輸入的顯示坐標(biāo)計(jì)算顯示RAM的地址
    if (y == 0)
        addr = 0x00 + x; //第一行字符地址從0x00起始
    else
        addr = 0x40 + x; //第二行字符地址從0x40起始

    //由起始顯示RAM地址連續(xù)寫(xiě)入字符串
    LcdWriteCmd(addr | 0x80); //寫(xiě)入起始地址
    while (*str != '\0')      //連續(xù)寫(xiě)入字符串?dāng)?shù)據(jù),直到檢測(cè)到結(jié)束符
    {
        LcdWriteDat(*str);
        str++;
    }
}
void LcdInit()  //液晶初始化函數(shù)
{
    LcdWriteCmd(0x38);  //16*2顯示,5*7點(diǎn)陣,8位數(shù)據(jù)接口
    LcdWriteCmd(0x0C);  //顯示器開(kāi),光標(biāo)關(guān)閉
    LcdWriteCmd(0x06);  //文字不動(dòng),地址自動(dòng)+1
    LcdWriteCmd(0x01);  //清屏
}
/***********************I2C.c文件程序源代碼*************************/
#include <reg52.h>
#include <intrins.h>

#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}

sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;

void I2CStart()  //產(chǎn)生總線起始信號(hào)
{
    I2C_SDA = 1; //首先確保SDASCL都是高電平
    I2C_SCL = 1;
    I2CDelay();
    I2C_SDA = 0; //先拉低SDA
    I2CDelay();
    I2C_SCL = 0; //再拉低SCL
}
void I2CStop()   //產(chǎn)生總線停止信號(hào)
{
    I2C_SCL = 0; //首先確保SDASCL都是低電平
    I2C_SDA = 0;
    I2CDelay();
    I2C_SCL = 1; //先拉高SCL
    I2CDelay();
    I2C_SDA = 1; //再拉高SDA
    I2CDelay();
}
bit I2CWrite(unsigned char dat) //I2C總線寫(xiě)操作,待寫(xiě)入字節(jié)dat,返回值為應(yīng)答狀態(tài)
{
    bit ack;  //用于暫存應(yīng)答位的值
    unsigned char mask;  //用于探測(cè)字節(jié)內(nèi)某一位值的掩碼變量

    for (mask=0x80; mask!=0; mask>>=1) //從高位到低位依次進(jìn)行
    {
        if ((mask&dat) == 0)  //該位的值輸出到SDA
            I2C_SDA = 0;
        else
            I2C_SDA = 1;
        I2CDelay();
        I2C_SCL = 1;          //拉高SCL
        I2CDelay();
        I2C_SCL = 0;          //再拉低SCL,完成一個(gè)位周期
    }
    I2C_SDA = 1;   //8位數(shù)據(jù)發(fā)送完后,主機(jī)釋放SDA,以檢測(cè)從機(jī)應(yīng)答
    I2CDelay();
    I2C_SCL = 1;   //拉高SCL
    ack = I2C_SDA; //讀取此時(shí)的SDA值,即為從機(jī)的應(yīng)答值
    I2CDelay();
    I2C_SCL = 0;   //再拉低SCL完成應(yīng)答位,并保持住總線

    return (~ack); //應(yīng)答值取反以符合通常的邏輯:0=不存在或忙或?qū)懭胧。?/font>1=存在且空閑或?qū)懭氤晒?/font>
}
unsigned char I2CReadNAK() //I2C總線讀操作,并發(fā)送非應(yīng)答信號(hào),返回值為讀到的字節(jié)
{
    unsigned char mask;
    unsigned char dat;

    I2C_SDA = 1;  //首先確保主機(jī)釋放SDA
    for (mask=0x80; mask!=0; mask>>=1) //從高位到低位依次進(jìn)行
    {
        I2CDelay();
        I2C_SCL = 1;      //拉高SCL
        if(I2C_SDA == 0)  //讀取SDA的值
            dat &= ~mask; //0時(shí),dat中對(duì)應(yīng)位清零
        else
            dat |= mask;  //1時(shí),dat中對(duì)應(yīng)位置1
        I2CDelay();
        I2C_SCL = 0;      //再拉低SCL,以使從機(jī)發(fā)送出下一位
    }
    I2C_SDA = 1;   //8位數(shù)據(jù)發(fā)送完后,拉高SDA,發(fā)送非應(yīng)答信號(hào)
    I2CDelay();
    I2C_SCL = 1;   //拉高SCL
    I2CDelay();
    I2C_SCL = 0;   //再拉低SCL完成非應(yīng)答位,并保持住總線

    return dat;
}
unsigned char I2CReadACK() //I2C總線讀操作,并發(fā)送應(yīng)答信號(hào),返回值為讀到的字節(jié)
{
    unsigned char mask;
    unsigned char dat;

    I2C_SDA = 1;  //首先確保主機(jī)釋放SDA
    for (mask=0x80; mask!=0; mask>>=1) //從高位到低位依次進(jìn)行
    {
        I2CDelay();
        I2C_SCL = 1;      //拉高SCL
        if(I2C_SDA == 0)  //讀取SDA的值
            dat &= ~mask; //0時(shí),dat中對(duì)應(yīng)位清零
        else
            dat |= mask;  //1時(shí),dat中對(duì)應(yīng)位置1
        I2CDelay();
        I2C_SCL = 0;      //再拉低SCL,以使從機(jī)發(fā)送出下一位
    }
    I2C_SDA = 0;   //8位數(shù)據(jù)發(fā)送完后,拉低SDA,發(fā)送應(yīng)答信號(hào)
    I2CDelay();
    I2C_SCL = 1;   //拉高SCL
    I2CDelay();
    I2C_SCL = 0;   //再拉低SCL完成應(yīng)答位,并保持住總線

    return dat;
}
/***********************main.c文件程序源代碼*************************/
#include <reg52.h>

bit flag300ms = 1;       //300ms定時(shí)標(biāo)志
unsigned char T0RH = 0;  //T0重載值的高字節(jié)
unsigned char T0RL = 0;  //T0重載值的低字節(jié)

unsigned char GetADCValue(unsigned char chn);
void ValueToString(unsigned char *str, unsigned char val);
void ConfigTimer0(unsigned int ms);
extern void LcdInit();
extern void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadACK();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);

void main ()
{
    unsigned char val;
    unsigned char str[10];

    EA = 1;           //開(kāi)總中斷
    ConfigTimer0(10); //配置T0定時(shí)10ms
    LcdInit();        //初始化液晶   
    LcdShowStr(0, 0, "AIN0  AIN1  AIN3");  //顯示通道指示

    while(1)
    {
        if (flag300ms)
        {
            flag300ms = 0;
            //顯示通道0的電壓
            val = GetADCValue(0);     //獲取ADC通道0的轉(zhuǎn)換值
            ValueToString(str, val);  //轉(zhuǎn)為字符串格式的電壓值
            LcdShowStr(0, 1, str);    //顯示到液晶上
            //顯示通道1的電壓
            val = GetADCValue(1);
            ValueToString(str, val);
            LcdShowStr(6, 1, str);
            //顯示通道3的電壓
            val = GetADCValue(3);
            ValueToString(str, val);
            LcdShowStr(12, 1, str);
        }
    }
}

unsigned char GetADCValue(unsigned char chn)  //讀取當(dāng)前的ADC轉(zhuǎn)換值,chnADC通道號(hào)0-3
{
    unsigned char val;

                    I2CStart();
            if (!I2CWrite(0x48<<1))   //尋址PCF8591,如未應(yīng)答,則停止操作并返回0
    {
                I2CStop();
                        return 0;
            }
    I2CWrite(0x40|chn);       //寫(xiě)入控制字節(jié),選擇轉(zhuǎn)換通道
    I2CStart();
    I2CWrite((0x48<<1)|0x01); //尋址PCF8591,指定后續(xù)為讀操作   
            I2CReadACK();             //先空讀一個(gè)字節(jié),提供采樣轉(zhuǎn)換時(shí)間
            val = I2CReadNAK();       //讀取剛剛轉(zhuǎn)換完的值
            I2CStop();

    return val;
}
void ValueToString(unsigned char *str, unsigned char val)  //ADC轉(zhuǎn)換值轉(zhuǎn)為實(shí)際電壓值的字符串形式
{
    val = (val*25) / 255;    //電壓值=轉(zhuǎn)換結(jié)果*2.5V/255,式中的25隱含了一位十進(jìn)制小數(shù)
    str[0] = (val/10) + '0'; //整數(shù)位字符
    str[1] = '.';            //小數(shù)點(diǎn)
    str[2] = (val%10) + '0'; //小數(shù)位字符
    str[3] = 'V';            //電壓?jiǎn)挝?/font>
    str[4] = '\0';           //結(jié)束符
}

void ConfigTimer0(unsigned int ms)  //T0配置函數(shù)
{
    unsigned long tmp;

    tmp = 11059200 / 12;      //定時(shí)器計(jì)數(shù)頻率
    tmp = (tmp * ms) / 1000;  //計(jì)算所需的計(jì)數(shù)值
    tmp = 65536 - tmp;        //計(jì)算定時(shí)器重載值
    tmp = tmp + 12;           //修正中斷響應(yīng)延時(shí)造成的誤差

    T0RH = (unsigned char)(tmp >> 8);  //定時(shí)器重載值拆分為高低字節(jié)
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0;   //清零T0的控制位
    TMOD |= 0x01;   //配置T0為模式1
    TH0 = T0RH;     //加載T0重載值
    TL0 = T0RL;
    ET0 = 1;        //使能T0中斷
    TR0 = 1;        //啟動(dòng)T0
}
void InterruptTimer0() interrupt 1  //T0中斷服務(wù)函數(shù)
{
    static unsigned char tmr300ms = 0;

    TH0 = T0RH;  //定時(shí)器重新加載重載值
    TL0 = T0RL;
    tmr300ms++;
    if (tmr300ms >= 30)  //定時(shí)300ms
    {
        tmr300ms = 0;
        flag300ms = 1;
    }
}
    細(xì)心閱讀程序的同學(xué)會(huì)發(fā)現(xiàn),我們程序在進(jìn)行A/D讀取數(shù)據(jù)的時(shí)候,共使用了兩條程序去讀了2個(gè)字節(jié)。I2CReadACK();        val = I2CReadNAK();PCF8591的轉(zhuǎn)換時(shí)鐘是I2CSCL,而A/D的特點(diǎn)是每次讀到的都是上一次的轉(zhuǎn)換結(jié)果,因此我們這里第一條語(yǔ)句的作用是產(chǎn)生一個(gè)整體的SCL時(shí)鐘提供給PCF8591進(jìn)行A/D轉(zhuǎn)換,第二次是讀取當(dāng)前的轉(zhuǎn)換結(jié)果。如果我們只使用第二條語(yǔ)句的話(huà),每次讀到的都是上一次的轉(zhuǎn)換結(jié)果。

17.5 A/D差分輸入信號(hào)
細(xì)心的同學(xué)在閱讀PCF8591手冊(cè)的時(shí)候,會(huì)發(fā)現(xiàn)控制字的第4位和第5位是用于控制PCF8591的模擬輸入引腳是單端輸入還是差分輸入。差分輸入是模擬電路常用的一個(gè)技巧,這里我們簡(jiǎn)單介紹一些相關(guān)內(nèi)容。
從嚴(yán)格意義上來(lái)講,所有的信號(hào)都是差分信號(hào),因?yàn)樗械碾妷褐荒苁窍鄬?duì)于另外一個(gè)電壓而言。但是大多數(shù)系統(tǒng),我們都是把系統(tǒng)的GND作為基準(zhǔn)點(diǎn)。而對(duì)于A/D來(lái)說(shuō)的差分輸入,通常情況下是除了GND以外,另外兩路幅度相同,極性相反的差分輸入信號(hào),其實(shí)理解起來(lái)很簡(jiǎn)單,就如同我們的蹺蹺板一樣。如圖17-8所示。

圖17-8 差分輸入原理
差分輸入的話(huà),就不是單個(gè)輸入,而是由2個(gè)輸入端構(gòu)成的一組差分輸入。我們的PCF8591一共是4個(gè)模擬輸入端,可以配置成4種模式,最典型的是4個(gè)輸入端構(gòu)造成的兩路差分模式,如圖17-9所示。

圖17-9 PCF8591差分輸入模式
當(dāng)控制字的第4位和第5位都是1的時(shí)候,那么4路模擬被配置成2路差分模式輸入channel 0channel 1。我們以channel 0為例,其中AIN0是正向輸入端,AIN1是反向輸入端,他們之間的信號(hào)輸入是幅度相同,極性相反的信號(hào),通過(guò)減法器后,得到的是兩個(gè)輸入通道的差值,如圖17-10所示。

圖17-10 差分輸入信號(hào)
通常情況下,差分輸入的中線是基準(zhǔn)電壓的一半,我們的基準(zhǔn)電壓是2.5V,假如1.25V作為中線,V+AIN0的輸入波形,V-AIN1的輸入波形,Signal Value就是經(jīng)過(guò)減法器后的波形。很多A/D都采用差分的方式輸入,因?yàn)椴罘州斎敕绞奖葐味溯斎雭?lái)說(shuō),有很強(qiáng)的抗干擾能力。
1、單端輸入信號(hào)時(shí),如果一線上發(fā)生干擾變化,比如幅度增大5mvGND不變,測(cè)到的數(shù)據(jù)會(huì)有偏差;而差分信號(hào)輸入時(shí),當(dāng)外界存在干擾信號(hào)時(shí),幾乎同時(shí)被耦合到兩條線上,幅度增大5mv會(huì)同時(shí)增大5mv,而接收端關(guān)心的只是兩個(gè)信號(hào)的差值,所以外界的這種共模噪聲可以被完全抵消掉。
2、由于兩根信號(hào)的極性相反,他們對(duì)外輻射的電磁場(chǎng)可以相互抵消,有效的抑制釋放到外界的電磁能量。
在我們的KST-51開(kāi)發(fā)板上,我們沒(méi)有做差分信號(hào)輸入的實(shí)驗(yàn)環(huán)境,由于這個(gè)內(nèi)容在A/D部分比較重要,所以大家還是要學(xué)習(xí)一下的。

17.6 D/A輸出
D/A是和A/D剛好反方向,一個(gè)8位的D/A,從0255,代表了02.55V的話(huà),那么我們用單片機(jī)給第三個(gè)字節(jié)發(fā)送100D/A引腳就會(huì)輸出一個(gè)1V的電壓,發(fā)送200就輸出一個(gè)2V的電壓,很簡(jiǎn)單,我們用一個(gè)簡(jiǎn)單的程序?qū)崿F(xiàn)出來(lái),并且通過(guò)上、下按鍵可以增大輸出幅度值,每次增加或減小0.1V。如果有萬(wàn)用表的話(huà),可以直接測(cè)試一下板子上AOUT點(diǎn)的輸出電壓,觀察它的變化。由于PCF8591的偏置誤差最大是50mv(由數(shù)據(jù)手冊(cè)提供),所以我們用萬(wàn)用表測(cè)到的電壓值和理論值之間的誤差就應(yīng)該在50mV以?xún)?nèi)。
/***********************I2C.c文件程序源代碼*************************/
                          略
/***********************keyboard.c文件程序源代碼*************************/
#include <reg52.h>

sbit KEY_IN_1  = P2^4;  //矩陣按鍵的掃描輸入引腳1
sbit KEY_IN_2  = P2^5;  //矩陣按鍵的掃描輸入引腳2
sbit KEY_IN_3  = P2^6;  //矩陣按鍵的掃描輸入引腳3
sbit KEY_IN_4  = P2^7;  //矩陣按鍵的掃描輸入引腳4
sbit KEY_OUT_1 = P2^3;  //矩陣按鍵的掃描輸出引腳1
sbit KEY_OUT_2 = P2^2;  //矩陣按鍵的掃描輸出引腳2
sbit KEY_OUT_3 = P2^1;  //矩陣按鍵的掃描輸出引腳3
sbit KEY_OUT_4 = P2^0;  //矩陣按鍵的掃描輸出引腳4

const unsigned char code KeyCodeMap[4][4] = { //矩陣按鍵編號(hào)到PC標(biāo)準(zhǔn)鍵盤(pán)鍵碼的映射表
    { '1',  '2',  '3', 0x26 }, //數(shù)字鍵1、數(shù)字鍵2、數(shù)字鍵3、向上鍵
    { '4',  '5',  '6', 0x25 }, //數(shù)字鍵4、數(shù)字鍵5、數(shù)字鍵6、向左鍵
    { '7',  '8',  '9', 0x28 }, //數(shù)字鍵7、數(shù)字鍵8、數(shù)字鍵9、向下鍵
    { '0', 0x1B, 0x0D, 0x27 }  //數(shù)字鍵0ESC鍵、  回車(chē)鍵、 向右鍵
};
unsigned char pdata KeySta[4][4] = {  //全部矩陣按鍵的當(dāng)前狀態(tài)
    {1, 1, 1, 1},
    {1, 1, 1, 1},
    {1, 1, 1, 1},
    {1, 1, 1, 1}
};

extern void KeyAction(unsigned char keycode);

void KeyDriver()  //按鍵動(dòng)作驅(qū)動(dòng)函數(shù)
{
    unsigned char i, j;
    static unsigned char pdata backup[4][4] = {  //按鍵值備份,保存前一次的值
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1}
    };

    for (i=0; i<4; i++)  //循環(huán)掃描4*4的矩陣按鍵
    {
        for (j=0; j<4; j++)
        {
            if (backup[ i][j] != KeySta[ i][j])  //檢測(cè)按鍵動(dòng)作
            {
                if (backup[ i][j] != 0)  //按鍵按下時(shí)執(zhí)行動(dòng)作
                {
                    KeyAction(KeyCodeMap[ i][j]);  //調(diào)用按鍵動(dòng)作函數(shù)
                }
                backup[ i][j] = KeySta[ i][j];
            }
        }
    }
}
void KeyScan()  //按鍵掃描函數(shù)
{
    unsigned char i;
    static unsigned char keyout = 0;  //矩陣按鍵掃描輸出計(jì)數(shù)器
    static unsigned char keybuf[4][4] = {  //按鍵掃描緩沖區(qū),保存一段時(shí)間內(nèi)的掃描值
        {0xFF, 0xFF, 0xFF, 0xFF},
        {0xFF, 0xFF, 0xFF, 0xFF},
        {0xFF, 0xFF, 0xFF, 0xFF},
        {0xFF, 0xFF, 0xFF, 0xFF}
    };

    //將一行的4個(gè)按鍵值移入緩沖區(qū)
    keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
    keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
    keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
    keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;

    //消抖后更新按鍵狀態(tài)
    for (i=0; i<4; i++)  //每行4個(gè)按鍵,所以循環(huán)4
    {
        if ((keybuf[keyout][ i] & 0x0F) == 0x00)
        {   //連續(xù)4次掃描值為0,即16ms(4*4ms)內(nèi)都只檢測(cè)到按下?tīng)顟B(tài)時(shí),可認(rèn)為按鍵已按下
            KeySta[keyout][ i] = 0;
        }
        else if ((keybuf[keyout][ i] & 0x0F) == 0x0F)
        {   //連續(xù)4次掃描值為1,即16ms(4*4ms)內(nèi)都只檢測(cè)到彈起狀態(tài)時(shí),可認(rèn)為按鍵已彈起
            KeySta[keyout][ i] = 1;
        }
    }

    //執(zhí)行下一次的掃描輸出
    keyout++;
    keyout &= 0x03;
    switch (keyout)
    {
        case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
        case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
        case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
        case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
        default: break;
    }
}
/***********************main.c文件程序源代碼*************************/
#include <reg52.h>

unsigned char T0RH = 0;  //T0重載值的高字節(jié)
unsigned char T0RL = 0;  //T0重載值的低字節(jié)

void ConfigTimer0(unsigned int ms);
extern void KeyScan();
extern void KeyDriver();
extern void I2CStart();
extern void I2CStop();
extern bit I2CWrite(unsigned char dat);

void main ()
{   
    EA = 1;           //開(kāi)總中斷
    ConfigTimer0(1);  //配置T0定時(shí)1ms

    while(1)
    {
        KeyDriver();
    }
}

void SetDACOut(unsigned char val)  //設(shè)置DAC輸出值
{
            I2CStart();
            if (!I2CWrite(0x48<<1)) //尋址PCF8591,如未應(yīng)答,則停止操作并返回
    {
                I2CStop();
                        return;
            }
    I2CWrite(0x40);         //寫(xiě)入控制字節(jié)
    I2CWrite(val);          //寫(xiě)如DA值  
            I2CStop();
}
void KeyAction(unsigned char keycode)  //按鍵動(dòng)作函數(shù),根據(jù)鍵碼執(zhí)行相應(yīng)動(dòng)作
{
    static unsigned char volt = 0;  //輸出電壓值,隱含了一位十進(jìn)制小數(shù)位

    if (keycode == 0x26)  //向上鍵,增加0.1V電壓值
    {
        if (volt < 25)
        {
            volt++;
            SetDACOut(volt*255/25); //轉(zhuǎn)換為AD輸出值
        }
    }
    else if (keycode == 0x28)  //向下鍵,減小0.1V電壓值
    {
        if (volt > 0)
        {
            volt--;
            SetDACOut(volt*255/25); //轉(zhuǎn)換為AD輸出值
        }
    }
}
void ConfigTimer0(unsigned int ms)  //T0配置函數(shù)
{
    unsigned long tmp;

    tmp = 11059200 / 12;      //定時(shí)器計(jì)數(shù)頻率
    tmp = (tmp * ms) / 1000;  //計(jì)算所需的計(jì)數(shù)值
    tmp = 65536 - tmp;        //計(jì)算定時(shí)器重載值
    tmp = tmp + 34;           //修正中斷響應(yīng)延時(shí)造成的誤差

    T0RH = (unsigned char)(tmp >> 8);  //定時(shí)器重載值拆分為高低字節(jié)
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0;   //清零T0的控制位
    TMOD |= 0x01;   //配置T0為模式1
    TH0 = T0RH;     //加載T0重載值
    TL0 = T0RL;
    ET0 = 1;        //使能T0中斷
    TR0 = 1;        //啟動(dòng)T0
}
void InterruptTimer0() interrupt 1  //T0中斷服務(wù)函數(shù)
{
    TH0 = T0RH;  //定時(shí)器重新加載重載值
    TL0 = T0RL;
    KeyScan();
}

17.7 PCF8591信號(hào)發(fā)生器
有了D/A這個(gè)武器,我們就不僅僅可以輸出方波信號(hào)了,可以輸出任意波形了,比如正弦波、三角波、鋸齒波等等。以正弦波為例,首先我們要建立一個(gè)正弦波的波表。這些不需要大家去逐一計(jì)算,可以通過(guò)搜索找到正弦波數(shù)據(jù)表,然后可以根據(jù)時(shí)間參數(shù)自己選取其中一定量數(shù)據(jù)作為我們程序的正弦波表,我們的程序代碼選取了32個(gè)點(diǎn)。
/***********************I2C.c文件程序源代碼*************************/
                          略
/***********************keyboard.c文件程序源代碼********************/
                          略
/***********************main.c文件程序源代碼************************/
#include <reg52.h>

unsigned char T0RH = 0;  //T0重載值的高字節(jié)
unsigned char T0RL = 0;  //T0重載值的低字節(jié)
unsigned char T1RH = 1;  //T1重載值的高字節(jié)
unsigned char T1RL = 1;  //T1重載值的低字節(jié)

unsigned char code SinWave[] = {  //正弦波波表
127, 152, 176, 198, 217, 233, 245, 252, 255, 252, 245, 233, 217, 198,
176, 152,127, 102,  78,  56,  37,  21,   9,   2,   0,   2,   9,  21,  
37,  56,  78, 102,
    };
unsigned char code TriWave[] = {  //三角波波表
    0,  16,  32,  48,  64,  80,  96, 112, 128, 144, 160, 176, 192, 208,
224, 240,255, 240, 224, 208, 192, 176, 160, 144, 128, 112,  96,  80,  
64,  48,  32,  16,
    };
unsigned char code SawWave[] = {  //鋸齒波表
0,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  88,  96, 104, 112,
120,128, 136, 144, 152, 160, 168, 176, 184, 192, 200, 208, 216, 224,
232, 240, 248,
    };
unsigned char code *pWave;  //波表指針

void SetWaveFreq(unsigned char freq);
void ConfigTimer0(unsigned int ms);
extern void KeyScan();
extern void KeyDriver();
extern void I2CStart();
extern void I2CStop();
extern bit I2CWrite(unsigned char dat);

void main ()
{   
    EA = 1;           //開(kāi)總中斷
    ConfigTimer0(1);  //配置T0定時(shí)1ms
    pWave = SinWave;  //默認(rèn)正弦波
    SetWaveFreq(10);  //默認(rèn)頻率10Hz

    while(1)
    {
        KeyDriver();
    }
}

void KeyAction(unsigned char keycode)  //按鍵動(dòng)作函數(shù),根據(jù)鍵碼執(zhí)行相應(yīng)動(dòng)作
{
    static unsigned char wave = 0;

    if (keycode == 0x26)  //向上鍵,切換波形
    {
        if (wave == 0)
        {
            wave = 1;
            pWave = TriWave;
        }
        else if (wave == 1)
        {
            wave = 2;
            pWave = SawWave;
        }
        else
        {
            wave = 0;
            pWave = SinWave;
        }
    }
}
void SetDACOut(unsigned char val)  //設(shè)置DAC輸出值
{
            I2CStart();
            if (!I2CWrite(0x48<<1)) //尋址PCF8591,如未應(yīng)答,則停止操作并返回
    {
                I2CStop();
                        return;
            }
    I2CWrite(0x40);  //寫(xiě)入控制字節(jié)
    I2CWrite(val);   //寫(xiě)如DA值  
            I2CStop();
}
void SetWaveFreq(unsigned char freq) //設(shè)置輸出波形的頻率
{
    unsigned long tmp;

    tmp = (11059200/12) / (freq*32); //定時(shí)器計(jì)數(shù)頻率,是波形頻率的32
    tmp = 65536 - tmp;               //計(jì)算定時(shí)器重載值
    tmp = tmp + 36;                  //修正中斷響應(yīng)延時(shí)造成的誤差

    T1RH = (unsigned char)(tmp >> 8);  //定時(shí)器重載值拆分為高低字節(jié)
    T1RL = (unsigned char)tmp;
    TMOD &= 0x0F;   //清零T1的控制位
    TMOD |= 0x10;   //配置T1為模式1
    TH1 = T1RH;     //加載T1重載值
    TL1 = T1RL;
    ET1 = 1;        //使能T1中斷
    PT1 = 1;        //設(shè)置為高優(yōu)先級(jí)
    TR1 = 1;        //啟動(dòng)T1
}
void ConfigTimer0(unsigned int ms)  //T0配置函數(shù)
{
    unsigned long tmp;

    tmp = 11059200 / 12;      //定時(shí)器計(jì)數(shù)頻率
    tmp = (tmp * ms) / 1000;  //計(jì)算所需的計(jì)數(shù)值
    tmp = 65536 - tmp;        //計(jì)算定時(shí)器重載值
    tmp = tmp + 34;           //修正中斷響應(yīng)延時(shí)造成的誤差

    T0RH = (unsigned char)(tmp >> 8);  //定時(shí)器重載值拆分為高低字節(jié)
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0;   //清零T0的控制位
    TMOD |= 0x01;   //配置T0為模式1
    TH0 = T0RH;     //加載T0重載值
    TL0 = T0RL;
    ET0 = 1;        //使能T0中斷
    TR0 = 1;        //啟動(dòng)T0
}
void InterruptTimer0() interrupt 1  //T0中斷服務(wù)函數(shù)
{
    TH0 = T0RH;  //定時(shí)器重新加載重載值
    TL0 = T0RL;
    KeyScan();
}
void InterruptTimer1() interrupt 3  //T1中斷服務(wù)函數(shù)
{
    static unsigned char i = 0;

    TH1 = T1RH;  //定時(shí)器重新加載重載值
    TL1 = T1RL;
    //循環(huán)輸出波表中的數(shù)據(jù)
    SetDACOut(pWave[ i]);
    i++;
    if (i >= 32)
    {
        i = 0;
    }
}
這個(gè)程序可以通過(guò)“向上”按鍵來(lái)實(shí)現(xiàn)波形輸出切換,但是我們的D/A輸出沒(méi)有辦法接到顯示界面,所以我們用示波器抓出來(lái)波形給大家看一下,如圖17-11、圖17-12、圖17-13所示。

17-11 D/A輸出正弦波形

17-12 D/A輸出三角波形

17-13 D/A輸出鋸齒波形
這幾張圖可以直接說(shuō)明我們實(shí)現(xiàn)的波形發(fā)生器的程序。細(xì)心的同學(xué)會(huì)發(fā)現(xiàn)我們波形上有很多小鋸齒,沒(méi)有平滑的連起來(lái)。這是因?yàn)槲覀?font face="Times New Roman">DA最多只能輸出0Vref之間的256個(gè)離散的電壓值,而不是連續(xù)的任意值,所以每個(gè)離散值都會(huì)持續(xù)一定的時(shí)間,然后跳變到下一個(gè)離散值,于是就呈現(xiàn)出了波形上的這種鋸齒。在實(shí)際開(kāi)發(fā)中,我們只需要在DA后級(jí)加一級(jí)低通濾波電路,就可以讓帶鋸齒的波形變得平滑起來(lái)。

17.8 作業(yè)
1、掌握A/DD/A的基本概念和性能指標(biāo)。
2、將AD采集到的數(shù)值顯示到數(shù)碼管上。
[size=12.0000pt]3、將信號(hào)發(fā)生器的程序改裝,可以通過(guò)按鍵實(shí)現(xiàn)頻率的調(diào)整。

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏10 分享淘帖 頂3 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:59133 發(fā)表于 2014-2-16 22:17 | 只看該作者
好文章,學(xué)習(xí)了。謝謝!
回復(fù)

使用道具 舉報(bào)

板凳
ID:69481 發(fā)表于 2014-11-26 16:22 | 只看該作者
講的不錯(cuò),保存吧!
回復(fù)

使用道具 舉報(bào)

地板
ID:72292 發(fā)表于 2015-1-15 23:36 | 只看該作者
你的帖子都自己寫(xiě)的哈,很不錯(cuò),今天才發(fā)現(xiàn)這個(gè)論壇。
回復(fù)

使用道具 舉報(bào)

5#
ID:78723 發(fā)表于 2015-5-29 10:43 | 只看該作者
老師 您好,不太明白您講的差分輸入情況下的ADC,比如說(shuō)一個(gè)10位的ADC,參考電壓是2.5V,對(duì)于單端輸入時(shí),輸入2.5V,對(duì)應(yīng)采集到的數(shù)字量是1023,于是有計(jì)算公式:Vin=ADC_Value*2.5/1023.若是同樣的ADC,改成差分輸入形式,對(duì)應(yīng)關(guān)系是怎么樣的呢?請(qǐng)您不吝賜教!
回復(fù)

使用道具 舉報(bào)

6#
ID:91448 發(fā)表于 2015-10-1 16:31 | 只看該作者
講的不錯(cuò),保存吧!
回復(fù)

使用道具 舉報(bào)

7#
ID:90889 發(fā)表于 2016-3-14 22:33 | 只看該作者
怎么收藏?
回復(fù)

使用道具 舉報(bào)

8#
ID:135316 發(fā)表于 2016-8-13 16:03 | 只看該作者
老師怎么接呢?
回復(fù)

使用道具 舉報(bào)

9#
ID:204609 發(fā)表于 2017-9-15 15:49 | 只看該作者
此 鍵盤(pán)程序有問(wèn)題  通過(guò)pdata定義的數(shù)據(jù) 我們好像是訪問(wèn)不了的 鍵盤(pán)運(yùn)行不起來(lái)
回復(fù)

使用道具 舉報(bào)

10#
ID:93625 發(fā)表于 2018-7-27 10:26 | 只看該作者
繼續(xù)學(xué)習(xí),這么好的教程不能浪費(fèi)了
回復(fù)

使用道具 舉報(bào)

11#
ID:358091 發(fā)表于 2021-12-8 23:33 | 只看該作者
好教程需要好好看看
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 国产成人a亚洲精品 | 国产精品成人国产乱一区 | 久久午夜剧场 | 99热在这里只有精品 | 午夜日韩 | 国产一二区免费视频 | 欧美日韩视频 | 日韩欧美大片在线观看 | 青青草网站在线观看 | av官网在线 | 日本一区二区不卡 | 国产精品久久久久久久久久免费看 | 美女毛片免费看 | 国产一级在线 | 亚洲免费人成在线视频观看 | 在线观看免费黄色片 | 99热视 | 成人在线精品 | 久久出精品 | 亚洲精品乱码久久久久久按摩观 | 一区二区三区国产 | 国产精品久久久久久久久久免费 | 成人在线精品视频 | 天天操夜夜爽 | 精品国产18久久久久久二百 | 久久精品青青大伊人av | 天天舔天天 | 国产免费麻豆视频 | 免费v片| 中文字幕国产视频 | 亚洲综合婷婷 | 久久一区二区三区电影 | 成人国产精品 | 亚洲一区二区三区四区视频 | 国产精品免费观看视频 | 啪一啪在线视频 | 久久69精品久久久久久久电影好 | 夏同学福利网 | 中文字幕在线播放第一页 | 在线观看a视频 | 久久剧场 |