Hendrik Langer
7 years ago
5 changed files with 212 additions and 95 deletions
@ -0,0 +1,158 @@ |
|||||
|
/* interrupt routine for Rotary Encoders
|
||||
|
tested with Noble RE0124PVB 17.7FINB-24 http://www.nobleusa.com/pdf/xre.pdf - available at pollin.de
|
||||
|
and a few others, seems pretty universal |
||||
|
|
||||
|
The average rotary encoder has three pins, seen from front: A C B |
||||
|
Clockwise rotation A(on)->B(on)->A(off)->B(off) |
||||
|
CounterCW rotation B(on)->A(on)->B(off)->A(off) |
||||
|
|
||||
|
and may be a push switch with another two pins, pulled low at pin 8 in this case |
||||
|
raf@synapps.de 20120107 |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
#include "rotary.h" |
||||
|
|
||||
|
//using namespace std;
|
||||
|
|
||||
|
TaskHandle_t Rotary::xTaskToNotify = NULL; |
||||
|
|
||||
|
Rotary::Rotary() {} |
||||
|
|
||||
|
bool Rotary::begin(uint8_t pinA, uint8_t pinB, uint8_t pinButton) { |
||||
|
this->pinA = pinA; |
||||
|
this->pinB = pinB; |
||||
|
this->pinButton = pinButton; |
||||
|
pinMode(pinA, INPUT_PULLUP); |
||||
|
pinMode(pinB, INPUT_PULLUP); |
||||
|
pinMode(pinButton, INPUT_PULLUP); |
||||
|
|
||||
|
xTaskCreate( |
||||
|
&cTaskWrapper, /* Task function. */ |
||||
|
"buttonTask", /* String with name of task. */ |
||||
|
2048, /* Stack size in words. */ |
||||
|
this, /* Parameter passed as input of the task */ |
||||
|
tskIDLE_PRIORITY+2, /* Priority of the task. */ |
||||
|
&buttonTaskHandle); /* Task handle. */ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void Rotary::cTaskWrapper(void* parameters) { |
||||
|
static_cast<Rotary*>(parameters)->button_task(NULL); |
||||
|
} |
||||
|
|
||||
|
void Rotary::button_task(void *pvParameters) { |
||||
|
xTaskToNotify = xTaskGetCurrentTaskHandle(); |
||||
|
|
||||
|
attachInterrupt(digitalPinToInterrupt(pinA), doEncoderA, CHANGE); |
||||
|
attachInterrupt(digitalPinToInterrupt(pinB), doEncoderB, CHANGE); |
||||
|
// attachInterrupt(digitalPinToInterrupt(pinButton), doButton, CHANGE);
|
||||
|
|
||||
|
uint32_t ulNotificationValue; |
||||
|
|
||||
|
while(true) { |
||||
|
|
||||
|
ulNotificationValue = xTaskNotifyWait(0x00, ULONG_MAX, &ulNotificationValue, portMAX_DELAY ); |
||||
|
Serial.printf("interrupt %d, rotating: %d\n", ulNotificationValue, rotating); |
||||
|
|
||||
|
if( ulNotificationValue == 1 ) { |
||||
|
// debounce
|
||||
|
if ( rotating ) delay (1); // wait a little until the bouncing is done
|
||||
|
|
||||
|
// Test transition, did things really change?
|
||||
|
if ( digitalRead(pinA) != A_set ) { // debounce once more
|
||||
|
A_set = !A_set; |
||||
|
|
||||
|
// adjust counter + if A leads B
|
||||
|
if ( A_set && !B_set ) |
||||
|
encoderPos += 1; |
||||
|
|
||||
|
rotating = false; // no more debouncing until loop() hits again
|
||||
|
changed(); |
||||
|
} |
||||
|
|
||||
|
} else if ( ulNotificationValue == 2 ) { |
||||
|
if ( rotating ) delay (1); |
||||
|
if ( digitalRead(pinB) != B_set ) { |
||||
|
B_set = !B_set; |
||||
|
// adjust counter - 1 if B leads A
|
||||
|
if ( B_set && !A_set ) |
||||
|
encoderPos -= 1; |
||||
|
|
||||
|
rotating = false; |
||||
|
changed(); |
||||
|
} |
||||
|
} else if ( ulNotificationValue == 4 ) { |
||||
|
delay(1); |
||||
|
if ( digitalRead(pinButton) != Button_set ) { |
||||
|
Button_set = !Button_set; |
||||
|
|
||||
|
if (! Button_set ) { |
||||
|
encoderPos = 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/*
|
||||
|
// main loop, work is done by interrupt service routines, this one only prints stuff
|
||||
|
void loop() { |
||||
|
rotating = true; // reset the debouncer
|
||||
|
|
||||
|
if (lastReportedPos != encoderPos) { |
||||
|
Serial.print("Index:"); |
||||
|
Serial.println(encoderPos, DEC); |
||||
|
lastReportedPos = encoderPos; |
||||
|
} |
||||
|
if (digitalRead(clearButton) == LOW ) { |
||||
|
encoderPos = 0; |
||||
|
} |
||||
|
} |
||||
|
*/ |
||||
|
|
||||
|
void Rotary::changed() { |
||||
|
if (lastReportedPos != encoderPos) { |
||||
|
Serial.print("encoder: "); |
||||
|
Serial.println(encoderPos); |
||||
|
// if (callback) callback(encoderPos);
|
||||
|
lastReportedPos = encoderPos; |
||||
|
} |
||||
|
rotating = true; // reset the debouncer
|
||||
|
} |
||||
|
|
||||
|
bool Rotary::registerCallback(std::function<void(int)> callback) { |
||||
|
this->callback = callback; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void Rotary::doButton() { |
||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; |
||||
|
|
||||
|
configASSERT( xTaskToNotify != NULL ); |
||||
|
uint32_t intrBits = 4; |
||||
|
xTaskNotifyFromISR( xTaskToNotify, intrBits, eSetValueWithOverwrite, &xHigherPriorityTaskWoken ); |
||||
|
portYIELD_FROM_ISR(); //portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||
|
} |
||||
|
|
||||
|
// Interrupt on A changing state
|
||||
|
void Rotary::doEncoderA() { |
||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; |
||||
|
|
||||
|
configASSERT( xTaskToNotify != NULL ); |
||||
|
uint32_t intrBits = 1; |
||||
|
xTaskNotifyFromISR( xTaskToNotify, intrBits, eSetValueWithOverwrite, &xHigherPriorityTaskWoken ); |
||||
|
portYIELD_FROM_ISR(); //portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||
|
} |
||||
|
|
||||
|
// Interrupt on B changing state, same as A above
|
||||
|
void Rotary::doEncoderB() { |
||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; |
||||
|
|
||||
|
configASSERT( xTaskToNotify != NULL ); |
||||
|
uint32_t intrBits = 2; |
||||
|
xTaskNotifyFromISR( xTaskToNotify, intrBits, eSetValueWithOverwrite, &xHigherPriorityTaskWoken ); |
||||
|
portYIELD_FROM_ISR(); //portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||
|
} |
||||
|
|
@ -1,90 +0,0 @@ |
|||||
/* interrupt routine for Rotary Encoders |
|
||||
tested with Noble RE0124PVB 17.7FINB-24 http://www.nobleusa.com/pdf/xre.pdf - available at pollin.de |
|
||||
and a few others, seems pretty universal |
|
||||
|
|
||||
The average rotary encoder has three pins, seen from front: A C B |
|
||||
Clockwise rotation A(on)->B(on)->A(off)->B(off) |
|
||||
CounterCW rotation B(on)->A(on)->B(off)->A(off) |
|
||||
|
|
||||
and may be a push switch with another two pins, pulled low at pin 8 in this case |
|
||||
raf@synapps.de 20120107 |
|
||||
|
|
||||
*/ |
|
||||
|
|
||||
// usually the rotary encoders three pins have the ground pin in the middle |
|
||||
enum PinAssignments { |
|
||||
encoderPinA = 2, // right |
|
||||
encoderPinB = 3, // left |
|
||||
clearButton = 8 // another two pins |
|
||||
}; |
|
||||
|
|
||||
volatile unsigned int encoderPos = 0; // a counter for the dial |
|
||||
unsigned int lastReportedPos = 1; // change management |
|
||||
static boolean rotating = false; // debounce management |
|
||||
|
|
||||
// interrupt service routine vars |
|
||||
boolean A_set = false; |
|
||||
boolean B_set = false; |
|
||||
|
|
||||
|
|
||||
void setup() { |
|
||||
pinMode(encoderPinA, INPUT); |
|
||||
pinMode(encoderPinB, INPUT); |
|
||||
pinMode(clearButton, INPUT); |
|
||||
// turn on pullup resistors |
|
||||
digitalWrite(encoderPinA, HIGH); |
|
||||
digitalWrite(encoderPinB, HIGH); |
|
||||
digitalWrite(clearButton, HIGH); |
|
||||
|
|
||||
// encoder pin on interrupt 0 (pin 2) |
|
||||
attachInterrupt(0, doEncoderA, CHANGE); |
|
||||
// encoder pin on interrupt 1 (pin 3) |
|
||||
attachInterrupt(1, doEncoderB, CHANGE); |
|
||||
|
|
||||
Serial.begin(9600); // output |
|
||||
} |
|
||||
|
|
||||
// main loop, work is done by interrupt service routines, this one only prints stuff |
|
||||
void loop() { |
|
||||
rotating = true; // reset the debouncer |
|
||||
|
|
||||
if (lastReportedPos != encoderPos) { |
|
||||
Serial.print("Index:"); |
|
||||
Serial.println(encoderPos, DEC); |
|
||||
lastReportedPos = encoderPos; |
|
||||
} |
|
||||
if (digitalRead(clearButton) == LOW ) { |
|
||||
encoderPos = 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Interrupt on A changing state |
|
||||
void doEncoderA() { |
|
||||
// debounce |
|
||||
if ( rotating ) delay (1); // wait a little until the bouncing is done |
|
||||
|
|
||||
// Test transition, did things really change? |
|
||||
if ( digitalRead(encoderPinA) != A_set ) { // debounce once more |
|
||||
A_set = !A_set; |
|
||||
|
|
||||
// adjust counter + if A leads B |
|
||||
if ( A_set && !B_set ) |
|
||||
encoderPos += 1; |
|
||||
|
|
||||
rotating = false; // no more debouncing until loop() hits again |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Interrupt on B changing state, same as A above |
|
||||
void doEncoderB() { |
|
||||
if ( rotating ) delay (1); |
|
||||
if ( digitalRead(encoderPinB) != B_set ) { |
|
||||
B_set = !B_set; |
|
||||
// adjust counter - 1 if B leads A |
|
||||
if ( B_set && !A_set ) |
|
||||
encoderPos -= 1; |
|
||||
|
|
||||
rotating = false; |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -0,0 +1,41 @@ |
|||||
|
#ifndef _ROTARY_H |
||||
|
#define _ROTARY_H |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <functional> |
||||
|
|
||||
|
// usually the rotary encoders three pins have the ground pin in the middle
|
||||
|
enum PinAssignments { |
||||
|
encoderPinA = 2, // right
|
||||
|
encoderPinB = 3, // left
|
||||
|
clearButton = 8 // another two pins
|
||||
|
}; |
||||
|
|
||||
|
class Rotary { |
||||
|
public: |
||||
|
Rotary(); |
||||
|
bool begin(uint8_t pinA, uint8_t pinB, uint8_t pinButton); |
||||
|
bool registerCallback(std::function<void(int)> callback); |
||||
|
static void IRAM_ATTR doEncoderA(void); |
||||
|
static void IRAM_ATTR doEncoderB(void); |
||||
|
static void IRAM_ATTR doButton(void); |
||||
|
static TaskHandle_t xTaskToNotify; |
||||
|
private: |
||||
|
volatile unsigned int encoderPos = 0; // a counter for the dial
|
||||
|
unsigned int lastReportedPos = 1; // change management
|
||||
|
boolean rotating = false; // debounce management
|
||||
|
// interrupt service routine vars
|
||||
|
boolean A_set = false; |
||||
|
boolean B_set = false; |
||||
|
boolean Button_set = false; |
||||
|
uint8_t pinA; |
||||
|
uint8_t pinB; |
||||
|
uint8_t pinButton; |
||||
|
void changed(void); |
||||
|
std::function<void(int)> callback = nullptr; |
||||
|
TaskHandle_t buttonTaskHandle; |
||||
|
void button_task(void*); |
||||
|
static void cTaskWrapper(void*); |
||||
|
}; |
||||
|
|
||||
|
#endif /* _ROTARY_H */ |
Loading…
Reference in new issue