简介
I2C有两根双向信号线。一根是数据线SDA,一根是时钟线SCL。两根线都必须通过一个上拉电阻接到电源。
信号
开始
void start()
{
SDA=1;
SCL=1;
delay(); //5us
SDA=0;
delay(); //5us
SCL=0;
}
发送字节
void send_data(unsigned char byte)
{
unsigned char i;
for(i=0;i<8;i++) //字节拆分按位传递
{ //SCL为高电平 读取SDA稳定数据 所以SDA变化在前
SDA=byte&(0x80>>i); //从最高位依次传递给SDA
delay();
SCL=1;
delay();
SCL=0;
}
}
接收字节
unsigned char read()
{
unsigned char i,byte=0x00;
SDA=1;
for(i=0;i<8;i++) //字节按位接收
{
SCL=1;
delay();
if(SDA){byte|=(0x80>>i);}
delay();
SCL=0;
}
return byte;
}
接收应答
bit read_sck()
{
bit ACK;
SDA=1;
delay(); //5us
SCL=1;
delay(); //5us
ACK=SDA;
delay(); //5us
SCL=0;
return ACK;
}
发送应答
void send_sck(bit ACK) {
// 根据ACK的值设置数据线SDA
SDA = ACK; // 注意:通常ACK是低电平,NACK是高电平
delay(); // 保持SDA状态,等待从机读取
SCL = 1; // 将时钟线SCL拉高
delay(); // 等待时钟线稳定
SCL = 0; // 将时钟线SCL拉低,结束应答
delay(); // 等待时钟线稳定
SDA = 1; //释放数据线 线权交给从机
}
结束
void end()
{
SDA=0;
SCL=1;
delay(); //5us
SDA=1;
delay_ms(10); //10ms
}
注意:I2C协议规定了数据如何发送与接收,但是时序会根据芯片而有所不同
24C08存储芯片为例
写操作
主机发送开始---器件地址(写)---接收应答---发送写地址---接收应答---发送数据---接收应答---结束
页写操作
主机发送开始---器件地址(写)---接收应答---发送写地址---接收应答---发送数据---接收应答---发送数据---接收应答(此时写入的数据地址会自增1,到达页边界会回到页首,覆盖原数据)---结束
当前地址读
主机发送开始---器件地址(读)---接收应答---接收数据(此时读取的地址是上次操作后的数据地址自增1,到达页边界会回到页首)---发送非应答--结束
随机读
主机发送开始---器件地址(写)---接收应答---读取的数据地址---接收应答---开始---器件地址(读)---接收应答---接收数据---发送非应答--结束
顺序读
主机发送开始---器件地址(写)---接收应答---读取的数据地址---接收应答---开始---器件地址(读)---接收应答---接收数据---发送应答---接收数据---(此时按顺序读,到达页边界会回到页首)---发送非应答--结束
代码
#include <reg52.h>
#include "Delay.H"
#include <intrins.H>
#define delay(){_nop_();_nop_();_nop_();_nop_();_nop_();} //五个机器周期 5微妙 一周期多长时间与晶振有关
sbit SDA =P2^0; //数据
sbit SCL =P2^1; //时钟
sbit button =P1^7; //按钮
/**
* @brief 开始信号
* @param
* @param
* @param
* @retval
*/
void start()
{
SDA=1;
SCL=1;
delay();
SDA=0;
delay();
SCL=0;
}
/**
* @brief 结束信号
* @param
* @param
* @param
* @retval
*/
void end()
{
SDA=0;
SCL=1;
delay();
SDA=1;
delay_ms(10);
}
void send_data(unsigned char dat)
{
unsigned int i=0;
for(i;i<8;i++)
{
SDA=dat&(0x80>>i);
delay();
SCL=1;
delay();
SCL=0;
}
}
unsigned char read()
{
unsigned char i=0,byte=0x00;
for(i;i<8;i++) //字节拆分按位接收
{
SCL=1;
delay();
if(SDA){byte|=(0x80>>i);} //高位在前低位在后
delay();
SCL=0;
}
return byte;
}
// 假设SCL和SDA是控制I2C时钟线和数据线的宏或变量
// delay函数用于提供必要的延迟,确保时序正确
void send_sck(bit ACK) {
// 根据ACK的值设置数据线SDA
SDA = ACK; // 注意:通常ACK是低电平,NACK是高电平
delay(); // 保持SDA状态,等待从机读取
SCL = 1; // 将时钟线SCL拉高
delay(); // 等待时钟线稳定
SCL = 0; // 将时钟线SCL拉低,结束应答
delay(); // 等待时钟线稳定
SDA = 1; //释放数据线 线权交给从机
}
//接收应答
bit read_sck()
{
bit ACK;
SDA=1;
delay(); //5us
SCL=1;
delay(); //5us
ACK=SDA;
delay(); //5us
SCL=0;
return ACK;
}
main(void)
{
if(!button)
{
unsigned int j=0;
while(!button);
//写数据
start();
send_data(0xa0); //器件
read_sck();
send_data(0x00); //地址
read_sck();
send_data(0xaa); //数据
read_sck();
end();
start();
send_data(0xa0); //器件
read_sck();
send_data(0x01); //地址
read_sck();
send_data(0x22); //数据
read_sck();
end();
start();
send_data(0xa0); //器件
read_sck();
send_data(0x02); //地址
read_sck();
send_data(0xdd); //数据
read_sck();
end();
// //页写
// start();
// send_data(0xa0); //器件
// read_sck();
// send_data(0x05); //地址
// read_sck();
// send_data(0xa1); //数据
// read_sck();
// send_data(0xa2); //数据
// read_sck();
// send_data(0xa3); //数据
// read_sck();
// send_data(0xa4); //数据
// read_sck();
// end();
// //当前地址读 需要与写或上一次读配合使用 因为读的数据地址指针需要有操作记录
// start();
// send_data(0xa1); //器件
// read_sck();
// P3=read();
// end();
// //随机读
// start();
// send_data(0xa0); //器件
// read_sck();
// send_data(0x03); //地址
// read_sck();
// start();
// send_data(0xa1); //器件
// read_sck();
// P3=read();
// send_sck(1); //非应答停止接收
// end();
// //顺序读
// start();
// send_data(0xa0); //器件
// read_sck();
// send_data(0x00); //地址
// read_sck();
// start();
// send_data(0xa1); //器件
// read_sck();
// read();
// send_sck(0); //应答继续接收
// read();
// send_sck(0); //应答继续接收
// P3=read();
// send_sck(1); //非应答停止接收
// end();
}
}