#include <at89x51.h>
#include <intrins.h>
#define uchar unsigned char
sbit VSDA=P2^1; //将p1.0口模拟数据口
sbit VSCL=P2^0; //将p1.1口模拟时钟口
uchar idata SLA; //从器件地址
uchar idata SLAW; //从器件写地址
uchar idata SLAR; //从器件读地址
uchar idata NUMBYT; //数据传送字节
uchar idata MTD[10]={0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}; //数据发送缓冲
区
uchar idata MRD[10]; //数据接收缓冲区
bit bdata NACK; //器件坏或错误标志位
//启动I2C总线子程序//
void STA(void)
{
VSDA=1; //启动I2C总线
VSCL=1;
_nop_(); //延时4.7us,根据晶振频率调整空操作个数,这里以fosc=12MHz,
下同
_nop_();
_nop_();
_nop_();
VSDA=0;
_nop_();
_nop_();
_nop_();
_nop_();
VSCL=0;
}
//停止I2C总线数据传送子程序//
void STOP(void)
{
VSDA=0; //停止I2C总线数据传送
VSCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
VSDA=1;
_nop_();
_nop_();
_nop_();
_nop_();
VSCL=0;
}
//发送应答位子程序//
void MACK(void)
{VSDA=0; //发送应答位
VSCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
VSDA=1;
VSCL=0;
}
//发送非应答位子程序//
void MNACK(void)
{
VSDA=1; //发送非应答位
VSCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
VSDA=0;
VSCL=0;
}
//应答位检查子程序//
void CACK(void)
{
VSDA=1; //应答位检查(将p1.0设置成输入,必须先向端口写1)
VSCL=1;
F0=0;
if (VSDA==1) F0=1; //若VSDA=1表明非应答,置位非应答标志F0
VSCL=0;
}
//发送一个字节数据子程序,程序入口p为发送缓冲区地址//
void WRBYT(p)
uchar idata *p;
{
uchar idata n=8; //向VSDA上发送一位数据字节,共八位
uchar idata temp;
temp=*p;
while (n--) {
if ((temp&0x80)==0x80) //若要发送的数据最高位为1则发送位1
{ VSDA=1; //传送位1
VSCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
VSDA=0;
VSCL=0;
}
else {
VSDA=0; //否则传送位0
VSCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
VSCL=0;
}
temp=temp<<1; //数据左移一位,或_crol_(*p,1)
}
}
//接收一字节子程序,入口参数p为接收缓冲区地址//
void RDBYT(p)
uchar idata *p;
{
uchar idata n=8; //从VSDA线上读取一上数据字节,共八位
uchar idata temp=0;
while (n--) { VSDA=1;
VSCL=1;
temp=temp<<1;//左移一位,或_crol_(temp,1)
if (VSDA==1)
temp=temp|0x01;//若接收到的位为1,则数据的最后一位置1
else temp=temp&0xfe;//否则数据的最后一位置0
VSCL=0;
}
*p=temp;
}
//发送n位数据子程序//
void WRNBYT(sla,n)
uchar *sla;
uchar n;
{
uchar idata *p;
STA(); //启动I2C
WRBYT(sla); //发送一上位数据
CACK(); //检查应答位
if (F0==1) {
NACK=1;return; //若非应答表明器件错误或已坏,
置错误标志位NACK
}
p=MTD;
while(n--){
WRBYT(p);
CACK();//检查应答位
if (F0==1) {
NACK=1;return; //若非应答
表明器件错误或已坏,置错误标志位NACK
}
p++;
}
STOP(); //全部发完则停止
}
//接收n位数据子程序
void RDNBYT(sla,n)
uchar idata *sla;
uchar n;
{
uchar idata *p;
STA();
WRBYT(sla);
CACK();
if (F0==1) {
NACK=1;return;
}
p=MRD;
while (n--) {
RDBYT (p);
MACK(); //收到一个字节后发送一个应答位
p++;
}
MNACK(); //收到最后一个字节后发送一个非应答位
STOP();
}
delay(int t)
{
int i,j;
for(i=0;i<t;i++)
for(j=0;j<125;j++);
}
main()
{
P2_2=0;
delay(20);
P2_2=1;
SLAW=0xA0;
SLA=SLAW;
NUMBYT=0x09;
WRNBYT(MRD,9);
if (MRD[5]==0x55) P1=0x55;
P2_3=0;
}
whf68cn_cn@sina.com.cn
谢谢
本程序在以下型号的单片机学习板上调试完成:
型号:TL-SST89E、TL-AT89S51、TL-SST89H、TL-SST89A、TL-STC89C、TL-Min51
AT89S52 晶振频率为11.0592MHz 指令周期:1.0852us
功能说明:24C01-16程序,能读写:24C01、24C02、24C04、24C08、24C16
读出的数据 送 P1 口显示
;***************************************************************/
#include "reg52.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
sbit sda=P3^7;//;模拟I2C 数据传送位
sbit scl=P3^6;//;模拟I2C 时钟控制状态标志
void delay1(uint z)//延时为 1ms
{
uchar x,x1;
for(;z> 0;z--)
{
for(x=0;x <114;x++)
{
for(x1=0;x1 <1;x1++);
}
}
}
void delay()//5us延时
{
_nop_();
_nop_();
_nop_();
}
void star()//开始
{
sda=1;
delay();//5us延时
scl=1;
delay();//5us延时
sda=0;
delay();//5us延时
}
void stop()//停止
{
sda=0;
delay();//5us延时
scl=1;
delay();//5us延时
sda=1;
delay();//5us延时
}
void ack()//应答
{ uchar z=0;
while((sda==1)&&(z <50))z++;//条件判断, sda=1,则没有应答。如果没有应答则延时:z <50,z++;后返回
scl=0;
delay();//5us延时
}
///写一个数据函数
//器件写地址 slave_write_address
//字节地址 byte_address
//待写入数据 data_data
void write(uchar slave_write_address,uchar byte_address,uchar data_data)//写一个数据
{
uchar temp,temp1,i,ii;
star();//开始
for(ii=0;ii <3;ii++)//根据 24CXX文档资料,和时序图,按顺序送:器件写地址,字节地址,数据
{
if(ii==0)
{
temp=slave_write_address;//送 器件写地址
temp1=slave_write_address;
}
else if(ii==1)
{
temp=byte_address;//送 字节地址
temp1=byte_address;
}
else if(ii==2)
{
temp=data_data;//送 数据
temp1=data_data;
}
for(i=0;i <8;i++)
{
scl=0;
delay();//5us延时
temp=temp1;
temp=temp&0x80;// 相 与 后,把不相关的位清零
if(temp==0x80)//根据前面 相 与 后,判断 temp是否等于0x80,是则该位为 1
sda=1;
else
sda=0;
delay();//5us延时
scl=1;
delay();//5us延时
scl=0;
delay();//5us延时
temp1=temp1 < <1;//向左移出1位
}
sda=1;
delay();//5us延时
scl=1;
delay();//5us延时
ack();
}
stop();//停止
}
///读一个数据函数
//器件写地址 slave_write_address
//器件读地址 slave_read_address
//字节地址 byte_address
//读出的数据 data_data
read(uchar slave_write_address,uchar byte_address,uchar slave_read_address)//读一个数据
{
uchar temp,temp1,i,ii,x,data_data;
star();//开始
for(ii=0;ii <3;ii++)//根据 24CXX文档资料,和时序图,按顺序送:器件写地址,字节地址,器件读地址
{
if(ii==0)
{
temp=slave_write_address;//送 器件写地址
temp1=slave_write_address;
}
else if(ii==1)
{
temp=byte_address;//送 字节地址
temp1=byte_address;
}
else if(ii==2)
{
star();//开始
temp=slave_read_address;//送 器件读地址
temp1=slave_read_address;
}
for(i=0;i <8;i++)//开始读数据
{
scl=0;
delay();//5us延时
temp=temp1;
temp=temp&0x80;// 相 与 后,把不相关的位清零
if(temp==0x80)//根据前面 相 与 后,判断 temp是否等于0x80,是则该位为 1
sda=1;
else
sda=0;
delay();//5us延时
scl=1;
delay();//5us延时
scl=0;
delay();//5us延时
temp1=temp1 < <1;//向左移出1位
}
sda=1;
delay();//5us延时
scl=1;
delay();//5us延时
ack();//应答
}
for(x=0;x <8;x++)
{
data_data=data_data < <1;//向左移入1位
sda=1;
delay();//5us延时
scl=0;
delay();//5us延时
scl=1;
delay();//5us延时
if(sda==1)//判断 数据线是否是高电平
data_data|=0x01;//把读到的数据 或 0X01
//else
//data_data|=0x00;
}
ack();//应答
stop();//停止
return data_data;//返回读到的数据
}
void main()
{
write(0xa0,0xff,0x66);//向器件写一个数据:(0xa0 是器件写地址;0xff 是字节地址;0x66 是待写入的数据)
delay1(5);//写与读的时间间隔应大于5ms,取决于器件 24C02的响应速度
//向器件读一个数据
//把读出的数据送 P1口显示
P1=read(0xa0,0xff,0xa1);//向器件读一个数据:(0xa0 是器件写地址;0xff 是字节地址;0xa1 是器件读地址)
while(1);//跳转,相当于汇编指令 JUMP $
}
//0x66==亮 灭 灭 亮 亮 灭 灭 亮