aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederik <[email protected]>2022-02-10 23:21:40 +0100
committerFrederik <[email protected]>2022-02-10 23:28:41 +0100
commit7a3d28ad0082ca62e806add32c8c0c52a3705b15 (patch)
tree82e15062c6eed9628b30ff2d0ae1ce5018054d4f
parentcbc7f620bd9aa96f2aa4acab232da69f39c4313a (diff)
stm32f4: add adc + example
Example tested on stm32f407vg Discovery Board. minimal adc: no vref, dma, complex sequence
-rw-r--r--embassy-stm32/src/adc/v2.rs241
-rw-r--r--examples/stm32f4/src/bin/adc.rs26
2 files changed, 267 insertions, 0 deletions
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index 8b1378917..e5a1ddc67 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -1 +1,242 @@
1use crate::adc::{AdcPin, Instance};
2use core::marker::PhantomData;
3use embassy::util::Unborrow;
4use embassy_hal_common::unborrow;
5use embedded_hal_02::blocking::delay::DelayUs;
1 6
7pub const VDDA_CALIB_MV: u32 = 3000;
8
9unsafe fn enable() {
10 // TODO do not enable all adc clocks if not needed
11 #[cfg(rcc_f4)]
12 crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true));
13 crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true));
14 crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true));
15}
16
17pub enum Resolution {
18 TwelveBit,
19 TenBit,
20 EightBit,
21 SixBit,
22}
23
24impl Default for Resolution {
25 fn default() -> Self {
26 Self::TwelveBit
27 }
28}
29
30impl Resolution {
31 fn res(&self) -> crate::pac::adc::vals::Res {
32 match self {
33 Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
34 Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
35 Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
36 Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
37 }
38 }
39
40 fn to_max_count(&self) -> u32 {
41 match self {
42 Resolution::TwelveBit => (1 << 12) - 1,
43 Resolution::TenBit => (1 << 10) - 1,
44 Resolution::EightBit => (1 << 8) - 1,
45 Resolution::SixBit => (1 << 6) - 1,
46 }
47 }
48}
49
50pub struct Vref;
51impl<T: Instance> AdcPin<T> for Vref {}
52impl<T: Instance> super::sealed::AdcPin<T> for Vref {
53 fn channel(&self) -> u8 {
54 let val = 17;
55 val
56 }
57}
58
59pub struct Temperature;
60impl<T: Instance> AdcPin<T> for Temperature {}
61impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
62 fn channel(&self) -> u8 {
63 let val = 16;
64 val
65 }
66}
67
68pub struct Vbat;
69impl<T: Instance> AdcPin<T> for Vbat {}
70impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
71 fn channel(&self) -> u8 {
72 let val = 18;
73 val
74 }
75}
76
77mod sample_time {
78 /// ADC sample time
79 ///
80 /// The default setting is 3 ADC clock cycles.
81 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
82 pub enum SampleTime {
83 Cycles3 = 0b000,
84 Cycles15 = 0b001,
85 Cycles28 = 0b010,
86 Cycles56 = 0b011,
87 Cycles85 = 0b100,
88 Cycles112 = 0b101,
89 Cycles144 = 0b110,
90 Cycles480 = 0b111,
91 }
92
93 impl SampleTime {
94 pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp {
95 match self {
96 SampleTime::Cycles3 => crate::pac::adc::vals::Smp::CYCLES3,
97 SampleTime::Cycles15 => crate::pac::adc::vals::Smp::CYCLES15,
98 SampleTime::Cycles28 => crate::pac::adc::vals::Smp::CYCLES28,
99 SampleTime::Cycles56 => crate::pac::adc::vals::Smp::CYCLES56,
100 SampleTime::Cycles85 => crate::pac::adc::vals::Smp::CYCLES84,
101 SampleTime::Cycles112 => crate::pac::adc::vals::Smp::CYCLES112,
102 SampleTime::Cycles144 => crate::pac::adc::vals::Smp::CYCLES144,
103 SampleTime::Cycles480 => crate::pac::adc::vals::Smp::CYCLES480,
104 }
105 }
106 }
107
108 impl Default for SampleTime {
109 fn default() -> Self {
110 Self::Cycles3
111 }
112 }
113}
114
115pub use sample_time::SampleTime;
116
117pub struct Adc<'d, T: Instance> {
118 sample_time: SampleTime,
119 calibrated_vdda: u32,
120 resolution: Resolution,
121 phantom: PhantomData<&'d mut T>,
122}
123
124impl<'d, T> Adc<'d, T>
125where
126 T: Instance,
127{
128 pub fn new(_peri: impl Unborrow<Target = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
129 unborrow!(_peri);
130 unsafe {
131 enable();
132 // disable before config is set
133 T::regs().cr2().modify(|reg| {
134 reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
135 });
136 }
137
138 delay.delay_us(20); // TODO?
139
140 Self {
141 sample_time: Default::default(),
142 resolution: Resolution::default(),
143 calibrated_vdda: VDDA_CALIB_MV,
144 phantom: PhantomData,
145 }
146 }
147
148 pub fn set_sample_time(&mut self, sample_time: SampleTime) {
149 self.sample_time = sample_time;
150 }
151
152 pub fn set_resolution(&mut self, resolution: Resolution) {
153 self.resolution = resolution;
154 }
155
156 /// Convert a measurement to millivolts
157 pub fn to_millivolts(&self, sample: u16) -> u16 {
158 ((u32::from(sample) * self.calibrated_vdda) / self.resolution.to_max_count()) as u16
159 }
160
161 /// Perform a single conversion.
162 fn convert(&mut self) -> u16 {
163 unsafe {
164 // clear end of conversion flag
165 T::regs().sr().modify(|reg| {
166 reg.set_eoc(crate::pac::adc::vals::Eoc::NOTCOMPLETE);
167 });
168
169 // Start conversion
170 T::regs().cr2().modify(|reg| {
171 reg.set_swstart(true);
172 });
173
174 while T::regs().sr().read().strt() == crate::pac::adc::vals::Strt::NOTSTARTED {
175 // spin //wait for actual start
176 }
177 while T::regs().sr().read().eoc() == crate::pac::adc::vals::Eoc::NOTCOMPLETE {
178 // spin //wait for finish
179 }
180
181 T::regs().dr().read().0 as u16
182 }
183 }
184
185 pub fn read<P>(&mut self, pin: &mut P) -> u16
186 where
187 P: AdcPin<T>,
188 P: crate::gpio::sealed::Pin,
189 {
190 unsafe {
191 // dissable ADC
192 T::regs().cr2().modify(|reg| {
193 reg.set_swstart(false);
194 });
195 T::regs().cr2().modify(|reg| {
196 reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
197 });
198
199 pin.set_as_analog();
200
201 // Configure ADC
202 T::regs()
203 .cr1()
204 .modify(|reg| reg.set_res(self.resolution.res()));
205
206 // Select channel
207 T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel()));
208
209 // Configure channel
210 Self::set_channel_sample_time(pin.channel(), self.sample_time);
211
212 // enable adc
213 T::regs().cr2().modify(|reg| {
214 reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
215 });
216
217 let val = self.convert();
218
219 // dissable ADC
220 T::regs().cr2().modify(|reg| {
221 reg.set_swstart(false);
222 });
223 T::regs().cr2().modify(|reg| {
224 reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
225 });
226
227 val
228 }
229 }
230
231 unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
232 if ch <= 9 {
233 T::regs()
234 .smpr2()
235 .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time()));
236 } else {
237 T::regs()
238 .smpr1()
239 .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time()));
240 }
241 }
242}
diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs
new file mode 100644
index 000000000..0a6ddbbca
--- /dev/null
+++ b/examples/stm32f4/src/bin/adc.rs
@@ -0,0 +1,26 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7
8use embassy::executor::Spawner;
9use embassy::time::{Delay, Duration, Timer};
10use embassy_stm32::adc::Adc;
11use embassy_stm32::Peripherals;
12use example_common::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello World!");
17
18 let mut adc = Adc::new(p.ADC1, &mut Delay);
19 let mut pin = p.PC1;
20
21 loop {
22 let v = adc.read(&mut pin);
23 info!("--> {} - {} mV", v, adc.to_millivolts(v));
24 Timer::after(Duration::from_millis(100)).await;
25 }
26}