Browse Source

rotary encoder test

main
Hendrik Langer 7 years ago
parent
commit
5c9ffbe87e
  1. 14
      src/main.cpp
  2. 4
      src/mp3.cpp
  3. 158
      src/rotary.cpp
  4. 90
      src/rotary.cpp.txt
  5. 41
      src/rotary.h

14
src/main.cpp

@ -8,10 +8,10 @@
* GND O O GND
* 5V O O 5V
* 3V3 O O 3V3
* GND O < 36
* GND O < 36 ROTARY_BTN
* RX * * 17
* TX * < 38
* RST * BUTTON < 39
* TX * < 38 ROTARY_A
* RST * BUTTON < 39 ROTARY_B
* 0 * < 34
* 22 * < 35
* BME280_SDO 19 * LoRa_MISO * 32
@ -44,6 +44,7 @@
#include "mp3.h"
#include "BME280.h"
#include "image.h"
#include "rotary.h"
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16);
@ -56,6 +57,7 @@ char timeStr[20];
BME280 bme280;
MP3 mp3;
Rotary rotary;
uint32_t lastButtonPress = 0;
@ -64,6 +66,9 @@ static const int buttonPin = 0;
static const int ResetPin = 17;
static const int SensorPin = 32;
static const int BatteryPin = 34;
static const int rotaryPinA = 38;
static const int rotaryPinB = 39;
static const int rotaryPinButton = 36;
int sensorValue = 0;
//The batteryLimit defines the point at which the battery is considered empty.
int batteryLimit = 3300;
@ -169,6 +174,9 @@ void setup() {
mp3.begin();
u8g2.setContrast(127);
rotary.registerCallback(nullptr);
rotary.begin(rotaryPinA, rotaryPinB, rotaryPinButton);
}

4
src/mp3.cpp

@ -69,7 +69,7 @@ void MP3::mp3_decoder_task(void *pvParameters) {
decoder = new AudioGeneratorMP3();
decoder->RegisterStatusCB(StatusCallback, (void*)"mp3");
decoder->begin(buff, out);
out->SetGain(1.0);
out->SetGain(0.2);
playing = true;
while(decoder->isRunning()) {
@ -113,7 +113,7 @@ bool MP3::begin() {
// First, preallocate all the memory needed for the buffering and codecs, never to be freed
preallocateBuffer = malloc(preallocateBufferSize);
if (!preallocateBuffer || !preallocateCodec) {
if (!preallocateBuffer) {
Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize);
return false;
}

158
src/rotary.cpp

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

90
src/rotary.cpp.txt

@ -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;
}
}

41
src/rotary.h

@ -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…
Cancel
Save