Demo for Sparkfun's RPG with internal RGB led using interrupts

Dependencies:   mbed PinDetect

Using a Rotary Pulse Generator (RPG) or Rotary Encoder

This is a demo for Sparkfun's RPG (rotary pulse generator or rotary encoder) with an internal RGB led using interrupts to read the 2-bit encoder signal. By monitoring these output bits, the amount and direction of rotation of the knob by the user can be determined. Pushing the knob down activates a pushbutton that can be used to change modes in a device. See comments in main.cpp for more details and pin hookups. The RPG is mounted on Sparkfun's breakout board. The code uses the encoder count to dim the built-in red LED using PWM. The RPG's shaft pushbutton is also debounced using interrupts.

While simple software polling (programmed I/O) could be used to read the encoder bits, interrupts are more efficient in this application since it would be necessary to sample and read the two encoder bits a couple hundred times a second when someone spins the knob. Since the vast majority of the time bits are not changing at all, constantly polling this fast wastes a lot of processor time and power!

https://os.mbed.com/media/uploads/4180_1/rpg_skNKbNa.jpg

https://os.mbed.com/media/uploads/4180_1/reinside.jpg

Inside view of a RPG (rotary encoder) showing encoder wheel (silver teeth on left) and dual switch contacts (right) from https://www.robotroom.com/Counter5.html. Two parallel contacts are often used to increase reliability and reduce contact bounce.

https://os.mbed.com/media/uploads/4180_1/rpg2.jpg This style RPG has one toothed encoder wheel but the contacts are at different angles for the phase shift between the two output bits.

https://os.mbed.com/media/uploads/4180_1/incremental_directional_encoder.gif

Encoder animation of A and B output bits from Wikipedia

https://os.mbed.com/media/uploads/4180_1/rpgtt.png

The truth table is stored in a predefined C array:

const int lookup_table[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};

An interrupt routine is called whenever one of the two bits changes state and the truth table in an array is checked to determine how to change the count (i.e.+1, -1, 0). The encoder uses Gray code (only 1 bit changes at a time), if no bits or two bits change it may be switch contact noise and the count is not changed (i.e., a "0") in the truth table. See http://makeatronics.blogspot.com/2013/02/efficiently-reading-quadrature-with.html for a detailed explanation of the RPG encoder counting algorithm using interrupts with a lookup table.

The header pins are spaced so far apart that all of the jumper wire pins are under the PCB in the normal breadboard locations for an IC, but if you put in across a power bus (power bus under middle of PCB) as seen in the photo below, you will have room to connect the jumper wires after inserting the breakout (otherwise pins for jumper wires are all under the breakout board).

https://os.mbed.com/media/uploads/4180_1/20220118_141642.jpg

Parts used:

https://www.sparkfun.com/products/15141 https://www.sparkfun.com/products/11722 https://www.sparkfun.com/products/10597

Wiring

RPG breakoutmbed LPC1768
A encoder output bitp14
B encoder output bitp15
C encoder commongnd
+ LED+ for RGB LEDVout 3.3
R LED-p21
G LED-p22
B LED-p23
SW pushbuttonp16

This same scheme of two phase shifted encoder output bits to determine CW or CCW rotation is often used for feedback in motor control systems. In this application, it is called a quadrature encoder (QE). In fast motors, an optical signal is used to read the bits from the encoder wheel instead of a mechanical switch contact as in this RPG. The mbed LPC1768 has a built-in hardware quadrature encoder interface with a counter that would be nice to use for the RPG, but it does not come out on the module's limited breadboard pins.

Revision:
1:5c21ef62c975
Parent:
0:3eea8ad2dbbc
--- a/main.cpp	Wed Jan 12 19:52:47 2022 +0000
+++ b/main.cpp	Thu Jan 13 14:29:07 2022 +0000
@@ -22,16 +22,21 @@
 volatile int new_enc = 0;
 volatile int enc_count = 0;
 //Instead of a slow 16 case statement use a faster table lookup of truth table
-//index with (previous AB value | current AB value) to find -1,0,1 change in count
-int lookup_table[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
+//index table with (previous encoder AB value<<2 | current current encoder AB value) 
+//to find -1(CCW),0,or 1(CW) change in count each time a bit changes state
+//Always want Interrupt routines to run fast!
+//const puts data in read only Flash instead of RAM
+const int lookup_table[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
 //Encoder bit change interrupt service routine
+//called whenever one of the two A,B encoder bits change state
 void Enc_change_ISR(void)
 {
-    new_enc = RPG_A<<1 | RPG_B;
+    new_enc = RPG_A<<1 | RPG_B;//current encoder bits
+    //check truth table for -1,0 or +1 added to count
     enc_count = enc_count + lookup_table[old_enc<<2 | new_enc];
     old_enc = new_enc;
 }
-//debounced pushbutton callback
+//debounced RPG pushbutton switch callback
 void PB_callback(void)
 {
     ledPB= !ledPB;
@@ -39,7 +44,7 @@
 int main()
 {
 //turn off built-in RPG encoder RGB led
-    red = 1.0;//1.0 is "off", 0.0 is full "on"
+    red = 1.0;//PWM value 1.0 is "off", 0.0 is full "on"
     green = 1.0;
     blue = 1.0;
 //current color adjust set to red
@@ -57,7 +62,6 @@
         // Scale/limit count to 0..100
         if (enc_count>100) enc_count = 100;
         if (enc_count<0) enc_count = 0;
-        ledPB = RPG_PB;
         red = 1.0 - enc_count/100.0;
 //        pc.printf("%D\n\r",enc_count);
     }