代碼編寫(二):
注意:I2C和EPROOM是兩個(gè)東西棋凳,EEPROM (Electrically Erasable Programmable read only memory)稳懒,帶電可擦可編程只讀存儲(chǔ)器--一種掉電后數(shù)據(jù)不丟失的存儲(chǔ)芯片勇哗。 EEPROM 可以在電腦上或?qū)S迷O(shè)備上擦除已有信息外邓,重新編程痊土。一般用在即插即用被辑。而單片機(jī)和EEPROM的通訊方式就是I2C協(xié)議,EEPROM用來(lái)存儲(chǔ)數(shù)據(jù)而I2C就是個(gè)通訊協(xié)議
1父能、EEPROM寫數(shù)據(jù)流程
第一步,首先是I2C的起始信號(hào)净神,接著跟上首字節(jié)何吝,也就是我們前邊講的I2C的器件地
址(EERPOM),并且在讀寫方向上選擇“寫”操作鹃唯。
第二步爱榕,發(fā)送數(shù)據(jù)的存儲(chǔ)地址。我們24C02一共256個(gè)字節(jié)的存儲(chǔ)空間坡慌,地址從0x00到0xFF黔酥,我們想把數(shù)據(jù)存儲(chǔ)在哪個(gè)位置,此刻寫的就是哪個(gè)地址洪橘。
第三步跪者,發(fā)送要存儲(chǔ)的數(shù)據(jù)第一個(gè)字節(jié),第二個(gè)字節(jié)......注意在寫數(shù)據(jù)的過(guò)程中熄求,EEPROM每個(gè)字節(jié)都會(huì)回應(yīng)一個(gè)“應(yīng)答位0”渣玲,來(lái)告訴我們寫EEPROM數(shù)據(jù)成功,如果沒(méi)有回應(yīng)答位弟晚,說(shuō)明寫入不成功忘衍。
在寫數(shù)據(jù)的過(guò)程中逾苫,每成功寫入一個(gè)字節(jié),EEPROM存儲(chǔ)空間的地址就會(huì)自動(dòng)加1枚钓,當(dāng)加到0xFF后铅搓,再寫一個(gè)字節(jié),地址會(huì)溢出又變成了0x00秘噪。
void At24c02Write(unsigned char addr, unsigned char dat) //往24c02(EEPROM)的一個(gè)地址寫入一個(gè)數(shù)據(jù)
{
I2CStart();
I2cSendByte(0xa0); //發(fā)送EEPROM所在地址
I2cSendByte(addr); //發(fā)送要寫入的內(nèi)存地址(是寫入EEPROM狸吞,在EEPROM里的地址)
I2cSendByte(dat); //發(fā)送數(shù)據(jù)
I2CStop();
}
I2總線在操控器件時(shí)首先要先發(fā)送器件(24c02)的地址,24景列的EEPROM也不列外指煎,在每次讀寫命令前發(fā)送一個(gè)器件地址和讀寫標(biāo)志蹋偏,也可稱為器件錄址
因?yàn)镋0, E1, E2被接地了,而地址的高四位又被固定成1010 所以24c02 的地址是0xa0(是寫模式時(shí)至壤,讀模式是0xa1)
2威始、EEPROM讀數(shù)據(jù)流程
第一步,首先是I2C的起始信號(hào)像街,接著跟上首字節(jié)黎棠,也就是我們前邊講的I2C的器件地
址(EERPOM),并且在讀寫方向上選擇“寫”操作镰绎。這個(gè)地方可能有同學(xué)會(huì)詫異脓斩,我們明明是讀數(shù)據(jù)為何方向也要選“寫”呢?剛才說(shuō)過(guò)了畴栖,我們24C02一共有256個(gè)地址随静,我們選擇寫操作,是為了把所要讀的數(shù)據(jù)的存儲(chǔ)地址先寫進(jìn)去吗讶,告訴EEPROM我們要讀取哪個(gè)地址的數(shù)據(jù)燎猛。這就如同我們打電話,先撥總機(jī)號(hào)碼(EEPROM器件地址)照皆,而后還要繼續(xù)撥分機(jī)號(hào)碼(數(shù)據(jù)地址)重绷,而撥分機(jī)號(hào)碼這個(gè)動(dòng)作,主機(jī)仍然是發(fā)送方膜毁,方向依然是“寫”昭卓。
第二步,發(fā)送要讀取的數(shù)據(jù)的地址爽茴,注意是地址而非存在EEPROM中的數(shù)據(jù)葬凳,通知EEPROM我要哪個(gè)分機(jī)的信息。
第三步室奏,重新發(fā)送I2C起始信號(hào)和器件地址,并且在方向位選擇“讀”操作劲装。
這三步當(dāng)中胧沫,每一個(gè)字節(jié)實(shí)際上都是在“寫”昌简,所以每一個(gè)字節(jié)EEPROM都會(huì)回應(yīng)一個(gè)“應(yīng)答位0”。
第四步绒怨,讀取從器件發(fā)回的數(shù)據(jù)纯赎,讀一個(gè)字節(jié),如果還想繼續(xù)讀下一個(gè)字節(jié)南蹂,就發(fā)送一個(gè)“應(yīng)答位ACK(0)”犬金,如果不想讀了,告訴EEPROM六剥,我不想要數(shù)據(jù)了晚顷,別再發(fā)數(shù)據(jù)了,那就發(fā)送一個(gè)“非應(yīng)答位NACK(1)”
unsigned char At24c02Read(unsigned char addr)
{
unsigned char num;
I2CStart();
I2cSendByte(0xa0); //發(fā)送EEPROM(24c02)器件所在地址疗疟,此時(shí)EEPROOM是寫指令因?yàn)橐葘懭胱x取的地址
I2cSendByte(addr); //發(fā)送要讀取的地址该默,該地址也是在EEPROM內(nèi)
I2CStart();
I2cSendByte(0xa1); //再發(fā)送一遍EEPROM的地址,此時(shí)是讀指令
num = I2cReadByte(); //用num接收讀取到的指令
I2CStop();
return num;
}
梳理一下幾個(gè)要點(diǎn):A策彤、在本例中單片機(jī)是主機(jī)栓袖,24C02是從機(jī);B店诗、無(wú)論是讀是寫裹刮,SCL始終都是由主機(jī)控制的;C庞瘸、寫的時(shí)候應(yīng)答信號(hào)由從機(jī)給出捧弃,表示從機(jī)是否正確接收了數(shù)據(jù);D恕洲、讀的時(shí)候應(yīng)答信號(hào)則由主機(jī)給出塔橡,表示是否繼續(xù)讀下去
名字是I2c.h的頭文件
#ifndef _iic_H //if not define
#define _iic_H //為了源文件方便調(diào)用
#include <reg52.h>
sbit SCL=P2^1;
sbit SDA=P2^0;
void I2CStart();
void I2CStop();
unsigned char I2cSendByte(unsigned char dat);
unsigned char I2cReadByte();
unsigned char At24c02Read(unsigned char addr);
void At24c02Write(unsigned char addr, unsigned char dat);
#endif
單片機(jī)運(yùn)行目標(biāo): 系統(tǒng)運(yùn)行時(shí),數(shù)碼管后4位顯示0霜第,按K1將數(shù)據(jù)寫入到EEPROM內(nèi)保存葛家,按K2顯示數(shù)據(jù)清零,按K3讀取EEPROM內(nèi)保存的數(shù)據(jù)泌类,按K4顯示數(shù)據(jù)加1癞谒。最大能寫入的數(shù)據(jù)是255。
#include "reg52.h"
#include <intrins.h>
#include "iic.h"
typedef unsigned char u8;
typedef unsigned int u16;
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
sbit k1 = P1^4;
sbit k2 = P1^6;
sbit k3 = P3^2;
sbit k4 = P1^7;
char num = 0;
u8 disp[4];
u8 code smgduan[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void delay(u16 i) //延時(shí)函數(shù)
{
while(i--);
}
void Keypros() //按鍵處理函數(shù)
{
if(k1==0){
delay(1000); //消抖
if(k1==0){
At24c02Write(1,num);
}
while(!k1);
}
if(k2==0){
delay(1000);
if(k2==0){
num=0;
}
while(!k2);
}
if(k3==0){
delay(1000);
if(k3==0){
num=At24c02Read(1); //讀取EEPROM地址1內(nèi)的數(shù)據(jù)保存在num中
}
while(!k3);
}
if(k4==0){
delay(1000);
if(k4==0){
num++;
if(num>255)num=0;
}
while(!k4);
}
}
void datapros() //數(shù)據(jù)處理函數(shù)
{
disp[0] = smgduan[num/1000]; //取數(shù)據(jù)的千位
disp[1] = smgduan[num%1000/100]; //取百位
disp[2] = smgduan[num%1000%100/10]; //十位
disp[3] = smgduan[num%1000%100%10]; // 個(gè)位
}
void DigDisplay()
{
u8 i;
for(i=0;i<4;i++){
switch(i){
case(0):
LSA=0;LSB=0;LSC=0; break;//顯示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//顯示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//顯示第2位
case(3):
LSA=1;LSB=1;LSC=0; break;//顯示第3位
}
P0 = disp[i]; //發(fā)送數(shù)據(jù)
delay(100); //間隔一段時(shí)間掃描
P0 = 0x00; //消隱
}
}
void main()
{
while(1)
{
Keypros(); //按鍵處理函數(shù)
datapros(); //數(shù)據(jù)處理函數(shù)
DigDisplay();//數(shù)碼管顯示函數(shù)
}
}