Arduino with external i2c EEPROM - Tine-G/plebian1 GitHub Wiki

Contents

  1. Disclaimer
  2. Support
  3. I2C Protocol & Components
    1. Start/Stop Condition
    2. Device Address
    3. Register Address
    4. Data
    5. ACK & NACK
    6. I2C Protocol
  4. Arduino Wire.h Library
  5. Working of the EEPROM
    1. Internal Page Address Counter
    2. Write Operation
      1. Byte Write
      2. Page Write
    3. Read Operation
      1. Current Read
      2. Random Read
      3. Sequential Read

Disclaimer

As of writing this (7th December 2020), the following code and explanation works for the i2c EEPROM parts listed below (provided they're used through the Arduino IDE):

  • AT24C64 - Atmel
  • M24C64 - ST Microelectronics

This document is mainly written for personal reference, if there are inaccuracies in this document please let me know.

Back to top

Support

The following examples and explanations work with the Arduino platform and should work with other 8-bit AVR microcontrollers provided they have 'cores' written for compatibility with the Arduino platform. For example, using the megaTinyCore by SpenceKonde a.k.a Dr.Azzy to program the new ATtiny 1-series chips. Note that some parts such as the classic ATtiny series do not have hardware implemented i2c and thus the i2c protocol is implemented through software using the hardware USI pins available. Please read the extensive and very detail documentation on Dr.Azzy's github repositoreis for:

Either way, If you are using the plain old Arduino Uno or other Arduino development boards, you should be fine.

Back to top

I2C Protocol & Components

Back to top

Start/Stop condition

All i2c communication begins and ends with a start and stop condition respectively. The slave device will not respond to any data sent over the data line (SDA) if a start condition is not first initiated. Likewise, if a stop condition is not initiated after data transmission, the bus is considered busy and other devices cannot access the bus. Start/stop conditions are defined as below:

  • Start : A high-to-low transition of SDA with SCL high
  • Stop : A low-to-high transition of SDA with SCL high

Device Address

Arduino i2c uses 7-bit addressing with the 8th bit being the read/write instruction (read: 1, write: 0). The address 0x00 is a general call and is a reserved address. You should not assign this address to your i2c slaves. So effectively, the i2c line on most Arduino MCUs can handle up to 127 slave devices.

Register Address

Most i2c devices, if not all, have internal accessible registers to facilitate their operation. These registers are similar to the Data Direction Registers (DDRx) and Port Registers (PORTx) on the Arduino boards. In the case of an EEPROM, instead of internal registers, we have access to the EEPROM's memory space. The number of bytes (1 or 2 in most cases) sent for register addressing varies depending on the size of the EEPROM or the number of registers in the i2c device. For the 2 parts listed, it is 2 bytes.

Data

Data is sent in 8-bit chunks over the SDA line on the i2c bus.

ACK & NACK

An acknowledge receipt bit (ACK) is sent by the slave device to the master after successful receipt of an 8-bit word (Device Address, Register Address, Data). The slave does this by pulling the SDA line LOW after receiving an 8-bit word through the same SDA line. A No Acknowledge receipt (NACK) is just as it sounds, it is the lack of an ACK signal. The master/slave considers a NACK bit if the SDA line remains HIGH after either a transmission of receipt of an 8-bit word.

I2C Protocol

  1. Start condition is generated by the master
  2. Device address is sent by the master along with a read/write bit (least significant bit in the 8-bit device address)
  3. ACK is sent by slave to the master
  4. Master sends register address that it wants to access (1 or 2 bytes)
  5. ACK is sent by slave after receipt of register address byte(s)
  6. Data is sent by master to slave or vice versa depending on the operation
  7. ACK is sent by master (read operation) or slave (write operation) after receipt of each 8-bit word
  8. Stop condition is generated by the master
Back to top

Arduino Wire.h Library

A brief explanation of the Arduino Wire.h library and the working of it's functions. To be updated as the rest of this document is written based on usage

Back to top

Working of the EEPROM

Back to top

The EEPROM has two basic operations, read and write. Unlike conventional flash storage such as SD cards and hard drives, EEPROMs cannot - as far as I know - store files in different file formats natively. EEPROMs store data in the most basic units, that is in the form of raw bytes. There is no formatting of encoding. The data acquired from the EEPROM is left to interpretation.

For example, if a cell in the EEPROM contains the binary 0b01100001, when this cell is read by the master (Arduino in this case), if it is stored in a variable of type int will read as the decimal 97 and if it is stored in a variable of type char will read as the character 'a'.

Internal Page Address Counter

The internal page address counter is something like a pointer that points to the current location in the memory array. It is incremented after each read/write of a data word. To avoid writing to and reading from addresses that we don't intend to, we will always specify the location in which we are going to write to or read from.

Write Operation

There are mainly two types of write operations, commonly referred to in datasheets as Byte Write and Page Write.

Back to top

Byte Write

Write one byte at a time, generating a stop condition after receiving the ACK from the slave. Writing multiple bytes such as a string literal (i.e. "Hello World") using this operation requires the master to generate and send the entire packet (start condition, device address, register address, data, stop condition) for every byte of data sent ("Hello World" is 11 bytes, whitespace included). Therefore, this is not a viable solution for writing more than 1 byte of data.

  1. Master generates start condition
  2. Master sends device address along with write instruction (least significant bit in device address set to LOW)
  3. Slave sends ACK bit
  4. Master sends register address (1 or 2 bytes)
  5. Slave sends ACK bit
  6. Master sends data byte
  7. Slave sends ACK bit
  8. Master generates stop condition

Page Write

Most, if not all EEPROMs have their memory configuration in what is called pages. These pages can be an arbitrary number of bytes wide (refer to the specific part datasheet). For the parts listed, they are 32 bytes wide, meaning that the entire chip has 8192 bytes / 32 bytes = 256 pages. In a page write operation, the master sends multiple data bytes at a time with the slave device sending an ACK bit after receipt of each byte. When all data is sent, the master generates a stop condition and the communication is terminated.

However, we cannot write more than 'page width' number of bytes in one operation. Doing this will cause the internal page address counter to rollover and overwrite the initial page address. For example, if the page width is 32 bytes, writing "Hello World yes this is 32 bytes" at memory address 0x00 will place the entire string nicely from 'H' at 0x00 to 's' at 0x1F. If instead the string "Hello World no this is not 32 bytes" was written at address 0x00, the internal counter will rollover and the memory will read "teslo World no this is not 32 by" with 't' at 0x00 and 'y' at 0x1F. This holds true even if the start address of the write operation is in the middle of a page (i.e. between address 0x00 and 0x1F).

To write data that is more than 32 bytes in length would require the master to initiate another page write operation from the beginning (start condition, device address, ... , stop condition) to send the other fragment of data.

Therefore it is helpful to keep our write operations organised into specific pages for specific types of information and also to keep our string literals as short and concise as possible.

  1. Steps 1-7 of byte write
  2. Master keeps sending data bytes and slave keeps sending ACK bits until master is done
  3. Master generates stop condition

Read Operation

There are mainly three types of read operations, commonly referred to in datasheets as Current Read, Random Read and Sequential Read. Current Read is not an addressed operation, in that it reads whatever data that the internal page address counter is pointing to in the current moment. Random and Sequential Reads are addressed operations, in that the address of the memory to be read is sent beforehand. In most cases, we would be using a Sequential Read operation.

Back to top

Current Read

  1. Master generates start condition
  2. Master sends device address with read instruction (least significant bit in device address set to HIGH)
  3. Slave sends ACK bit
  4. Slave sends data byte to master
  5. Master sends a NACK bit (does not acknowledge)
  6. Master generates a stop condition

Random Read

In a Random Read operation, the memory address that we want to read from is specified through a dummy write operation. A dummy write operation is done to set the internal page address counter to the location in which we wish to read from. After that, the process is the same as a Current Read operation. Note : In a dummy write operation, the device address is sent along with a WRITE instruction, then the register address bytes are sent, then the master generates a stop condition without sending any data bytes. A Current Read operation is subsequently initiated.

  1. Master generates start condition
  2. Master sends device address with write instruction (least significant bit of device address set to LOW)
  3. Slave sends ACK bit
  4. Master sends register address (1 or 2 bytes)
  5. Slave sends ACK bit
  6. Master generates stop condition (datasheets show that a stop condition is not generated before initiating the next stage. Actual testing indicates that generating a stop condition before initiating the next stage makes no difference)
  7. Steps 1-6 of Current Read operation is initiated

Sequential Read

A Sequential Read operation is exactly the same as a Random Read operation except that it reads multiple bytes rather than one byte. Meaning that the only change is when the slave sends a data byte, the master sends an ACK bit instead of a NACK bit. This tells the slave that the master has received the data byte and to send more bytes. The internal page address counter of the slave is automatically incremented as bytes are sent out to the master. When the master wishes to stop reading data from the slave, it sends a NACK bit followed by generating a stop condition. This terminates the communication.

  1. Master initiates a dummy write operation (Steps 1-6 of Random Read operation)
  2. Master generates start condition
  3. Master sends device address along with read instruction (least significant bit of device address set to HIGH)
  4. Slave sends ACK bit to master
  5. Slave sends data byte to master
  6. Master sends ACK bit to slave
  7. Steps 5-6 are repeated until master decides to stop reading
  8. Master sends NACK bit when it decides to stop reading data
  9. Master generates stop condition

A sequential read operation, contrary to a page write operation, does not roll over at the border of a page. Instead, it rolls over at the end of the entire memory space. Meaning that theoretically, if we had enough SRAM on our MCU, we would be able to read the entire EEPROM in one go. However, I'm not too sure how accurate this is since I have never been able to test this.

Back to top