This is a continuation of a series of posts on simple experiments I’m performing with my new Bus Pirate v4.0. Click to see me trying out the bus pirate v4.
I’ll connect up 3 EEPROM ICs on a breadboard and interface with them through the I2C bus. The particular ICs I will use are 24LC256 EEPROM chips each with a 256Kbit or 32KByte capacity.
The I2C bus is a two wire communication interface. One wire carries bidirectional data in a half duplex mode, that is data moves in only one direction at a time. It is named SDA. The other wire carries the clock signal. This signal provides the timing of data transfer on the bus. It is named SCK. The I2C bus has a simple messaging protocol consisting of START, STOP, and ACKnowledge signals. This allows a master on the bus (in our case that will be the Bus Pirate) to initiate and stop communications with any and all slave devices on the bus.
Each slave device has its own address. Three bits of the EEPROM ICs are set via three pins that are hardwired to set its address. This allows up to 8 of these EEPROM ICs to be connected on the same bus.
All the SDA and all the SCK pins on the EEPROM ICs are connected together and then wired to the corresponding data (MOSI) and clock (CLK) pins on the Bus Pirate. The address pins are connected to give each IC a different address. You will also need to wire up the Bus Pirate’s Vpu pin to 5V; this provides power for the pull-up resistors.
The 24LC256 chips provide a simple command interface for reading from and writing to the memory: Each chip has two (virtual) addresses: one for writing to the memory and one to read from the memory. To write to the memory, the master had to follow these steps:
- Issue an I2C START signal to indicate to slave devices that they should “listen” to the address that follows.
- Transmit the write address for the specific IC. The slave which recognized it’s own address will ACKnowledge.
- Transmit two bytes to give a starting memory address to the slave EEPROM.
- Transmit the bytes to write to the memory. Each byte needs to be ACK by the slave. The master need to only send the data bytes while the EEPROM memory address is automatically incremented.
- Issue an I2C STOP to indicate the end of the transaction.
- Each byte is followed by an ACK bit from the EEPROM IC if it accepts the data.
- The master device always stays in control of the CLK signal, setting the place at which data is transmitted.
To read from the EEPROM the master needs to:
- Issue an I2C START signal.
- Optionally send the write address followed by the EEPROM memory address where reading should begin.
- Send the read address for the specific IC which will respond with an ACK.
- Clock out the data 8 bits at a time, acknowledging each received byte with an ACK bit.
- Issue an I2C STOP signal to end the transaction.
The Bus Pirate has a simple command interface over the serial connection.
First we need to select the correct communication mode and power up the bus:
- Enter . We are presented with a list of modes to select from:
1. HiZ 2. 1-WIRE 3. I2C 4. SPI 5. 2WIRE 6. 3WIRE 7. LCD 8. DIO x. exit(without change) (1)>
- Enter to select I2C mode. We are again presented with a selection:
I2C mode: 1. Software 2. Hardware (1)>
- We might just as well use the hardware interface so it is. We get presented with a speed selection:
Set speed: 1. 100KHz 2. 400KHz 3. 1MHz (1)>
- We press Enter to select the default value
The Bus Pirate gives us a new prompt indicating that we are now in I2C mode:
We now need to power up the powersupply and activate the pull-up resistors.
The I2C bus needs pull-up resistors to keep the bus lines high. This allows the master and slaves to communicate by pulling the lines low.
- Enter to power up the powersupply.
Power supplies ON
- Enter to switch on the pull-up resistors.
Pull-up resistors ON
We are now ready to communicate with the EEPROM ICs. But how do know which addresses to use? We could read the datasheet for the 24LC256 to get the address or we could use a built in macro on the Bus Pirate that searches for addresses on the bus.
I2C>(0) 0.Macro menu 1.7bit address search 2.I2C sniffer
I2C>(1) Searching I2C address space. Found devices at: 0xA0(0x50 W) 0xA1(0x50 R) 0xA2(0x51 W) 0xA3(0x51 R) 0xA4(0x52 W) 0xA5(0x52 R)
We now have a list of all the addresses on the bus. Here we can see that for each IC there is a read and write address.
We now know which addresses to use. Let’s write something to the first EEPROM. The following commands will be written on one line to do the deed. Each command may be issued individually as well. The Bus Pirate allows that.
- will issue a START signal.
- writes the address to the bus.
- two bytes to tell the EEPROM from which memory address to start writing.
- A byte to be written to the EEPROM.
- A string of characters to write into the EEPROM.
- Another byte to be written to the EEPROM.
- will issue a STOP signal.
We end up with something like this:
I2C>[ 0xA0 0x00 0x00 0xF0 "Hello" 0x00 ] I2C START BIT WRITE: 0xA0 ACK WRITE: 0x00 ACK WRITE: 0x00 ACK WRITE: 0xF0 ACK WRITE: "H ACKe ACKl ACKl ACKo ACK" WRITE: 0x00 ACK I2C STOP BIT
Take note the
ACK bit after the transmission of each byte.
Let’s try reading from the EEPROM this time:
- will issue a START signal.
- writes the address to the bus – we will be “writing” to the EEPROM in order to reset the memory counter/address back to zero
- two bytes to tell the EEPROM from which memory address to start reading
- Issue a START signal again
- is the read address of the first device
- will read 6 bytes from the bus
This is the result:
I2C>[ 0xa0 0x00 0x00 [ 0xa1 rrrrrrr ] I2C START BIT WRITE: 0xA0 ACK WRITE: 0x00 ACK WRITE: 0x00 ACK I2C START BIT WRITE: 0xA1 ACK READ: 0xF0 READ: ACK 0x48 READ: ACK 0x65 READ: ACK 0x6C READ: ACK 0x6C READ: ACK 0x6F READ: ACK 0x00 NACK I2C STOP BIT
Note the string wrote out earlier was now read back as ASCII values. The Bus Pirate has an option to display the RAW value to the terminal. This is done using the command:
I2C>o 1. HEX 2. DEC 3. BIN 4. RAW (1)>4 Display format set
And we try again:
I2C>[ 0xa0 0x00 0x01 [ 0xa1 rrrrr ] I2C START BIT WRITE: ACK WRITE: ACK WRITE: ACK I2C START BIT WRITE: ACK READ: READ: ACK H READ: ACK e READ: ACK l READ: ACK l READ: ACK o NACK I2C STOP BIT