微信公眾平臺實時監控室內溫度和濕度

摘要:這是一個記錄軟硬件結合的物聯網項目,目的是打造微信實時監控室內環境,由于傳感器簡陋,這篇文章只介紹溫度和濕度的監控,使用的是DHT11溫濕度傳感器,主機為Arduino UNO+W5100 Ethernet/SD擴展版,路由器為HG255D Pandorabox by lintel,物聯網服務由樂聯網提供,本篇文章主要內容有(1)DHT11硬件基礎(2)Arduino讀取DHT11的溫濕度數據(3)LCD1602_I2C硬件基礎及在LCD模塊上顯示DHT11獲取的溫濕度數據(4)將數據上傳到樂聯網(5)微信查詢傳感器數據

DHT11硬件基礎

DHT11是一款性價比極高的含有已校準數字信號輸出的溫濕度復合傳感器,傳感器包括一個電阻式感濕元件和一個NTC測溫元件并與一個高性能8位單片機相連接。

引腳說明

DHT11有四個引腳,一個引腳為空腳不連接元器件(所以一些采用DHT11傳感器的封裝好的溫濕度傳感器只能看到3個引腳),其中正極接VCC(工作電壓為3-5.5VDC),負極接GND,由于DHT11是以串行接口以高低電平傳遞數據,所以數據總線(SDATA)引腳連接單片機或Arduino的數字信號(Digital)引腳,通常在元器件上正極標有(+),負極標有(—),數據引腳標有(S),我用的是KEYES的元器件,左邊標有(S)為數據引腳,右邊標有(-)為負極引腳,中間則為正極引腳,不同廠家的元器件引腳順序不同,連接時要找準引腳,否者單片機則讀取不到數據。


引腳說明
引腳說明

應用電路
應用電路

數據傳輸原理

MCU發送開始信號(+-+)->觸發DHT響應(-+-)數據采集->{[準備發送(-)+發送數據(+)]*5}->機械拉低(-)->MCU拉高(+)

DHT11采用的是串行接口(單線雙向)的通訊方式,DATA用于微處理器與DHT11之間的通訊和同步,采用單總線數據格式,一次通訊時間4ms左右,數據分小數部分和整數部分,具體格式在下面說明,當前小數部分用于以后擴展,現讀出為零.操作流程如下:一次完整的數據傳輸為40bit,高位先出。

數據格式:

40bit=8bit濕度整數數據+8bit濕度小數數據+8bi溫度整數數據+8bit溫度小數數據+8bit校驗和數據

傳送正確時校驗和數據等于“8bit濕度整數數據+8bit濕度小數數據+8bi溫度整數數據+8bit溫度小數數據”所得結果的末8位。

數據傳輸過程

用戶MCU發送一次開始信號后,DHT11從低功耗模式轉換到高速模式,等待主機開始信號結束后,DHT11發送響應信號,送出40bit的數據,并觸發一次信號采集,用戶可選擇讀取部分數據.從模式下,DHT11接收到開始信號觸發一次溫濕度采集,如果沒有接收到主機發送開始信號,DHT11不會主動進行溫濕度采集.采集數據后轉換到低速模式。
通訊過程如圖所示


通訊過程
通訊過程

總線空閑狀態為高電平,主機把總線拉低等待DHT11響應,主機把總線拉低必須大于18毫秒,保證DHT11能檢測到起始信號。DHT11接收到主機的開始信號后,等待主機開始信號結束,然后發送80us低電平響應信號.主機發送開始信號結束后,延時等待20-40us后, 讀取DHT11的響應信號,主機發送開始信號后,可以切換到輸入模式,或者輸出高電平均可, 總線由上拉電阻拉高。

總線為低電平,說明DHT11發送響應信號,DHT11發送響應信號后,再把總線拉高80us,準備發送數據,每一bit數據都以50us低電平時隙開始,高電平的長短定了數據位是0還是1.格式見下面圖示.如果讀取響應信號為高電平,則DHT11沒有響應,請檢查線路是否連接正常.當最后一bit數據傳送完畢后,DHT11拉低總線50us,隨后總線由上拉電阻拉高進入空閑狀態。
數字0信號表示方法如圖所示


