1use embassy_time::{Duration, Ticker};
3use esp_hal::gpio::{AnyPin, Input, InputConfig};
4
5use crate::driver::pcnt::PcntDriver;
6
7pub struct RpmConfig {
8 pub loop_time_ms: u64,
9 pub pole_pairs: f32,
10 pub pulley_ratio: f32,
11}
12
13pub struct RpmResources<'a> {
14 pub pcnt: esp_hal::peripherals::PCNT<'a>,
15 pub pin: AnyPin<'a>,
16}
17
18impl RpmResources<'static> {
19 pub fn into_driver(self) -> PcntDriver {
20 let input = Input::new(
21 self.pin,
22 InputConfig::default().with_pull(esp_hal::gpio::Pull::Down),
23 );
24 PcntDriver::new(self.pcnt, input)
25 }
26}
27
28pub fn read_rpm(pcnt: &mut PcntDriver, config: &RpmConfig) -> f32 {
30 let pulse_count = pcnt.get_and_reset();
31 pulse_count as f32
32 * 60. * (1. / config.pole_pairs / 2.) * (1000. / config.loop_time_ms as f32) * config.pulley_ratio
36}
37
38const NO_SIGNAL_INTERVALS: u32 = 30;
42
43pub async fn rpm_loop(resources: RpmResources<'static>, config: RpmConfig, on_rpm: fn(f32)) {
55 let mut pcnt_driver = resources.into_driver();
56 let mut ticker = Ticker::every(Duration::from_millis(config.loop_time_ms));
57 let mut zero_intervals: u32 = 0;
58 loop {
59 let rpm = read_rpm(&mut pcnt_driver, &config);
60 if rpm > 0.0 {
61 zero_intervals = 0;
62 on_rpm(rpm);
63 } else {
64 let prev = zero_intervals;
65 zero_intervals = zero_intervals.saturating_add(1);
66 if prev == 0 {
67 on_rpm(0.0); } else if prev == NO_SIGNAL_INTERVALS {
69 on_rpm(f32::NAN); }
71 }
73 ticker.next().await;
74 }
75}