软件I2C读写24cl64异常 可读不可写

3月末在调试一个eeprom芯片24lc64,我们要用软件模拟I2C控制它,但遇到了奇特的现象。

用我们之前写的I2C驱动去控制它时,不可写入,但是可以读取。

为了控制它,我们用硬件I2C去对它进行操作,可读写,证明芯片没有坏。经过示波器的帮助,发现它在写入的时序中有ack,但最后总是存不进去数据。
在与公司内的一位大佬长时间的共同调试后,发现问题在写时序的最后:
发出停止信号需要拉高时钟线再给数据线一个上升沿。我们的程序也是这么写的,但是,

拉高时钟线和拉低数据线的先后顺序影响了24lc64的判断,让它以为出现了一个起始信号(拉高时钟线后给数据线一个下降沿)

SDA_OUT;
SCL_H;//被误认为是起始信号
SDA_L;//后面的是停止信号
Delay();
SDA_H;
Delay();

解决办法也很简单,把上面的第二行和第三行调换一下位置就行了

SDA_OUT;
SDA_L;
SCL_H;
Delay();
SDA_H;
Delay();

对此我只能表示 ○( \\ 皿 // )っ…
毕竟原来的I2C驱动用了好几年了,只在这个芯片上出现了问题。

另外在调试过程中有了一个意外发现,若是把I2C引脚的输出方式改为推挽输出,那么24lc64给你的回复的高电平将变为2.5V,每次写操作结束它都会保持数据线在2.5V的状态约3.5ms,大概是在保存数据吧。整个过程中耗电量增加。
把引脚的输出方式改为开漏输出后,24lc64的回复高电平变为正常的3.3V,过量耗电的情况消失。
这个就是单片机的推挽输出的特性了。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
以下是使用软件模拟I2C总线进行读写数据的示例代码,假设要读写的设备地址为0x50,要读取的寄存器地址为0x10,要入的数据为0xAB: ``` #include <stdio.h> #include <stdlib.h> #include <wiringPi.h> #define SDA_PIN 0 // SDA引脚 #define SCL_PIN 1 // SCL引脚 #define I2C_ADDR 0x50 // 设备地址 // I2C总线初始化 void i2c_init() { pinMode(SDA_PIN, OUTPUT); pinMode(SCL_PIN, OUTPUT); digitalWrite(SDA_PIN, HIGH); digitalWrite(SCL_PIN, HIGH); } // I2C总线起始信号 void i2c_start() { digitalWrite(SDA_PIN, HIGH); digitalWrite(SCL_PIN, HIGH); delayMicroseconds(10); digitalWrite(SDA_PIN, LOW); delayMicroseconds(10); digitalWrite(SCL_PIN, LOW); delayMicroseconds(10); } // I2C总线停止信号 void i2c_stop() { digitalWrite(SDA_PIN, LOW); digitalWrite(SCL_PIN, HIGH); delayMicroseconds(10); digitalWrite(SDA_PIN, HIGH); delayMicroseconds(10); } // I2C总线入一个字节数据并等待ACK信号 int i2c_write_byte(unsigned char data) { int i, ack; for (i = 0; i < 8; i++) { digitalWrite(SDA_PIN, (data & 0x80) ? HIGH : LOW); data <<= 1; digitalWrite(SCL_PIN, HIGH); delayMicroseconds(10); digitalWrite(SCL_PIN, LOW); } // 等待ACK信号 pinMode(SDA_PIN, INPUT); digitalWrite(SCL_PIN, HIGH); delayMicroseconds(10); ack = digitalRead(SDA_PIN); digitalWrite(SCL_PIN, LOW); pinMode(SDA_PIN, OUTPUT); return (ack == 0); } // I2C总线读取一个字节数据并发送ACK信号 unsigned char i2c_read_byte(int ack) { int i; unsigned char data = 0; pinMode(SDA_PIN, INPUT); for (i = 0; i < 8; i++) { data <<= 1; digitalWrite(SCL_PIN, HIGH); delayMicroseconds(10); if (digitalRead(SDA_PIN)) data |= 0x01; digitalWrite(SCL_PIN, LOW); } pinMode(SDA_PIN, OUTPUT); digitalWrite(SDA_PIN, (ack == 0) ? LOW : HIGH); digitalWrite(SCL_PIN, HIGH); delayMicroseconds(10); digitalWrite(SCL_PIN, LOW); digitalWrite(SDA_PIN, LOW); return data; } // I2C总线入一个字节数据 int i2c_write(unsigned char reg, unsigned char data) { i2c_start(); if (!i2c_write_byte(I2C_ADDR << 1)) return 0; if (!i2c_write_byte(reg)) return 0; if (!i2c_write_byte(data)) return 0; i2c_stop(); return 1; } // I2C总线读取一个字节数据 unsigned char i2c_read(unsigned char reg) { unsigned char data; i2c_start(); if (!i2c_write_byte(I2C_ADDR << 1)) return 0; if (!i2c_write_byte(reg)) return 0; i2c_start(); if (!i2c_write_byte((I2C_ADDR << 1) | 0x01)) return 0; data = i2c_read_byte(0); i2c_stop(); return data; } int main() { wiringPiSetup(); i2c_init(); // 入数据 i2c_write(0x10, 0xAB); // 读取数据 unsigned char data = i2c_read(0x10); printf("Read data: 0x%X\n", data); return 0; } ``` 注意,这段代码仅作为示例,实际使用时需要根据具体的设备协议进行修改。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值