easy to code an essential I2C object in Spin. So, let's
Just to ensure we know the state of the pins, the dira
bits for SDA and SCL are set to input mode to float the
lines to the buss pull-ups. Before proceeding, the SCL pin
is checked to ensure that it did, in fact, go high. A low on
the SCL line can indicate a device on the buss is not
ready (this is called "clock stretching"). As soon as SCL is
clear (high), we take the SDA line low by writing a 1 to
the SDA's dira bit. Remember, in the setupx() method,
we had previously written a 0 to the outa bit. The next
line takes the SCL line low in preparation for clock pulses
used by write() and read().
Off the bat, we're going to change the name of our
object's entry point. While we would traditionally use
start(), this name conflicts with a fundamental process
that is part of I2C. We'll use setup() as it's descriptive. Just
remember that change when using the I2C object.
There are two variations of setup(): standard and
explicit. The first uses the Propeller's I2C pins; the other
uses pins of our choice. This allows us to have multiple
Let's have a look at the write() method:
pub start dira[sda] := 0 dira[scl] := 0 repeat while (ina[scl] == 0) dira[sda] := 1 dira[scl] := 1
pub write(i2cbyte) | ackbit i2cbyte := (i2cbyte $FF) << 24 repeat 8 dira[sda] := i2cbyte <-= 1 dira[scl] := 0 dira[scl] := 1 dira[sda] := 0 dira[scl] := 0 ackbit := ina[sda] dira[scl] := 1 return ackbit
I2C busses in a project. That doesn't happen often, but it's
possible. For example, I created a little DS1307 clock
module for the EFX-TEK HC- 8+ controller. That controller
makes four user I/O pins available, but not the I2C buss.
This is not a problem as the object allows us to define
the SCL and SDA pins. Note how the setup() method
actually calls setupx() with the Propeller's I2C pins:
pub setup setupx(EE_SCL, EE_SDA)
pub setupx(sclpin, sdapin) longmove(@scl, @sclpin, 2) dira[scl] := 0 outa[scl] := 0 dira[sda] := 0 outa[sda] := 0 repeat 9 dira[scl] := 1 dira[scl] := 0 if (ina[sda]) quit
Honestly, this method is simpler than it appears at
first blush — but it has generated more questions than
nearly anything I've ever produced. Let's tackle it, line by
We begin by making a copy of the pins for use in
other methods. For each pin, we write a 0 to the dira bit
(which floats it to the pull-up), and then write 0 to the
output bit. This last step is important as we'll only be
manipulating the direction bit later; when we make a pin
We begin by inverting the bits of our data byte using
XOR (^) with $FF. Why? Because we're not going to be
moving the bits from i2cbyte directly to the SDA pin;
we're going to control the output using the dira bit for
the pin. To output a 1 to the buss, we need to put a 0 in
the dira bit. This puts the pin in input mode and allows
the pull-up to take the SDA line high. Conversely, to write
a 0 to the buss, we need to write a 1 to the dira bit to
put it into output mode (again, we previously wrote 0 to
the outa bit).
0 in the associated outa bit.
The next section is a trick I learned from Mike
After flipping the bits, we shift everything left by 24.
This moves bit7 (MSB) of i2cbyte to bit31. This is
necessary because all parameters and local variables
Green's object. This wiggles the clock line until the SDA
pin is released by the slave. This is useful for clearing the
state of the buss before doing anything else.
Transactions begin with the Start condition:
A simple repeat loop takes care of moving the bits
from i2cbyte to the SDA line. The trick employed here is
the rotate left operator. We're using the assignment
version to simplify the code. The output will be bit0 of
i2cbyte after the rotate.
When the loop is complete, the SDA and SCL lines
are floated; the latter process causes the slave to output
the ACK/NAK bit. The master samples the SDA line into
ackbit, then returns the SCL line low for the next byte.
The read() method is similarly constructed, though a
little easier to get through:
pub read(ackbit) | i2cbyte dira[sda] := 0
62 May 2014