I2C总线的特征
协议我们参考|EEPROM 24C64的DataSheet
硬件特征:
SDA:串行数据线
SCL:串行时钟线
每个连接到总线上的设备都有唯一的地址速度
标准模式下可以达到100kbit/s
快速模式下可以达到400kbit/s
高速模式下可以达到3.4Mbit/sI2C总线术语
发送器: 发送数据到总线的器件
接收器: 从总线接收数据的器件
主机: 初始化发送 产生时钟信号和终止发送的器件
从机: 被主机寻址的器件
多主机: 同时有多于一个主机尝试控制总线 但不破坏报文
仲裁: 是一个在有多个主机同时尝试控制总线 但只允许其中一个控制总线并使报文不被破坏
的过程
同步: 两个或多个器件同步时钟信号的过程
协议格式
开始位
在 SCL 线是高电平时 SDA 线从高电平向低电平切换
停止位
当 SCL 是高电平时 SDA 线由低电平向高电平切换
数据位
发送到 SDA 线上的每个字节必须为 8 位,数据的高位在前(先发数据高位);
代码范例
void i2c_delay(void)
{
unsigned char i;
for(i=0;i<10;i++);
}
void i2c_start(void)
{
I2C_SDA = 1;
I2C_CLK = 1;
i2c_delay();
I2C_SDA = 0;
i2c_delay();
I2C_CLK = 0;
}
void i2c_stop(void)
{
I2C_SDA = 0;
I2C_CLK = 0;
i2c_delay();
I2C_CLK = 1;
i2c_delay();
I2C_SDA = 1;
}
unsigned char read_i2c_onebyte(bit ack_or_nak)
{
unsigned char read_data, i;
SET_SDA_INPUTMODE;
read_data = 0x00;
for(i=0;i<7;i++)
{
I2C_CLK = 1;
if(I2C_SDA)
read_data |= 0x01;
i2c_delay();
I2C_CLK = 0;
read_data <<= 1;
i2c_delay();
}
I2C_CLK = 1;
if(I2C_SDA)
read_data |= 0x01;
i2c_delay();
I2C_CLK = 0;
SET_SDA_OUTPUTMODE; //SDA output
i2c_delay();
if(ack_or_nak)
I2C_SDA = 1;
else
I2C_SDA = 0;
I2C_CLK = 1;
i2c_delay();
I2C_CLK = 0;
i2c_delay();
I2C_SDA = 0;
return read_data;
}
bit write_i2c_onebyte(char w_buf)
{
unsigned char i;
bit ack_or_nak;
for(i=0;i<8;i++)
{
if(w_buf&0x80)
I2C_SDA = 1;
else
I2C_SDA = 0;
i2c_delay();
I2C_CLK = 1;
i2c_delay();
I2C_CLK = 0;
w_buf <<= 1;
}
SET_SDA_INPUTMODE;
i2c_delay();
I2C_CLK = 1;
i2c_delay();
ack_or_nak = 0;
if(I2C_SDA)
ack_or_nak = 1;
I2C_CLK = 0;
I2C_SDA = 0;
SET_SDA_OUTPUTMODE; //SDA output
return ack_or_nak;
}
写操作
设备地址
字节写操作
时序分析:
1、开始信号;
2、写从设备地址,地址最低位R/W=0,表示为写操作,从设备收到数据后会自动返回ACK,判断设备存不存在可以根据ACK信号;
3、写存储地址高八位(写完地址后从设备会返回ACK),(有的设备是10bit的地址,24C64是8bit地址)
4、写存储地址低八位(写完地址后从设备会返回ACK)
5、写8bit数据(1Byte)
6、写停止位
void 24cxx_write(unsigned short addr, unsigned char dat)
{
i2c_start();
write_i2c_onebyte((EEPROM_ADDR<<1)+WRITE_CMD);
write_i2c_onebyte(addr>>8);
write_i2c_onebyte(addr);
write_i2c_onebyte(dat);
i2c_stop();
delay_ms(12);
}
- 页写操作
时序分析:
1、开始信号;
2、写从设备地址,地址最低位R/W=0,表示为写操作,从设备收到数据后会自动返回ACK,判断设备存不存在可以根据ACK信号;
3、写存储地址高八位(写完地址后从设备会返回ACK),(有的设备是10bit的地址,24C64是8bit地址)
4、写存储地址低八位(写完地址后从设备会返回ACK)
5、写停止位
7、写开始位
8、写从设备地址
6、写停止位
int 24cxx_write_page(unsigned short addr, unsigned short len, unsigned char* idata buf)
{
i2c_start();
write_i2c_onebyte((EEPROM_ADDR<<1)+WRITE_CMD);
write_i2c_onebyte(addr>>8);
write_i2c_onebyte(addr);
while(len--) {
write_i2c_onebyte(*buf++);
}
i2c_stop();
delay_ms(12);
return 0;
}
//用于拷贝芯片的数据
int 24cxx_write_buf(unsigned short addr, unsigned short num, unsigned char* idata buf)
{
int stat;
unsigned char NumOfPage = 0, NumOfSingle = 0;
if (addr % _24CXX_PageSize) //位一页空间的大小
NumOfSingle = _24CXX_PageSize - addr%_24CXX_PageSize;
else
NumOfSingle = 0;
if (num <= NumOfSingle)
NumOfSingle = num;
else
NumOfPage = (num - NumOfSingle + (_24CXX_PageSize - 1)) / _24CXX_PageSize;
if (NumOfSingle) {
if (0!=(stat=_24cxx_write_page(addr, NumOfSingle, buf)))
return stat;
addr += NumOfSingle;
buf += NumOfSingle;
num -= NumOfSingle;
}
while (NumOfPage--) {
if (0!=(stat=_24cxx_write_page(addr, (num>=_24CXX_PageSize)?_24CXX_PageSize:num, buf)))
return stat;
addr += _24CXX_PageSize;
buf += _24CXX_PageSize;
num -= _24CXX_PageSize;
}
return 0;
}
读操作
- 随机读操作
时序分析:
1、开始信号;
2、写从设备地址,地址最低位R/W=1,表示为读操作,从设备收到数据后会自动返回ACK,判断设备存不存在可以根据ACK信号;
3、读存储地址高八位(写完地址后从设备会返回ACK),(有的设备是10bit的地址,24C64是8bit地址)
4、读存储地址低八位(写完地址后从设备会返回ACK)
5、写停止位
6、写开始信号
7、写从设备地址
8、读取数据,并告诉从设备这次不用回ACK
9、写停止位
unsigned char 24cxx_read(unsigned short addr)
{
unsigned char tmp;
i2c_start2();
write_i2c_onebyte((EEPROM_ADDR<<1)+WRITE_CMD);
write_i2c_onebyte(addr>>8);
write_i2c_onebyte(addr);
i2c_stop();
i2c_start();
write_i2c_onebyte((EEPROM_ADDR<<1)+READ_CMD);
i2c_delay();
tmp = read_i2c_onebyte(NAK_FLAG);
i2c_stop();
return tmp;
}
- 顺序读
时序分析:
1、开始信号;
2、写从设备地址,地址最低位R/W=1,表示为读操作,从设备收到数据后会自动返回ACK,判断设备存不存在可以根据ACK信号;
3、读存储地址高八位(写完地址后从设备会返回ACK),(有的设备是10bit的地址,24C64是8bit地址)
4、读存储地址低八位(写完地址后从设备会返回ACK)
5、写停止位
6、写开始信号
7、写从设备地址
8、读取数据,并告诉从设备这次不用回ACK
9、写停止位
void _24cxx_read_buf(unsigned short addr, unsigned short len, unsigned char* idata buf)
{
i2c_start();
write_i2c_onebyte((EEPROM_ADDR<<1)+WRITE_CMD);
write_i2c_onebyte(addr>>8);
write_i2c_onebyte(addr);
i2c_stop2();
i2c_start2();
write_i2c_onebyte((EEPROM_ADDR<<1)+READ_CMD);
while(--len) {
i2c_delay();
*buf++ = read_i2c_onebyte(ACK_FLAG);
}
i2c_delay();
*buf = read_i2c_onebyte(NAK_FLAG);
i2c_stop();
}
附图:
临时保存I2C逻辑采样图片,MSB在前