1use embassy_stm32::adc::{Adc, AdcConfig, AnyAdcChannel, Averaging, Resolution, SampleTime};
8use embassy_stm32::peripherals::ADC1;
9
10pub const COUNT: usize = 4;
12
13#[derive(Clone, Copy, PartialEq, Eq, defmt::Format)]
15pub enum Knob {
16 Tl,
18 Tr,
20 Bl,
22 Br,
24}
25
26impl Knob {
27 pub const ALL: [Knob; COUNT] = [Knob::Tl, Knob::Tr, Knob::Bl, Knob::Br];
28
29 pub const fn index(self) -> usize {
30 self as usize
31 }
32
33 pub const fn label(self) -> &'static str {
34 match self {
35 Knob::Tl => "TL",
36 Knob::Tr => "TR",
37 Knob::Bl => "BL",
38 Knob::Br => "BR",
39 }
40 }
41}
42
43#[derive(Clone, Copy)]
45pub struct Reading {
46 pub a: u16,
47 pub b: u16,
48}
49
50impl Reading {
51 pub fn angle_deg(&self) -> i32 {
53 const MID: f32 = 32768.0; let rad = libm::atan2f(self.a as f32 - MID, self.b as f32 - MID);
55 (rad * 180.0 / core::f32::consts::PI) as i32
56 }
57}
58
59const SAMPLE_TIME: SampleTime = SampleTime::CYCLES810_5;
61
62pub struct Knobs {
64 adc: Adc<'static, ADC1>,
65 wipers: [AnyAdcChannel<'static, ADC1>; COUNT * 2],
67}
68
69impl Knobs {
70 pub fn new(
71 adc1: embassy_stm32::Peri<'static, ADC1>,
72 wipers: [AnyAdcChannel<'static, ADC1>; COUNT * 2],
73 ) -> Self {
74 let adc = Adc::new_with_config(
76 adc1,
77 AdcConfig {
78 resolution: Some(Resolution::BITS16),
79 averaging: Some(Averaging::Samples8),
80 },
81 );
82 Self { adc, wipers }
83 }
84
85 pub fn read(&mut self, k: Knob) -> Reading {
87 let i = k.index() * 2;
88 let a = self.adc.blocking_read(&mut self.wipers[i], SAMPLE_TIME);
89 let b = self.adc.blocking_read(&mut self.wipers[i + 1], SAMPLE_TIME);
90 Reading { a, b }
91 }
92
93 pub fn read_all(&mut self) -> [Reading; COUNT] {
95 core::array::from_fn(|i| {
96 let a = self.adc.blocking_read(&mut self.wipers[i * 2], SAMPLE_TIME);
97 let b = self.adc.blocking_read(&mut self.wipers[i * 2 + 1], SAMPLE_TIME);
98 Reading { a, b }
99 })
100 }
101}