Improve Rotary Encoder Logic

This commit is contained in:
Mona Mayrhofer 2025-06-27 20:39:08 +02:00
parent e023814626
commit 654bd8bdf6
No known key found for this signature in database
GPG key ID: 3E2BDA732A957188

View file

@ -7,7 +7,7 @@ use embassy_executor::Spawner;
use embassy_futures::select::{Either, select};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::channel::{Channel, Sender};
use embassy_time::{Duration, Instant, Timer};
use embassy_time::{Duration, Instant, Timer, WithTimeout};
use esp_hal::clock::CpuClock;
use esp_hal::gpio::{Input, InputConfig, Pull};
use esp_hal::peripherals::GPIO;
@ -116,7 +116,7 @@ async fn main(spawner: Spawner) {
// .unwrap();
//
let cnt = 4;
let cnt = 12;
led_strip
.write((0..cnt).map(|it| {
@ -136,43 +136,23 @@ async fn main(spawner: Spawner) {
// for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0-beta.0/examples/src/bin
}
pub struct Throttler<'a> {
input: Input<'a>,
throttle: Duration,
last_fire: Option<Instant>,
}
impl<'a> Throttler<'a> {
pub fn new(input: Input<'a>, throttle: Duration) -> Self {
Self {
input,
throttle,
last_fire: None,
}
}
pub async fn wait_for(&mut self) {
if let Some(allow_next_fire) = self
.last_fire
.and_then(|last_fire| last_fire.checked_add(self.throttle))
{
Timer::at(allow_next_fire).await;
}
self.input.wait_for_falling_edge().await;
self.last_fire = Some(Instant::now());
}
}
struct RotaryEncoderConfig<'a, 'b> {
clk: Input<'a>,
ot: Input<'b>,
}
#[derive(Clone, Copy, Debug)]
enum RotaryState {
Clk,
Ot,
struct RotaryState {
clk: bool,
ot: bool,
}
#[derive(Clone, Copy, Debug)]
enum RotaryDirection {
Cw,
Ccw,
}
#[embassy_executor::task]
async fn listen_rotary_encoder(
mut config: RotaryEncoderConfig<'static, 'static>,
@ -180,51 +160,79 @@ async fn listen_rotary_encoder(
) {
info!("Waiting for rotary.");
let mut state: Option<RotaryState> = None;
let mut state = RotaryState {
clk: false,
ot: false,
};
let mut clk_input = Throttler::new(config.clk, Duration::from_millis(50));
let mut ot_input = Throttler::new(config.ot, Duration::from_millis(50));
let mut cw_count = 0;
let mut ccw_count = 0;
loop {
let clk = async {
clk_input.wait_for().await;
config.clk.wait_for_any_edge().await;
};
let ot = async {
ot_input.wait_for().await;
config.ot.wait_for_any_edge().await;
};
let next_state = match select(clk, ot).await {
Either::First(_) => (RotaryState::Clk),
Either::Second(_) => (RotaryState::Ot),
select(clk, ot).await;
let new_state = RotaryState {
clk: config.clk.is_low(),
ot: config.ot.is_low(),
};
//info!("State: {next_state:?}");
let direction = match ((state.clk, state.ot), (new_state.clk, new_state.ot)) {
((false, false), (false, false)) => None,
((false, true), (false, true)) => None,
((true, false), (true, false)) => None,
((true, true), (true, true)) => None,
match (state, next_state) {
(None, RotaryState::Ot) => {
if clk_input.input.is_high() {
state = Some(RotaryState::Ot);
}
((false, false), (true, false)) => Some(RotaryDirection::Cw),
((true, false), (true, true)) => Some(RotaryDirection::Cw),
((true, true), (false, true)) => Some(RotaryDirection::Cw),
((false, true), (false, false)) => Some(RotaryDirection::Cw),
((false, false), (false, true)) => Some(RotaryDirection::Ccw),
((false, true), (true, true)) => Some(RotaryDirection::Ccw),
((true, true), (true, false)) => Some(RotaryDirection::Ccw),
((true, false), (false, false)) => Some(RotaryDirection::Ccw),
((false, false), (true, true)) => None, //???
((false, true), (true, false)) => None, //???
((true, false), (false, true)) => None, //???
((true, true), (false, false)) => None, //???
};
match direction {
Some(RotaryDirection::Cw) => {
cw_count += 1;
}
(None, RotaryState::Clk) => {
if ot_input.input.is_high() {
state = Some(RotaryState::Clk);
}
Some(RotaryDirection::Ccw) => {
ccw_count += 1;
}
(Some(RotaryState::Clk), RotaryState::Ot) => {
info!("Clk -> Ot");
state = None;
None => {}
}
if (state.clk, state.ot) == (true, true) {
if (cw_count - 1) > ccw_count {
info!("CW");
info!("Direction {cw_count} {ccw_count}");
cw_count = 0;
ccw_count = 0;
channel.send(1).await;
}
(Some(RotaryState::Ot), RotaryState::Clk) => {
info!("Ot -> Clk");
state = None;
if (ccw_count - 1) > cw_count {
info!("CCW");
info!("Direction {cw_count} {ccw_count}");
cw_count = 0;
ccw_count = 0;
channel.send(-1).await;
}
(Some(RotaryState::Ot), RotaryState::Ot)
| (Some(RotaryState::Clk), RotaryState::Clk) => {}
}
state = new_state;
}
}