數字0信號表示方法
數字0信號表示方法

數字1信號表示方法如圖所示


數字1信號表示方法
數字1信號表示方法

Arduino讀取DHT11的溫濕度數據

int DHpin=8;    //定義數字引腳8為DHT11數據接收/發送引腳
byte dat[5];    //聲明數組,用于儲存5組8bit的數據

void setup()    //只執行一次
{
  Serial.begin(9600);   //串口通信波特率(和電腦)
  pinMode(DHpin,OUTPUT);    //輸出模式
  digitalWrite(DHpin,HIGH); //靜息電平,空閑狀態
}

void start_test()   //MCU發送開始信號
{
  digitalWrite(DHpin,LOW);  //拉低電平發送信號
  delay(30);
  digitalWrite(DHpin,HIGH); //拉高電平,發送信號結束
  delayMicroseconds(40);
  pinMode(DHpin,INPUT); //輸入模式,開始檢測DHT發來的信號
  while(digitalRead(DHpin)==LOW);   //DHT發來響應信號,低電平80us
  delayMicroseconds(80);
  while(digitalRead(DHpin)==HIGH);  //高電平80us,響應信號結束
  delayMicroseconds(80);
  for(int i=0;i<4;i++)  //開始讀取數據
  dat[i]=read_data();
  pinMode(DHpin,OUTPUT);    //讀取數據結束
  digitalWrite(DHpin,HIGH); //拉高電平,空閑狀態
}

byte read_data()    //讀取數據的方法
{
  byte data;
  for(int i=0;i<8;i++)  //1bit的讀取數據
  {
    if(digitalRead(DHpin)==HIGH)    
    {
      while(digitalRead(DHpin)==HIGH);  //檢測到高電平開始,延遲30us
      delayMicroseconds(30);
      if(digitalRead(DHpin)==HIGH)  //如果30us后,電平拉高,則為1,否者則為0
      data|=(1<<(7-i));
    }
  }
return data;
}

void loop() //串口輸出數據
{
  start_test(); 
  Serial.print("t1:"); 
  Serial.print(dat[0],DEC); //顯示濕度的整數位; 
  Serial.print('.'); 
  Serial.print(dat[1],DEC); //顯示濕度的小數位; 
  Serial.print('%');
  Serial.print("t2:"); 
  Serial.print(dat[2],DEC); //顯示溫度的整數位; 
  Serial.print('.'); 
  Serial.print(dat[3],DEC); //顯示溫度的小數位; 
  Serial.print('C');
  delay(5000);      //延遲5s

}

LCD1602_I2C硬件基礎

LCD1602_I2C由兩部分組成,LCD1602和一個I2C并行串口電路構成,LCD提供液晶屏顯示,I2C并行串口電路為單片機提供并行串口通訊,減少LCD1602所需的引腳。

LCD1602硬件基礎

LCD:英文全稱為Liquid Crystal Display,即為液態晶體顯示,也就是我們常說的液晶顯示了。1602則是表示這個液晶一共能顯示2行數據,每一行顯示16個字符。這個就是LCD1602的全部來由。

我們首先來看1602的引腳定義,1602的引腳是很整齊的SIP單列直插封裝,所以器件手冊只給出了引腳的功能數據表:


LCD1602引腳定義
LCD1602引腳定義

