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_futures::select::{Either, select};
use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::channel::{Channel, Sender}; 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::clock::CpuClock;
use esp_hal::gpio::{Input, InputConfig, Pull}; use esp_hal::gpio::{Input, InputConfig, Pull};
use esp_hal::peripherals::GPIO; use esp_hal::peripherals::GPIO;
@ -116,7 +116,7 @@ async fn main(spawner: Spawner) {
// .unwrap(); // .unwrap();
// //
let cnt = 4; let cnt = 12;
led_strip led_strip
.write((0..cnt).map(|it| { .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 // 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> { struct RotaryEncoderConfig<'a, 'b> {
clk: Input<'a>, clk: Input<'a>,
ot: Input<'b>, ot: Input<'b>,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
enum RotaryState { struct RotaryState {
Clk, clk: bool,
Ot, ot: bool,
} }
#[derive(Clone, Copy, Debug)]
enum RotaryDirection {
Cw,
Ccw,
}
#[embassy_executor::task] #[embassy_executor::task]
async fn listen_rotary_encoder( async fn listen_rotary_encoder(
mut config: RotaryEncoderConfig<'static, 'static>, mut config: RotaryEncoderConfig<'static, 'static>,
@ -180,51 +160,79 @@ async fn listen_rotary_encoder(
) { ) {
info!("Waiting for rotary."); 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 cw_count = 0;
let mut ot_input = Throttler::new(config.ot, Duration::from_millis(50)); let mut ccw_count = 0;
loop { loop {
let clk = async { let clk = async {
clk_input.wait_for().await; config.clk.wait_for_any_edge().await;
}; };
let ot = async { let ot = async {
ot_input.wait_for().await; config.ot.wait_for_any_edge().await;
}; };
let next_state = match select(clk, ot).await { select(clk, ot).await;
Either::First(_) => (RotaryState::Clk),
Either::Second(_) => (RotaryState::Ot), 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) { ((false, false), (true, false)) => Some(RotaryDirection::Cw),
(None, RotaryState::Ot) => { ((true, false), (true, true)) => Some(RotaryDirection::Cw),
if clk_input.input.is_high() { ((true, true), (false, true)) => Some(RotaryDirection::Cw),
state = Some(RotaryState::Ot); ((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) => { Some(RotaryDirection::Ccw) => {
if ot_input.input.is_high() { ccw_count += 1;
state = Some(RotaryState::Clk);
}
} }
(Some(RotaryState::Clk), RotaryState::Ot) => { None => {}
info!("Clk -> Ot"); }
state = 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; channel.send(1).await;
} }
(Some(RotaryState::Ot), RotaryState::Clk) => { if (ccw_count - 1) > cw_count {
info!("Ot -> Clk"); info!("CCW");
state = None; info!("Direction {cw_count} {ccw_count}");
cw_count = 0;
ccw_count = 0;
channel.send(-1).await; channel.send(-1).await;
} }
(Some(RotaryState::Ot), RotaryState::Ot)
| (Some(RotaryState::Clk), RotaryState::Clk) => {}
} }
state = new_state;
} }
} }