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    // ADC prescaler: PCLK2 / 4
39    common.ccr.modify(|_, w| w.adcpre().div4());
40}
41
42fn init_basic_adc(adc: &pac::adc1::RegisterBlock) {
43    // Power off to configure
44    adc.cr2.modify(|_, w| w.adon().clear_bit());
45
46    // 12-bit, right-aligned, software trigger
47    adc.cr1.modify(|_, w| w.res().bits(0b00));
48    adc.cr2.modify(|_, w| {
49        w.cont().clear_bit();
50        w.align().right();
51        w.exten().disabled();
52        w
53    });
54
55    // Default minimal sample times
56    adc.smpr2.modify(|_, w| unsafe { w.bits(0) });
57
58    // Power on
59    adc.cr2.modify(|_, w| w.adon().set_bit());
60}
61
62impl Adc<pac::ADC1> {
63    /// Create and initialize ADC1.
64    pub fn adc1(adc1: pac::ADC1) -> Self {
65        let rcc = unsafe { &*pac::RCC::ptr() };
66        rcc.apb2enr.modify(|_, w| w.adc1en().set_bit());
67
68        configure_common();
69        init_basic_adc(&adc1);
70
71        Self { adc: adc1 }
72    }
73}
74
75impl Adc<pac::ADC2> {
76    /// Create and initialize ADC2.
77    pub fn adc2(adc2: pac::ADC2) -> Self {
78        let rcc = unsafe { &*pac::RCC::ptr() };
79        rcc.apb2enr.modify(|_, w| w.adc2en().set_bit());
80
81        configure_common();
82        init_basic_adc(&adc2);
83
84        Self { adc: adc2 }
85    }
86}
87
88impl Adc<pac::ADC3> {
89    /// Create and initialize ADC3.
90    pub fn adc3(adc3: pac::ADC3) -> Self {
91        let rcc = unsafe { &*pac::RCC::ptr() };
92        rcc.apb2enr.modify(|_, w| w.adc3en().set_bit());
93
94        configure_common();
95        init_basic_adc(&adc3);
96
97        Self { adc: adc3 }
98    }
99}
100
101/// Read a single channel from the given ADC peripheral.
102fn read_channel(adc: &pac::adc1::RegisterBlock, channel: u8) -> u16 {
103    // Configure long sample time for channel stability
104    if channel <= 9 {
105        adc.smpr2.modify(|_, w| match channel {
106            0 => w.smp0().bits(0b111),
107            1 => w.smp1().bits(0b111),
108            2 => w.smp2().bits(0b111),
109            3 => w.smp3().bits(0b111),
110            4 => w.smp4().bits(0b111),
111            5 => w.smp5().bits(0b111),
112            6 => w.smp6().bits(0b111),
113            7 => w.smp7().bits(0b111),
114            8 => w.smp8().bits(0b111),
115            9 => w.smp9().bits(0b111),
116            _ => unreachable!(),
117        });
118    } else if channel == 12 {
119        adc.smpr1.modify(|_, w| w.smp12().bits(0b111));
120    }
121
122    // Sequence length = 1 conversion
123    adc.sqr1.modify(|_, w| w.l().bits(0));
124
125    // Set channel
126    adc.sqr3
127        .modify(|_, w| unsafe { w.sq1().bits(channel & 0x1F) });
128
129    let mut sum: u32 = 0;
130    const SAMPLES: u32 = 16;
131
132    for _ in 0..SAMPLES {
133        // Start conversion
134        adc.cr2.modify(|_, w| w.swstart().set_bit());
135
136        // Wait for completion
137        while adc.sr.read().eoc().bit_is_clear() {}
138
139        sum += adc.dr.read().data().bits() as u32;
140    }
141
142    (sum / SAMPLES) as u16
143}
144
145impl Adc<pac::ADC1> {
146    /// Read a single channel.
147    #[inline]
148    pub fn read(&self, channel: u8) -> u16 {
149        read_channel(&self.adc, channel)
150    }
151}
152
153impl Adc<pac::ADC2> {
154    /// Read a single channel.
155    #[inline]
156    pub fn read(&self, channel: u8) -> u16 {
157        read_channel(&self.adc, channel)
158    }
159}
160
161impl Adc<pac::ADC3> {
162    /// Read a single channel.
163    #[inline]
164    pub fn read(&self, channel: u8) -> u16 {
165        read_channel(&self.adc, channel)
166    }
167}
168
169impl AdcRead for Adc<pac::ADC1> {
170    fn read_channel(&mut self, ch: u8) -> u16 {
171        self.read(ch)
172    }
173}
174
175impl AdcRead for Adc<pac::ADC2> {
176    fn read_channel(&mut self, ch: u8) -> u16 {
177        self.read(ch)
178    }
179}
180
181impl AdcRead for Adc<pac::ADC3> {
182    fn read_channel(&mut self, ch: u8) -> u16 {
183        self.read(ch)
184    }
185}
186
187impl<ADC> Adc<ADC>
188where
189    Adc<ADC>: AdcRead,
190{
191    /// Create a closure that reads the given channel from the ADC reference.
192    pub fn make_reader<'a>(adc_ref: &'a RefCell<Self>, channel: u8) -> impl FnMut() -> u16 + 'a {
193        move || adc_ref.borrow_mut().read_channel(channel)
194    }
195}
196
197/// Convert raw ADC value to voltage, assuming 12-bit resolution.
198pub fn volts_from_adc(adc_value: u16, v_ref: f32) -> f32 {
199    let max_adc = (1 << 12) - 1;
200    (adc_value as f32 / max_adc as f32) * v_ref
201}