Skip to content

Commit db6cb3f

Browse files
authored
Merge pull request #173 from braun-embedded/adc
Add ADC API
2 parents 9f130c7 + 785fadf commit db6cb3f

File tree

5 files changed

+319
-0
lines changed

5 files changed

+319
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ debug = true
8888
lto = true
8989

9090

91+
[[example]]
92+
name = "adc"
93+
required-features = ["rt", "stm32l4x3"]
94+
9195
[[example]]
9296
name = "irq_button"
9397
required-features = ["rt"]

examples/adc.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use panic_rtt_target as _;
5+
6+
use cortex_m_rt::entry;
7+
use rtt_target::{rprint, rprintln};
8+
use stm32l4xx_hal::{adc::ADC, delay::Delay, pac, prelude::*};
9+
10+
#[entry]
11+
fn main() -> ! {
12+
rtt_target::rtt_init_print!();
13+
rprint!("Initializing...");
14+
15+
let cp = pac::CorePeripherals::take().unwrap();
16+
let dp = pac::Peripherals::take().unwrap();
17+
18+
let mut rcc = dp.RCC.constrain();
19+
let mut flash = dp.FLASH.constrain();
20+
let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1);
21+
22+
let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr);
23+
24+
let mut delay = Delay::new(cp.SYST, clocks);
25+
let mut adc = ADC::new(dp.ADC, &mut rcc.ahb2, &mut rcc.ccipr, &mut delay);
26+
27+
let mut gpioc = dp.GPIOC.split(&mut rcc.ahb2);
28+
let mut a1 = gpioc.pc0.into_analog(&mut gpioc.moder, &mut gpioc.pupdr);
29+
30+
rprintln!(" done.");
31+
32+
loop {
33+
let value = adc.read(&mut a1).unwrap();
34+
rprintln!("Value: {}", value);
35+
}
36+
}

