EEPROM memory/Reading and writing/08. Sixty-four-byte pages
Reading and writing · 08 of 10

Sixty-four-byte pages

One write can carry up to 64 bytes, and costs one 5 ms wait instead of 64. The catch is the page: the memory is cut into 64-byte pages, and a write that runs past the end of one wraps round to the start of the same page and overwrites it, with no error. So cut every write at page edges, and at 30 bytes or fewer on an Uno.

Pages

Sixty-four-byte pages
Start at byte56
Bytes to write
Sent
Wrapped
8 bytes
Writes needed
1
Fill all 16,384
88 s a byte, 6.9 s by 16
16 bytes from 56 as one write. Byte 64 is on the next page, and the chip never goes there: the address counter only counts inside a page, so the last 8 bytes landed at 0 onwards, on top of whatever was there. No error, no warning. You find out when you read it back.

The chip's 16,384 bytes are cut into 256 pages of 64: bytes 0 to 63, 64 to 127, and so on. A write sends one start address and then up to 64 bytes, and the chip counts up from the start address as they arrive.

But it only counts within the page. Slide the start to 56 and write 16 bytes as one: the first eight fill 56 to 63, and the next eight do not go to 64. They go to 0, 1, 2, on top of whatever was there. The datasheet calls it roll-over. There is no error and no warning; you find out when you read it back.

Switch to Cut at page edges and the same 16 bytes go as two writes, 8 and 8, each inside its own page. Everything lands where it should.

Why bother with pages at all

Every write, one byte or 64, costs one wait of up to 5 ms. Filling the whole chip a byte at a time is 16,384 waits: about a minute and a half at 100 kHz. In 16-byte pieces it is 1,024 waits, under ten seconds. The figure's last readout works both out.

The Uno's 32 bytes

A second limit sits on the Arduino side. The Uno's Wire library holds 32 bytes per transmission, and the two address bytes take two of them, so a write can carry 30 bytes at most. Longer ones are cut off without an error. The sketch uses pieces of 16: that fits every board's buffer and divides 64, so pieces never straddle an edge once they start on one.

What you should see

wrote: Written across a page edge, read back whole.
read:  Written across a page edge, read back whole.

The text starts at byte 50 and is 45 bytes long with its terminating zero, so it crosses the edge at 64. writeBlock() sends it as 14, 16 and 15 bytes, and it comes back whole.

The code

Stores a line of text that crosses a page edge, then reads it back. writeBlock() cuts the write at every page edge and every 16 bytes, and waits for the chip after each piece.

eeprom_pages.ino
/*
  EEPROM Memory - text across a page edge               TK31 / /p/tk31

  Wiring. Count from the square pad on the TinkerBlock board, parts
  up, header at the bottom:

    GND -> GND
    VCC -> your board's logic supply: 5V on an Uno, 3V3 on an
           ESP32 or ESP32-S3, 3V3(OUT) on a Pico. Never 5V beside
           a 3.3 V board: the pull-ups would put 5 V on its pins.
    SDA -> A4 on an Uno, GPIO 21 on an ESP32, GPIO 8 on an
           ESP32-S3, GP4 on a Raspberry Pi Pico
    SCL -> A5 on an Uno, GPIO 22 on an ESP32, GPIO 9 on an
           ESP32-S3, GP5 on a Raspberry Pi Pico

  Arduino IDE
    Tools > Board                 your board, e.g. Arduino Uno
    Tools > Port                  the one that appears when you plug in
    Tools > USB CDC On Boot       Enabled   (ESP32-S3 only)
    No library to install: Wire comes with every board.
*/

#include <Wire.h>

const int EEPROM_ADDR = 0x50;
const uint16_t PAGE = 64;      // the chip's page
const uint16_t PIECE = 16;     // fits every board's Wire buffer
const uint16_t START = 50;     // crosses the edge at 64 on purpose
const char TEXT[] = "Written across a page edge, read back whole.";

void waitReady() {
  unsigned long t0 = millis();
  do {
    Wire.beginTransmission(EEPROM_ADDR);
  } while (Wire.endTransmission() != 0 && millis() - t0 < 20);
}

void writeBlock(uint16_t at, const uint8_t *data, uint16_t n) {
  while (n > 0) {
    uint16_t room = PAGE - at % PAGE;   // bytes left in this page
    uint16_t len = min(n, min(room, PIECE));
    Wire.beginTransmission(EEPROM_ADDR);
    Wire.write((uint8_t)(at >> 8));
    Wire.write((uint8_t)(at & 0xFF));
    Wire.write(data, len);
    Wire.endTransmission();
    waitReady();                        // one wait per piece
    at += len;
    data += len;
    n -= len;
  }
}

void readBlock(uint16_t at, uint8_t *out, uint16_t n) {
  while (n > 0) {
    uint16_t len = min(n, PIECE);
    Wire.beginTransmission(EEPROM_ADDR);
    Wire.write((uint8_t)(at >> 8));
    Wire.write((uint8_t)(at & 0xFF));
    Wire.endTransmission(false);
    Wire.requestFrom(EEPROM_ADDR, (int)len);
    for (uint16_t i = 0; i < len; i++) {
      out[i] = Wire.available() ? Wire.read() : '?';
    }
    at += len;
    out += len;
    n -= len;
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Wire.begin();

  writeBlock(START, (const uint8_t *)TEXT, sizeof(TEXT));

  char back[sizeof(TEXT)];
  readBlock(START, (uint8_t *)back, sizeof(back));
  back[sizeof(back) - 1] = '\0';
  Serial.print("wrote: ");
  Serial.println(TEXT);
  Serial.print("read:  ");
  Serial.println(back);
}

void loop() {
}

The text starts at byte 50, so it crosses the page edge at 64 on purpose. On an Uno never raise PIECE above 30: its Wire buffer holds 32 bytes and the address takes two. Leave writeBlock() as it is and any address and length work.

When it does not work

My text comes back with its end at the start.

The write crossed a page edge, at a multiple of 64, and its tail wrapped to the start of the page. Cut writes where the page ends, the way writeBlock() does, and write it again.

On an Uno only the first 30 bytes arrive.

The Uno's Wire library holds 32 bytes per transmission, and two of them are the address. Anything past 30 bytes of data is dropped without an error. Keep each write at 30 bytes or fewer; this sketch uses 16.

Do reads have to stop at page edges too?

No. A sequential read runs on across pages, and wraps only at the very end of the chip. This sketch still reads in 16-byte pieces, because the Uno's Wire library can only take 32 bytes per request.

Can I write a whole page at once on an ESP32?

Yes: the ESP32's Wire buffer is larger than 66 bytes, so a full page with its two address bytes fits. Start the write exactly on a multiple of 64 and it takes one 5 ms wait for 64 bytes.

Where this goes next

How long a byte lasts, and the habit that makes it last decades.

A million writes

Edit this page — content/books/eeprom-memory/sixty-four-byte-pages.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

EEPROM Memory

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum