EEPROM芯片: 掉電不會丟失數據,可以保存數據。
IIC串行總線的組成及工作原理:
IIC總線傳輸協議
IIC產生起始與終止信號:
IIC字節的傳送與應答:
應答位作用:
數據幀格式:
總線尋址
軟件模擬IIC通信時序
IIc通信代碼示例(通過數碼管顯示單片機通過IIC通信接收到的數據):
#include <reg52.h>
#include <intrins.h>#define uint unsigned int
#define uchar unsigned char
#define At24c02ADDR 0XA0 //AT24C02硬件地址
#define I2cRead 1 //I2C讀方向位
#define I2cWrite 0 //I2C寫方向位sbit DU = P2^6;//數碼管段選
sbit WE = P2^7;//數碼管段選
sbit SCL = P2^1;//I2C時鐘總線
sbit SDA = P2^0;//I2C數據總線
uchar num;//數碼管顯示的值
bit AckFlag;//應答標志位//共陰數碼管段選表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//數碼管位選碼
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};void delay(uint z)
{uint x,y;for(x = z; x > 0; x--)for(y = 114; y > 0 ; y--);
} void display(uchar i)
{static uchar wei; P0 = 0XFF;//清除斷碼WE = 1;//打開位選鎖存器P0 = SMGwei[wei];WE = 0;//鎖存位選數據switch(wei){case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break; case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break; }wei++;if(wei == 3)wei = 0;
}
//定時器0初始化
void timer0Init()
{EA = 1; //打開總中斷ET0 = 1;//打開定時器0中斷TR0 = 1; //啟動定時器0TMOD |= 0X01; //定時器工作模式1,16位定時模式TH0 = 0xED;TL0 = 0xFF; //定時5ms
}
/****************************************************
IIC通信代碼
****************************************************/
//延時5us
void delay5us()
{_nop_();//執行一次是一個機器周期,進入這個函數需要3個多機器周期
}
//時鐘總線為高電平期間數據總線由高變低產生起始信號
void I2cStart()
{SCL = 1; SDA = 1;delay5us();//狀態保持5usSDA = 0;delay5us();//狀態保持5us//這個函數根據圖像來寫
}
//時鐘總線為高電平期間,數據總線從高變低產生終止信號
void I2cStop()
{SCL = 0;SDA = 0;SCL = 1;delay5us();//狀態保持5usSDA = 1;delay5us();//狀態保持5us//這個函數根據圖像來寫
}bit ReadACK()
{SCL = 0;//拉低時鐘總線,允許從機控制SDASCL = 1;//拉高,讀SDAdelay5us();if(SDA)//非應答{SCL = 0;return(1);//返回1}else//應答 {SCL = 0;return(0);//返回0}
}void SendACK(bit i)
{SCL = 0;//拉低時鐘總線,允許主機控制SDAif(i) //發非應答SDA = 1;else //發應答SDA = 0;SCL = 1; //拉高總線,讓從機讀SDAdelay5us();//保持5usSCL = 0; //拉低時鐘總線,允許SDA釋放SDA = 1;//釋放數據總線
}void I2cSendByte(uchar DAT)
{uchar i; for(i=0; i<8; i++) //分別寫8次,每次寫1位{SCL = 0;//拉低時鐘總線,允許SDA變化if(DAT & 0x80)//先寫數據最高位SDA = 1; //寫1elseSDA = 0; //寫0SCL = 1; //拉高時鐘,讓從機讀SDADAT <<= 1; //為發送下一位左移1位}SCL = 0; //拉低時鐘總線,允許SDA釋放SDA = 1;//釋放數據總線
}void At24c02Write(uchar ADDR, DAT)
{I2cStart();//I2C起始信號I2cSendByte(At24c02ADDR + I2cWrite);//發送器件地址加讀寫方向位if(ReadACK()) //讀從機應答AckFlag = 1; //NOACKelseAckFlag = 0; //ACKI2cSendByte(ADDR);//發送儲存單元地址字節if(ReadACK())//讀從機應答AckFlag = 1; //NOACKelseAckFlag = 0; //ACKI2cSendByte(DAT);//發送一字節數據if(ReadACK())//讀從機應答AckFlag = 1; //NOACKelseAckFlag = 0; //ACKI2cStop(); //I2C停止信號
}uchar I2cReadByte()
{uchar i, DAT;for(i=0; i<8; i++)//分別讀8次,每次讀一位{DAT <<= 1; //數據左移1位,準備接收一位SCL = 0; //拉低時鐘總線,允許從機控制SDA變化SCL = 1; //拉高時鐘總線,讀取SDA上的數據if(SDA)DAT |= 0X01;//為1則寫1,否則不行執行寫1,通過左移補0}return(DAT); //返回讀出的數據
}uchar At24c02Read(uchar ADDR)
{uchar DAT;I2cStart();//I2C起始信號I2cSendByte(At24c02ADDR + I2cWrite);//發送器件地址加讀寫方向位if(ReadACK())//讀從機應答AckFlag = 1; //NOACKelseAckFlag = 0; //ACKI2cSendByte(ADDR);//I2C發送一個字節ReadACK();//讀從機應答I2cStart();//再次產生I2C起始信號I2cSendByte(At24c02ADDR + I2cRead);//發送器件地址加讀寫方向位 讀if(ReadACK())//讀從機應答AckFlag = 1; //NOACKelseAckFlag = 0; //ACKDAT = I2cReadByte();//讀一字節SendACK(1);//主機發送非應答I2cStop(); //I2C停止信號return(DAT);//返回讀出數據}void main()//main函數自身會循環
{ timer0Init();//定時器0初始化EA = 0;//屏蔽中斷At24c02Write(3, 188);//給第3單元寫入數據“188”delay(2);//延時等待AT24C02處理num = At24c02Read(3);//讀出第3單元內數據送給顯示變量if(AckFlag)//當從機非應答P1 = 0;//亮P1所有燈elseP1 = 0XFF;//滅P1所有燈EA = 1;//開中斷while(1);
} //定時器0中斷函數
void timer0() interrupt 1
{TH0 = 0xED;TL0 = 0xFF; //定時5msdisplay(num); //數碼管顯示函數
}