我們只需要關注以下幾個管腳:
3腳:VL,液晶顯示偏壓信號,用于調整LCD1602的顯示對比度,一般會外接電位器用以調整偏壓信號,注意此腳電壓為0時可以得到最強的對比度。
4腳:RS,數據/命令選擇端,當此腳為高電平時,可以對1602進行數據字節的傳輸操作,而為低電平時,則是進行命令字節的傳輸操作。命令字節,即是用來對LCD1602的一些工作方式作設置的字節;數據字節,即使用以在1602上顯示的字節。這是一個選擇數據寄存器還是選擇指令寄存器的過程,值得一提的是,LCD1602的數據是8位的。
5腳:R/W,讀寫選擇端。當此腳為高電平可對LCD1602進行讀數據操作,反之進行寫數據操作。筆者認為,此腳其實用處不大,直接接地永久置為低電平也不會影響其正常工作。但是尚未經過復雜系統驗證,保留此意見。
6腳:E,使能信號,其實是LCD1602的數據控制時鐘信號,利用該信號的上升沿實現對LCD1602的數據傳輸。
7~14腳:8位并行數據口,使得對LCD1602的數據讀寫大為方便。

LCD1602通訊過程

現在來看LCD1602的操作時序:

在此,我們可以先不讀出它的數據的狀態或者數據本身。所以只需要看兩個寫時序:

當我們要寫入指令字節,設置LCD1602的工作方式時:需要把RS置為低電平,RW置為低電平,然后將數據送到數據口D0~D7,最后E引腳一個高脈沖將數據寫入。
當我們要寫入數據字節,設置LCD1602的工作方式時:需要把RS置為高電平,RW置為低電平,然后將數據送到數據口D0~D7,最后E引腳一個高脈沖將數據寫入。
發現了么,寫指令和寫數據,差別僅僅在于RS的電平不一樣而已。以下是LCD1602的讀時序圖:

讀指令
讀指令

當要寫命令字節的時候,時間由左往右,RS變為低電平,R/W變為低電平,注意看是RS的狀態先變化完成。然后這時,DB0~DB7上數據進入有效階段,接著E引腳有一個整脈沖的跳變,接著要維持時間最小值為tpw=400ns的E脈沖寬度。然后E引腳負跳變,RS電平變化,R/W電平變化。這樣便是一個完整的LCD1602寫命令的時序。


讀寫指令
讀寫指令

LCD1602基本指令

基本指令
基本指令

Arduino與LCD1602的通訊

8針接法,數據輸出口:DB0-DB7。

// 8pinlcd1602.ino
int DI = 12;    //定義數據/命令針腳
int RW = 11;    //定義讀/寫針腳
int DB[] = {3, 4, 5, 6, 7, 8, 9, 10};//使用數組來定義總線需要的管腳
int Enable = 2; //定義使能管腳
 
//LCD寫命令的方法
void LcdCommandWrite(int value) {
 // 定義所有引腳
 int i = 0;
digitalWrite(DI, LOW);  //DI為O,RW為0,寫入命令
 digitalWrite(RW, LOW);
 for (i=DB[0]; i <= DI; i++) //總線賦值
{
   digitalWrite(i,value & 01);  //獲取每個字節的第一位
   value >>= 1; //2進制移位,將要獲取的位移至第一位
 }
 digitalWrite(Enable,LOW);  //使能拉低電平,準備觸發高脈沖
 delayMicroseconds(1);
 digitalWrite(Enable,HIGH); //Arduino拉高電平,高脈沖將數據泵入指令寄存器
 delayMicroseconds(1);  
 digitalWrite(Enable,LOW);  //使能恢復低電平,靜息電平
 delayMicroseconds(1);  
}
 
void LcdDataWrite(int value) {
 // 定義所有引腳
 int i = 0;
 digitalWrite(DI, HIGH);    //DI為1,RW為0,寫入數據
 digitalWrite(RW, LOW);
 for (i=DB[0]; i <= DB[7]; i++) {
   digitalWrite(i,value & 01);  //獲取每個字節的第一位,0xXX & 01 =0 | 1
   value >>= 1;
 }
 digitalWrite(Enable,LOW);
 delayMicroseconds(1);
 digitalWrite(Enable,HIGH); //高脈沖泵入數據,寫入數據寄存器
 delayMicroseconds(1);
 digitalWrite(Enable,LOW);
 delayMicroseconds(1);  
}
 
