i2c.write(ctrl | $01) value := i2c.read(i2c#NAK) i2c.stop return value
As I've touched on my EEPROM objects, let's look at
practical examples of writing and reading values to the
Note that for reading, we have to set bit0 of the
control byte; this is done by OR-ing with $01. The byte is
read back and we send a NAK to tell the EEPROM this is
the last byte to read.
EEPROM. The approach is to use a master method for
writing which wants an EEPROM address, the number of
bytes to write, and a pointer (address of) to the block of
bytes to write:
pub page_num(addr) return (addr / PG_SIZE)
pub page_ok(addr, len) | pg0, pg1 pg0 := page_num(addr) pg1 := page_num(addr + len-1) return (pg1 == pg0)
pub wr_block(addr, n, p_src) | ackbit i2c.wait(devid) i2c.write(addr.byte[1]) i2c.write(addr.byte[0]) ackbit := i2c#ACK repeat n ackbit |= i2c.write(byte[p_src++]) i2c.stop return ackbit
This works but is not terribly practical because it's
counting on the address pointer in the EEPROM to be in
the right place. A better idea is a method that can read
from any address. Here's the process:
• Start condition
• Write control byte ($A0 - write mode)
• Write high byte of address
• Write low byte of address
• Start condition
Right off the bat, you'll notice that I replaced the
i2c.start() method with i2c.wait() — let's have a look at
the latter:
• Write control byte ($A1 - read mode)
• Read data byte
• Stop condition
Do you see what we did there? We use the write
mode to move the EEPROM's address pointer, then we
restarted the transaction in read mode to read the byte.
pub wait(ctrl) | ackbit repeat start ackbit := write(ctrl) until (ackbit == ACK)
You should note that when we read or write a byte
from the EEPROM, the internal address pointer is
automatically incremented; this allows us to read blocks.
That said, the EEPROM has page boundaries with the
address that affect block writes and reads; when the
address pointer hits the end of a page, it will wrap
around to the beginning. This can be problematic if we're
not paying attention. In the 24LC256 (32K) EEPROM, the
pages are on 64-byte boundaries; in the 24LC512
This method generates a start condition, writes the
control byte (ctrl), then checks the ACK/NAK bit. If we
try to access the EEPROM while it's busy storing the last
value we wrote to it, we'll get a NAK. As soon as the
EEPROM is ready, we'll return from this method and
continue. By using wait(), we don't have to pad our code
with arbitrary delays.
(64K — what I tend to use), the pages are on 128-byte
boundaries. We need to be aware of this when writing
multi-byte values. In my EEPROM objects, I have a couple
helper methods for this:
Back to the wr_block() method ... after getting the
go-ahead from the EEPROM via wait, we write the
address (Big-Endian order) then drop into a loop that
writes bytes from the hub, incrementing the hub pointer
after each write. Note that we have a check value
(ackbit) that was preset to ACK (0); if there's a problem
with any of the writes, this will get set to 1. The calling
code can check this to ensure all bytes were written
without issue.
With the wr_block() method in place, we can create
named methods for simple data types:
pub wr_byte(addr, b) return wr_block(addr, 1, @b)
The first converts a target address of a page number.
Jon "JonnyMac" McPhalen
The second will check to see that the length of what I
want to write to an EEPROM will fit within the page of
the starting address. This is especially useful if we're
going to store message strings that could have variable
lengths.
jon@jonmcphalen.com
Parallax, Inc.
www.parallax.com
Propeller chips and programming tools
64 May 2014