i2c通信程序(解读)
程序员文章站
2022-06-08 21:37:58
...
main函数(过程参考收藏文章–应答部分,写读部分编写安装时序图)
# include"reg51.h"
# include"i2c.h"
//单片机是主机,AT24C02芯片是从机,其中AT24C02芯片的SCL和SDA
//已与硬件连接单片机管脚P21和P20
typedef unsigned int u16;
//typedef unsigned char u8;
u8 dat;
u8 code smg[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f
,0x6f}; //显示0-9,用P0口,smg[0]对应0x3f,显示0;
//以上早都一一对应好了
sbit A0=P1^0;
sbit A1=P1^1;
sbit A2=P1^2;//138译码器进行8个数码管位选的3个输入控制8个输出电平状态
sbit k1=P3^0;
sbit k2=P3^1;
sbit k3=P3^2;
sbit k4=P3^3;//通过4个独立按键改变输入电平状态
void delay(u16 i)//延时函数
{
while(i--);
}
void delay10us()//延时10us
{
u8 a,b;
for(b=1;b>0;b--)
for(a=2;a>0;a--);
}
void i2cstart()//i2c起始信号,根据时序图编写模拟时序
{
SDA=1;
delay10us();
SCL=1;
delay10us();
SDA=0;
delay10us();
}
void i2cpause()//i2c终止信号和起始信号的SCL和SDA的延时先后没有太大关系,只要保证
{ //在SCL高电平期间,SDA电平由高变低为起始信号,同理为终止信号;
//当然了,时间条件依然要满足总线时序图中的要求
SDA=0;
delay10us();
SCL=1;
delay10us();
SDA=1;
delay10us();//时序从同步的角度去分析,SDA的电平状态在SCL延时期间会保持
}
u8 i2csendbyte(u8 dat)//根据总线时序图进行编写(不管主机,还是从机)
{
u8 i;
for(i=0;i<8;++i)
{
SDA=dat>>7;//一个8位数据把低7位都移除,剩下最高位(第8位数据)赋值给SDA
dat<<=1; //注意是dat不是SDA,该8位数据左移一位,将第8位移除,第7位移到第8位
delay10us();//这个延时对应总线时序图中数据输入保持时间
//(SDA的每一位数据保持高或低电平一段稳定时间)
SCL=1;
delay10us();
SCL=0;
delay10us(); //到此dat的第一位数据发送出
}//这时候经过上面循环8位数据都已经在SDA线上,
SDA=1;
delay10us(); //这是第9个SCL时钟周期
SCL=1;
delay10us();//要求(规定)主控制器在第9个时钟脉冲位上释放SDA线,以便受控器发出应答信号,
//将SDA线拉低,表示接收数据的应答(ACK)。如果在
//第9个时钟周期收到非应答信号(NACK),则表示停止数据的发送或接收。
//也是按时序图编写
while(SDA) //接收产生一个应答SDA为0,表示接收成功
{ //接收一个非应答SDA=1,表示接收失败
delay10us();
SCL=0;
delay10us();
return 0;//执行return直接结束函数
}
SCL=0;//时序图是最后拉低时钟
delay10us();
return 1;//发送成功返回1,失败返回0
}
u8 i2creaddat( )//(不管主机,还是从机)
{
u8 i,dat=0;
SDA=1;//让总线处于空闲状态
delay10us();
for(i=0;i<8;++i)
{
SCL=1;//时钟线为高电平,数据不会变化
delay10us();
dat<<=1;
dat|=SDA; //每循环一次数据线上的数据移给dat一位
delay10us();
SCL=0; //时钟线为低电平,数据才会更新改变
delay10us();
}
return dat;
}//上述读的过程:在数据稳定和变化这个期间进行,同时注意移位指令先后的顺序
void AT24C02write(u8 add,u8 dat)//都是通过i2c总线进行通信的
{
i2cstart();//发送起始信号
i2csendbyte(0xa0) ;//发送接收方器件地址 1010 0000,第8位0表示写入
i2csendbyte(add);//发送数据首地址
i2csendbyte(dat); //发送数据
i2cpause();//发送停止信号
}
u8 AT24C02read(u8 add)
{
u8 dat;
i2cstart();//发送起始信号
i2csendbyte(0xa0);//发送接收方器件地址 1010 0000,第8位0表示写入
i2csendbyte(add);//发送数据首地址
i2cstart();//再次发送起始信号
i2csendbyte(0xa1);//改成读
dat=i2creaddat();//读数据
i2cpause();//发送停止信号
return dat;
}
void keyxd()//按键消抖处理函数
{
u8 dat;
if(k1==0)//k1按下写入数据
{
delay(1000);
if(k1==0)
{
AT24C02write(1,dat);//因为AT24C02芯片硬件地址有256个,这里选地址1
}
while(!k1);
}
if(k2==0)//k2按下读出数据
{
delay(1000);
if(k2==0)
{
dat=AT24C02read(1);//这就是没有用到指针的缘故,直接知道具体的地址
}
while(!k2);
}
if(k3==0)//k3计数
{
delay(1000);
if(k3==0)
{
dat++;
if(dat>255)//超过255,8位就不够存
{
dat=0;
}
}
while(!k2);
}
if(k4==0)//k2按下清0
{
delay(1000);
if(k4==0)
{
dat=0;
}
while(!k4);
}
}
u8 dx[4];
void datprocess()
{
dx[0]=smg[dat/1000];//dat是一个8位二进制数,能表示的十进制数最大为255(千位)
dx[1]=smg[dat%1000/100];//计算机内部都是以二级制数进行运算的 (百位)
dx[2]=smg[dat%1000%100/10]; //(十位上的数)
dx[3]=smg[dat%1000%100%10];//(个位上的数)
}
void smgdx()
{
u8 i;
for(i=0;i<4;++i)
{
switch(i)
{
case 0:A0=0,A1=0,A2=0; //SMG1开发板最右边,且对应个位
break;
case 1:A0=1,A1=0,A2=0; //SMG2
break;
case 2:A0=0,A1=1,A2=0; //SMG3
break;
case 3:A0=1,A1=1,A2=0; //SMG4
break;
/* case 4:A0=0,A1=0,A2=1; //SMG5
break;
case 5:A0=1,A1=0,A2=1; //SMG6
break;
case 6:A0=0,A1=1,A2=1; //SMG7
break;
case 7:A0=1,A1=1,A2=1; //SMG8
break;*/
}
P0=dx[i];//让对应的数码管显示数字 //看看开发板具体对应哪个啥情况?
delay(10000);
P0=0x00;//消影
}
}
void main()
{
while(1)
{
keyxd();
datprocess();
smgdx();
}
}
i2c.h
# ifndef _i2c_H
# define _i2c_H
# include"reg51.h"
sbit SCL=P2^1;
sbit SDA=P2^0;
typedef unsigned char u8;
void AT24C02write(u8 add,u8 dat);//都是通过i2c总线进行通信的
u8 AT24C02read(u8 add);
# endif