最近公司那边给了个任务,帮调下PCF8563时钟芯片,是一款IIC总线的器件,之前也有写过AT24C02这样的ICC,不过已经忘记的差不多了,现今重拾旧知,总结如下:
I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。
I2C总线通过上拉电阻(一般取10K)接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。
起始和终止信号:
SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号
SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号
数据位的有效性规定
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
数据传送格式:每一个字节必须保证是8位长度。数据传送时,先传送{zg}位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
总线的寻址:I2C总线协议有明确的规定:采用7位的寻址字节(寻址字节是起始信号后的{dy}个字节)。
D7~D1位组成从机的地址。D0位是数据传送方向位,为“0”时表示主机向从机写数据,为“1”时表示主机由从机读数据。
51单片机IO口模拟ICC典型的子程序:
sbit SDA=P1^0; // 将p1.0口模拟数据口
sbit SCL=P1^1; // 将p1.1口模拟时钟口
#define delay_NOP(); {_nop_();_nop_();}; //晶振11.0592M,差不多5us
/****************************************
*函数名称:void iic_start(void)
*输入:无
*输出:无
*功能说明:启动I2C总线子程序
*****************************************/
void iic_start(void) //时钟保持高,数据线从高到低一次跳变,I2C通信开始
{
EA=0;
SDA = 1;
SCL = 1;
delay_NOP(); // 延时5us (大于4.5us即可)
SDA = 0;
delay_NOP();
SCL = 0;
}
/****************************************
*函数名称:void iic_stop(void)
*输入:无
*输出:无
*功能说明:停止I2C总线数据传送子程序
*****************************************/
void iic_stop(void) //时钟保持高,数据线从低到高一次跳变,I2C通信停止
{
SDA = 0;
SCL = 0;
delay_NOP();
SCL = 1;
delayNOP();
SDA = 1;
delay_NOP();
EA = 1;
}
/******************************************************************************
*函数名称:void WriteACK(uchar ack)
*输入:uchar ack
*输出:无
*功能说明:内部函数:每个字节传输完成,ack=0,有应答;结束读数据,ack=1,无应答;
*******************************************************************************/
void WriteACK(uchar ack)
{
SDA=ack;
delay_NOP();
SCL=1;
delay_NOP();
SCL=0;
}
/********************************************
*函数名称:void WaitACK(void)
*输入:无
*输出:无
*功能说明:等待ACK
**********************************************/
void WaitACK(void)
{
uchar errtime=20;
SDA=1;
delay_NOP(); //读ACK
SCL=1;
delay_NOP();
while(SDA)
{
errtime--;
if(!errtime)
iic_stop();//等待了一段时间后,没有应答,即停止ICC
}
SCL=0; //拉低时钟
delay_NOP();
}
/******************************************************
*函数名称:void writebyte(uchar wdata)
*输入:uchar wdata
*输出:无
*功能说明:内部函数.写入数据字节,一位一位的写,先写MSB
********************************************************/
void writebyte(uchar wdata)
{
uchar i;
for(i=0;i<8;i++)
{
if(wdata&0x80) //每次都是wdata{zg}位&0x80,为1时:高电平;为0时,低电平
{
SDA=1;
}
else
{
SDA=0;
}
wdata<<=1; //wdata左移一位
SCL=1; //数据保持
delay_NOP();
SCL=0; //数据可改变
}
WaitACK(); //写完一个8位字节数据后等待应答
}
/********************************************
*函数名称:uchar Readbyte(void)
*输入:无
*输出:return(bytedata)
*功能说明:内部函数.读出一个字节数据
**********************************************/
uchar Readbyte(void)
{
uchar i,bytedata;
SDA=1; //先释放总线
for(i=0;i<8;i++)
{
SCL=1; //数据保持稳定,可读取.
bytedata<<=1; //bytedata左移一位,最地位是0
bytedata|=SDA; //bytedata或上SDA,即bytedata{zd1}位值为SDA
SCL=0; //数据可改变
delay_NOP();
}
return(bytedata); //返回bytedata
}