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    }
119
120    // Sequence length = 1 conversion
121    adc.sqr1.modify(|_, w| w.l().bits(0));
122
123    // Set channel
124    adc.sqr3
125        .modify(|_, w| unsafe { w.sq1().bits(channel & 0x1F) });
126
127    // Start
128    adc.cr2.modify(|_, w| w.swstart().set_bit());
129
130    // Wait for completion
131    while adc.sr.read().eoc().bit_is_clear() {}
132
133    adc.dr.read().data().bits() as u16
134}
135
136impl Adc<pac::ADC1> {
137    /// Read a single channel.
138    #[inline]
139    pub fn read(&self, channel: u8) -> u16 {
140        read_channel(&self.adc, channel)
141    }
142}
143
144impl Adc<pac::ADC2> {
145    /// Read a single channel.
146    #[inline]
147    pub fn read(&self, channel: u8) -> u16 {
148        read_channel(&self.adc, channel)
149    }
150}
151
152impl Adc<pac::ADC3> {
153    /// Read a single channel.
154    #[inline]
155    pub fn read(&self, channel: u8) -> u16 {
156        read_channel(&self.adc, channel)
157    }
158}
159
160impl AdcRead for Adc<pac::ADC1> {
161    fn read_channel(&mut self, ch: u8) -> u16 {
162        self.read(ch)
163    }
164}
165
166impl AdcRead for Adc<pac::ADC2> {
167    fn read_channel(&mut self, ch: u8) -> u16 {
168        self.read(ch)
169    }
170}
171
172impl AdcRead for Adc<pac::ADC3> {
173    fn read_channel(&mut self, ch: u8) -> u16 {
174        self.read(ch)
175    }
176}
177
178impl<ADC> Adc<ADC>
179where
180    Adc<ADC>: AdcRead,
181{
182    /// Create a closure that reads the given channel from the ADC reference.
183    pub fn make_reader<'a>(adc_ref: &'a RefCell<Self>, channel: u8) -> impl FnMut() -> u16 + 'a {
184        move || adc_ref.borrow_mut().read_channel(channel)
185    }
186}
187
188/// Convert raw ADC value to voltage, assuming 12-bit resolution.
189pub fn volts_from_adc(adc_value: u16, v_ref: f32) -> f32 {
190    let max_adc = (1 << 12) - 1;
191    (adc_value as f32 / max_adc as f32) * v_ref
192}