Advertisements

Archive for December 11th, 2012

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:

Quadrature Knob with Push Switch

Quadrature Knob with Push Switch

Using the Arduino’s internal pullups means you just plug the wires into the header sockets and it works:

Quadrature Knob and Switch - breadboard

Quadrature Knob and Switch – breadboard

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: 0

Yes, 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: 0

Not 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) > 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;
	}

}

Advertisements

8 Comments