validated (and corrected if necessary) by the object.
Here’s the method we’ll call to initialize the encoder
object:
pub init(base, btn, detent, lo, hi, preset)
finalize
enctiming := (clkfreq / 1_000) >> 2
basepin := base
btnmask := |< btn
if detent
hasdetent := true
lolimit := lo << 2
hilimit := hi << 2
encoder := preset << 2
else
hasdetent := false
lolimit := lo
hilimit := hi
encoder := preset
cog := cognew(@grayenc, @encoder) + 1
return cog
The first parameter is the base pin, which is the A pin
of the encoder. To keep the code simple, we expect the
next higher pin to be the encoder’s B pin. The second
parameter is the encoder’s button input. Note that all
three pins are active low, that is, they are pulled up to Vdd
through 10K and will go low when active.
Next up is the true (non-zero) or false (zero) value that
specifies whether the encoder has detents or not. Finally,
we’ll pass the low, high, and initial (preset) values for the
driver. If, for example, we had an encoder with a base pin
of 3, a switch pin of 5, is detented, will span from -100 to
+100, and starts at zero, we would initialize it like this:
■ FIGURE 2. Encoder
Connections.
At par is the address of the encoder value. After
reading the preset value into tmp1, we use signed versions
of min and max to ensure that it is within the stated
bounds. Yes, we could have done this in Spin in the init()
method, but it’s easy and fast so it seemed like this was
the best place to handle it.
Next up is basic initialization of the button debounce
workspace, the previous scan result (stored in oldscan),
and creating a timer. We don’t need the timer for the
encoder, but it does come into play for debouncing the
button input.
setup mov btnwork, #0
mov tmp1, par
add tmp1, #4
wrlong btnwork, tmp1
mov oldscan, ina
shr oldscan, basepin
and oldscan, #%11
mov timer, cnt
add timer, enctiming
And now we get to the guts of it.
encoder.init(3, 5, true, -100, 100, 0)
When a detented encoder is used, the limits and
preset values are shifted left by two, which is the same as
multiplying by four. We have to do this to account for the
steps in between each detent. Shifting negative values left
for the range we would typically use is not a problem. I
tested this theory on values down to minus 10 million — a
value we’d never use in an actual project — just to make
sure.
The range limits take priority over the preset value; if
the preset is outside the specified range, the object will fix
it. Let’s have a look at that.
grayenc rdlong tmp1, par
mins tmp1, lolimit
maxs tmp1, hilimit
wrlong tmp1, par
encloop waitcnt timer, enctiming
scan mov newscan, ina
mov tmp1, newscan
chkbutton test btnmask, tmp1 wc
if_c mov btnwork, #0
if_nc add btnwork, #1
max btnwork, BTN_TM wc
if_c mov tmp1, #0
if_nc mov tmp1, IS_PRESSED
mov tmp2, par
add tmp2, #4
wrlong tmp1, tmp2
May 2010 15
As always, delays are a breeze in PASM with the
waitcnt instruction. I’ve set the encoder to run on a 250
microsecond loop. That way, if we get really zippy with