void setup (void) { //起始函數,只執行一次
 int i = 0;
 for (i=Enable; i <= DI; i++) { //將所有管腳設置為輸出
   pinMode(i,OUTPUT);
 }
 delay(100);
 // 短暫的停頓后初始化LCD
 // 用于LCD控制需要
 LcdCommandWrite(0x38);  // 設置為8-bit接口,2行顯示,5x7文字大小                     
 delay(64);                      
 LcdCommandWrite(0x38);  // 設置為8-bit接口,2行顯示,5x7文字大小                        
 delay(50);                      
 LcdCommandWrite(0x38);  // 設置為8-bit接口,2行顯示,5x7文字大小                        
 delay(20);                      
 LcdCommandWrite(0x06);  // 輸入方式設定
                         // 自動增量,沒有顯示移位
 delay(20);                      
 LcdCommandWrite(0x0E);  // 顯示設置
                         // 開啟顯示屏,光標顯示,無閃爍
 delay(20);                      
 LcdCommandWrite(0x01);  // 屏幕清空,光標位置歸零  
 delay(100);                      
 LcdCommandWrite(0x80);  // 顯示設置
                         // 開啟顯示屏,光標顯示,無閃爍
 delay(20);                      
}
 
void loop (void) {
  LcdCommandWrite(0x01);  // 屏幕清空,光標位置歸零  
  delay(10); 
  LcdCommandWrite(0x80+3); //0x80+為第一行命令地址,0x80+3表示第1行第4列的字符
  delay(10);                     
  // 寫入歡迎信息 
  LcdDataWrite('W');
  LcdDataWrite('e');
  LcdDataWrite('l');
  LcdDataWrite('c');
  LcdDataWrite('o');
  LcdDataWrite('m');
  LcdDataWrite('e');
  LcdDataWrite(' ');
  LcdDataWrite('t');
  LcdDataWrite('o');
  delay(10);
  LcdCommandWrite(0xc0+1);  // 定義光標位置為第二行第二個位置  
  delay(10); 
  LcdDataWrite('M');
  LcdDataWrite('a');
  LcdDataWrite('k');
  LcdDataWrite('e');
  LcdDataWrite('B');
  LcdDataWrite('l');
  LcdDataWrite('a');
  LcdDataWrite('z');
  LcdDataWrite('e');
  delay(5000);
  LcdCommandWrite(0x01);  // 屏幕清空,光標位置歸零  
  delay(10);
  LcdDataWrite('I');
  LcdDataWrite(' ');
  LcdDataWrite('a');
  LcdDataWrite('m');
  LcdDataWrite(' ');
  LcdDataWrite('R');
  LcdDataWrite('i');
  LcdDataWrite('c');
  LcdDataWrite('e');
  LcdDataWrite('L');
  LcdDataWrite('y');
  LcdDataWrite('n');
  delay(3000);
  LcdCommandWrite(0x02); //設置模式為新文字替換老文字,無新文字的地方顯示不變。
  delay(10);
  LcdCommandWrite(0x80+5); //定義光標位置為第一行第六個位置
  delay(10);  
  LcdDataWrite('t');
  LcdDataWrite('h');
  LcdDataWrite('e');
  LcdDataWrite(' ');
  LcdDataWrite('a');
  LcdDataWrite('d');
  LcdDataWrite('m');
  LcdDataWrite('i');
  LcdDataWrite('n');
  delay(5000);
}

4針接法,數據輸出口:DB4-DB7,傳輸一個字節分兩次傳輸,每次傳輸4bit,需要2倍的時間。

// 4pinlcd1602.ino

int LCD1602_RS=12;   
int LCD1602_RW=11;   
int LCD1602_EN=10;   
int DB[] = { 6, 7, 8, 9};   //定義4位總線
char str1[]="Welcome to";   //自定義字符,數據格式為字符(一個數組的值等于一個字符)
char str2[]="MakeBlaze";
char str3[]="this is the";
char str4[]="4-bit interface";
 
