[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