slowed — the multiplier restores the access speed to the
ADC. The other major subsystem is the I2C memory. I
selected the 24LC512 because it holds 64K bytes of data
and is really simple to use. Figure 3 shows the connections
to the 24LC512.
When you look through the complete listing, you’ll see
a lot of code having to do with the EEPROM. The reason is
that there are four instructions in SX/B (I2CSTART,
I2CSTOP, I2CSEND, and I2CRECV) that get encapsulated
in subroutines or functions to save code space.
I’ve also created a subroutine that lets us write a byte
or word to the EEPROM, and two functions for reading: one
for bytes, the other for words. All of this code is highly
portable and you can use it in many applications. The only
critical note is that the SCL pin is aligned with the SDA pin,
i.e., the SCL pin always follows the SDA pin on the same
port (RA, RB, RC, RD, or RE). For example, if SDA is RA. 2,
then we must connect SCL to RA. 3.
Since writing to the EEPROM is a critical task for this
program, let’s have a look at the PUT_EE function. If you
compare the code to the 24LC512 data sheet, you should
see that it’s an easy match — my point is that once you’ve
got the I2C routines set up, access to any I2C device is
very straightforward.
SUB PUT_EE
IF __PARAMCNT = 3 THEN
tmpW1 = __WPARAM12
tmpB1 = __PARAM3
i2cMulti = 0
ELSE
tmpW1 = __WPARAM12
tmpB1 = __PARAM3
tmpB2 = __PARAM4
i2cMulti = 1
ENDIF
I2C_START
I2C_OUT SlaveWr
I2C_OUT tmpW1_MSB
I2C_OUT tmpW1_LSB
I2C_OUT tmpB1
IF i2cMulti = 1 THEN
I2C_OUT tmpB2
ENDIF
I2C_STOP
‘‘‘{$IFNOTDEF NOEEWAIT}
DO
I2C_START
I2C_OUT SlaveWr
LOOP UNTIL ackNak = Ack
‘‘‘{$ENDIF}
ENDSUB
■ FIGURE 3.
24LC512 Connections.
82
September 2007
This subroutine expects a word address and then one
or two bytes after that. Since we want to use the same
subroutine to write bytes and words, we’re forced into
accepting the address as two bytes. If we’re using a word
variable for the EE address, the compiler will sort that out
for us. When using a constant value, however, we have to
be careful. Let’s say, for example, that we want to write a
value to EEPROM address $000F. Here’s how we have to
do that when using constants:
PUT_EE $0F, $00, value
As you can see, we’re using two bytes for the address
and the bytes are aligned Little-Endian. If we did this:
PUT_EE $000F, value
the compiler wouldn’t understand that the address is a
word since the address value is less than 256 — so a byte is
assumed and used. If the value to be written is also a byte,
the compiler will complain that we don’t have enough
parameters for the subroutine. By forcing a two-byte
address, the subroutine can determine whether we want
to write a byte (__PARAMCNT is three) or a word
(__PARAMCNT is four). When we are writing two bytes, a
flag is set that gets used later in the routine.
The subroutine might look a little complicated but it is,
in fact, very straightforward. We start with the I2C start
sequence, write the device address (a constant in this
program, but could be a variable if you want to expand to
multiple EEPROMs), the address to write to, and then the
byte(s) to write. As you can see, we use the flag to control
writing the second byte. Finally, the I2C stop sequence is
generated to tell the EEPROM to save its buffer contents.
EEPROMs are not particularly fast and this device can
take up to five milliseconds to store the values we just sent to
it. We don’t care about this delay because we’ll only access
the EEPROM every 20 milliseconds, but in other applications,
we can have the subroutine wait for the write cycle to complete before returning to the caller. We do this by generating
another start, and then writing the slave address. While the
EEPROM is busy with its write cycle, the acknowledge bit will
be set to NAK (1). A conditional-compilation constant allows
us to enable or disable the write-wait option.
PUTTING IT TOGETHER
Like the animation controller project in May, this
program uses a virtual servo controller that runs in the ISR;
since we’ve been through that in detail, we won’t hash
through it again. The only thing that’s been added to the
ISR is an LED control option. The reason is that the program
has four modes:
1) Idle (servos follow joystick)
2) Recording (servos follow joystick and positions are saved
to EEPROM)