網路城邦
上一篇 回創作列表 下一篇   字體:
I2C_PCF8574AT LCD介面應用於STC89C516RD+之溫度與濕度計
2017/06/17 08:37:15瀏覽1706|回應0|推薦0

I2C LCD介面應用於STC89C516RD+之溫度與濕度計

 

一,       前言

市面上有應用於Arduino樹莓派專用於I2C_LCD介面,使用的是PCF8574AT I2C擴充I/O,在網路上到處都能搜尋到相關前輩的設計範例,但是應用89C51環境的介紹,卻非常的稀少,本來在使用89C51時,使用直接由PORT去推動,可說是簡單而且資料充足,但是當在設計上需要更多的I/O PORT時,就因為直接推動佔去了 8+3=11 PINS,而大傷腦筋,因此才動起如何將此I2C_LCD介面轉換至89C51環境,所以花數天時間,將I2C  PCF8574AT的驅動程式改寫,並且調整時脈,總算整體I2C程式撰寫完畢,在此分享給各位同好,如此下來將原先需佔用11 PINS I/O PORT節省為2PIN即可,可說是讓89C51的設計保留了更多的I/O腳,如此下來將來的24C02 FLASH RAM或是PCF8574KEY INPUT/ LED OUT等的I2C元件也可以一起併入在同一個SMB BUS中,可以說是一個突破,此次使用的CPUSTC89C516RD+ PLCC44包裝,而相關的IC DATASHEETI2C BUSLCD INTERFACEDHT-11溫濕度運等資料在此就不再多加撰述,各位同好可在網路中搜尋,就可取得。

 

二, 程式主要參數說明

        PCF8574AT        I2C Addr’s Port = 0x70h

        (如果I2C_LCD介面板是使用的PCF8574時,修改I2C Addr’s Port = 0x40h)

        此位址在圖片I2C_LCD#3.JPG中的A0/A1/A2選擇上,需加焊三只PULL-LOW  0歐姆電阻,各家的I2C_LCD介面板可能略有不同,請取得您所購得版本的資料去修改之。

[LCD]   PCF8574AT

   1    GND-  8  GND

   2    +5V- 16  VCC 

   3    VLC- LCD contrast control voltage 20K歐姆可調電阻到地

   4    RS -  4    PCF8574AT-P0    //RW低電平時,當RS=1 RW=0寫資料;RS=0 RW=0寫指令

   5    RW -  5    PCF8574AT-P1    //RW引腳設置低電平

   6    EN -  6    PCF8574AT-P2    //EN可讀寫使能端,高電平有效,下降沿鎖定

        LED-  7    PCF8574AT-P3    //背光 LED

  11    D4 -  9    PCF8574AT-P4    //4位元資料匯流排,要用PORT的高4位

  12    D5 - 10    PCF8574AT-P5

  13    D6 - 11    PCF8574AT-P6

  14    D7 - 12    PCF8574AT-P7

 

DHT-11溫濕度檢知器接腳定義

P#1         +5Volt Power

P#2         DHT-11 DATA IN/OUT=>連接至P4^3

P#3         N.C.

P#4         GND

目前DHT-11溫濕度檢知器在資料讀取時,共有5個位元組,請詳看DATASHEET,唯一的是應用上,所檢知到的溫度或濕度在精確度上只到個位數,所以誤差會很大,這是比較遺憾之處,這可能未來需取用更精確至小數點一位數的下一代檢知器,才能比較符合使用的條件。

 

而時鐘部份的程式是利用STC89C516RD+內的TIMER 2產生20ms計時中斷,之後在中斷服務程式內,計次50次,形成每達一秒鐘就設定旗號,讓主程式在每次旗號被設置時,就將時鐘的"秒,分,時"等進行計算與進位,同時也讀取DHT-11溫濕度檢知器在資料一次,並顯示在LCD螢幕上,因為是使用STC89C516RD+內的TIMER 2計時中斷之原故,難免時間長了以後,會有一些誤差秒數出現,如果需要更精準的時鐘,那就可能需用外加DS1302時鐘IC來設計,在本案例中,暫時未採用此元件。

 

