The loop used in this method is conditional. If the
number of bytes left to read is greater than one, then the
loop will run, reading a byte replying with ACK. When
we're down to the last byte, the loop is skipped, and we
do the read with a NAK reply. Easy peasy.
May 2014 65
Propeller Activity Board Parallax #32910
pub wr_word(addr, w) return wr_block(addr, 2, @w)
pub wr_long(addr, l) return wr_block(addr, 4, @l)
By now, you don't need me to spell out how the
other read methods work. Open the EEPROM object and
have a look. After you're done looking around, have a go
at it. I know I tend to harp on this, but the best way to
learn to write code is to write code!
Auto-Loading Values
What about strings? You'll recall that the Propeller
doesn't have a string type per se, but will treat an array
of characters that is terminated with 0 as a string. We
can, of course, use wr_block() with a string but we'd
need to know its length for writing and reading. For this
reason, my EEPROM object has separate support
methods for strings:
From time to time, we'll create an application that
requires the preservation of values between reset/power-up cycles. In Propeller projects, this is a particularly easy
feature to add. How we approach it depends on an
additional requirement: Do we want to preserve the
values between new program downloads?
pub wr_str(addr, p_zstr) | ackbit, b ackbit := i2c#ACK repeat b := byte[p_zstr++] ackbit |= wr_byte(addr++, b) if (b == 0) quit return ackbit
If the answer to the question is yes, then we need to
use a 64K or larger EEPROM and write any saved values
to address $8000 and higher. I did this for the EFX-TEK
EZ- 8+ controller which stores sequence data in the upper
32K of a 64K EEPROM.
For wr_str(), we need to pass the EEPROM storage
address and the hub address of the string. A loop is used
to read a byte from the string and write it to the
If we can forgo saving data between program
updates, there's a nifty little trick that we can take
advantage of by storing a value in the EEPROM at the
same address it uses in RAM. When the EEPROM is
copied into RAM, variables and tables are populated —
whatever was last written to the EEPROM will be loaded
in the value.
EEPROM. As the wr_byte() method does write the
address, we don't have to worry about page boundaries
with this method. The loop will terminate when it finds
the 0 at the end of the string. The 0 was written to the
EEPROM as well, so that the rd_str() method can detect
the end of the string.
For regular variables, the first value after a download
will be 0. For many applications, I like to pre-set values to
something other than 0, and this is where a DAT table is
very helpful. As a reminder, a DAT table allows us to
store values that look like constants in a program listing,
yet behave like variables when the program runs. For
example, in a game program I'm working on, there is a
player table that has values like this:
pub rd_block(addr, n, p_dest) i2c.wait(devid) i2c.write(addr.byte[1]) i2c.write(addr.byte[0]) i2c.start i2c.write(devid | $01) repeat while (n > 1) byte[p_dest++] := i2c.read(i2c#ACK) --n byte[p_dest] := i2c.read(i2c#NAK) i2c.stop
dat { player information } PlayerID byte 1 WeaponType byte 1
As with writing, we have a master method for
reading bytes from the EEPROM:
Once the game has been set up, we want to
preserve these values between power cycles (e.g.,
battery change). Updating a table value in the EEPROM
is as simple as this:
ee.wr_byte(@PlayerID, PlayerID)
As you can see, the setup is similar to wr_block(),
but this time we provide a destination address (in the
hub) for the byte(s) read from the EEPROM.
The wr_byte() method takes two parameters: the
storage address and the byte value. Note that I'm using
the address (@) of PlayerID to save the value of PlayerID.
Again, this process also works with regular variables.
The difference is that we can initialize DAT table values
in the program listing; regular variables cannot be
initialized in a listing.