|
本帖最后由 pull1121 于 2020-2-22 11:00 编辑
Protues 仿真51单片机 用AT24C2数据不保存,重启后,数据归零。
假如 数码管上显示 XX,(XX为任意数)重新上电后数码管变成 00效果图:如下
#include <reg51.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[]={ //数码管显示0到F
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
sbit dula=P2^6;
sbit wela=P2^7;
sbit SDA=P2^0;
sbit SCL=P2^1;
bit write=0;
uchar sec,num1;
void delay(uint cc) //ms级别的延时
{
uint aa,bb;
for(aa=cc;aa>0;aa--)
for(bb=110;bb>0;bb--);
}
void delay_ws() //ns级别的延时
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void display(uchar bai_c,uchar sh_c)
{
//shi=flag/10;
//ge=flag%10;
P0=0x00;
dula=1;
P0=table[bai_c];
dula=0;
P0=0xff;
wela=1;
P0=0x7e;
wela=0;
delay(5);
P0=0x00;
dula=1;
P0=table[sh_c];
dula=0;
P0=0xff;
wela=1;
P0=0x7d;
wela=0;
delay(5);
}
void init ()
{
/* TMOD=0x11; //T0定时器初始化
TH0=(65536-45872)/256;
TL0=(65536-45872)%256;
TR0=1;
ET0=1;
EA=1;
*/
SCL=1; //I2C的2条总线初始化
delay_ws();
SDA=1;
delay_ws();
}
void start() //单片机开始写信号
{ //严格按照时序图来写,SCK=1时候,SDA从1到0,一个下降沿表示开始
SDA=1;
delay_ws();
SCL=1;
delay_ws();
SDA=0;
delay_ws();
SCL=0; //等待下一步开始 写数据 或者 读数据
delay_ws();
}
void respons() //从机应答信号
{ //严格按照时序图来写
uchar i=0;
SCL=1;
delay_ws();
while((SDA==1)&&(i<255)) // SDA=0时候表示应答,或者等待一下无应答
i++; // 都表示结束
SCL=0;
delay_ws();
}
void stop() //单片机结束写信号
{ //严格按照时序图来写,SCK=1时候,SDA从0到1,一个上升沿表示结束
SDA=0;
delay_ws();
SCL=1;
delay_ws();
SDA=1;
delay_ws();
}
void write_byte(uchar date)//单片机写一个字节信号,从1个字节的最高位到最低位依次写入
{ //和读数据一样 最高到最低
uchar i,temp;
temp=date;
for(i=0;i<8;i++) //将date的最高位到最低位依次赋给SDA
{
temp=temp<<1; //将数据 date 最高位右移一位赋给变量 temp
SCL=0;
delay_ws();
SDA=CY; //变量 temp移出来的一位通过PSW寄存器的CY位赋给SDA
delay(5);
SCL=1; //SDA数据的变化 需要SCK从0到1变化
delay_ws();
}
SCL=0; //等待下次sda上数据变化,个人理解是等待从机相应
delay_ws();
SDA=1; //释放数据总线
delay_ws();
}
uchar read_byte()//单片机读一个字节信号,从1个字节的最高位到最低位依次读出
{ //此子函数带返回值 return k
uchar i,k;
SCL=0; //置0,等待SDA上的数据被读取
delay_ws();
SDA=1; //置1,释放SDA,等待SDA上的数据被读取
delay_ws();
for(i=0;i<8;i++)
{
SCL=1; //SCL从0到1,SDA上的一个字节数据被依次放入返回值 k
delay_ws();
k=(k<<1)|SDA; //将第一位SDA数据置于最高位,k0 | X(0或者1)
// 0|X都为X,所以将第一位SDA的数据置于K的最高位
//然后依次循环8次,将SDA从最高位放到最低位
SCL=0;
delay_ws();
}
return k; //SDA上的数据从第一位到第八位依次放入K的最高位到最低位
}
void write_add(uchar address,uchar date) //从E2Prom任意地址写任意数据
{
start(); //开始信号
write_byte(0xa0); // 1010 0000 单片机发地址,在I2C上面找
// 设备(低4位前3位代表设备地址000,后1位的0代表方向主机到从机)
respons(); // E2Prom设备应答
write_byte(address); // 从E2Prom设备的address地址开始写数据
respons(); // E2Prom设备应答
write_byte(date); //给E2Prom设备开始写数据 date
respons(); // E2Prom设备应答
stop(); //停止
} //从E2Prom任意地址写任意数据的流程是:1.开始 2.设备地址
//3.被寻址的设备应答 4.给被寻址的设备要写数据的位置
//5.被寻址的设备应答 6.给被寻址的设备写数据
//7.应答 8.停止
uchar read_add(uchar address) //从E2Prom任意地址读任意数据
{
uchar date;
start(); //开始信号
write_byte(0xa0); // 1010 0000 单片机发地址,在I2C上面找
//设备(低4位前3位代表设备地址000,后1位的0代表方向主机到从机)
respons();
write_byte(address);//从E2Prom设备的address地址开始读数据
respons(); // E2Prom设备应答
start(); //开始信号
write_byte(0xa1); // 1010 0001 单片机发地址,在I2C上面找
// 设备(低4位前3位代表设备地址000,后1位的1代表方向从机到主机)
respons(); // E2Prom设备应答
date=read_byte(); //把读到的数据赋给date
stop(); //停止
return date; //返回读到的数据 date
}
//从E2Prom任意地址读数据的流程是:1.开始 2.设备地址(主机到从机)
//3.被寻址的设备应答 4.给被寻址的设备要读数据的位置
//5.开始 6. 设备地址(从机到主机)7.被寻址的设备应答
//8.从被寻址的设备读数据赋给date 9.停止 10.将date返回
void Time0 () interrupt 1 //T0中断子函数
{
TH0=(65536-45872)/256; //频率为11.0592MHZ下的 高8位 初值
TL0=(65536-45872)%256; //频率为11.0592MHZ下的 低8位 初值
num1++; //每50ms加一次
if(num1==20) //T0计满20次(1秒)时
{
num1=0; //清零,继续计数
sec++; //数码管上的数1s加一次
write=1; //一秒写一次AT24C02(E2PROM)
if(sec==100)//如果计时到了100S就重新计时
sec=0; //清零
}
}
void main()
{
init(); //初始化 ,其实就是SDA和SCL释放
sec=read_add(2);//读出保存的数据赋给sec
if(sec>99)//读出的E2PROM如果大于99,只有2位数码管显示最多显示2位数
sec=0; //清零
TMOD=0x01;//设置T0定时器工作方式为1
TH0=(65536-45872)/256; //频率为11.0592MHZ下的 高8位 初值
TL0=(65536-45872)%256; //频率为11.0592MHZ下的 低8位 初值
TR0=1; //T0的运行位打开,开始计时
ET0=1; //T0中断打开
EA=1; //总中断打开
while(1)
{
display(sec/10,sec%10); //将数sec显示在数码管上
if(write==1) //判断计时器是否计时1S
{
write=0; //清零
write_add(2,sec);//在E2PROM的列地址2中写入数据sec
delay(5);
}
}
}
|
|