程式範例:

/*

file: LT-37.c I2C LCD TEST PROGRAM STC89C516RD+ (@12MHz)

/**************************************************************************

LCD1602液晶通過PCF8574AT轉換成I2C介面驅動函數

LCD1602液晶與PCF8574AT之間的連接方式:

  [LCD]   PCF8574AT

   1    GND-  8  GND

   2    +5V- 16  VCC 

   3    VLC- LCD contrast control voltage 20K歐姆可調電阻到地

   4    RS -  4    PCF8574AT-P0    //RW低電平時,當RS=1 RW=0寫資料;RS=0 RW=0寫指令

   5    RW -  5    PCF8574AT-P1    //RW引腳設置低電平

   6    EN -  6    PCF8574AT-P2    //EN可讀寫使能端,高電平有效,下降沿鎖定

        LED-  7    PCF8574AT-P3    //背光 LED

  11    D4 -  9    PCF8574AT-P4    //4位元資料匯流排,要用PORT的高4位

  12    D5 - 10    PCF8574AT-P5

  13    D6 - 11    PCF8574AT-P6

  14    D7 - 12    PCF8574AT-P7

說明:LCD驅動採用4線驅動方法,LCD1602的RW端接低電平,這樣無法讀數據,

    操作需插入合適的延時,由於LCD1602是慢速器件,因此時序要注意,參考其手冊

    LCD第一行顯示寄存器地址:0x80-0x8F 

    LCD第二行顯示寄存器位址:0xC0-0xCF

    PCF8754AT設置地址:0x70H    (此需視各家所設計的I2C轉換LCD介面板的設計位址而定)

    如為使用PCF8574時,則選用的位址為0x40H

    I2C介面線序定義:1=Vdd    2=Vss    3=SDA    4=SCL

 

***************************************************************************/

#include

#include

#include

#include

#include "delay.c"

 

#define XIO_ID  0x70        // Device identifier of the I/O-Expander (PCF8574AT)

#define  _Nop()  _nop_()      //定義空指令

sbit I2C_SDA= P1^2;           //PCF8674AT SDA

sbit I2C_SCL= P1^3;           //PCF8574AT SCL

sbit second = P4^1;           //LED每秒閃動一次

sbit DHT11  = P4^3;         //DHT-11溫濕檢知

//--------------函數聲明----------------------

unsigned char temp[20];

 

unsigned xdata AC_ms,AC_sec,AC_min,AC_hrs,LCDtemp=0;

unsigned xdata I_RH,D_RH,I_Temp,D_Temp,CheckSum;

bit SetFlag,Turn_flag=0,LCD_flag=0;

void Init_Timer2(void);

void LCD1602_Write_Command(unsigned char command);    //向LCD1602寫入指令

void LCD1602_Write_Data(unsigned char Data);      //向LCD1602寫入資料

void LCD1602_Device_Init(void);                      //LCD1602初始化函數

void LCD1602_Set_Position(unsigned char x, unsigned char y);      //向LCD1602設置下一個準備寫入的字元位置

void LCD1602_Write_String(unsigned char X, unsigned char Y, char *String);       //向指定的位置開始寫字串

void XIO_write(unsigned char content);

void Start_I2c();

void Stop_I2c();

unsigned char i2c_write(unsigned char value);

unsigned char i2c_check(void);

void Request();

void Response();

unsigned int Receive_data();

void DHT_11();

/*******************************************************************/

int main()

