Understanding the 24C64 EEPROM I2C Communication Issue
Introduction
When working with I2C EEPROM devices like the 24C64, proper communication requires understanding the specific protocol requirements of the device. In this article, we'll analyze an issue encountered when trying to read from a 24C64 EEPROM I2C adapter, where the Linux driver implementation fails while the Windows implementation works correctly.
The Problem
The issue manifests as follows:
This discrepancy suggests a fundamental difference in how the commands construct the I2C transaction.
Root Cause Analysis
1. 24C64 EEPROM Addressing Requirements
The 24C64 is a 64Kbit (8K x 8) EEPROM that requires a two-byte address for accessing its memory locations. This is clearly shown in the datasheet diagrams:
- START condition
- Device address with Write bit (0x50 << 1)
- First address byte (high bits)
- Second address byte (low bits)
- Repeated START
- Device address with Read bit ((0x50 << 1) | 1)
- Read data byte(s)
- STOP condition
- START condition
- Device address with Write bit
- First address byte
- Second address byte
- Data byte
- STOP condition
2. I2C Command Behavior
Looking at the I2C bus captures:
- Sends device address (0x50) with write bit
- Sends ONE address bytes (0x00)
- Performs repeated start
- Sends device address (0x50) with read bit
- EEPROM doesn't know which memory location to read, returns NAK (0xFF)
- Sends device address (0x50) with write bit
- Sends two address bytes (0x00, 0x00)
- Performs repeated start
- Sends device address (0x50) with read bit
- Successfully reads the byte (0xCC)
Evidence from Bus Captures
The I2C bus captures clearly show:
- Write [0x50] + ACK
- 0x00 + ACK
- Read [0x50] + ACK
- 0xFF + NAK (device doesn't recognize the command sequence)
- Write [0x50] + ACK
- 0x00 + ACK
- 0x00 + ACK
- Read [0x50] + ACK
- 0xCC + NAK (successful read)
Why Clock Stretching Doesn't Help
Clock stretching allows the slave device to hold the clock line low to request more time for processing. While enabling clock stretching can help with timing-sensitive I2C devices, it doesn't solve the fundamental protocol issue here.
The problem isn't that the EEPROM needs more time; it's that the command sequence is incorrect. The EEPROM expects a two-byte address before it can perform a read operation, and no amount of waiting will change this protocol requirement.
Solution Approaches
- The i2ctransfer command allows specifying the complete transaction sequence
- Example: i2ctransfer -y 15 w2@0x50 0x00 0x00 r1
- The Linux kernel has an at24 driver specifically for EEPROMs
- This driver understands the two-byte addressing requirement
Conclusion
The issue with reading from the 24C64 EEPROM stems from its requirement for a two-byte address before any read operation. The i2cget command doesn't provide this address, while i2ctransfer does. Understanding device-specific protocol requirements is crucial when working with I2C devices.
For users encountering this issue, the simplest solution is to use i2ctransfer with the proper sequence of write address bytes followed by read operations, rather than trying to use i2cget directly.