Button management by multiplexing, second part

published: 27 July 2021 / updated 27 July 2021

Lire cette page en français

 


Multiplexing management

Summary. We have eight buttons, each button being connected to an output of a serial / parallel converter 74HC595. These eight buttons have an output set common and connected to a digital input on the ARDUINO card. We will start by testing this digital input. To do this, we will define:

\ input pin - buttons output 
$25 constant PORTB 
flash 
PORTB %00000001 defPIN: BUTTONS \ PB0 (8) 
ram 
 
\ init. PORT B PB0 
: init.buttons ( ---) 
    BUTTONS input 
  ; 

To eliminate unwanted signals, we add a 10K resistor and a LED between pin PB0 and GND:

This resistor and this LED are connected in series. Their goal is to force entry PB0 at low level if no signal arrives from the buttons. The LED has a role of indicator signal. If you press a button, it should light up. To test the good connection of the buttons, we define:

\ set all bits to high level on buttons input 
: FFtoButtons ( -- ) 
    255 toButtons 
  ; 

Executing FFtoButtons sends a high level signal on inputs for all buttons. We can therefore test the buttons if these are correctly supplied. You have to press button by button:

By successively pressing each button, the LED connected to PB0 must light up. To selectively check a single button, we will inject a value corresponding to an exponent of 2:

  1 toButtons  \ LED on PB0 ON only if button 0 pressed 
  2 toButtons  \ LED on PB0 ON only if button 1 pressed 
  4 toButtons  \ LED on PB0 ON only if button 2 pressed 
  8 toButtons  \ LED on PB0 ON only if button 3 pressed 
\ .... 
128 toButtons  \ LED on PB0 ON only if button 7 pressed 

If the buttons are correctly supplied, it is not possible to light the LED in PB0 only by pressing the powered button. For example, the sequence 4 toButtons will only power the 3rd button (button 2). Only pressing this button will turn on the LED. The other buttons must leave the LED in PB0 off.

The buttons are numbered from 0 to 7.

We understand, by the course of the explanations, how much the programming language FORTH provides ease of debugging, both hardware and software.

With FORTH. No compilation apart from the ARDUINO map. No upload. The code is injected through the terminal. We can run any word to test the proper functioning of our assembly.

Button scan

To know if a button is activated, a button by button signal must be injected. This injection value is always an exponent of 2, included in the interval 1..128. Here is a loop that will generate these exponents from the word lshift:

: buttonsScan ( -- ) 
    8 for 
        r@ . 
    next 
  ; 

As is, the word buttonsScan will simply display the index values of the for next loop: 7 6 5 4 3 2 1 0.

It is this index which will allow us to shift to the left a bit in a byte.

For example: 1 3 lshift shifts three positions, leaving the value 8 on the stack. Let's modify buttonsScan:

: buttonsScan ( -- ) 
    8 for 
        1 r@ lshift . 
    next 
  ; 

Running buttonsScan displays: 128 64 32 16 8 4 2 1.

Now, we will inject these values into our converter instead of displaying them the value. For our test, we will inject these values into our ramp of LEDs and set a time delay:

: buttonsScan ( -- ) 
    8 for 
        1 r@ lshift   toLeds 
        300 ms 
    next 
  ; 

The powers of two in binary

The values 1 2 4 8 16 ... 128 are not chosen at random. The smallest storage unit memory is byte. It is an area which is 8 bits:

  x x x x x x x x

Here, each "x" is a bit. From left to right, each bit has this position:

  x x x x x x x x
  7 6 5 4 3 2 1 0

We call it the weight of a bit. We modify the bit of weight 3 by setting it to 1:

  x x x x 1 x x x
  7 6 5 4 3 2 1 0

In decimal, our byte will therefore have the value 2 EXP n where n is the weight of our bit.

The result of 2 EXP 3 is 8.

Executing buttonsScan successively lights our LEDs, starting from the left. We now modify our code to inject the values into the buttons instead of LEDs. In passing, we will test the state of PB0:

: buttonsScan ( -- ) 
    8 for 
        1 r@ lshift   toButtons 
        BUTTONS pin@ 
        if 
            r@ . 
        then 
    next 
  ; 

Running buttonsScan displays a value between 0 and 7 if a button is activated. It does not display anything if no button is activated.

Arrived here, we will start to take care of our LEDs.

LED management

On our assembly, we have LEDs. But keep in mind that the initial problem consists of managing railway switches. These turnouts are activated by electromagnets. If the electromagnet is active, the train changes direction. Yes the electromagnet is deactivated, the train does not change direction.

The idea is therefore to make the position of a turnout stable each time you press the corresponding button:

button
bouton
rel./LED
00
11
01
10

We are typically in a sequential logic. The push of a button changes the state of the corresponding relay/LED. This state remains stable until the next press of this button.

We are going to manage a variable corresponding to the state of these relays/LEDs:

variable LEDs    \ store result of active buttons 
 
\ toggle LED state, c value 1 or 2 or 4.. 128 
: toggleLED ( c --- ) 
    dup LEDs mtst       \ test if bit = 1 
    if      LEDs mclr   \ clear bit 
    else    LEDs mset   \ set bit 
    then 
  ; 

The LEDs variable is managed bit by bit using the word toggleLED. The value of c is a power of 2 between 1 and 128.

We position an LED of row n like this:

\ set LED state. n between 0..7 
: setLED ( n -- ) 
    1 swap lshift   \ convert n to 2 EXP n 
    toggleLED       \ toggle LED/relay state 
    LEDs @ toLeds   \ change LEDs/relay state 
  ; 

This word setLED accepts as parameter a value within the range 0..7. This parameter corresponds to the position of the scanned button. Word test setLED:

0 toLeds        \ set all LEDs OFF                                                           
1 setLED                                                             
0 setLED                                                            
3 setLED                                                             

Result:

We just have to modify buttonsScan like this:

: buttonsScan ( -- ) 
    8 for 
        1 r@ lshift   toButtons 
        BUTTONS pin@    \ button activated? 
        if 
            r@ setLED 
        then 
    next 
  ; 

Complete code is available here:

ARDUINO-FORTH/blob/master/buttonsMultiplex.txt