Rotary Encoder: Don't call callbacks in the isr (#1456)

This commit is contained in:
mknjc 2021-01-10 20:05:53 +01:00 committed by GitHub
parent 5df398ec31
commit 02dc49c272
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 6 deletions

View file

@ -90,16 +90,34 @@ void ICACHE_RAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensor
if (arg->pin_b->digital_read()) if (arg->pin_b->digital_read())
input_state |= STATE_PIN_B_HIGH; input_state |= STATE_PIN_B_HIGH;
int8_t rotation_dir = 0;
uint16_t new_state = STATE_LOOKUP_TABLE[input_state]; uint16_t new_state = STATE_LOOKUP_TABLE[input_state];
if ((new_state & arg->resolution & STATE_HAS_INCREMENTED) != 0) { if ((new_state & arg->resolution & STATE_HAS_INCREMENTED) != 0) {
if (arg->counter < arg->max_value) if (arg->counter < arg->max_value)
arg->counter++; arg->counter++;
arg->on_clockwise_callback_.call(); rotation_dir = 1;
} }
if ((new_state & arg->resolution & STATE_HAS_DECREMENTED) != 0) { if ((new_state & arg->resolution & STATE_HAS_DECREMENTED) != 0) {
if (arg->counter > arg->min_value) if (arg->counter > arg->min_value)
arg->counter--; arg->counter--;
arg->on_anticlockwise_callback_.call(); rotation_dir = -1;
}
if (rotation_dir != 0) {
auto first_zero = std::find(arg->rotation_events.begin(), arg->rotation_events.end(), 0); // find first zero
if (first_zero == arg->rotation_events.begin() // are we at the start (first event this loop iteration)
|| std::signbit(*std::prev(first_zero)) !=
std::signbit(rotation_dir) // or is the last stored event the wrong direction
|| *std::prev(first_zero) == std::numeric_limits<int8_t>::lowest() // or the last event slot is full (negative)
|| *std::prev(first_zero) == std::numeric_limits<int8_t>::max()) { // or the last event slot is full (positive)
if (first_zero != arg->rotation_events.end()) { // we have a free rotation slot
*first_zero += rotation_dir; // store the rotation into a new slot
} else {
arg->rotation_events_overflow = true;
}
} else {
*std::prev(first_zero) += rotation_dir; // store the rotation into the previous slot
}
} }
arg->state = new_state; arg->state = new_state;
@ -137,6 +155,35 @@ void RotaryEncoderSensor::dump_config() {
} }
} }
void RotaryEncoderSensor::loop() { void RotaryEncoderSensor::loop() {
std::array<int8_t, 8> rotation_events;
bool rotation_events_overflow;
ets_intr_lock();
rotation_events = this->store_.rotation_events;
rotation_events_overflow = this->store_.rotation_events_overflow;
this->store_.rotation_events.fill(0);
this->store_.rotation_events_overflow = false;
ets_intr_unlock();
if (rotation_events_overflow) {
ESP_LOGW(TAG, "Captured more rotation events than expected");
}
for (auto events : rotation_events) {
if (events == 0) // we are at the end of the recorded events
break;
if (events > 0) {
while (events--) {
this->on_clockwise_callback_.call();
}
} else {
while (events++) {
this->on_anticlockwise_callback_.call();
}
}
}
if (this->pin_i_ != nullptr && this->pin_i_->digital_read()) { if (this->pin_i_ != nullptr && this->pin_i_->digital_read()) {
this->store_.counter = 0; this->store_.counter = 0;
} }

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <array>
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/esphal.h" #include "esphome/core/esphal.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
@ -27,8 +29,8 @@ struct RotaryEncoderSensorStore {
int32_t last_read{0}; int32_t last_read{0};
uint8_t state{0}; uint8_t state{0};
CallbackManager<void()> on_clockwise_callback_; std::array<int8_t, 8> rotation_events{};
CallbackManager<void()> on_anticlockwise_callback_; bool rotation_events_overflow{false};
static void gpio_intr(RotaryEncoderSensorStore *arg); static void gpio_intr(RotaryEncoderSensorStore *arg);
}; };
@ -66,11 +68,11 @@ class RotaryEncoderSensor : public sensor::Sensor, public Component {
float get_setup_priority() const override; float get_setup_priority() const override;
void add_on_clockwise_callback(std::function<void()> callback) { void add_on_clockwise_callback(std::function<void()> callback) {
this->store_.on_clockwise_callback_.add(std::move(callback)); this->on_clockwise_callback_.add(std::move(callback));
} }
void add_on_anticlockwise_callback(std::function<void()> callback) { void add_on_anticlockwise_callback(std::function<void()> callback) {
this->store_.on_anticlockwise_callback_.add(std::move(callback)); this->on_anticlockwise_callback_.add(std::move(callback));
} }
protected: protected:
@ -79,6 +81,9 @@ class RotaryEncoderSensor : public sensor::Sensor, public Component {
GPIOPin *pin_i_{nullptr}; /// Index pin, if this is not nullptr, the counter will reset to 0 once this pin is HIGH. GPIOPin *pin_i_{nullptr}; /// Index pin, if this is not nullptr, the counter will reset to 0 once this pin is HIGH.
RotaryEncoderSensorStore store_{}; RotaryEncoderSensorStore store_{};
CallbackManager<void()> on_clockwise_callback_;
CallbackManager<void()> on_anticlockwise_callback_;
}; };
template<typename... Ts> class RotaryEncoderSetValueAction : public Action<Ts...> { template<typename... Ts> class RotaryEncoderSetValueAction : public Action<Ts...> {