模拟I2C总线通信实现EEPROM读写功能
# include <reg52.h>
# include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define AT24C02_ADDR 0xa0 //将地址赋给常量
sbit SDA = P2^0;
sbit SCL = P2^1;
void delay(unsigned int z)
{
unsignedint x,y;
for(x= z;x > 0;x--)
for(y= 114; y > 0; y--);
}
void delay_5us() //5us多一点
{
_nop_();//进入、退出函数,执行语句需要5个机器周期(5.43)
}
//I2C 初始化
void I2C_init()
{
SDA= 1; //初始化,使得两根总线处于闲置状态
_nop_();
SCL= 1;
_nop_();
}
//I2C起始信号程序
void I2C_Start()
{
SCL= 1;
_nop_();//1.08506us
SDA= 1;
delay_5us();
SDA= 0;
delay_5us();
}
//I2C终止信号程序
void I2C_Stop()
{
SDA= 0;
_nop_(); //执行一个机器周期稳定下来
SCL= 1;
delay_5us();
SDA= 1;
delay_5us();
}
//I2C主机检测从机应答 返回0不应答,返回1应答
bit Test_ACK()
{
SCL= 1; //在时钟总线为高电平期间可以读取应答信号
delay_5us();
if(SDA)
{
SCL= 0; //SCL为0时才允许变化,然后发送停止信号
_nop_();
I2C_Stop();
return(0); //返回并跳出函数
}
else
{
SCL= 0;
_nop_();
return(1);
}
}
//I2C主机发送应答
void Master_ACK(bit i)
{
SCL= 0; //拉低时钟总线,允许SDA数据总线变化
_nop_(); //空指令
if(i)
{
SDA= 0; //应答
}
else
{
SDA= 1; //发送非应答
}
_nop_(); //数据保持稳定
SCL= 1; //拉高时钟总线,让从机从SDA线上 读数据
delay_5us();
SCL= 0; //拉低时钟总线,占用总线继续通信
_nop_();
SDA= 1; //释放SDA线,交由从机控制
_nop_();
}
//发送字节函数
void I2C_send_byte(uchar byte)
{
uchari;
for(i= 0; i < 8; i++)
{
SCL= 0;
_nop_();
if(byte& 0x80)
{
SDA= 1;
_nop_();
}
else
{
SDA= 0;
_nop_();
}
SCL= 1;
_nop_();
byte<<= 1; //将byte左移一位,次高位被放在最高位
//左移1位,然后赋值给自己
}
SCL= 0;
_nop_();
SDA= 1;
_nop_();
}
//读取一个字节子函数
uchar I2C_read_byte()
{
uchardat,i;
SCL= 0;
_nop_();
SDA= 1;
_nop_();
for(i= 0; i < 8; i++)
{
SCL= 1; //SCL置高
_nop_();
if(SDA)
{
dat|= 0x01;//dat = dat | 0x01; //给最低位 1
}
else
{
dat&= 0xfe;//dat <<= 1; // == dat = dat & 0xfe; 给最低位 0
}
_nop_();
SCL= 0;
_nop_();
if(i< 7)
{
dat<<= 1;
}
}
return(dat);
}
void main()
{
bit ACK_flag = 0;
I2C_init();
I2C_Start();
I2C_send_byte(AT24C02_ADDR+0);
if(!Test_ACK())
{
ACK_flag= 1;
}
I2C_send_byte(8);
if(!Test_ACK())
{
ACK_flag= 1;
}
I2C_send_byte(0xaa);
if(!Test_ACK())
{
ACK_flag= 1;
}
I2C_Stop();
delay(5);
I2C_Start();
I2C_send_byte(AT24C02_ADDR+0);
if(!Test_ACK())
{
ACK_flag= 1;
}
I2C_send_byte(8);
Master_ACK(0);
I2C_Start();
I2C_send_byte(AT24C02_ADDR+1);
if(!Test_ACK())
{
ACK_flag= 1;
}
P1= I2C_read_byte();
Master_ACK(0);
I2C_Stop();
if(ACK_flag)
{
P1= 0;
}
while(1);
}
全部以函数的方式写I2C
# include <reg52.h>
# include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define AT24C02_ADDR 0xa0 //将地址赋给常量
sbit SDA = P2^0;
sbit SCL = P2^1;
void delay(unsigned int z)
{
unsignedint x,y;
for(x= z;x > 0;x--)
for(y= 114; y > 0; y--);
}
void delay_5us() //5us多一点
{
_nop_();//进入、退出函数,执行语句需要5个机器周期(5.43)
}
//I2C 初始化
void I2C_init()
{
SDA= 1; //初始化,使得两根总线处于闲置状态
_nop_();
SCL= 1;
_nop_();
}
//I2C起始信号程序
void I2C_Start()
{
SCL= 1;
_nop_();//1.08506us
SDA= 1;
delay_5us();
SDA= 0;
delay_5us();
}
//I2C终止信号程序
void I2C_Stop()
{
SDA= 0;
_nop_(); //执行一个机器周期稳定下来
SCL= 1;
delay_5us();
SDA= 1;
delay_5us();
}
//I2C主机检测从机应答 返回0不应答,返回1应答
bit Test_ACK()
{
SCL= 1; //在时钟总线为高电平期间可以读取应答信号
delay_5us();
if(SDA)
{
SCL= 0; //SCL为0时才允许变化,然后发送停止信号
_nop_();
I2C_Stop();
return(0); //返回并跳出函数
}
else
{
SCL= 0;
_nop_();
return(1);
}
}
//I2C主机发送应答
void Master_ACK(bit i)
{
SCL= 0; //拉低时钟总线,允许SDA数据总线变化
_nop_(); //空指令
if(i)
{
SDA= 0; //应答
}
else
{
SDA= 1; //发送非应答
}
_nop_(); //数据保持稳定
SCL= 1; //拉高时钟总线,让从机从SDA线上 读数据
delay_5us();
SCL= 0; //拉低时钟总线,占用总线继续通信
_nop_();
SDA= 1; //释放SDA线,交由从机控制
_nop_();
}
//发送字节函数
void I2C_send_byte(uchar byte)
{
uchari;
for(i= 0; i < 8; i++)
{
SCL= 0;
_nop_();
if(byte& 0x80)
{
SDA= 1;
_nop_();
}
else
{
SDA= 0;
_nop_();
}
SCL= 1;
_nop_();
byte<<= 1; //将byte左移一位,次高位被放在最高位
//左移1位,然后赋值给自己
}
SCL= 0;
_nop_();
SDA= 1;
_nop_();
}
//读取一个字节子函数
uchar I2C_read_byte()
{
uchardat,i;
SCL= 0;
_nop_();
SDA= 1;
_nop_();
for(i= 0; i < 8; i++)
{
SCL= 1; //SCL置高
_nop_();
if(SDA)
{
dat|= 0x01;//dat = dat | 0x01; //给最低位 1
}
else
{
dat&= 0xfe;//dat <<= 1; // == dat = dat & 0xfe; 给最低位 0
}
_nop_();
SCL= 0;
_nop_();
if(i< 7)
{
dat<<= 1;
}
}
return(dat);
}
/*I2C发送数据*/
bit I2C_TransmitData(uchar ADDR, DAT)
{
I2C_Start();
I2C_send_byte(AT24C02_ADDR+0);
if(!Test_ACK())
{
return(0);
}
I2C_send_byte(ADDR);
if(!Test_ACK())
{
return(0);
}
I2C_send_byte(DAT);
if(!Test_ACK())
{
return(0);
}
I2C_Stop();
return(1);
}
/*I2C接收数据*/
uchar I2C_ReceiveData(uchar ADDR)
{
ucharDAT;
I2C_Start();
I2C_send_byte(AT24C02_ADDR+0);
if(!Test_ACK())
{
return(0);
}
I2C_send_byte(ADDR);
Master_ACK(0);
I2C_Start();
I2C_send_byte(AT24C02_ADDR+1);
if(!Test_ACK())
{
return(0);
}
DAT= I2C_read_byte();
Master_ACK(0);
I2C_Stop();
return(DAT);
}
void main()
{
I2C_init(); //I2C初始化
I2C_TransmitData(255,0xf0);
delay(5);
P1= I2C_ReceiveData(255);
while(1);
}
往QX-MCS51开发板的AT24C02内任意一个单元写数据,开发板上电后首先读取此单元的原有的数据,然后赋给程序中的计数变量,计数变量再以5秒的速度+1并且写入AT24C02,当计数大于99时清零再写,用数码管显示。
/*体现eeprom掉电存储功能*/
# include <reg52.h>
# include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define AT24C02_ADDR 0xa0 //将地址赋给常量
sbit SDA = P2^0;
sbit SCL = P2^1;
sbit LED1 = P1^0;
sbit wei = P2^7;
sbit duan = P2^6;
uchar EEPROM_DATA; //存放从AT24C02读取的值
unsigned char leddata[]={
0x3F, //"0"
0x06, //"1"
0x5B, //"2"
0x4F, //"3"
0x66, //"4"
0x6D, //"5"
0x7D, //"6"
0x07, //"7"
0x7F, //"8"
0x6F, //"9"
0x77, //"A"
0x7C, //"B"
0x39, //"C"
0x5E, //"D"
0x79, //"E"
0x71, //"F"
0x76, //"H"
0x38, //"L"
0x37, //"n"
0x3E, //"u"
0x73, //"P"
0x5C, //"o"
0x40, //"-"
0x00, //熄灭
0x00 //自定义
};
void delay_5us() //5us多一点
{
_nop_();//进入、退出函数,执行语句需要5个机器周期(5.43)
}
/*1ms延时函数*/
void delay(unsigned int z)
{
unsignedint x,y;
for(x= z;x > 0;x--)
for(y= 114; y > 0; y--);
}
void display(uchar i)
{
ucharshi,ge;
shi= i / 10; //求模
ge= i % 10; //求余
P0= 0xff; //清除断码
wei= 1;
P0= 0xfe;//点亮第1位数码管
wei= 0;
duan= 1;
P0= leddata[shi];
duan= 0;
delay(1);
P0 = 0xff;
wei= 1;
P0= 0xfd;//点亮第2位数码管
wei= 0;
duan= 1;
P0= leddata[ge];
duan= 0;
delay(1);
}
//I2C 初始化
void I2C_init()
{
SDA= 1; //初始化,使得两根总线处于闲置状态
_nop_();
SCL= 1;
_nop_();
}
void Timer0_init() //50ms定时
{
TMOD|= 0x01; //定时器T0 16位计数模式
TH0= 0x4b;
TL0= 0xfe; //50ms
ET0= 1; //T0 中断
TR0= 1; //启动T0
EA= 1; //开总中断
}
//I2C起始信号程序
void I2C_Start()
{
SCL= 1;
_nop_();//1.08506us
SDA= 1;
delay_5us();
SDA= 0;
delay_5us();
}
//I2C终止信号程序
void I2C_Stop()
{
SDA= 0;
_nop_(); //执行一个机器周期稳定下来
SCL= 1;
delay_5us();
SDA= 1;
delay_5us();
}
//I2C主机检测从机应答 返回0不应答,返回1应答
bit Test_ACK()
{
SCL= 1; //在时钟总线为高电平期间可以读取应答信号
delay_5us();
if(SDA)
{
SCL= 0; //SCL为0时才允许变化,然后发送停止信号
_nop_();
I2C_Stop();
return(0); //返回并跳出函数
}
else
{
SCL= 0;
_nop_();
return(1);
}
}
//I2C主机发送应答
void Master_ACK(bit i)
{
SCL= 0; //拉低时钟总线,允许SDA数据总线变化
_nop_(); //空指令
if(i)
{
SDA= 0; //应答
}
else
{
SDA= 1; //发送非应答
}
_nop_(); //数据保持稳定
SCL= 1; //拉高时钟总线,让从机从SDA线上 读数据
delay_5us();
SCL= 0; //拉低时钟总线,占用总线继续通信
_nop_();
SDA= 1; //释放SDA线,交由从机控制
_nop_();
}
//发送字节函数
void I2C_send_byte(uchar byte)
{
uchari;
for(i= 0; i < 8; i++)
{
SCL= 0;
_nop_();
if(byte& 0x80)
{
SDA= 1;
_nop_();
}
else
{
SDA= 0;
_nop_();
}
SCL= 1;
_nop_();
byte<<= 1; //将byte左移一位,次高位被放在最高位
//左移1位,然后赋值给自己
}
SCL= 0;
_nop_();
SDA= 1;
_nop_();
}
//读取一个字节子函数
uchar I2C_read_byte()
{
uchardat,i;
SCL= 0;
_nop_();
SDA= 1;
_nop_();
for(i= 0; i < 8; i++)
{
SCL= 1; //SCL置高
_nop_();
if(SDA)
{
dat|= 0x01;//dat = dat | 0x01; //给最低位 1
}
else
{
dat&= 0xfe;//dat <<= 1; // == dat = dat & 0xfe; 给最低位 0
}
_nop_();
SCL= 0;
_nop_();
if(i< 7)
{
dat<<= 1;
}
}
return(dat);
}
/*I2C发送数据*/
bit I2C_WriteData(uchar ADDR, DAT)
{
I2C_Start();
I2C_send_byte(AT24C02_ADDR+0);
if(!Test_ACK())
{
return(0);
}
I2C_send_byte(ADDR);
if(!Test_ACK())
{
return(0);
}
I2C_send_byte(DAT);
if(!Test_ACK())
{
return(0);
}
I2C_Stop();
return(1);
}
/*I2C接收数据*/
uchar I2C_ReadData(uchar ADDR)
{
ucharDAT;
I2C_Start();
I2C_send_byte(AT24C02_ADDR+0);
if(!Test_ACK())
{
return(0);
}
I2C_send_byte(ADDR);
Master_ACK(0);
I2C_Start();
I2C_send_byte(AT24C02_ADDR+1);
if(!Test_ACK())
{
return(0);
}
DAT= I2C_read_byte();
Master_ACK(0);
I2C_Stop();
return(DAT);
}
void main()
{
I2C_init(); //I2C初始化
Timer0_init();
EEPROM_DATA= I2C_ReadData(255);
while(1)
{
display(EEPROM_DATA);//数码管显示
}
}
/* 定时器0 中断服务程序*/
void timer0() interrupt 1 //T0内部优先级
{
uchari;
TH0= 0x4b;
TL0= 0xfe;
i++;
if(i== 100)
{
i= 0;
if(EEPROM_DATA< 99)//实现5s加1
{
EEPROM_DATA++;
}
else
{
EEPROM_DATA= 0;
}
if(!I2C_WriteData(255,EEPROM_DATA))//判断数据是否写入
{
LED1= 0;
}
else
{
LED1= 1;
}
}
}
哦,长篇大论这是做什么呢?
这些代码并不算长吧。 这个程序就是跟题目是一致的,并未有什么不妥。 我不喜欢压缩让别人下载啊什么的,纯粹在论坛里发发贴,如果程序有什么更好的想法都值得直接交流和吐槽,因为我本人也是在学习阶段。 如果正要做这方面学习的朋友也直接能copy相应的代码。
写得很不错,最后一个程序,编释不成攻,提示最后一个else出错,我是新手,不知错在那里,请白忙中指点一下。谢谢前辈。