{   unsigned int i;

   

    P1 = 0xFF;LCD_flag=0;

    Init_Timer2();          //時鐘計數器,20mS/次

    for(i=0;i<50;i++) led="" p="">

    {    DelayMs(50);second=~second; }

    LCD1602_Device_Init();  //啟動I2C LCD介面板與1602液晶螢幕

    DelayMs(5);             //需延遲待待LCD螢幕內部運作

    LCD1602_Write_Command(0x01);    //清除螢幕內容

    DelayMs(5);

    ET2   = 1;      //打開時鐘計數器2中斷予能

    TR2   = 1;      //時鐘計數器2啟動率數

    EA    = 1;      //打開總中斷

   

    Turn_flag = 0;

    LCD1602_Write_String(20,1, "LT-37 I2C_LCD TEST"); //顯示軟體版本與名稱

    while(1)    //主程式

    {

        if(    Turn_flag==1)       //時鐘計數器2旗號已動作

        {    AC_sec++;

            if(AC_sec>=60)

            {AC_sec=0;AC_min++;}

            if(AC_min>=60)

            {AC_min=0;AC_hrs++;}

            if(AC_hrs>=24)

            {AC_hrs=0;}

           DHT_11();            //偵測DHT-11 溫度與濕度之讀取資料.並顯所取得之數據

            second=~second;     //LED每秒閃爍一次

            Turn_flag=0;        //清除時鐘計數器2旗號置0

        }

        sprintf(temp,"%02d:%02d:%02d ",(int)AC_hrs,(int)AC_min,(int)AC_sec); //顯示時鐘 時:分:秒

        LCD1602_Write_String(0, 0, temp);

        //************************************************

    }

    return 0;

}

//************************************************************

void DHT_11()    //溫度與濕度讀取檢知數據

{  

    EA=0;       // 禁能中斷,以避免影響讀取數據的時脈

    Request();  // 送出啟動DHT-11的START BIT

    Response(); // 待待回應

 

    I_RH=Receive_data();    // 讀取濕度低位元至 I_RH

    D_RH=Receive_data();    // 讀取濕度高位元至 D_RH

    I_Temp=Receive_data();  // 讀取溫度低位元至 I_Temp

    D_Temp=Receive_data();  // 讀取溫度高位元至 D_Temp

    CheckSum=Receive_data();// 讀取檢查和位元至 CheckSum

    EA=1;                   // 重新予能中斷

    if ((I_RH + D_RH + I_Temp + D_Temp) != CheckSum)

    {    LCD1602_Write_String(20, 0, "Error");    //如果比對CHECKSUM與讀取之數據有差時,顯示ERROR字樣

    }      

    else

    {  

        sprintf(temp,"Hum = %d Tem = %d.C",(int)I_RH,(int)I_Temp);    //CHECKSUM吻合時顯示溫度與濕度

        LCD1602_Write_String(20, 0, temp);

    }

}

void Request()       // DHT-11啟動START BIT

{

   DHT11 = 0;        // 設定資料位元至"LOW"

    DelayMs(16);    //*** 等待16ms,此時脈非常重要,會影響讀取數據的正確性,

   DHT11 = 1;        // 設定資料位元至"HIGH"

}

void Response()          //等待 DHT11回應

{

    while(DHT11==1) _nop_();

    while(DHT11==0) _nop_();

    while(DHT11==1) DelayUs2x(2);;

}

unsigned int Receive_data()      /* DHT-11讀取數據 */