void LCD_Command_Write(int command) //LCD寫命令函數
{
 int i,temp;
 digitalWrite( LCD1602_RS,LOW);
 digitalWrite( LCD1602_RW,LOW);
 digitalWrite( LCD1602_EN,LOW);
 
 temp=command & 0xf0;   //切割字節,獲取高4位,0xff=11110000
 for (i=DB[0]; i <= 9; i++)
 {
   digitalWrite(i,temp & 0x80); //獲取最高位,0x80=10000000
   temp <<= 1;
 }
 
 digitalWrite( LCD1602_EN,HIGH);    //高脈沖泵入指令寄存器,位于棧底,D7位為0
 delayMicroseconds(1);
 digitalWrite( LCD1602_EN,LOW);
 
 temp=(command & 0x0f)<<4;  ////切割字節,獲取低4位,0xff=11110000
 for (i=DB[0]; i <= 9; i++)
 {
   digitalWrite(i,temp & 0x80); //獲取最高位,0x80=10000000
   temp <<= 1;
 }
 
 digitalWrite( LCD1602_EN,HIGH);    //高脈沖泵入指令寄存器,位于棧頂
 delayMicroseconds(1); 
 digitalWrite( LCD1602_EN,LOW);
}
 
void LCD_Data_Write(int dat)    //LCD寫數據的函數
{
 int i=0,temp;
 digitalWrite( LCD1602_RS,HIGH);
 digitalWrite( LCD1602_RW,LOW);
 digitalWrite( LCD1602_EN,LOW);
 
 temp=dat & 0xf0;
 for (i=DB[0]; i <= 9; i++)
 {
   digitalWrite(i,temp & 0x80);
   temp <<= 1;
 }
 
 digitalWrite( LCD1602_EN,HIGH);
 delayMicroseconds(1);
 digitalWrite( LCD1602_EN,LOW);
 
 temp=(dat & 0x0f)<<4;
 for (i=DB[0]; i <= 9; i++)
 {
   digitalWrite(i,temp & 0x80);
   temp <<= 1;
 }
 
 digitalWrite( LCD1602_EN,HIGH);
 delayMicroseconds(1); 
 digitalWrite( LCD1602_EN,LOW);
}
 
void LCD_SET_XY( int x, int y ) //設置字符位置
{
  int address;
  if (y ==0)    address = 0x80 + x; //如果是第一行,則地址設置為0x80+
  else          address = 0xC0 + x; //如果是第二行,則地址設置為0xC0+
  LCD_Command_Write(address); //寫入地址寄存器
}
 
void LCD_Write_Char( int x,int y,int dat)   //設置LCD顯示數據
{
  LCD_SET_XY( x, y );   //設置LCD位置
  LCD_Data_Write(dat);  //將字模對應的地址編號寫入數據寄存器
}
 
void LCD_Write_String(int X,int Y,char *s)
{
    LCD_SET_XY( X, Y );    //設置地址 
    while (*s)             //寫字符串
    {
      LCD_Data_Write(*s);   
      s ++;
    }
}
 
void setup (void) 
{
  int i = 0;    //設置所有管腳為輸出
  for (i=6; i <= 12; i++) 
   {
     pinMode(i,OUTPUT);
   }
  delay(100);
  LCD_Command_Write(0x28);  //4線 2行 5x7,8線我0x38
  delay(50); 
  LCD_Command_Write(0x06);  //寫一個字符后地址指針加1,光標向后移一位
  delay(50); 
  LCD_Command_Write(0x0c);  //打開顯示,不顯示光標
  delay(50); 
  LCD_Command_Write(0x80);  // 顯示設置
                            // 開啟顯示屏,光標顯示,無閃爍
  delay(50); 
  LCD_Command_Write(0x01);  //顯示清屏
  delay(50); 
 
}
 
void loop (void)
{
   LCD_Command_Write(0x01);
   delay(50);
   LCD_Write_String(3,0,str1);//第1行,第4個地址起
   delay(50);
   LCD_Write_String(1,1,str2);//第2行,第2個地址起
   delay(5000);
   LCD_Command_Write(0x01);
   delay(50);
   LCD_Write_String(0,0,str3);
   delay(50);
   LCD_Write_String(0,1,str4);
   delay(5000);
 
}

