ARDUINO ports: manage a trafic light

published: 25 May 2019 / updated 4 June 2020

Lire cette page en français

 


un feu tricolore expliqué en détail

Digital ports

Let's go back in more detail on the functioning of the digital ports. To do this, we we will only process the PORT B. This port is accessible in FORTH through three registers:

Caution: do not confuse physical connector numbers and pin numbers

On the ARDUINO cards, the physical connectors are marked, some by numbers, others by their function, example: GND, TX, RX, 5V, etc ...

The mapping for associating pin numbers with physical connectors is in this document:
 Pin out map on ARDUINO DUE
 Pin out map on ARDUINO MEGA 2560
 Pin out map on ARDUINO MICRO
 Pin out map on ARDUINO NANO
 Pin out map on ARDUINO UNO
 Pin out map on ARDUINO YUN

For example, the led '13' which is connected to the ARDUINO MEGA board with the physical terminal 13 is related to the PIN 26. In all our texts, the term "pin" will always refer to PIN code XX as referenced in the technical documents of ARDUINO cards. The mention of a physical connector will be done with the term 'terminal'. Example:

  PIN 19 (terminal 53) (penultimate terminal, all at the bottom, left on an ARDUINO MEGA 2560 board)

To learn more about how to program the connectors of the different ARDUINO boards:
Understanding ARDUINO card connectors

Here is a diagram showing the link between these registers and the associated port:

PORT B structure

In PORT B, the "X" indicate an indeterminate state of this port.

Before activating or deactivating a PIN, you must indicate in the DDR register of PORT B, here DDRB which pins are in or out.

Only PINs 24 and 26 should be output (terminals 11 and 13). So you have to drop in this DDR register the binary value 10100000 (160 in decimal, A0 in hexadecimal).

To do this, it is necessary to write to the decimal address 36 the value 160. We thus define beforehand three constants:

\ PORTB 
37 constant PORTB	\ Port B Data Register 
\ 36 constant DDRB	\ Port B Data Direction Register 
\ 35 constant PINB	\ Port B Input Pins 

The DDR register

To act on the DDRB registry, we could have written this:

160 36 c!               \ met 160 dans DDRB, 10100000 en binaire 

But it is more explicit to write this:

160 DDRB c!               \ met 160 dans DDRB, 10100000 en binaire 

The word C! (c addr ---) stores an 8-bit value at the address addr. On the Arduino board, these low addresses are exploited by the port registers. Here is what it gives on our diagram:

activation pins 24 and 26

The PORT register

It is this register which makes it possible to say which pin must be activated or not:

Activate PIN 26. To do this, you must inject the binary value 10000000 in the PORTB register. Diagram:

pin 26 set on (terminal 13)

Code correspondant:

128 37 c!               \ met 128 dans registre PORT B, 10000000 en binaire 

Who can also write:

128 PORTB c!            \ met 128 dans registre PORT B, 10000000 en binaire 

To activate pin 11:

set Pin 11 on

Ah damn! We turned off the pine 26 and only lit the pine 24.

So we have to find a way to selectively turn on or off pines without alter the state of those who are active or not active. For example, how to light pin 26 without turning off pin 11 if it is already active?

Let's see this in a little binary juggling ...

Binary juggling

Take the values injected into the PORTB register, this without taking into account DDR register status report:

128 PORTB c!            \ pin 26: 10000000  128  80 
 64 PORTB c!            \ pin 26: 01000000   64  40 
 32 PORTB c!            \ pin 26: 00100000   32  20 
 16 PORTB c!            \ pin 26: 00010000   16  10 

Let's take a close look at the injected binary values to activate selectively a pine:

10000000 80
01000000 40
00100000 20
00010000 10

The bits 1 correspond to the position of the pin in the DDR and PORT registry.

We will use this value as mask to set the PINs. To do this, we will create the word defPIN: like this:

The words defPIN: high low output input and pin@ are described in the article Definition and management of PORT connections

In FORTH, there are words of creation of words: : constant variable , etc ...

The word defPIN: is now a new word creation word, word having as the sole purpose of creating "PINs":

\ définition pinXX 
PORTB $80 defPIN: LED.red 
PORTB $40 defPIN: LED.yellow 
PORTB $20 defPIN: LED.green 

What does the word LED.red next, for example:

\ définition pinXX 
LED.red   \ empile   128 37 

So we find on the stack two values: 128 which is the "mask" that we will handle further and then 37 which is the address of the register PORTB

Turn on a bit in a register

Let's go crazy and activate all the bits of PORT B:

$ff DDRB  c! 
$ff PORTB c!  

There, if you have LEDs connected to pins 24 to 26, they should light up. Otherwise, trust us!

Now, let's turn them off:

$00 PORTB c!  

So we have 00000000 in the PORTB register. It is therefore to turn on pin12 which has the mask 01000000. We are going to use this "mask" like this:

