Hacking an rotary encoder

published: 23 October 2019 / updated 31 October 2019

Lire cette page en français

 

The rotary encoder

We will focus on a rotary encoder. Here is the one we use:

rotary encoder

See its technical characteristics

Connection diagram

Here is our connection diagram from the rotary encoder to the ARDUINO MEGA board:

rotary encoder connection

Our rotary encoder has 5 connectors:

GNDto Arduino GND
+to +5 V Arduino
SW-- not used --
DTto connector 22 - pin 78 / PA0
CLK>to connector 23 - pin 77 / PA1

These are the port numbers PA0 and PA1 that we will keep in the program in FORTH language.

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

Decoding signals from the encoder

Definition of ports and pins used:

34 constant PORTA	\ Port A Data Register 
33 constant DDRA	\ Port A Data Direction Register 
32 constant PINA	\ Port A Input Pins 
 
%00000001 constant pin78 \ pin 78 connector 22 PA0 
%00000010 constant pin77 \ pin 77 connector 23 PA1 
 
: init-ddra-input ( mask ---) 
    DDRA mclr    \ init PORT A to input for PA0 and PA1 
  ; 

Rather than defining globally all the ports and pines, in FORTH, one is satisfied to define the only ports and pines actually used. This strategy avoids proliferation definitions, most of which are not used as is the case in other languages of programming.

The word init-ddra-input accepts a bit mask as input.

Example: pin77 pin78 or init-ddra-input. This activates pins 77 and 78 corresponding to bits 0 and 1 of the PORT A.

Management of the pins used:

: pin@ ( pin PINx --- fl) 
    c@   swap and 
    if    true 
    else  false  then ; 
 
: PA0@ ( --- fl) 
    pin78 PINA pin@ ; 
 
: PA1@ ( --- c) 
    pin77 PINA pin@ ; 

The word pin @ is applicable to any registry bit. He accepts two settings:

Example: pin78 PORTA pin @.

This word pin@ is used in the definitions of PA0@ words and PA1@.

These PA0@ and PA1@ words directly return a Boolean flag. This solution facilitates subsequent treatments.

Signal analysis

Here are the signals emitted by our rotary encoder:

Sequence of signals clockwise (CW) and counterclockwise (CCW):

CW | CCW
1 1 | 1 1
1 0 | 0 1
0 0 | 0 0
0 1 | 1 0
1 1 | 1 1

Depending on the meaning, there are two interesting sequential sequences:

These are the sequences we will manage:

variable oldPA0 
variable oldPA1 
 
variable counter 
 
: getRotation ( PA0 PA1 --- direction ) 
    over oldPA1 @ <>  over oldPA0 @ =  and 
    if 
        -1 counter +! 
    then 
    over oldPA1 @ =   over oldPA0 @ <> and 
    if 
         1 counter +! 
    then 
    oldPA1 !   oldPA0 ! 
  ; 

We define two variables oldPA0 and oldPA1 loaded with remember the previous state of PA0 and PA1.

The counter variable is incremented or decremented based the result of the execution getRotation.

Ports and variables are initialized:

: inits ( ---) 
    pin78 pin77 or  init-ddra-input 
    PA0@ oldPA0 ! 
    PA1@ oldPA1 ! 
  ; 

This part inits must be adapted according to the wiring specific to your ARDUINO card.

: changedPA01? ( PA0 PA1 --- fl) 
    oldPA1 @ <> swap oldPA0 @ <> or 
  ; 

The word changedPA01? returns a boolean flag TRUE if the state of the data PA0 and PA1 are no longer equal to those stored in oldPA0 and oldPA1 variables.

And finally, our last definition getR:

: getR ( ---) 
    inits 
    begin 
        PA0@ PA1@ 
        2dup changedPA01? 
        if 
            getRotation 
            counter @ . cr \ for tracing 
        else 
            2drop 
        then 
    key? until ; 

The word getR initializes the ports and variables via inits .

Then a begin .. until loop retrieves the values of PA0 and PAI. If their values have changed, we run getRotation, a word that increments or decrements the contents of the counter variable.

Here is what gives the execution of the word getR:

result of executing the word getR

In final operation, the code part counter @. cr could be deleted or commented out.

The complete listing is available by clicking here.

Redefinition of words PA0@ and PA1@

We can delete the word pin @ and replace it with the word pin:

: pin: ( compil: pin PINX ---  | exec: --- fl) 
    create  
        , , 
    does> 
        dup @ swap 2+ @ swap 
        c@   swap and 
        if    true 
        else  false  then  
  ; 

The code part after create compiles the pin and PINx values.

The code part after does> retrieves the pin and PINx values and performs the treatment on these values.

Using the word pin: to setPAO@ and PA1@:

pin78 PINA pin: PA0@
pin77 PINA pin: PA1@