aboutsummaryrefslogtreecommitdiff
path: root/embassy-mspm0
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mspm0')
-rw-r--r--embassy-mspm0/build.rs59
-rw-r--r--embassy-mspm0/src/adc.rs483
-rw-r--r--embassy-mspm0/src/lib.rs1
3 files changed, 543 insertions, 0 deletions
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
index e8364e31a..ad90b5223 100644
--- a/embassy-mspm0/build.rs
+++ b/embassy-mspm0/build.rs
@@ -68,6 +68,7 @@ fn generate_code() {
68 g.extend(generate_pin_trait_impls()); 68 g.extend(generate_pin_trait_impls());
69 g.extend(generate_groups()); 69 g.extend(generate_groups());
70 g.extend(generate_dma_channel_count()); 70 g.extend(generate_dma_channel_count());
71 g.extend(generate_adc_constants());
71 72
72 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 73 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
73 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); 74 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
@@ -220,6 +221,59 @@ fn generate_dma_channel_count() -> TokenStream {
220 quote! { pub const DMA_CHANNELS: usize = #count; } 221 quote! { pub const DMA_CHANNELS: usize = #count; }
221} 222}
222 223
224fn generate_adc_constants() -> TokenStream {
225 let vrsel = METADATA.adc_vrsel;
226 let memctl = METADATA.adc_memctl;
227
228 if vrsel == 3 {
229 quote! {
230 pub const ADC_VRSEL: u8 = #vrsel;
231 pub const ADC_MEMCTL: u8 = #memctl;
232
233 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
234 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
235 /// Reference voltage (Vref) selection for the ADC channels.
236 pub enum Vrsel {
237 /// VDDA reference
238 VddaVssa = 0,
239
240 /// External reference from pin
241 ExtrefVrefm = 1,
242
243 /// Internal reference
244 IntrefVssa = 2,
245 }
246 }
247 } else if vrsel == 5 {
248 quote! {
249 pub const ADC_VRSEL: u8 = #vrsel;
250 pub const ADC_MEMCTL: u8 = #memctl;
251
252 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
253 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
254 /// Reference voltage (Vref) selection for the ADC channels.
255 pub enum Vrsel {
256 /// VDDA reference
257 VddaVssa = 0,
258
259 /// External reference from pin
260 ExtrefVrefm = 1,
261
262 /// Internal reference
263 IntrefVssa = 2,
264
265 /// VDDA and VREFM connected to VREF+ and VREF- of ADC
266 VddaVrefm = 3,
267
268 /// INTREF and VREFM connected to VREF+ and VREF- of ADC
269 IntrefVrefm = 4,
270 }
271 }
272 } else {
273 panic!("Unsupported ADC VRSEL value: {vrsel}");
274 }
275}
276
223#[derive(Debug, Clone)] 277#[derive(Debug, Clone)]
224struct Singleton { 278struct Singleton {
225 name: String, 279 name: String,
@@ -561,6 +615,7 @@ fn generate_peripheral_instances() -> TokenStream {
561 "uart" => Some(quote! { impl_uart_instance!(#peri); }), 615 "uart" => Some(quote! { impl_uart_instance!(#peri); }),
562 "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), 616 "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }),
563 "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), 617 "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }),
618 "adc" => Some(quote! { impl_adc_instance!(#peri); }),
564 _ => None, 619 _ => None,
565 }; 620 };
566 621
@@ -609,6 +664,10 @@ fn generate_pin_trait_impls() -> TokenStream {
609 ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }), 664 ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }),
610 ("i2c", "SDA") => Some(quote! { impl_i2c_sda_pin!(#peri, #pin_name, #pf); }), 665 ("i2c", "SDA") => Some(quote! { impl_i2c_sda_pin!(#peri, #pin_name, #pf); }),
611 ("i2c", "SCL") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }), 666 ("i2c", "SCL") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }),
667 ("adc", s) => {
668 let signal = s.parse::<u8>().unwrap();
669 Some(quote! { impl_adc_pin!(#peri, #pin_name, #signal); })
670 }
612 671
613 _ => None, 672 _ => None,
614 }; 673 };
diff --git a/embassy-mspm0/src/adc.rs b/embassy-mspm0/src/adc.rs
new file mode 100644
index 000000000..32fea4453
--- /dev/null
+++ b/embassy-mspm0/src/adc.rs
@@ -0,0 +1,483 @@
1#![macro_use]
2
3use crate::interrupt;
4use crate::interrupt::{Interrupt, InterruptExt};
5use crate::mode::{Async, Blocking, Mode};
6use core::future::poll_fn;
7use core::marker::PhantomData;
8use core::task::Poll;
9
10use crate::pac::adc::{vals, Adc as Regs};
11use crate::Peri;
12use embassy_hal_internal::{impl_peripheral, PeripheralType};
13use embassy_sync::waitqueue::AtomicWaker;
14
15/// Interrupt handler.
16pub struct InterruptHandler<T: Instance> {
17 _phantom: PhantomData<T>,
18}
19
20impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
21 unsafe fn on_interrupt() {
22 // Mis is cleared upon reading iidx
23 let iidx = T::info().regs.cpu_int(0).iidx().read().stat();
24 // TODO: Running in sequence mode, we get an interrupt per finished result. It would be
25 // nice to wake up only after all results are finished.
26 if vals::CpuIntIidxStat::MEMRESIFG0 <= iidx && iidx <= vals::CpuIntIidxStat::MEMRESIFG23 {
27 T::state().waker.wake();
28 }
29 }
30}
31
32// Constants from the metapac crate
33const ADC_VRSEL: u8 = crate::_generated::ADC_VRSEL;
34const ADC_MEMCTL: u8 = crate::_generated::ADC_MEMCTL;
35
36#[derive(Clone, Copy, PartialEq, Eq, Debug)]
37#[cfg_attr(feature = "defmt", derive(defmt::Format))]
38/// Conversion resolution of the ADC results.
39pub enum Resolution {
40 /// 12-bits resolution
41 BIT12,
42
43 /// 10-bits resolution
44 BIT10,
45
46 /// 8-bits resolution
47 BIT8,
48}
49
50impl Resolution {
51 /// Number of bits of the resolution.
52 pub fn bits(&self) -> u8 {
53 match self {
54 Resolution::BIT12 => 12,
55 Resolution::BIT10 => 10,
56 Resolution::BIT8 => 8,
57 }
58 }
59}
60
61pub use crate::_generated::Vrsel;
62
63/// ADC configuration.
64pub struct AdcConfig {
65 /// Resolution of the ADC conversion. The number of bits used to represent an ADC measurement.
66 pub resolution: Resolution,
67 /// ADC voltage reference selection.
68 ///
69 /// This value is used when reading a single channel. When reading a sequence
70 /// the vr_select is provided per channel.
71 pub vr_select: Vrsel,
72 /// The sample time in number of ADC sample clock cycles.
73 pub sample_time: u16,
74}
75
76/// ADC (Analog to Digial Converter) Driver.
77pub struct Adc<'d, T: Instance, M: Mode> {
78 #[allow(unused)]
79 adc: crate::Peri<'d, T>,
80 info: &'static Info,
81 state: &'static State,
82 config: AdcConfig,
83 _phantom: PhantomData<M>,
84}
85
86impl<'d, T: Instance> Adc<'d, T, Blocking> {
87 /// A new blocking ADC driver instance.
88 pub fn new_blocking(peri: Peri<'d, T>, config: AdcConfig) -> Self {
89 let mut this = Self {
90 adc: peri,
91 info: T::info(),
92 state: T::state(),
93 config,
94 _phantom: PhantomData,
95 };
96 this.setup();
97 this
98 }
99}
100
101impl<'d, T: Instance> Adc<'d, T, Async> {
102 /// A new asynchronous ADC driver instance.
103 pub fn new_async(
104 peri: Peri<'d, T>,
105 config: AdcConfig,
106 _irqs: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>
107 + interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>
108 + 'd,
109 ) -> Self {
110 let mut this = Self {
111 adc: peri,
112 info: T::info(),
113 state: T::state(),
114 config,
115 _phantom: PhantomData,
116 };
117 this.setup();
118 unsafe {
119 this.info.interrupt.enable();
120 }
121 this
122 }
123}
124
125impl<'d, T: Instance, M: Mode> Adc<'d, T, M> {
126 const SINGLE_CHANNEL: u8 = 0;
127
128 fn setup(&mut self) {
129 let config = &self.config;
130 assert!(
131 (config.vr_select as u8) < ADC_VRSEL,
132 "Reference voltage selection out of bounds"
133 );
134 // Reset adc
135 self.info.regs.gprcm(0).rstctl().write(|reg| {
136 reg.set_resetstkyclr(true);
137 reg.set_resetassert(true);
138 reg.set_key(vals::ResetKey::KEY);
139 });
140
141 // Power up adc
142 self.info.regs.gprcm(0).pwren().modify(|reg| {
143 reg.set_enable(true);
144 reg.set_key(vals::PwrenKey::KEY);
145 });
146
147 // Wait for cycles similar to TI power setup
148 cortex_m::asm::delay(16);
149
150 // Set clock config
151 self.info.regs.gprcm(0).clkcfg().modify(|reg| {
152 reg.set_key(vals::ClkcfgKey::KEY);
153 reg.set_sampclk(vals::Sampclk::SYSOSC);
154 });
155 self.info.regs.ctl0().modify(|reg| {
156 reg.set_sclkdiv(vals::Sclkdiv::DIV_BY_4);
157 });
158 self.info.regs.clkfreq().modify(|reg| {
159 reg.set_frange(vals::Frange::RANGE24TO32);
160 });
161
162 // Init single conversion with software trigger and auto sampling
163 //
164 // We use sequence to support sequence operation in the future, but only set up a single
165 // channel
166 self.info.regs.ctl1().modify(|reg| {
167 reg.set_conseq(vals::Conseq::SEQUENCE);
168 reg.set_sampmode(vals::Sampmode::AUTO);
169 reg.set_trigsrc(vals::Trigsrc::SOFTWARE);
170 });
171 let res = match config.resolution {
172 Resolution::BIT12 => vals::Res::BIT_12,
173 Resolution::BIT10 => vals::Res::BIT_10,
174 Resolution::BIT8 => vals::Res::BIT_8,
175 };
176 self.info.regs.ctl2().modify(|reg| {
177 // Startadd detemines the channel used in single mode.
178 reg.set_startadd(Self::SINGLE_CHANNEL);
179 reg.set_endadd(Self::SINGLE_CHANNEL);
180 reg.set_res(res);
181 reg.set_df(false);
182 });
183
184 // Set the sample time used by all channels for now
185 self.info.regs.scomp0().modify(|reg| {
186 reg.set_val(config.sample_time);
187 });
188 }
189
190 fn setup_blocking_channel(&mut self, channel: &mut impl AdcChannel<T>) {
191 channel.setup();
192
193 // CTL0.ENC must be 0 to write the MEMCTL register
194 while self.info.regs.ctl0().read().enc() {
195 // Wait until the ADC is not in conversion mode
196 }
197
198 // Conversion mem config
199 let vrsel = vals::Vrsel::from_bits(self.config.vr_select as u8);
200 self.info.regs.memctl(Self::SINGLE_CHANNEL as usize).modify(|reg| {
201 reg.set_chansel(channel.channel());
202 reg.set_vrsel(vrsel);
203 reg.set_stime(vals::Stime::SEL_SCOMP0);
204 reg.set_avgen(false);
205 reg.set_bcsen(false);
206 reg.set_trig(vals::Trig::AUTO_NEXT);
207 reg.set_wincomp(false);
208 });
209 self.info.regs.ctl2().modify(|reg| {
210 // Set end address to the number of used channels
211 reg.set_endadd(Self::SINGLE_CHANNEL);
212 });
213 }
214
215 fn enable_conversion(&mut self) {
216 // Enable conversion
217 self.info.regs.ctl0().modify(|reg| {
218 reg.set_enc(true);
219 });
220 }
221
222 fn start_conversion(&mut self) {
223 // Start conversion
224 self.info.regs.ctl1().modify(|reg| {
225 reg.set_sc(vals::Sc::START);
226 });
227 }
228
229 fn conversion_result(&mut self, channel_id: usize) -> u16 {
230 // Read result
231 self.info.regs.memres(channel_id).read().data()
232 }
233
234 /// Read one ADC channel in blocking mode using the config provided at initialization.
235 pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
236 self.setup_blocking_channel(channel);
237 self.enable_conversion();
238 self.start_conversion();
239
240 while self.info.regs.ctl0().read().enc() {}
241
242 self.conversion_result(Self::SINGLE_CHANNEL as usize)
243 }
244}
245
246impl<'d, T: Instance> Adc<'d, T, Async> {
247 async fn wait_for_conversion(&self) {
248 let info = self.info;
249 let state = self.state;
250 poll_fn(move |cx| {
251 state.waker.register(cx.waker());
252
253 if !info.regs.ctl0().read().enc() {
254 Poll::Ready(())
255 } else {
256 Poll::Pending
257 }
258 })
259 .await;
260 }
261
262 fn setup_async_channel(&self, id: usize, channel: &impl AdcChannel<T>, vrsel: Vrsel) {
263 let vrsel = vals::Vrsel::from_bits(vrsel as u8);
264 // Conversion mem config
265 self.info.regs.memctl(id).modify(|reg| {
266 reg.set_chansel(channel.channel());
267 reg.set_vrsel(vrsel);
268 reg.set_stime(vals::Stime::SEL_SCOMP0);
269 reg.set_avgen(false);
270 reg.set_bcsen(false);
271 reg.set_trig(vals::Trig::AUTO_NEXT);
272 reg.set_wincomp(false);
273 });
274
275 // Clear interrupt status
276 self.info.regs.cpu_int(0).iclr().write(|reg| {
277 reg.set_memresifg(id, true);
278 });
279 // Enable interrupt
280 self.info.regs.cpu_int(0).imask().modify(|reg| {
281 reg.set_memresifg(id, true);
282 });
283 }
284
285 /// Read one ADC channel asynchronously using the config provided at initialization.
286 pub async fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
287 channel.setup();
288
289 // CTL0.ENC must be 0 to write the MEMCTL register
290 self.wait_for_conversion().await;
291
292 self.info.regs.ctl2().modify(|reg| {
293 // Set end address to the number of used channels
294 reg.set_endadd(Self::SINGLE_CHANNEL);
295 });
296
297 self.setup_async_channel(Self::SINGLE_CHANNEL as usize, channel, self.config.vr_select);
298
299 self.enable_conversion();
300 self.start_conversion();
301 self.wait_for_conversion().await;
302
303 self.conversion_result(Self::SINGLE_CHANNEL as usize)
304 }
305
306 /// Read one or multiple ADC channels using the Vrsel provided per channel.
307 ///
308 /// `sequence` iterator and `readings` must have the same length.
309 ///
310 /// Example
311 /// ```rust,ignore
312 /// use embassy_mspm0::adc::{Adc, AdcChannel, Vrsel};
313 ///
314 /// let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs);
315 /// let pin1 = p.PA14.degrade_adc();
316 /// let pin2 = p.PA25.degrade_adc();
317 /// let sequence = [(&pin1, Vrsel::VddaVssa), (&pin2, Vrsel::VddaVssa)];
318 /// let mut readings = [0u16; 2];
319 ///
320 /// adc.read_sequence(
321 /// sequence.into_iter(),
322 /// &mut readings,
323 /// )
324 /// .await;
325 /// defmt::info!("Measurements: {}", readings);
326 /// ```
327 pub async fn read_sequence<'a>(
328 &mut self,
329 sequence: impl ExactSizeIterator<Item = (&'a AnyAdcChannel<T>, Vrsel)>,
330 readings: &mut [u16],
331 ) where
332 T: 'a,
333 {
334 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
335 assert!(
336 sequence.len() == readings.len(),
337 "Sequence length must be equal to readings length"
338 );
339 assert!(
340 sequence.len() <= ADC_MEMCTL as usize,
341 "Asynchronous read sequence cannot be more than {} in length",
342 ADC_MEMCTL
343 );
344
345 // CTL0.ENC must be 0 to write the MEMCTL register
346 self.wait_for_conversion().await;
347
348 self.info.regs.ctl2().modify(|reg| {
349 // Set end address to the number of used channels
350 reg.set_endadd((sequence.len() - 1) as u8);
351 });
352
353 for (i, (channel, vrsel)) in sequence.enumerate() {
354 self.setup_async_channel(i, channel, vrsel);
355 }
356 self.enable_conversion();
357 self.start_conversion();
358 self.wait_for_conversion().await;
359
360 for (i, r) in readings.iter_mut().enumerate() {
361 *r = self.conversion_result(i);
362 }
363 }
364}
365
366/// Peripheral instance trait.
367#[allow(private_bounds)]
368pub trait Instance: SealedInstance + PeripheralType {
369 type Interrupt: crate::interrupt::typelevel::Interrupt;
370}
371
372/// Peripheral state.
373pub(crate) struct State {
374 waker: AtomicWaker,
375}
376
377impl State {
378 pub const fn new() -> Self {
379 Self {
380 waker: AtomicWaker::new(),
381 }
382 }
383}
384
385/// Peripheral information.
386pub(crate) struct Info {
387 pub(crate) regs: Regs,
388 pub(crate) interrupt: Interrupt,
389}
390
391/// Peripheral instance trait.
392pub(crate) trait SealedInstance {
393 fn info() -> &'static Info;
394 fn state() -> &'static State;
395}
396
397macro_rules! impl_adc_instance {
398 ($instance: ident) => {
399 impl crate::adc::SealedInstance for crate::peripherals::$instance {
400 fn info() -> &'static crate::adc::Info {
401 use crate::adc::Info;
402 use crate::interrupt::typelevel::Interrupt;
403
404 static INFO: Info = Info {
405 regs: crate::pac::$instance,
406 interrupt: crate::interrupt::typelevel::$instance::IRQ,
407 };
408 &INFO
409 }
410
411 fn state() -> &'static crate::adc::State {
412 use crate::adc::State;
413
414 static STATE: State = State::new();
415 &STATE
416 }
417 }
418
419 impl crate::adc::Instance for crate::peripherals::$instance {
420 type Interrupt = crate::interrupt::typelevel::$instance;
421 }
422 };
423}
424
425/// A type-erased channel for a given ADC instance.
426///
427/// This is useful in scenarios where you need the ADC channels to have the same type, such as
428/// storing them in an array.
429pub struct AnyAdcChannel<T> {
430 channel: u8,
431 _phantom: PhantomData<T>,
432}
433
434impl_peripheral!(AnyAdcChannel<T: Instance>);
435impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {}
436impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> {
437 fn channel(&self) -> u8 {
438 self.channel
439 }
440}
441
442impl<T> AnyAdcChannel<T> {
443 #[allow(unused)]
444 pub(crate) fn get_hw_channel(&self) -> u8 {
445 self.channel
446 }
447}
448
449/// ADC channel.
450#[allow(private_bounds)]
451pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized {
452 /// Allows an ADC channel to be converted into a type-erased [`AnyAdcChannel`].
453 #[allow(unused_mut)]
454 fn degrade_adc(mut self) -> AnyAdcChannel<T> {
455 self.setup();
456
457 AnyAdcChannel {
458 channel: self.channel(),
459 _phantom: PhantomData,
460 }
461 }
462}
463
464pub(crate) trait SealedAdcChannel<T> {
465 fn setup(&mut self) {}
466
467 fn channel(&self) -> u8;
468}
469
470macro_rules! impl_adc_pin {
471 ($inst:ident, $pin:ident, $ch:expr) => {
472 impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {}
473 impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {
474 fn setup(&mut self) {
475 <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self);
476 }
477
478 fn channel(&self) -> u8 {
479 $ch
480 }
481 }
482 };
483}
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
index 0cb19a379..13f0ce662 100644
--- a/embassy-mspm0/src/lib.rs
+++ b/embassy-mspm0/src/lib.rs
@@ -13,6 +13,7 @@ pub(crate) mod fmt;
13// This must be declared early as well for 13// This must be declared early as well for
14mod macros; 14mod macros;
15 15
16pub mod adc;
16pub mod dma; 17pub mod dma;
17pub mod gpio; 18pub mod gpio;
18pub mod i2c; 19pub mod i2c;