%00000000      \ valeur PORTB 
%01000000 or   \ fait OU logique, reste 010000000 sur la pile 

At any time, we can recover the contents of the PORTB registry like this:

PORTB c@   \ empile contenu du registre PORTB 

If we have not changed the contents of PORTB in the meantime, we must find 01000000. activate now the pin24 that has the "mask" in binary 00100000:

2 base ! 
01000000      \ valeur PORTB 
00100000 or   \ fait OU logique, reste 011000000 sur la pile 

Here is the whole sequence to activate the LED.red as it is typed on the Arduino programmable in FORTH (text capture - comments have been added):

LED.red  ok<#,ram> 128 37     \ récupère masque LED.red et adresse PORTB
dup  ok<#,ram> 128 37 37    \ duplique adresse PORTB
c@  ok<#,ram> 128 37 0      \ récupère contenu PORTB
rot  ok<#,ram> 37 0 128     \ met masque LED.red au sommet de la pile
or  ok<#,ram> 37 128        \ OU logique => laisse val sur pile
swap  ok<#,ram> 128 37      \ inverse pour avoir  val adrPORTB
c!  ok<#,ram>               \ envoie val dans PORTB

Now that LED.red is active, let's see how to activate LED.green:

LED.green  ok<#,ram> 32 37
dup  ok<#,ram> 32 37 37
c@  ok<#,ram> 32 37 128
rot  ok<#,ram> 37 128 32
or  ok<#,ram> 37 160
swap  ok<#,ram> 160 37
c!  ok<#,ram>

Now let's see how to selectively shut down a pin.

Turn off a bit in a register

It's hardly a bit more acrobatic. Comments have been added:

LED.green   ok<#,ram> 32 37  \ récupère masque LED.green et adresse PORTB
dup  ok<#,ram> 32 37 37     \ duplique adresse PORTB
c@  ok<#,ram> 32 37 160     \ récupère contenu PORTB
rot  ok<#,ram> 37 160 32    \ met masque LED.green au sommet de la pile
$ff  ok<#,ram> 37 160 32 255 \ met 11111111 (FF) au sommet pile
xor  ok<#,ram> 37 160 223   \ XOR logique (inrse bits) => laisse val sur pile
and  ok<#,ram> 37 128       \ ET logique avec contenu PORTB
swap  ok<#,ram> 128 37      \ inverse pour avoir  val adrPORTB
c!  ok<#,ram>               \ envoie val dans PORTB

Heureusement, nous avons les mots high et low qui simplifient ces manipulations:

LED.green high  \ turn on green LED

Manage a traffic light

As an example, implement this by connecting 3 LEDs:

trafic lights circuit diagram

Let's create 6 words to selectively enable and disable each pin:

For each LED, let's plan an activation time:

: feu-vert ( n ---) 
    LED.green high 
    ms              \ n valeur délai d'allumage en millisecondes 
    LED.green low ; 
: feu-orange ( n ---) 
    LED.yellow high 
    ms              \ n valeur délai d'allumage en millisecondes 
    LED.yellow low ; 
: feu-rouge ( n ---) 
    LED.red high 
    ms              \ n valeur délai d'allumage en millisecondes 
    LED.red low ; 

If you enter 1000 feu-vert, the green LED lights for one second. Let's put this in application in a new word feux-tricolores:

: feux-tricolores ( ---) 
    \ valeurs 3000 500 et 3000 corespondent au délai en millisecondes 
    \ d'activation de chaque feu. 
    3000 feu-vert 
    500  feu-orange 
    3000 feu-rouge ; 
\ feux-tricolores exécute un seul cycle de feux 

Running the traffic light will trigger a single fire cycle. For this cycle to be repeated, we put everything in an infinite loop:

: feux ( ---) 
    init-feux 
    begin 
        feux-tricolores 
    key? until ; 

The execution of lights can be interrupted simply by tapping a key on the keyboard.

Manage a German traffic light

You love managing fire so much that you have a customer in Germany. But there, the fires work a little differently from those in France. Certainly the green goes orange then red. But before coming back to green, both red and orange lights light up simultaneously.

Do not get sweaty to call a computer cracker. It's flaring in two stages:

\ variante feux allemands 
: feu-mixte ( n ---) 
    LED.yellow high        \ allume feu rouge et orange en même temps 
    LED.red high 
    ms              \ n valeur délai d'allumage en millisecondes 
    LED.yellow off 
    LED.red low ; 
: feux-allemands ( ---) 
    init-feux 
    begin 
        3000 feu-vert 
        500  feu-orange 
        3000 feu-rouge  
        500 feu-mixte 
    key? until ; 

The complete and functional listing is available here.

Conclusion

The last words defined in this article, namely feux and feux-allemands show that thanks to certain words in French the code is quite readable.

At the risk of insisting, the strong point of the FORTH language is to allow the testing of words very simple in situ, ie directly on the Arduino board. This is impossible in C language.