{   unsigned int q,c=0;

 

   for (q=0; q<8; q="" p="">

    {  

        while(DHT11==0);        // 等待DHT-11開始送出數據

        DelayUs2x(20);          //*** 延遲時脈

        if(DHT11 == 1)           // 判別所讀到的位元是"0"或"1"

        {c = (c<<1)|(0x01);} high="" p="">

        else                    // 如果不符合"HIGH"時

        {c = (c<<1);} low="" p="">

        while(DHT11==1);       

       

    }

   return c;                    //結束一個位元組之讀取,並送回所讀取之資料

}

/*------------向LCD1602寫入指令----------------------------

函數名稱:void LCD1602_Write_Command(unsigned char command)       

          向LCD1602寫入指令

函數參數:unsigned char command //準備寫入的指令

函數說明:向LCD1602的指令寄存器寫入一個指令

---------------------------------------------------------------*/

void LCD1602_Write_Command(unsigned char command)    //向LCD1602寫入指令

{  

    DelayMs(2); //此處插入一個等待很重要,小於這個時間會導致連續寫時出現錯誤

    //先寫入高4位

    LCDtemp = command & 0xf0 | 0x08;    //先處理高4位,EN=0,RW=0,RS=0

    XIO_write(LCDtemp);

    LCDtemp |= 0x0c;        //拉高EN

    XIO_write(LCDtemp);

    LCDtemp &= 0xfb;        //EN置低,下降沿寫入液晶

    XIO_write(LCDtemp);

    //接下來寫入低4位

    if(LCD_flag==0) return; //判斷是否是冷開機

    LCDtemp = command<<4; p="">

    XIO_write(LCDtemp);

    LCDtemp |= 0x0c;        //拉高EN

    XIO_write(LCDtemp);

    LCDtemp &= 0xfb;        //EN置低,下降沿寫入液晶

    XIO_write(LCDtemp);

}

/*------------向LCD1602寫入資料----------------------------

函數名稱:void LCD1602_Write_Data(unsigned char data)       

          向LCD1602寫入資料

函數參數:unsigned char data //準備寫入的資料

函數說明:向LCD1602的指令寄存器寫入一個資料

---------------------------------------------------------------*/

void LCD1602_Write_Data(unsigned char Data)        //向LCD1602寫入資料 

{

    DelayMs(2); //此處插入一個等待很重要,小於這個時間會導致連續寫時出現錯誤

    //先寫入高4位

    LCDtemp = (Data & 0xf0) | 0x09;    //先處理高4位,EN=0,RW=0,RS=1

    XIO_write(LCDtemp);

    LCDtemp |= 0x0c;                //拉高EN

    XIO_write(LCDtemp);

    LCDtemp &= 0xfb;                //EN置低,下降沿寫入液晶

    XIO_write(LCDtemp);

    //接下來寫入低4位

    LCDtemp = (Data<<4) 0x09="" p="">

    XIO_write(LCDtemp);

    LCDtemp |= 0x0c;                //拉高EN

    XIO_write(LCDtemp);

    LCDtemp &= 0xfb;                //EN置低,下降沿寫入液晶

    XIO_write(LCDtemp);

}

/*------------------------------------------------------------------

函數名稱:void LCD1602_Init(void)//LCD1602初始化函數

函數參數:無

函數說明:參考LCD1602液晶說明手冊,請特別留意LCD_flag

------------------------------------------------------------------*/

void LCD1602_Device_Init(void)      //LCD1602初始化函數

{  

    DelayMs(50);                    //冷開機

    LCD1602_Write_Command(0x38);

    DelayMs(50);

    LCD1602_Write_Command(0x28);

    DelayMs(5);

    LCD1602_Write_Command(0x28);

    DelayMs(5);

    LCD1602_Write_Command(0x28);

    DelayMs(50);                //先等待50毫秒VDD上電穩定

    LCD_flag=1;

    LCD1602_Write_Command(0x28);    //先進行功能設置,四位元資料介面,兩行顯示,5′7點陣字元。

    DelayMs(25);                    //等待上條指令完成

    LCD1602_Write_Command(0x0f);    //Display On/Off Control,顯示開關:D=1打開,游標開關:C=1打開,閃爍開關:B=1打開

    DelayMs(5);

    LCD1602_Write_Command(0x01);    //清屏

    DelayUs2x(5);

    LCD1602_Write_Command(0x06);    //AC位址遞增模式

    DelayUs2x(5);  

    LCD1602_Write_Command(0x01);    //清屏

    DelayUs2x(5);

    LCD1602_Write_Command(0x02);    //HOME

    DelayMs(5);

    LCD1602_Write_Command(0x0f);    //Display On/Off Control,顯示開關:D=1打開,游標開關:C=1打開,閃爍開關:B=1打開

    DelayMs(5);

}

/*--------設置AC地址--------------------------------------------------

函數名稱:void LCD1602_Set_Position(unsigned char x, unsigned char y)

          向LCD1602設置下一個準備寫入的字元位置

函數參數:unsigned char x 行地址 0~F

          unsigned char y 列地址 0~1

函數說明:設置AC地址,

---------------------------------------------------------------------*/

void LCD1602_Set_Position(unsigned char x, unsigned char y)  

{   //向LCD1602設置下一個準備寫入的字元位置

    unsigned char address = (y * 0x40) + (0x80 + x) ;

    LCD1602_Write_Command(address);

    DelayUs2x(25);

}

/*--------向指定的位置開始寫字串 ---------------------------------------

函數名稱:LCD1602_Write_String(unsigned char X, unsigned char Y, char *s)

          向指定的位置開始寫字串

函數參數:unsigned char X      //列位置

          unsigned char Y      //行位置

          char *String          //要顯示的字串

函數說明:向指定的位置開始寫字串

------------------------------------------------------------------------*/

void LCD1602_Write_String(unsigned char X, unsigned char Y, char *String)     

{   //向指定的位置開始寫字串

    LCD1602_Set_Position(X, Y);

    while(*String)

    {

        LCD1602_Write_Data(*String++);

        DelayUs2x(25);

    }

}

void XIO_write(unsigned char content)

{

    Start_I2c(); // 產生i2c啟動位元

    if ( i2c_write(XIO_ID)==1 )    //設定I2C位址 0x70h

    {

        return;

    }

        // 送出I2C DATA   

    if ( i2c_write(content)==1 )

    {

        return;

    }

    Stop_I2c(); // 產生終止位元

    return;

}

void Start_I2c()

{    DelayUs2x(1);

    I2C_SDA=1;  //發送起始條件的資料信號

    DelayUs2x(1);

    I2C_SCL=1;

    DelayUs2x(2);

    I2C_SDA=0;  //發送起始信號

    DelayUs2x(2);   

    I2C_SCL=0;  //鉗住I2C匯流排,準備發送或接收資料

    DelayUs2x(1);

}

/*------------------------------------------------

                    結束匯流排

------------------------------------------------*/

void Stop_I2c()

{

    I2C_SCL=1;  //發送結束條件的資料信號

    DelayUs2x(2);

    I2C_SDA=1;  //結束條件建立時間大於4μ

    DelayUs2x(2);

}

unsigned char i2c_write(unsigned char value)

{

    idata unsigned char counter =0;     // 8位元計次

    for ( counter =0; counter < 8; counter++ )

    {

        I2C_SDA =(bit)((value & 0x80) >> 7); // 檢測所要送出字元是否為"HIGH",並移位

        I2C_SCL =1;                     // 送出 SCL

        while(I2C_SCL==0);              // 同步SCL信號

       EA=0;                            // 禁能中斷,以避免影響時脈

        DelayUs2x(2);

       EA=1;                            // 予能中斷

        I2C_SCL =0;                     // 完成一個位元的週期

        value <<=1; p="">

    };

     return(i2c_check());

}

unsigned char i2c_check(void)

{  

    EA =0;              // 禁能中斷,以避免影響時脈   

    I2C_SDA =1;

    I2C_SCL =1;

     while(I2C_SCL==0);    // 同步時脈週期

    DelayUs2x(2);  

    if (I2C_SDA==1)

    {  

        I2C_SCL =0;     // 開始一個週期時脈

        EA =1;         // 予能中斷

        return(1);      // 未取得Acknowledge

    }

    I2C_SCL =0;         // Force a clock cycle

    EA =1;              // 予能中斷

     return(0);          // 取得Acknowledge

}                      

/*------------------------------------------------

                計時器 2 的初始化

------------------------------------------------*/

void Init_Timer2(void)       // 計時器 2 的初始化

{

    RCAP2H=(65536-20000)/256;   //

    RCAP2L=(65536-20000)%256;   //

    ET2=1;                    //禁能中斷

    TR2=1;                    // timer2 run

    C_T2=0;                 // interrupt mode

   CP_RL2=0;               // reload mode

}

//***********************************

void TIMER2(void) interrupt 5 using 1

{  

    RCAP2H=(65536-20000)/256;   //

    RCAP2L=(65536-20000)%256;   //

 

    AC_ms++;

    if(AC_ms>=50)    {AC_ms=0;Turn_flag=1;    }   //每秒滿足時,設定旗號

    TF2=0;      //清除timer2中斷旗號

}

 

 

 

 

 

( 興趣嗜好電腦3C )
回應 推薦文章 列印 加入我的文摘
上一篇 回創作列表 下一篇

引用
引用網址:https://classic-blog.udn.com/article/trackback.jsp?uid=PYANG&aid=104686595