I2C协议在项目开发中使用很常见,很多存储芯片使用I2C接口。由于
51单片机没有I2C接口,这里使用IO口模拟I2C通讯协议,来完成I2C芯片驱动。使用的I2C芯片为
AT24C02。
原理图如下:
代码如下:
//程序功能:计时器每一秒向AT24C02保持数据,同时数码管显示,重启之后读取出数据接着计时,100S循环
//程序问题:无法写入
#include
#define uint unsigned int
#define uchar unsigned char
//变量定义
uint timer_flag; //用于判断定时器T0方式一是否计满1s
uchar shiwei,gewei; //数码管显示的十位与个位
uint number;
uchar time_date; //当前时间数据
//控制端口声明
sbit duanxuan=P2^6; //数码管段选端
sbit weixuan=P2^7; //数码管位选端
sbit AT24C02_SDA=P2^0; //AT24C02串行数据输入输出端口
sbit AT24C02_SCLK=P2^1; //时钟信号端口
//数码管显示数字段码定义
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
//相关函数声明
void delay_minute(); //延时us级函数声明
void delay_xs(uint); //延时s级函数声明
void timer_init(); //定时器T0方式一初始化函数声明
void AT24C02_init(); //I^2C通讯总线初始化函数声明
void star(); //I^2C通讯启动函数声明
void stop(); //I^2C通讯停止函数声明
void response(); //应答函数声明
void write_in_byte(uchar); //写入一个字节函数声明
uchar read_out_byte(); //读出一个字节函数声明,函数放回到读出的值
//注意写入一个字节与写入数据的区别,前者是后者的一个子过程,读取同
void saveing(uchar,uchar); //向AT24C02保存数据函数声明
uchar load(uchar); //从AT24C02载入掉电之前的数据,函数放回到读出的值
void quwei(uchar); //显示数字取出各位与十位
void display(uchar); //数码管显示函数声明
//主函数
void main()
{
AT24C02_init(); //AT24C02初始化
time_date=load(3); //开机首先载入AT24C02中的数据,这里读取的是第三位址的数据
if(time_date>100) //防止上一次保存的数据是100,这里计时是100循环的
{
time_date=1;
}
timer_init(); //计时器T0方式一初始化
while(1)
{
display(time_date); //显示时间
if(timer_flag==1) //如果计满一秒,就保存时间数据
{
timer_flag=0;
saveing(3,time_date); //保存时间数据
}
}
}
//延时us级函数主体
void delay_minute()
{;;}
//延时s级函数主体
void delay_xs(uint s)
{
uint i,j;
for(i=s;i>0;i--)
{
for(j=110;j>0;j--)
{
}
}
}
//定时器T0方式一初始化函数主体
void timer_init()
{
//方式选择
TMOD=0x01;
//预装初值
TH0=(65536-46080)/256;
TL0=(65536-46080)%6;
//开中断
EA=1;
ET0=1;
//启动定时器
TR0=1;
}
//I^2C通讯总线初始化函数主体
void AT24C02_init() //两线均写1 AT24C02数据有效规则,sclk为高电平且sda数据稳定,当sda发生有效数据变化时,sclk必须要是0
{
AT24C02_SDA=1;
delay_minute();
AT24C02_SCLK=1;
delay_minute();
}
//I^2C通讯启动函数主体
void star() //启动规则:SCLK-1,SDA下降沿
{
AT24C02_SDA=1; //一定要SDA首先为1,再是SCLK为1,否则会出现一个停止信号
delay_minute();
AT24C02_SCLK=1;
delay_minute();
AT24C02_SDA=0;
delay_minute();
}
//I^2C通讯停止函数主体
void stop() //停止规则:SCLK-1,SDA上升沿
{
AT24C02_SDA=0; //一定要SDA首先为0,再是SCLK为1,否则会出现一个启动信号
delay_minute();
AT24C02_SCLK=1;
delay_minute();
AT24C02_SDA=1;
delay_minute();
}
//应答函数主体
void response() //应答规则:SCLK-1,等待从机把SDA拉低
{
uint i;
AT24C02_SCLK=1;
delay_minute();
while(AT24C02_SDA==1&&i<255)//等待从机把SDA拉低,且超过一定时间没有应答,默认已经应答
{
i++;
}
AT24C02_SCLK=0; //应答之后,SCLK-0.防止数据误操作,因为SCLK-1,数据就是有效地
delay_minute();
}
//写入一个字节函数主体
void write_in_byte(uchar date)
{
uint i,buffer; //i用于循环发送每一位,buffer缓冲数据
buffer=date;
for(i=0;i<8;i++)
{
buffer=buffer<1; //buffer左旋,高位就放入CY
AT24C02_SCLK=0; //有效发送规则:sclk-0变化数据,sclk-1数据稳定后有效
delay_minute();
AT24C02_SDA=CY;
delay_minute();
AT24C02_SCLK=1;
delay_minute();
}
AT24C02_SCLK=0; //不发送,一定是SCLK-0,SDA-1
delay_minute();
AT24C02_SDA=1;
delay_minute();
}
//读出一个字节函数主体
uchar read_out_byte() //读出规则:启动-控制字片选+写(为了写入要读取的地址)-应答-写入存储地址-应答-读取数据-应答-停止
{
uchar i,buffer_writting;
//--------------------------------------------------
//不能少的
AT24C02_SCLK=0; //方便SDA数据发生有效变化
delay_minute();
AT24C02_SDA=1;
delay_minute();
//--------------------------------------------------
for(i=0;i<8;i++)
{
AT24C02_SCLK=1;
delay_minute();
buffer_writting=(buffer_writting<1)|AT24C02_SDA;//buffer_writting左旋之后与AT24C02_SDA的数据线与
delay_minute();
AT24C02_SCLK=0;
delay_minute();