/* 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::instance = NULL; Rotary::Rotary() { instance = this; } 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(parameters)->button_task(NULL); } void Rotary::button_task(void *pvParameters) { xTaskToNotify = xTaskGetCurrentTaskHandle(); attachInterrupt(digitalPinToInterrupt(pinA), doEncoderA, RISING); attachInterrupt(digitalPinToInterrupt(pinB), doEncoderB, RISING); // attachInterrupt(digitalPinToInterrupt(pinButton), doButton, FALLING); uint32_t ulNotificationValue; uint32_t ulStatusValue; while(true) { rotating = true; // reset the debouncer ulNotificationValue = xTaskNotifyWait(0x00, ULONG_MAX, &ulStatusValue, portMAX_DELAY ); // if ( rotating ) delay (1); // wait a little until the bouncing is done if( ulStatusValue == 1 ) { // adjust counter + if A leads B encoderPos += 1; changed(); rotating = false; // no more debouncing until loop() hits again } else if ( ulStatusValue == 2 ) { // adjust counter -1 if B leads A encoderPos -= 1; changed(); rotating = false; // no more debouncing until loop() hits again } else if ( ulStatusValue == 4 ) { Button_set = digitalRead(pinButton); if (! Button_set ) { encoderPos = 0; } } } } void Rotary::changed() { if (lastReportedPos != encoderPos) { if (callback) callback(encoderPos); lastReportedPos = encoderPos; } } bool Rotary::registerCallback(std::function callback) { this->callback = callback; return true; } void Rotary::doButton() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; configASSERT( xTaskToNotify != NULL ); uint32_t intrBits = 4; xTaskNotifyFromISR( xTaskToNotify, intrBits, eSetValueWithoutOverwrite, &xHigherPriorityTaskWoken ); portYIELD_FROM_ISR(); //portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } // Interrupt on A changing state void Rotary::doEncoderA() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (instance->skipA) { instance->skipA = false; instance->debounceA = millis(); return; } if (!digitalRead(instance->pinB) && ((millis() - instance->debounceA) > instance->debounceDelay)) { instance->debounceA = millis(); instance->skipB = true; configASSERT( xTaskToNotify != NULL ); uint32_t intrBits = 1; xTaskNotifyFromISR( xTaskToNotify, intrBits, eSetValueWithoutOverwrite, &xHigherPriorityTaskWoken ); portYIELD_FROM_ISR(); //portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } } // Interrupt on B changing state, same as A above void Rotary::doEncoderB() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (instance->skipB) { instance->skipB = false; instance->debounceB = millis(); return; } if (!digitalRead(instance->pinA) && ((millis() - instance->debounceB) > instance->debounceDelay)) { instance->debounceB = millis(); instance->skipA = true; configASSERT( xTaskToNotify != NULL ); uint32_t intrBits = 2; xTaskNotifyFromISR( xTaskToNotify, intrBits, eSetValueWithoutOverwrite, &xHigherPriorityTaskWoken ); portYIELD_FROM_ISR(); //portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } }