鍍金池/ 教程/ 數(shù)據(jù)庫(kù)/ 17.4 PCF8591 應(yīng)用程序
18. RS485 通信與 Modbus 協(xié)議
17.5 A/D 差分輸入信號(hào)
15.8 C 語(yǔ)言復(fù)合數(shù)據(jù)類型(結(jié)構(gòu)體,共用體,枚舉類型)
16.3 NEC 協(xié)議紅外遙控器
13.1 單片機(jī)通信時(shí)序解析
14.4 單片機(jī) EEPROM 單字節(jié)讀寫操作時(shí)序
13.3 多個(gè) .c 文件的初步認(rèn)識(shí)
18.2 Modbus 通信協(xié)議介紹
15.1 BCD 碼介紹
18.3 單片機(jī) Modbus 多機(jī)通信程序設(shè)計(jì)
18.1 單片機(jī) RS485 通信接口、控制線、原理圖及程序?qū)嵗?/span>
15. 實(shí)時(shí)時(shí)鐘 DS1302
14.7 單片機(jī) I2C 和 EEPROM 的綜合編程
17. 模數(shù)轉(zhuǎn)換與數(shù)模轉(zhuǎn)換
16.2 紅外遙控通信原理
13.2 1602 液晶整屏移動(dòng)程序
17.6 D/A 輸出
17.7 單片機(jī)信號(hào)發(fā)生器程序
16.4 溫度傳感器 DS18B20
14.6 單片機(jī)EEPROM的頁(yè)寫入
13.4 單片機(jī)計(jì)算器程序設(shè)計(jì)[詳細(xì)]
17.2 A/D(模數(shù)轉(zhuǎn)換)的主要指標(biāo)
17.4 PCF8591 應(yīng)用程序
17.1 A/D 和 D/A 的基本概念
17.3 PCF8591硬件接口(電路圖引腳圖)
14.3 單片機(jī) EEPROM 簡(jiǎn)介
13.5 單片機(jī)串口通信原理和控制程序
15.5 DS1302 寄存器介紹
15.2 單片機(jī) SPI 通信接口
15.6 DS1302 通信時(shí)序介紹
14.5 單片機(jī) EEPROM 多字節(jié)讀寫操作時(shí)序
16. 紅外通信與 DS18B20 溫度傳感器
14.1 單片機(jī) I2C 時(shí)序介紹
15.3 實(shí)時(shí)時(shí)鐘芯片 DS1302 介紹
15.9 單片機(jī)電子時(shí)鐘程序設(shè)計(jì)
16.1 紅外光的基本原理
15.4 DS1302 的硬件信息
15.7 DS1302 的 BURST 模式
14.2 單片機(jī) I2C 尋址模式
14. 單片機(jī) I2C 總線與 EEPROM
13. 單片機(jī) 1602 液晶與串口的應(yīng)用實(shí)例

17.4 PCF8591 應(yīng)用程序

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

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/46.png" alt="" />

圖17-5 PCF8591 地址字節(jié)

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

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/47.png" alt="" />

圖17-6 PCF8591 控制字節(jié)

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

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/48.png" alt="" />

圖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è)通道的值。為了保持程序的通用性,我們的代碼沒有使用這個(gè)功能,直接做了一個(gè)通用的程序。

控制字節(jié)的第0位和第1位就是通道選擇位了,00、01、10、11代表了從0到3的一共4個(gè)通道選擇。

發(fā)送給 PCF8591 的第三個(gè)字節(jié) D/A 數(shù)據(jù)寄存器,表示 D/A 模擬輸出的電壓值。D/A 模擬我們一會(huì)介紹,大家知道這個(gè)字節(jié)的作用即可。我們?nèi)绻麅H僅使用 A/D 功能的話,就可以不發(fā)送第三個(gè)字節(jié)。

下面我們用一個(gè)程序,把 AIN0、AIN1、AIN3 測(cè)到的電壓值顯示在液晶上,同時(shí)大家可以轉(zhuǎn)動(dòng)電位器,會(huì)發(fā)現(xiàn) AIN0 的值發(fā)生變化。 /*Lcd1602.c 文件程序源代碼***/ (此處省略,可參考之前章節(jié)的代碼) /*I2C.c 文件程序源代碼***/ (此處省略,可參考之前章節(jié)的代碼)

/*****************************main.c 文件程序源代碼******************************/
#include <reg52.h>
bit flag300ms = 1; //300ms 定時(shí)標(biāo)志
unsigned char T0RH = 0; //T0 重載值的高字節(jié)
unsigned char T0RL = 0; //T0 重載值的低字節(jié)

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

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

    EA = 1; //開總中斷
    ConfigTimer0(10); //配置 T0 定時(shí) 10ms
    InitLcd1602(); //初始化液晶
    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);
        }
    }
}
/* 讀取當(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ù)為讀操作
    I2CReadACK(); //先空讀一個(gè)字節(jié),提供采樣轉(zhuǎn)換時(shí)間
    val = I2CReadNAK(); //讀取剛剛轉(zhuǎn)換完的值
    I2CStop();
    return val;
}
/* 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é)束符
}
/* 配置并啟動(dòng) T0,ms-T0 定時(shí)時(shí)間 */
void ConfigTimer0(unsigned int ms){
    unsigned long tmp; //臨時(shí)變量
    tmp = 11059200 / 12; //定時(shí)器計(jì)數(shù)頻率
    tmp = (tmp * ms) / 1000; //計(jì)算所需的計(jì)數(shù)值
    tmp = 65536 - tmp; //計(jì)算定時(shí)器重載值
    tmp = tmp + 12; //補(bǔ)償中斷響應(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
}
/* T0 中斷服務(wù)函數(shù),執(zhí)行 300ms 定時(shí) */
void InterruptTimer0() interrupt 1{
    static unsigned char tmr300ms = 0;
    TH0 = T0RH; //重新加載重載值
    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í)鐘是 I2C 的 SCL,8個(gè) SCL 周期完成一次轉(zhuǎn)換,所以當(dāng)前的轉(zhuǎn)換結(jié)果總是在下一個(gè)字節(jié)的8個(gè) SCL 上才能讀出,因此我們這里第一條語(yǔ)句的作用是產(chǎn)生一個(gè)整體的 SCL 時(shí)鐘提供給 PCF8591 進(jìn)行 A/D 轉(zhuǎn)換,第二次是讀取當(dāng)前的轉(zhuǎn)換結(jié)果。如果我們只使用第二條語(yǔ)句的話,每次讀到的都是上一次的轉(zhuǎn)換結(jié)果。