-
Arduino Snippets: Quadrature Knob
Any gadget goes better with a knob and what better knob than one with detents all the way around? This one even has a push-on momentary switch on the shaft, so you can switch gain / change modes / control power while turning:
Using the Arduino’s internal pullups means you just plug the wires into the header sockets and it works:
The code uses the same interrupt-driven state machine I described there to avoid glitches and twitches. The test code dumps the accumulated knob rotation count and switch state out the serial port:
Knob encoder Ed Nisley - KE4ZNU - November 2012 Knob count: 0 Switch: 0 Knob count: 0 Switch: 1 Knob count: 1 Switch: 1 Knob count: 1 Switch: 0 Knob count: 2 Switch: 0 Knob count: 3 Switch: 0 Knob count: 4 Switch: 0 Knob count: 4 Switch: 1 Knob count: 3 Switch: 1 Knob count: 2 Switch: 1 Knob count: 1 Switch: 1 Knob count: 0 Switch: 1 Knob count: -1 Switch: 1 Knob count: -2 Switch: 1 Knob count: -3 Switch: 1 Knob count: -4 Switch: 1 Knob count: -5 Switch: 1 Knob count: -5 Switch: 0 Knob count: -4 Switch: 0Yes, you (well, I) can spin the knob fast enough to accumulate a few counts between the 10 ms updates and it will return to zero at the same physical location, so it’s not losing any counts in the process.
Knob encoder Ed Nisley - KE4ZNU - November 2012 Knob count: 0 Switch: 0 Knob count: -4 Switch: 0 Knob count: -6 Switch: 0 Knob count: -7 Switch: 0 Knob count: -6 Switch: 0 Knob count: -4 Switch: 0 Knob count: -3 Switch: 0 Knob count: -6 Switch: 0 Knob count: -8 Switch: 0 Knob count: -9 Switch: 0 Knob count: -10 Switch: 0 Knob count: -12 Switch: 0 Knob count: -18 Switch: 0 Knob count: -19 Switch: 0 Knob count: -20 Switch: 0 Knob count: -19 Switch: 0 Knob count: -18 Switch: 0 Knob count: -15 Switch: 0 Knob count: -9 Switch: 0 Knob count: -7 Switch: 0 Knob count: -6 Switch: 0 Knob count: -4 Switch: 0 Knob count: -3 Switch: 0 Knob count: -2 Switch: 0 Knob count: -1 Switch: 0 Knob count: 0 Switch: 0Not very exciting as it stands, but it’s got plenty of upside potential.
The Arduino source code:
// Quadrature knob with switch // Ed Nisley - KE4ANU - November 2012 // Based on: // https://softsolder.com/2009/03/03/reading-a-quadrature-encoded-knob-in-double-quick-time/ //---------- // Pin assignments const byte PIN_KNOB_A = 2; // knob A switch - must be on ext interrupt 2 const byte PIN_KNOB_B = 4; // .. B switch const byte PIN_KNOB_SW = A5; // .. push-close momentary switch const byte PIN_SYNC = 13; // scope sync //---------- // Constants const int UPDATEMS = 10; // update LEDs only this many ms apart #define TCCRxB 0x02 // Timer prescaler //---------- // Globals enum KNOB_STATES {KNOB_CLICK_0,KNOB_CLICK_1}; volatile char KnobCounter = 0; volatile char KnobState; char PrevKnobCounter = 0; char KnobSwitch, PrevSwitch = 0; unsigned long MillisNow; unsigned long MillisThen; //-- Helper routine for printf() int s_putc(char c, FILE *t) { Serial.write(c); } //-- Knob interrupt handler void KnobHandler(void) { byte Inputs; Inputs = digitalRead(PIN_KNOB_B) << 1 | digitalRead(PIN_KNOB_A); // align raw inputs // Inputs ^= 0x02; // fix direction switch (KnobState << 2 | Inputs) { case 0x00 : // 0 00 - glitch break; case 0x01 : // 0 01 - UP to 1 KnobCounter++; KnobState = KNOB_CLICK_1; break; case 0x03 : // 0 11 - DOWN to 1 KnobCounter--; KnobState = KNOB_CLICK_1; break; case 0x02 : // 0 10 - glitch break; case 0x04 : // 1 00 - DOWN to 0 KnobCounter--; KnobState = KNOB_CLICK_0; break; case 0x05 : // 1 01 - glitch break; case 0x07 : // 1 11 - glitch break; case 0x06 : // 1 10 - UP to 0 KnobCounter++; KnobState = KNOB_CLICK_0; break; default : // something is broken! KnobCounter = 0; KnobState = KNOB_CLICK_0; } } //-- Read switch, flip sense char ReadSwitch(int PinNumber) { return !digitalRead(PinNumber); } //------------------ // Set things up void setup() { pinMode(PIN_SYNC,OUTPUT); digitalWrite(PIN_SYNC,LOW); // show we arrived // TCCR1B = TCCRxB; // set frequency for PWM 9 & 10 // TCCR2B = TCCRxB; // set frequency for PWM 3 & 11 pinMode(PIN_KNOB_B,INPUT_PULLUP); pinMode(PIN_KNOB_A,INPUT_PULLUP); pinMode(PIN_KNOB_SW,INPUT_PULLUP); KnobState = digitalRead(PIN_KNOB_A); PrevSwitch = digitalRead(PIN_KNOB_SW); attachInterrupt((PIN_KNOB_A - 2),KnobHandler,CHANGE); Serial.begin(9600); fdevopen(&s_putc,0); // set up serial output for printf() printf("Knob encoder\r\nEd Nisley - KE4ZNU - November 2012\r\n"); MillisThen = millis(); } //------------------ // Run the test loop void loop() { MillisNow = millis(); if ((MillisNow - MillisThen) &gt; UPDATEMS) { digitalWrite(PIN_SYNC,HIGH); KnobSwitch = ReadSwitch(PIN_KNOB_SW); if ((PrevKnobCounter != KnobCounter) || (PrevSwitch != KnobSwitch)) { printf("Knob count: %d Switch: %d\n",KnobCounter,KnobSwitch); PrevKnobCounter = KnobCounter; PrevSwitch = KnobSwitch; } digitalWrite(PIN_SYNC,LOW); MillisThen = MillisNow; } }

