December 31, 2015

On Rotary Encoders and Debouncing Inputs to an Arduino Due

When interfacing push buttons, rotary encoders, etc. to an Arduino board, glitches in the input signal usually lead to many more inputs being recorded than the user intended to give. The typical countermeasure is called debouncing, and one finds endless posts on the interwebs, e.g. [1], about how to implement this in software or, by adding resistors and capacitors, in hardware.

I'm working with an Arduino Due, based on the ATMEL SAM3X8E processor. Today, I found in its specs [2] that this chip supports glitch filtering and debouncing on any input pin.

The following snippet uses the DIFSR register of the PIO unit to add hardware debouncing to pins D.0, D.1, and D.2 (pin numbers 25, 26, and 27) of the Due.
For other pins, use the PIOA, PIOB, or PIOC units and set the desired pin's bit to one in the binary bitmask. An overview of all Due pins, their numbers, and their assignments to PIOA ... PIOD units can be found at [3].

The debouncing filter also has a width, which comes in fractions of the slow clock (SLOWCLK) and can be set via the SCDR register of the PIO unit that hosts the pins in question. The divisor applies to all debounced pins of the unit.
Using this, I can interface a rotary encoder with push button to the Due without any additional debouncing hardware or software. The rotary encoder has three pins, named A, C, and B from the left and in this order. C gets grounded, A and B go to pins 25 (D.0) and 26 (D.1). The pushbutton pins are on the reverse side; one gets grounded, the other goes to pin 27 (D.2).

This is the setup code to be executed during startup(). It configures pins 25 to 27 as input pins, activates the internal pull-up resistors so the pins read 1 if the rotary encoder leaves them open, and get pulled to 0 if the rotary encoder closes them. Interrupt service routines are attached to each pin and get called on every change of the pin's reading from 0 to 1 or from 1 to 0. Finally, debouncing is added on the pins:

Edit: In the original post, I forgot to set PIO_IFER.

The following interrupt service routines track changes of the pin readings and update the encoder position accordingly. I started with bits of code I found at [1], and took it from there. I avoid calls to digitalRead(), which is very slow. Instead, I read the PIO unit's pin data status register PDSR directly:

Inside loop(), one may now query knob_pending, and if it's 1, look at knob_pushed and knob_pos to learn the rotary encoder's pushbutton state and relative position:

Update: A CKnob class implementing rotary encoder and push button access is now available for download from the project's github repository.

References

[1] http://playground.arduino.cc/Main/RotaryEncoders
[2] http://www.atmel.com/images/atmel-11057-32-bit-cortex-m3-microcontroller-sam3x-sam3a_datasheet.pdf
[3] http://forum.arduino.cc/index.php?topic=132130.0

No comments:

Post a Comment