Skip to main content

omnitiles/hw/
adc.rs

1// SPDX-License-Identifier: MIT
2// © 2025–2026 Christopher Liu
3
4//! Basic ADC support for STM32F7 using direct PAC register access.
5//!
6//! Thin wrapper around ADC1/ADC2/ADC3 with blocking single-channel reads.
7//!
8//! Example:
9//! ```no_run
10//! let adc1 = Adc::adc1(dp.ADC1, &rcc);
11//! let value = adc1.read(3);
12//! ```
13
14use core::cell::RefCell;
15
16use stm32f7xx_hal::pac;
17
18/// Generic ADC wrapper over a PAC ADCx peripheral.
19pub struct Adc<ADC> {
20    adc: ADC,
21}
22
23impl<ADC> Adc<ADC> {
24    #[inline]
25    pub fn free(self) -> ADC {
26        self.adc
27    }
28}
29
30/// Trait for reading a single channel from an ADC peripheral.
31pub trait AdcRead {
32    fn read_channel(&mut self, ch: u8) -> u16;
33}
34
35fn configure_common() {
36    let common = unsafe { &*pac::ADC_COMMON::ptr() };
37
38    // Write the full CCR — not modify — so stale MULTI/DMA/DELAY bits are cleared.
39    common.ccr.write(|w| w.adcpre().div4());
40}
41
42fn init_basic_adc(adc: &pac::adc1::RegisterBlock) {
43    // Full register writes (not modify) to guarantee clean state after soft-reset.
44    // CR2: power off, single conversion, right-aligned, no external trigger
45    adc.cr2.write(|w| {
46        w.cont().clear_bit();
47        w.align().right();
48        w.exten().disabled();
49        w
50    });
51
52    // CR1: 12-bit resolution, no scan, no interrupts
53    adc.cr1.write(|w| w.res().bits(0b00));
54
55    // Zero all sample times
56    adc.smpr1.write(|w| unsafe { w.bits(0) });
57    adc.smpr2.write(|w| unsafe { w.bits(0) });
58
59    // Single conversion in sequence position 1, sequence length = 1
60    adc.sqr1.write(|w| w.l().bits(0));
61    adc.sqr2.write(|w| unsafe { w.bits(0) });
62    adc.sqr3.write(|w| unsafe { w.bits(0) });
63
64    // Clear all status flags
65    adc.sr.write(|w| unsafe { w.bits(0) });
66
67    // Power on
68    adc.cr2.modify(|_, w| w.adon().set_bit());
69}
70
71impl Adc<pac::ADC1> {
72    /// Create and initialize ADC1.
73    pub fn adc1(adc1: pac::ADC1) -> Self {
74        let rcc = unsafe { &*pac::RCC::ptr() };
75        // Enable clock, then reset the peripheral so all registers start from known state.
76        // Without the reset, stale values survive a probe-rs soft-reset (MULTI, SCAN, etc.).
77        rcc.apb2enr.modify(|_, w| w.adc1en().set_bit());
78        rcc.apb2rstr.modify(|_, w| w.adcrst().set_bit());
79        rcc.apb2rstr.modify(|_, w| w.adcrst().clear_bit());
80
81        configure_common();
82        init_basic_adc(&adc1);
83
84        Self { adc: adc1 }
85    }
86}
87
88impl Adc<pac::ADC2> {
89    /// Create and initialize ADC2.
90    pub fn adc2(adc2: pac::ADC2) -> Self {
91        let rcc = unsafe { &*pac::RCC::ptr() };
92        rcc.apb2enr.modify(|_, w| w.adc2en().set_bit());
93
94        configure_common();
95        init_basic_adc(&adc2);
96
97        Self { adc: adc2 }
98    }
99}
100
101impl Adc<pac::ADC3> {
102    /// Create and initialize ADC3.
103    pub fn adc3(adc3: pac::ADC3) -> Self {
104        let rcc = unsafe { &*pac::RCC::ptr() };
105        rcc.apb2enr.modify(|_, w| w.adc3en().set_bit());
106
107        configure_common();
108        init_basic_adc(&adc3);
109
110        Self { adc: adc3 }
111    }
112}
113
114/// Read a single channel from the given ADC peripheral.
115fn read_channel(adc: &pac::adc1::RegisterBlock, channel: u8) -> u16 {
116    // Configure long sample time for channel stability
117    if channel <= 9 {
118        adc.smpr2.modify(|_, w| match channel {
119            0 => w.smp0().bits(0b111),
120            1 => w.smp1().bits(0b111),
121            2 => w.smp2().bits(0b111),
122            3 => w.smp3().bits(0b111),
123            4 => w.smp4().bits(0b111),
124            5 => w.smp5().bits(0b111),
125            6 => w.smp6().bits(0b111),
126            7 => w.smp7().bits(0b111),
127            8 => w.smp8().bits(0b111),
128            9 => w.smp9().bits(0b111),
129            _ => unreachable!(),
130        });
131    } else {
132        adc.smpr1.modify(|_, w| match channel {
133            10 => w.smp10().bits(0b111),
134            11 => w.smp11().bits(0b111),
135            12 => w.smp12().bits(0b111),
136            13 => w.smp13().bits(0b111),
137            14 => w.smp14().bits(0b111),
138            15 => w.smp15().bits(0b111),
139            _ => w,
140        });
141    }
142
143    // Sequence length = 1 conversion
144    adc.sqr1.modify(|_, w| w.l().bits(0));
145
146    // Set channel
147    adc.sqr3
148        .modify(|_, w| unsafe { w.sq1().bits(channel & 0x1F) });
149
150    let mut sum: u32 = 0;
151    const SAMPLES: u32 = 16;
152
153    for _ in 0..SAMPLES {
154        // Start conversion
155        adc.cr2.modify(|_, w| w.swstart().set_bit());
156
157        // Wait for completion
158        while adc.sr.read().eoc().bit_is_clear() {}
159
160        sum += adc.dr.read().data().bits() as u32;
161    }
162
163    // Point mux away from the external pin to avoid parasitic loading between reads
164    adc.sqr3.modify(|_, w| unsafe { w.sq1().bits(0x1F) });
165
166    (sum / SAMPLES) as u16
167}
168
169impl Adc<pac::ADC1> {
170    /// Read a single channel.
171    #[inline]
172    pub fn read(&self, channel: u8) -> u16 {
173        read_channel(&self.adc, channel)
174    }
175}
176
177impl Adc<pac::ADC2> {
178    /// Read a single channel.
179    #[inline]
180    pub fn read(&self, channel: u8) -> u16 {
181        read_channel(&self.adc, channel)
182    }
183}
184
185impl Adc<pac::ADC3> {
186    /// Read a single channel.
187    #[inline]
188    pub fn read(&self, channel: u8) -> u16 {
189        read_channel(&self.adc, channel)
190    }
191}
192
193impl AdcRead for Adc<pac::ADC1> {
194    fn read_channel(&mut self, ch: u8) -> u16 {
195        self.read(ch)
196    }
197}
198
199impl AdcRead for Adc<pac::ADC2> {
200    fn read_channel(&mut self, ch: u8) -> u16 {
201        self.read(ch)
202    }
203}
204
205impl AdcRead for Adc<pac::ADC3> {
206    fn read_channel(&mut self, ch: u8) -> u16 {
207        self.read(ch)
208    }
209}
210
211impl<ADC> Adc<ADC>
212where
213    Adc<ADC>: AdcRead,
214{
215    /// Create a closure that reads the given channel from the ADC reference.
216    pub fn make_reader<'a>(adc_ref: &'a RefCell<Self>, channel: u8) -> impl FnMut() -> u16 + 'a {
217        move || adc_ref.borrow_mut().read_channel(channel)
218    }
219
220    /// Create a closure that reads `N` channels from the ADC reference in a
221    /// single invocation, returning them as a fixed-size array.
222    pub fn make_multi_reader<'a, const N: usize>(
223        adc_ref: &'a RefCell<Self>,
224        channels: [u8; N],
225    ) -> impl FnMut() -> [u16; N] + 'a {
226        move || {
227            let mut adc = adc_ref.borrow_mut();
228            let mut out = [0u16; N];
229            for i in 0..N {
230                out[i] = adc.read_channel(channels[i]);
231            }
232            out
233        }
234    }
235}
236
237/// Convert raw ADC value to voltage, assuming 12-bit resolution.
238pub fn volts_from_adc(adc_value: u16, v_ref: f32) -> f32 {
239    let max_adc = (1 << 12) - 1;
240    (adc_value as f32 / max_adc as f32) * v_ref
241}