摘要:這是一個記錄軟硬件結合的物聯網項目,目的是打造微信實時監控室內環境,由于傳感器簡陋,這篇文章只介紹溫度和濕度的監控,使用的是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信號表示方法如圖所示
數字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單列直插封裝,所以器件手冊只給出了引腳的功能數據表:
我們只需要關注以下幾個管腳:
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模塊內部由一塊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個數字引腳)。
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流程,一個字節的寫入完成。電平由低至高變化定義為總線的停止信號。
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();
}
/*******************************************************************