LCD1602內部構造

LCD1602內部構造
LCD1602內部構造

LCD1602模塊內部由一塊LCD顯示屏(LCDpanel),控制器(controller),列驅動器(segment driver)和偏壓產生電路構成。
控制器接收來自MPU(這里為Arduino)的指令和數據,控制著整個模塊的運行,控制器主要指令寄存器(IR),數據寄存器(DR),忙標志(BF),地址計數器(AC),顯示數據寄存器(DDRAM),字符發生器(CGROM(內置字符庫),CGRAM(自定義字符庫))構成。

Arduino傳輸過來的指令儲存在指令寄存器之中,內部存儲著DDRAM和CGRAM中數據顯示的指令代碼或地址信息,然后主控芯片(SoC)讀取數據,儲存DDRAM中根據指令代碼執行具體的命令,如字符的位置,光標的開關,字符和光標的移位等等。
而Arduino傳輸過來的數據則儲存在數據寄存器之中,從CGROM中查找到想要顯示的字符的字符碼,送入DDRAM之中,在LCD顯示屏上與DDRAM存儲單元對應的規定位置顯示出該字符。

主控芯片根據DDRAM中儲存的信息,從數字引腳中發出40SEG的掃描信號外加將信息傳輸到Segment Driver,由Segment Driver構成的40SEG掃描信號,構成LCD顯示屏的列,行由公共電極(ROM),原理類似掃描鍵盤,從而控制整個LCD顯示屏的輸出。


I2C并口擴展電路

我這里用的是一顆PCF8574T芯片,其他型號的芯片應該工作原理相同,它通過兩條雙向總線(I2C)可以使大多數的MCU(這里是Arduino)實現遠程I/O口擴展,當然要以犧牲部分性能為代價,不過這里我們傳遞的數據量小,所以性能的丟失可以忽略不計,該器件包含一個8位準雙向口(這里用于連接LCD1602,采用4位接法,加上RS,RW,E共占用7個I/O,當然也可以使用I/O口更多的芯片實現8位接法)和一個I2C總線接口(這里連接Arduino,占用2個數字引腳)。


PCF8574T針腳定義
PCF8574T針腳定義

I2C通訊過程

I2C總線由兩條線組成,一條串行數據中線(SDA)和一條串行時鐘總線(SCL),當總線空閑時,SDA和SCL均保持高電平,這時如果,SDA電平由高電平變化為低電平,標志著起始信號的開始,隨后SCL電平由高變低開始位傳輸,SDA開始傳遞數據(電平拉高或拉低),SCL隨后由低變高,并保持一段時間,若SDA線上的電平保持穩定,則認為SDA是在傳輸數據bit,此時SDA上高電平表示1,低電平表示0,SLC由高變低,改變數據,準備檢測下一個bit,連續傳遞8個bit(7個數據位加一個R/W操作位1bit),等待PCF8574T應答,第9個clock,若從IC發ACK,SDA會被拉低,若沒有ACK,SDA會被置高,這會引起Master發生RESTART或STOP流程,一個字節的寫入完成。電平由低至高變化定義為總線的停止信號。


I2C時序圖
I2C時序圖

PCF8574T內部結構


由MCU傳遞過來的數據存儲在每個引腳的寄存器中,然后寫入LCD1602。完成通過I2C控制LCD1602的過程。

I2C驅動

********************************************************************/
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char /*宏定義*/
#define uint unsigned int
#define _Nop() _nop_()        /*定義空指令*/

