单片机小白学习之路(二十九)---EPROOM代码编写(二)
2018-09-23 本文已影响0人
飞向深空
代码编写(二):
注意:I2C和EPROOM是两个东西,EEPROM (Electrically Erasable Programmable read only memory),带电可擦可编程只读存储器--一种掉电后数据不丢失的存储芯片。 EEPROM 可以在电脑上或专用设备上擦除已有信息,重新编程。一般用在即插即用。而单片机和EEPROM的通讯方式就是I2C协议,EEPROM用来存储数据而I2C就是个通讯协议
1、EEPROM写数据流程
第一步,首先是I2C的起始信号,接着跟上首字节,也就是我们前边讲的I2C的器件地
址(EERPOM),并且在读写方向上选择“写”操作。
第二步,发送数据的存储地址。我们24C02一共256个字节的存储空间,地址从0x00到0xFF,我们想把数据存储在哪个位置,此刻写的就是哪个地址。
第三步,发送要存储的数据第一个字节,第二个字节......注意在写数据的过程中,EEPROM每个字节都会回应一个“应答位0”,来告诉我们写EEPROM数据成功,如果没有回应答位,说明写入不成功。
在写数据的过程中,每成功写入一个字节,EEPROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址会溢出又变成了0x00。
void At24c02Write(unsigned char addr, unsigned char dat) //往24c02(EEPROM)的一个地址写入一个数据
{
I2CStart();
I2cSendByte(0xa0); //发送EEPROM所在地址
I2cSendByte(addr); //发送要写入的内存地址(是写入EEPROM,在EEPROM里的地址)
I2cSendByte(dat); //发送数据
I2CStop();
}
I2总线在操控器件时首先要先发送器件(24c02)的地址,24景列的EEPROM也不列外,在每次读写命令前发送一个器件地址和读写标志,也可称为器件录址


因为E0, E1, E2被接地了,而地址的高四位又被固定成1010 所以24c02 的地址是0xa0(是写模式时,读模式是0xa1)
2、EEPROM读数据流程
第一步,首先是I2C的起始信号,接着跟上首字节,也就是我们前边讲的I2C的器件地
址(EERPOM),并且在读写方向上选择“写”操作。这个地方可能有同学会诧异,我们明明是读数据为何方向也要选“写”呢?刚才说过了,我们24C02一共有256个地址,我们选择写操作,是为了把所要读的数据的存储地址先写进去,告诉EEPROM我们要读取哪个地址的数据。这就如同我们打电话,先拨总机号码(EEPROM器件地址),而后还要继续拨分机号码(数据地址),而拨分机号码这个动作,主机仍然是发送方,方向依然是“写”。
第二步,发送要读取的数据的地址,注意是地址而非存在EEPROM中的数据,通知EEPROM我要哪个分机的信息。
第三步,重新发送I2C起始信号和器件地址,并且在方向位选择“读”操作。
这三步当中,每一个字节实际上都是在“写”,所以每一个字节EEPROM都会回应一个“应答位0”。
第四步,读取从器件发回的数据,读一个字节,如果还想继续读下一个字节,就发送一个“应答位ACK(0)”,如果不想读了,告诉EEPROM,我不想要数据了,别再发数据了,那就发送一个“非应答位NACK(1)”
unsigned char At24c02Read(unsigned char addr)
{
unsigned char num;
I2CStart();
I2cSendByte(0xa0); //发送EEPROM(24c02)器件所在地址,此时EEPROOM是写指令因为要先写入读取的地址
I2cSendByte(addr); //发送要读取的地址,该地址也是在EEPROM内
I2CStart();
I2cSendByte(0xa1); //再发送一遍EEPROM的地址,此时是读指令
num = I2cReadByte(); //用num接收读取到的指令
I2CStop();
return num;
}
梳理一下几个要点:A、在本例中单片机是主机,24C02是从机;B、无论是读是写,SCL始终都是由主机控制的;C、写的时候应答信号由从机给出,表示从机是否正确接收了数据;D、读的时候应答信号则由主机给出,表示是否继续读下去
名字是I2c.h的头文件
#ifndef _iic_H //if not define
#define _iic_H //为了源文件方便调用
#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
单片机运行目标: 系统运行时,数码管后4位显示0,按K1将数据写入到EEPROM内保存,按K2显示数据清零,按K3读取EEPROM内保存的数据,按K4显示数据加1。最大能写入的数据是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) //延时函数
{
while(i--);
}
void Keypros() //按键处理函数
{
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内的数据保存在num中
}
while(!k3);
}
if(k4==0){
delay(1000);
if(k4==0){
num++;
if(num>255)num=0;
}
while(!k4);
}
}
void datapros() //数据处理函数
{
disp[0] = smgduan[num/1000]; //取数据的千位
disp[1] = smgduan[num%1000/100]; //取百位
disp[2] = smgduan[num%1000%100/10]; //十位
disp[3] = smgduan[num%1000%100%10]; // 个位
}
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]; //发送数据
delay(100); //间隔一段时间扫描
P0 = 0x00; //消隐
}
}
void main()
{
while(1)
{
Keypros(); //按键处理函数
datapros(); //数据处理函数
DigDisplay();//数码管显示函数
}
}