[CCC DEV] The third button problem - update
Steve Pretty
steve.g.pretty at btinternet.com
Thu Feb 10 12:13:52 GMT 2011
Thank you, Matt, for your detailed response to my posting about using
more than two buttons.
I do have the occam 2.1 reference manual - but it is a little terse in
places (I guess this book was never trying to be a language tutorial!).
I was not able to fully understand the utility of channel arrays, or the
syntax for using them. Thanks to your response, I have now been able to
use them successfully.
I now have a first working implementation of pcint.input. (Currently
only tested on 4 pins of port D)
I started with your set.interrupt code. This is specialized for
configuring the external interrupts 0 and 1 for the associated pins. I
therefore wrote a PROC set.pci.interrupts to configure PCINT for
specific ports.
I then wrote PROC pcint.input. This takes a reference to a port, a bit
mask to identify which bits on that port are of interest and a [8]CHAN
array for reporting results. This PROC:
* Reads initial state of port
* sets up the relevant PCINT
* starts an interrupt handler loop. On interrupt:
* reads the port
* for bits 0 to 7
* if it is of a bit of interest and bit has changed, send
appropriate result
* update current state
In the above routine, I was not able to use the digital.read function,
as this is specialized for bit reading.
A thought - the pattern where you need to define the placed ports and
then read from or write to them is found in many places in the code. It
might be better to isolate this IO code into a single pair helper PROCs
- port.read and port.write (rather as you have PROCs for pin.read and
pin.write). It would improve code robustness but it is a pretty large
refactoring task for no real functional gain!
Here is my pcint.module, followed by my test case:
Steve
-- module: pcint
-- exports: set.pci.interrupts, pcint.input
-- by: Steve Pretty
-- on: 10FEB11
-- A first successful attempt at implementing PCINT input on Arduino UNO
-- This version is designed to be used on a per port basis.
-- Port is selected using the PINB, PINC or PIND constants
-- Code should work on all ports/pins, but has currently only been
-- tested on PortD, arduino pins 2-5
--{{{ PROC set.pci.interrupts
--* Sets up the interrupt registers for the ATmega328.
--
-- [@code vintr] gets set in this process.
-- @param port - the group of pins to be enabled (PINB, PINC, PIND)
-- @param mask - which pins of the port to enable
-- @param vintr The interrupt vector associated with the given port.
PROC set.pci.interrupts (VAL INT port, VAL BYTE mask, RESULT INT vintr)
PLACED [MAX.PORT]BYTE ports 0:
#PRAGMA DEFINED ports
#PRAGMA DEFINED vintr
CASE port
PINB
SEQ
vintr := VINTR.PCINT0
ports[PCICR] := ports[PCICR] \/ (1 << PCIE0)
ports[PCMSK0] := mask
PINC
SEQ
vintr := VINTR.PCINT1
ports[PCICR] := ports[PCICR] \/ (1 << PCIE1)
ports[PCMSK1] := mask
PIND
SEQ
vintr := VINTR.PCINT2
ports[PCICR] := ports[PCICR] \/ (1 << PCIE2)
ports[PCMSK2] := mask
ELSE
die ("port does not support interrupts")
:
--}}}
--{{{ PROC pcint.input
--* Read digital levels on external pins using Pin Change Interrupt 2
-- This procedure will output a LEVEL (either LOW or HIGH) whenever the
-- pin changes value. It can be used on Arduino pins 0-7
--
-- @param port - the group of pins to be enabled (PINB, PINC, PIND)
-- @param mask - which pins of the port to enable - binary mask.
-- @param out The LEVEL, output when the pin changes level.
PROC pcint.input (VAL INT port, VAL BYTE mask, [8]CHAN LEVEL out!)
PLACED [MAX.PORT]BYTE ports 0:
INITIAL INT vintr IS (- 1):
BYTE input, state, filter:
SEQ
#PRAGMA DEFINED ports
#PRAGMA DEFINED vintr
CASE port -- establish initial state on input pins on the port
PINB
state := ports [port]
PINC
state := ports [port]
PIND
state := ports [port]
set.pci.interrupts (port, mask, vintr) -- set up interrupts
WHILE TRUE -- interrupt handler loop
SEQ
INT any:
wait.for.interrupt (vintr, any)
CASE port -- interrupt has occurred, so read port
PINB
input := ports [port]
PINC
input := ports [port]
PIND
input := ports [port]
SEQ i = 0 FOR 8 -- Scan through results, find changed bits and
send results
SEQ
filter := (1 << i)
IF
(mask /\ filter) <> 0 -- are we interested in this bit?
IF
(input /\ filter) <> (state /\ filter) -- has this
bit changed?
IF
(input /\ filter) = 0 -- decide if new bit state
is LOW or HIGH
out[i] ! LOW
TRUE
out[i] ! HIGH
TRUE
SKIP
TRUE
SKIP
-- end of scanning block
state := input -- save current state of inputs
:
--}}}
TEST CASE
#INCLUDE "plumbing.module"
#INCLUDE "pcint.module"
-- pcint.module test case
-- There are 4 buttons, connected to Arduino pins 2-5
-- State changes reported via serial interface.
PROC main ()
[8]CHAN LEVEL x:
LEVEL input:
PAR
pcint.input (PIND, #3C, x!) -- set up port D input interrupt
WHILE TRUE
SEQ
ALT -- wait for interrupt results - print source pin
x[2] ? input
serial.write.string (TX0, "Arduino Pin 2 *n")
x[3] ? input
serial.write.string (TX0, "Arduino Pin 3 *n")
x[4] ? input
serial.write.string (TX0, "Arduino Pin 4 *n")
x[5] ? input
serial.write.string (TX0, "Ardunio Pin 5 *n")
IF -- print LEVEL received from source pin
input = LOW
serial.write.string (TX0, "LOW received *n*n")
TRUE
serial.write.string (TX0, "HIGH received *n*n")
:
More information about the developers
mailing list