sbit SDA=P3^4;            /*模擬I2C數據傳送位*/
sbit SCL=P3^5;            /*模擬I2C時鐘控制位*/
bit ack;          /*應答標志位*/
/*******************************************************************
                     起動總線函數               
函數原型: void Start_I2c(); 
功能:     啟動I2C總線,即發送I2C起始條件. 
********************************************************************/
void Start_I2c()
{
SDA=1;   /*發送起始條件的數據信號*/
_Nop();
SCL=1;
_Nop();    /*起始條件建立時間大于4.7us,延時*/
_Nop();
_Nop();
_Nop();
_Nop();     SDA=0;   /*發送起始信號*/
_Nop();    /* 起始條件鎖定時間大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();       
SCL=0;   /*鉗住I2C總線,準備發送或接收數據 */
_Nop();
_Nop();
}
/*******************************************************************
                      結束總線函數               
函數原型: void Stop_I2c(); 
功能:     結束I2C總線,即發送I2C結束條件. 
********************************************************************/
void Stop_I2c()
{
SDA=0; /*發送結束條件的數據信號*/
_Nop();   /*發送結束條件的時鐘信號*/
SCL=1; /*結束條件建立時間大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1; /*發送I2C總線結束信號*/
_Nop();
_Nop();
_Nop();
_Nop();
}
/*******************************************************************
                 字節數據發送函數               
函數原型: void SendByte(uchar c);
功能:     將數據c發送出去,可以是地址,也可以是數據,發完后等待應答,并

對
          此狀態位進行操作.(不應答或非應答都使ack=0)     
        發送數據正常,ack=1; ack=0表示被控器無應答或損壞。
********************************************************************/
void SendByte(uchar c) {
uchar BitCnt;

for(BitCnt=0;BitCnt<8;BitCnt++) /*要傳送的數據長度為8位*/
    {
     if((c<<BitCnt)&0x80)SDA=1;   /*判斷發送位*/
       else SDA=0;                
     _Nop();
     SCL=1;               /*置時鐘線為高,通知被控器開始接收數據位*/
      _Nop(); 
      _Nop();             /*保證時鐘高電平周期大于4μs*/
      _Nop();
      _Nop();
      _Nop();         
     SCL=0; 
    }
    
    _Nop();
    _Nop();
    SDA=1;                /*8位發送完后釋放數據線,準備接收應答位*/
    _Nop();
    _Nop();   
    SCL=1;
    _Nop();
    _Nop();
    _Nop();
    if(SDA==1)ack=0;     
       else ack=1;        /*判斷是否接收到應答信號*/
    SCL=0;
    _Nop();
    _Nop();
}
/*******************************************************************
                 字節數據接收函數               
函數原型: uchar RcvByte();
功能:    用來接收從器件傳來的數據,并判斷總線錯誤(不發應答信號),
          發完后請用應答函數應答從機。 
********************************************************************/ 
uchar RcvByte()
{
uchar retc;
uchar BitCnt; 
retc=0; 
SDA=1;                /*置數據線為輸入方式*/
for(BitCnt=0;BitCnt<8;BitCnt++)
      {
        _Nop();           
        SCL=0;                  /*置時鐘線為低,準備接收數據位*/
        _Nop();
        _Nop();                 /*時鐘低電平周期大于4.7μs*/
        _Nop();
        _Nop();
        _Nop();
        SCL=1;                  /*置時鐘線為高使數據線上數據有效*/
        _Nop();
        _Nop();
        retc=retc<<1;
        if(SDA==1)retc=retc+1; /*讀數據位,接收的數據位放入retc中 */
        _Nop();
        _Nop(); 
      }
SCL=0;    
_Nop();
_Nop();
return(retc);
}
/********************************************************************
                     應答子函數
函數原型: void Ack_I2c(bit a);
功能:      主控器進行應答信號(可以是應答或非應答信號,由位參數a決定)
********************************************************************/
void Ack_I2c(bit a)
{

if(a==0)SDA=0;           /*在此發出應答或非應答信號 */
        else SDA=1;
_Nop();
_Nop();
_Nop();      
SCL=1;
_Nop();
_Nop();                    /*時鐘低電平周期大于4μs*/
_Nop(); _Nop();
_Nop(); 
SCL=0;                     /*清時鐘線,鉗住I2C總線以便繼續接收*/
_Nop();
_Nop();    
}
/******************************************************************* 

I2C&LCD連接電路

I2C&LCD連接電路
I2C&LCD連接電路
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容