Skip to content

Commit 712c831

Browse files
committed
Add basic ADC API
1 parent 9226cc7 commit 712c831

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

src/adc.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//! # Analog to Digital converter
2+
3+
use core::convert::Infallible;
4+
5+
use crate::{
6+
gpio::Analog,
7+
hal::{
8+
adc::{Channel, OneShot},
9+
blocking::delay::DelayUs,
10+
},
11+
pac,
12+
rcc::{AHB2, CCIPR},
13+
};
14+
15+
/// Analog to Digital converter interface
16+
pub struct ADC {
17+
inner: pac::ADC,
18+
}
19+
20+
impl ADC {
21+
/// Initialize the ADC
22+
pub fn new(
23+
inner: pac::ADC,
24+
ahb: &mut AHB2,
25+
ccipr: &mut CCIPR,
26+
delay: &mut impl DelayUs<u32>,
27+
) -> Self {
28+
// Reset peripheral
29+
ahb.rstr().modify(|_, w| w.adcrst().set_bit());
30+
ahb.rstr().modify(|_, w| w.adcrst().clear_bit());
31+
32+
// Select system clock as ADC clock source
33+
ccipr.ccipr().modify(|_, w| {
34+
// This is sound, as `0b11` is a valid value for this field.
35+
unsafe {
36+
w.adcsel().bits(0b11);
37+
}
38+
39+
w
40+
});
41+
42+
// Enable peripheral
43+
ahb.enr().modify(|_, w| w.adcen().set_bit());
44+
45+
// Initialize the ADC, according to the STM32L4xx Reference Manual,
46+
// section 16.4.6.
47+
inner.cr.write(|w| {
48+
w.deeppwd().clear_bit(); // exit deep-power-down mode
49+
w.advregen().set_bit(); // enable internal voltage regulator
50+
51+
w
52+
});
53+
54+
// According to the STM32L4xx Reference Manual, section 16.4.6, we need
55+
// to wait for T_ADCVREG_STUP after enabling the internal voltage
56+
// regulator. For the STM32L433, this is 20 us.
57+
delay.delay_us(20);
58+
59+
// Calibration procedure according to section 16.4.8.
60+
inner.cr.modify(|_, w| {
61+
w.adcal().set_bit(); // start calibration
62+
w.adcaldif().clear_bit(); // single-ended mode
63+
64+
w
65+
});
66+
while inner.cr.read().adcal().bit_is_set() {}
67+
68+
Self { inner }
69+
}
70+
71+
/// Release the ADC peripheral
72+
///
73+
/// Drops `ADC` and returns the `pac::ADC` that is was wrapping, giving the
74+
/// user full access to the peripheral.
75+
pub fn release(self) -> pac::ADC {
76+
self.inner
77+
}
78+
}
79+
80+
impl<C> OneShot<ADC, u16, C> for ADC
81+
where
82+
C: Channel<ADC, ID = u8>,
83+
{
84+
type Error = Infallible;
85+
86+
fn read(&mut self, _: &mut C) -> nb::Result<u16, Self::Error> {
87+
// Enable ADC
88+
self.inner.isr.write(|w| w.adrdy().set_bit());
89+
self.inner.cr.modify(|_, w| w.aden().set_bit());
90+
while self.inner.isr.read().adrdy().bit_is_clear() {}
91+
92+
// Select channel
93+
self.inner.sqr1.write(|w| {
94+
// This is sound, as all `Channel` implementations set valid values.
95+
unsafe {
96+
w.sq1().bits(C::channel());
97+
}
98+
99+
w
100+
});
101+
102+
// Start conversion
103+
self.inner.isr.modify(|_, w| w.eos().set_bit());
104+
self.inner.cr.modify(|_, w| w.adstart().set_bit());
105+
while self.inner.isr.read().eos().bit_is_clear() {}
106+
107+
// Read ADC value
108+
let val = self.inner.dr.read().bits() as u16;
109+
110+
// Disable ADC
111+
self.inner.cr.modify(|_, w| w.addis().set_bit());
112+
113+
Ok(val)
114+
}
115+
}
116+
117+
macro_rules! external_channels {
118+
(
119+
$(
120+
$id:expr,
121+
$pin:ident;
122+
)*
123+
) => {
124+
$(
125+
impl Channel<ADC> for crate::gpio::$pin<Analog> {
126+
type ID = u8;
127+
128+
fn channel() -> Self::ID {
129+
$id
130+
}
131+
}
132+
)*
133+
};
134+
}
135+
136+
external_channels!(
137+
1, PC0;
138+
2, PC1;
139+
3, PC2;
140+
4, PC3;
141+
5, PA0;
142+
6, PA1;
143+
7, PA2;
144+
8, PA3;
145+
9, PA4;
146+
10, PA5;
147+
11, PA6;
148+
12, PA7;
149+
13, PC4;
150+
14, PC5;
151+
15, PB0;
152+
16, PB1;
153+
);

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ pub use crate::pac as stm32;
6060

6161
pub mod traits;
6262

63+
#[cfg(feature = "stm32l4x3")]
64+
pub mod adc;
6365
#[cfg(any(
6466
feature = "stm32l4x1",
6567
feature = "stm32l4x2",

src/rcc.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ impl RccExt for RCC {
7272
bdcr: BDCR { _0: () },
7373
csr: CSR { _0: () },
7474
crrcr: CRRCR { _0: () },
75+
ccipr: CCIPR { _0: () },
7576
cfgr: CFGR {
7677
hse: None,
7778
lse: None,
@@ -111,6 +112,8 @@ pub struct Rcc {
111112
pub csr: CSR,
112113
/// Clock recovery RC register
113114
pub crrcr: CRRCR,
115+
/// Peripherals independent clock configuration register
116+
pub ccipr: CCIPR,
114117
}
115118

116119
/// CSR Control/Status Register
@@ -151,6 +154,18 @@ impl CRRCR {
151154
}
152155
}
153156

157+
/// Peripherals independent clock configuration register
158+
pub struct CCIPR {
159+
_0: (),
160+
}
161+
162+
impl CCIPR {
163+
pub(crate) fn ccipr(&mut self) -> &rcc::CCIPR {
164+
// NOTE(unsafe) this proxy grants exclusive access to this register
165+
unsafe { &(*RCC::ptr()).ccipr }
166+
}
167+
}
168+
154169
/// BDCR Backup domain control register registers
155170
pub struct BDCR {
156171
_0: (),

0 commit comments

Comments
 (0)