Demo for Sparkfun's RPG with internal RGB led using interrupts
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!
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.
This style RPG has one toothed encoder wheel but the contacts are at different angles for the phase shift between the two output bits.
Encoder animation of A and B output bits from Wikipedia
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).
Parts used:
https://www.sparkfun.com/products/15141 https://www.sparkfun.com/products/11722 https://www.sparkfun.com/products/10597
Wiring
RPG breakout | mbed LPC1768 |
---|---|
A encoder output bit | p14 |
B encoder output bit | p15 |
C encoder common | gnd |
+ LED+ for RGB LED | Vout 3.3 |
R LED- | p21 |
G LED- | p22 |
B LED- | p23 |
SW pushbutton | p16 |
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.
main.cpp
- Committer:
- 4180_1
- Date:
- 2022-01-13
- Revision:
- 1:5c21ef62c975
- Parent:
- 0:3eea8ad2dbbc
File content as of revision 1:5c21ef62c975:
#include "mbed.h" #include "PinDetect.h" //See http://makeatronics.blogspot.com/2013/02/efficiently-reading-quadrature-with.html //for a detailed explanation of the RPG encoder counting algorithm //uses Sparkfun RPG with RGB led on breakout board (#15141,11722,10597) //place RPG PCB across a breadboard power bus strip for easier pin hookup! InterruptIn RPG_A(p14,PullUp);//encoder A and B pins/bits use interrupts InterruptIn RPG_B(p15,PullUp); PinDetect RPG_PB(p16); //encode pushbutton switch "SW" on PCB //PWM setup for RGB LED in enocder PwmOut red(p21);//"R" pin PwmOut blue(p22);//"G" pin PwmOut green(p23);//"B" pin //Note: also tie RPG PCB "C" pin to ground, "+" pin to 3.3 //mbed status leds DigitalOut ledPB(LED1); DigitalOut red_adjust_mode(LED2); DigitalOut green_adjust_mode(LED3); DigitalOut blue_adjust_mode(LED4); //Serial pc(USBTX,USBRX); volatile int old_enc = 0; 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 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;//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 RPG pushbutton switch callback void PB_callback(void) { ledPB= !ledPB; } int main() { //turn off built-in RPG encoder RGB led 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 red_adjust_mode = 1; //debounce RPG center pushbutton RPG_PB.mode(PullDown); RPG_PB.attach_deasserted(&PB_callback); RPG_PB.setSampleFrequency(); // generate an interrupt on any change in either encoder bit (A or B) RPG_A.rise(&Enc_change_ISR); RPG_A.fall(&Enc_change_ISR); RPG_B.rise(&Enc_change_ISR); RPG_B.fall(&Enc_change_ISR); while (true) { // Scale/limit count to 0..100 if (enc_count>100) enc_count = 100; if (enc_count<0) enc_count = 0; red = 1.0 - enc_count/100.0; // pc.printf("%D\n\r",enc_count); } }