From 7928ce00568e8914c875732dab0b46d68cc3bb70 Mon Sep 17 00:00:00 2001 From: Jen Tak Date: Mon, 11 Dec 2023 18:59:12 +0100 Subject: [PATCH] Fix volume dial backlash Fixes https://github.com/tonarino/portal/issues/2666 --- Cargo.lock | 3 +++ Cargo.toml | 1 + src/counter.rs | 31 ++++++++++++++++++++++++++++--- src/main.rs | 6 ++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30212b3..a44cbe8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "aligned" version = "0.3.4" @@ -210,6 +212,7 @@ dependencies = [ name = "panel-firmware" version = "0.3.0" dependencies = [ + "arrayvec", "cortex-m 0.7.2", "cortex-m-rt", "embedded-hal", diff --git a/Cargo.toml b/Cargo.toml index a023675..05c5340 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT" edition = "2018" [dependencies] +arrayvec = { version = "0.5.2", default-features = false } stm32f4xx-hal = { version = "0.9", features = ["rt", "stm32f411", "usb_fs"] } embedded-hal = "0.2" cortex-m = "0.7" diff --git a/src/counter.rs b/src/counter.rs index a100a7a..3697bfe 100644 --- a/src/counter.rs +++ b/src/counter.rs @@ -1,21 +1,42 @@ -use hal::{prelude::*, qei::Qei, stm32::TIM1}; +use hal::{ + prelude::*, + qei::Qei, + stm32::TIM1, + timer::{Instant, MonoTimer}, +}; use stm32f4xx_hal as hal; +const DETENT_RESET_TIMOUT_MS: u32 = 200; + pub struct Counter { qei: Qei, last_count: u16, + timer: MonoTimer, + last_update: Instant, } impl Counter { - pub fn new(qei: Qei) -> Self { + pub fn new(qei: Qei, timer: MonoTimer) -> Self { let last_count = qei.count(); - Counter { qei, last_count } + Counter { qei, last_count, last_update: timer.now(), timer } } pub fn poll(&mut self) -> Option { let count = self.qei.count(); let diff = count.wrapping_sub(self.last_count) as i16; + // Sometimes it happens that last_count gets out of sync with the physical detents. + // We require count to increment by 4 to fire a single tick but when last_count lands in between + // detents we get backlash or missed ticks. + // Assume that when the dial rests for more than DETENT_RESET_TIMOUT_MS it is aligned to a detent + // and reset last_count to current position. + if self.last_update.elapsed() > self.timer.frequency().0 * (DETENT_RESET_TIMOUT_MS / 1000) { + self.last_count = count; + } + + if diff != 0 { + self.last_update = self.timer.now(); + } if diff.abs() >= 4 { self.last_count = count; Some((diff / 4) as i8) @@ -23,4 +44,8 @@ impl Counter { None } } + + pub fn current_count(&self) -> u16 { + self.qei.count() + } } diff --git a/src/main.rs b/src/main.rs index 6e7a07d..5dc0062 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,8 @@ use crate::{ rgb_led::{LedStrip, Pulser}, serial::{Command, Report, SerialProtocol}, }; +use arrayvec::ArrayString; +use core::fmt::Write; use cortex_m_rt::entry; use embedded_hal::digital::v2::OutputPin; use hal::{ @@ -184,6 +186,10 @@ fn main() -> ! { _ => {}, } + let mut buf = ArrayString::<[u8; 256]>::new(); + write!(&mut buf, "count: {}", counter.current_count()).expect("can write count"); + protocol.debug(buf.as_str()); + if let Some(diff) = counter.poll() { if !encoder_button.is_pressed() { protocol.report(Report::DialValue { diff }).unwrap();