src/adc.rs

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
//! # Analog to Digital converter
2+
3+
use core::convert::Infallible;
4+
5+
use crate::{
6+
gpio::Analog,
7+
hal::{
8+
adc::{Channel as EmbeddedHalChannel, 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+
resolution: Resolution,
19+
sample_time: SampleTime,
20+
}
21+
22+
impl ADC {
23+
/// Initialize the ADC
24+
pub fn new(
25+
inner: pac::ADC,
26+
ahb: &mut AHB2,
27+
ccipr: &mut CCIPR,
28+
delay: &mut impl DelayUs<u32>,
29+
) -> Self {
30+
// Reset peripheral
31+
ahb.rstr().modify(|_, w| w.adcrst().set_bit());
32+
ahb.rstr().modify(|_, w| w.adcrst().clear_bit());
33+
34+
// Select system clock as ADC clock source
35+
ccipr.ccipr().modify(|_, w| {
36+
// This is sound, as `0b11` is a valid value for this field.
37+
unsafe {
38+
w.adcsel().bits(0b11);
39+
}
40+
41+
w
42+
});
43+
44+
// Enable peripheral
45+
ahb.enr().modify(|_, w| w.adcen().set_bit());
46+
47+
// Initialize the ADC, according to the STM32L4xx Reference Manual,
48+
// section 16.4.6.
49+
inner.cr.write(|w| {
50+
w.deeppwd().clear_bit(); // exit deep-power-down mode
51+
w.advregen().set_bit(); // enable internal voltage regulator
52+
53+
w
54+
});
55+
56+
// According to the STM32L4xx Reference Manual, section 16.4.6, we need
57+
// to wait for T_ADCVREG_STUP after enabling the internal voltage
58+
// regulator. For the STM32L433, this is 20 us.
59+
delay.delay_us(20);
60+
61+
// Calibration procedure according to section 16.4.8.
62+
inner.cr.modify(|_, w| {
63+
w.adcal().set_bit(); // start calibration
64+
w.adcaldif().clear_bit(); // single-ended mode
65+
66+
w
67+
});
68+
while inner.cr.read().adcal().bit_is_set() {}
69+
70+
Self {
71+
inner,
72+
resolution: Resolution::default(),
73+
sample_time: SampleTime::default(),
74+
}
75+
}
76+
77+
/// Set the ADC resolution
78+
pub fn set_resolution(&mut self, resolution: Resolution) {
79+
self.resolution = resolution;
80+
}
81+
82+
/// Set the sample time
83+
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
84+
self.sample_time = sample_time;
85+
}
86+
87+
/// Release the ADC peripheral
88+
///
89+
/// Drops `ADC` and returns the `pac::ADC` that is was wrapping, giving the
90+
/// user full access to the peripheral.
91+
pub fn release(self) -> pac::ADC {
92+
self.inner
93+
}
94+
}
95+
96+
impl<C> OneShot<ADC, u16, C> for ADC
97+
where
98+
C: Channel,
99+
{
100+
type Error = Infallible;
101+
102+
fn read(&mut self, channel: &mut C) -> nb::Result<u16, Self::Error> {
103+
// Enable ADC
104+
self.inner.isr.write(|w| w.adrdy().set_bit());
105+
self.inner.cr.modify(|_, w| w.aden().set_bit());
106+
while self.inner.isr.read().adrdy().bit_is_clear() {}
107+
108+
// Configure ADC
109+
self.inner.cfgr.write(|w| {
110+
// This is sound, as all `Resolution` values are valid for this
111+
// field.
112+
unsafe { w.res().bits(self.resolution as u8) }
113+
});
114+
115+
// Configure channel
116+
channel.set_sample_time(&self.inner, self.sample_time);
117+
118+
// Select channel
119+
self.inner.sqr1.write(|w| {
120+
// This is sound, as all `Channel` implementations set valid values.
121+
unsafe {
122+
w.sq1().bits(C::channel());
123+
}
124+
125+
w
126+
});
127+
128+
// Start conversion
129+
self.inner.isr.modify(|_, w| w.eos().set_bit());
130+
self.inner.cr.modify(|_, w| w.adstart().set_bit());
131+
while self.inner.isr.read().eos().bit_is_clear() {}
132+
133+
// Read ADC value
134+
let val = self.inner.dr.read().bits() as u16;
135+
136+
// Disable ADC
137+
self.inner.cr.modify(|_, w| w.addis().set_bit());
138+
139+
Ok(val)
140+
}
141+
}
142+
143+
/// ADC resolution setting
144+
///
145+
/// The default setting is 12 bits.
146+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
147+
pub enum Resolution {
148+
/// 12-bit resolution
149+
Bits12 = 0b00,
150+
151+
/// 10-bit resolution
152+
Bits10 = 0b01,
153+
154+
/// 8-bit resolution
155+
Bits8 = 0b10,
156+
157+
/// 6-bit resolution
158+
Bits6 = 0b11,
159+
}
160+
161+
impl Default for Resolution {
162+
fn default() -> Self {
163+
Self::Bits12
164+
}
165+
}
166+
167+
/// ADC sample time
168+
///
169+
/// The default setting is 2.5 ADC clock cycles.
170+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
171+
pub enum SampleTime {
172+
/// 2.5 ADC clock cycles
173+
Cycles2_5 = 0b000,
174+
175+
/// 6.5 ADC clock cycles
176+
Cycles6_5 = 0b001,
177+
178+
/// 12.5 ADC clock cycles
179+
Cycles12_5 = 0b010,
180+
181+
/// 24.5 ADC clock cycles
182+
Cycles24_5 = 0b011,
183+
184+
/// 47.5 ADC clock cycles
185+
Cycles47_5 = 0b100,
186+
187+
/// 92.5 ADC clock cycles
188+
Cycles92_5 = 0b101,
189+
190+
/// 247.5 ADC clock cycles
191+
Cycles247_5 = 0b110,
192+
193+
/// 640.5 ADC clock cycles
194+
Cycles640_5 = 0b111,
195+
}
196+
197+
impl Default for SampleTime {
198+
fn default() -> Self {
199+
Self::Cycles2_5
200+
}
201+
}
202+
203+
/// Implemented for all types that represent ADC channels
204+
pub trait Channel: EmbeddedHalChannel<ADC, ID = u8> {
205+
fn set_sample_time(&mut self, adc: &pac::ADC, sample_time: SampleTime);
206+
}
207+
208+
macro_rules! external_channels {
209+
(
210+
$(
211+
$id:expr,
212+
$pin:ident,
213+
$smpr:ident,
214+
$smp:ident;
215+
)*
216+
) => {
217+
$(
218+
impl EmbeddedHalChannel<ADC> for crate::gpio::$pin<Analog> {
219+
type ID = u8;
220+
221+
fn channel() -> Self::ID {
222+
$id
223+
}
224+
}
225+
226+
impl Channel for crate::gpio::$pin<Analog> {
227+
fn set_sample_time(&mut self,
228+
adc: &pac::ADC,
229+
sample_time: SampleTime,
230+
) {
231+
adc.$smpr.modify(|_, w| {
232+
// This is sound, as all `SampleTime` values are valid
233+
// for this field.
234+
unsafe {
235+
w.$smp().bits(sample_time as u8)
236+
}
237+
})
238+
}
239+
}
240+
)*
241+
};
242+
}
243+
244+
external_channels!(
245+
1, PC0, smpr1, smp1;
246+
2, PC1, smpr1, smp2;
247+
3, PC2, smpr1, smp3;
248+
4, PC3, smpr1, smp4;
249+
5, PA0, smpr1, smp5;
250+
6, PA1, smpr1, smp6;
251+
7, PA2, smpr1, smp7;
252+
8, PA3, smpr1, smp8;
253+
9, PA4, smpr1, smp9;
254+
10, PA5, smpr2, smp10;
255+
11, PA6, smpr2, smp11;
256+
12, PA7, smpr2, smp12;
257+
13, PC4, smpr2, smp13;
258+
14, PC5, smpr2, smp14;
259+
15, PB0, smpr2, smp15;
260+
16, PB1, smpr2, smp16;
261+
);

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: 16 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,19 @@ impl CRRCR {
151154
}
152155
}
153156

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

0 commit comments

Comments
 (0)