aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authori509VCB <[email protected]>2025-09-17 05:18:03 +0000
committerGitHub <[email protected]>2025-09-17 05:18:03 +0000
commitbbc93851fb9fb31342cc44ba096bed84134427f6 (patch)
tree08dc127a384184ffc77aea612dbf11a2617ba54c
parentab81b797c25996d8649971c1570c4bb949763c5b (diff)
parent31b5a3f0a4fafd425aef34b9d6fb93ead851b4c6 (diff)
Merge pull request #4646 from Iooon/mspm0-adc
MSPM0: add adc implementation
-rw-r--r--embassy-mspm0/CHANGELOG.md1
-rw-r--r--embassy-mspm0/build.rs22
-rw-r--r--embassy-mspm0/src/adc.rs510
-rw-r--r--embassy-mspm0/src/lib.rs1
-rw-r--r--examples/mspm0g3507/src/bin/adc.rs39
-rw-r--r--examples/mspm0l1306/src/bin/adc.rs39
6 files changed, 612 insertions, 0 deletions
diff --git a/embassy-mspm0/CHANGELOG.md b/embassy-mspm0/CHANGELOG.md
index c7da4eb33..b846f21b1 100644
--- a/embassy-mspm0/CHANGELOG.md
+++ b/embassy-mspm0/CHANGELOG.md
@@ -12,3 +12,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
12- fix gpio interrupt not being set for mspm0l110x 12- fix gpio interrupt not being set for mspm0l110x
13- feat: Add window watchdog implementation based on WWDT0, WWDT1 peripherals (#4574) 13- feat: Add window watchdog implementation based on WWDT0, WWDT1 peripherals (#4574)
14- feat: Add MSPM0C1105/C1106 support 14- feat: Add MSPM0C1105/C1106 support
15- feat: Add adc implementation (#4646)
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
index e8364e31a..d294bc422 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(&mut cfgs));
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,22 @@ 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(cfgs: &mut CfgSet) -> TokenStream {
225 let vrsel = METADATA.adc_vrsel;
226 let memctl = METADATA.adc_memctl;
227
228 cfgs.declare("adc_neg_vref");
229 match vrsel {
230 3 => (),
231 5 => cfgs.enable("adc_neg_vref"),
232 _ => panic!("Unsupported ADC VRSEL value: {vrsel}"),
233 }
234 quote! {
235 pub const ADC_VRSEL: u8 = #vrsel;
236 pub const ADC_MEMCTL: u8 = #memctl;
237 }
238}
239
223#[derive(Debug, Clone)] 240#[derive(Debug, Clone)]
224struct Singleton { 241struct Singleton {
225 name: String, 242 name: String,
@@ -561,6 +578,7 @@ fn generate_peripheral_instances() -> TokenStream {
561 "uart" => Some(quote! { impl_uart_instance!(#peri); }), 578 "uart" => Some(quote! { impl_uart_instance!(#peri); }),
562 "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), 579 "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }),
563 "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), 580 "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }),
581 "adc" => Some(quote! { impl_adc_instance!(#peri); }),
564 _ => None, 582 _ => None,
565 }; 583 };
566 584
@@ -609,6 +627,10 @@ fn generate_pin_trait_impls() -> TokenStream {
609 ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }), 627 ("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); }), 628 ("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); }), 629 ("i2c", "SCL") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }),
630 ("adc", s) => {
631 let signal = s.parse::<u8>().unwrap();
632 Some(quote! { impl_adc_pin!(#peri, #pin_name, #signal); })
633 }
612 634
613 _ => None, 635 _ => None,
614 }; 636 };
diff --git a/embassy-mspm0/src/adc.rs b/embassy-mspm0/src/adc.rs
new file mode 100644
index 000000000..5b93e9a6e
--- /dev/null
+++ b/embassy-mspm0/src/adc.rs
@@ -0,0 +1,510 @@
1#![macro_use]
2
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::task::Poll;
6
7use embassy_hal_internal::{impl_peripheral, PeripheralType};
8use embassy_sync::waitqueue::AtomicWaker;
9
10use crate::interrupt::{Interrupt, InterruptExt};
11use crate::mode::{Async, Blocking, Mode};
12use crate::pac::adc::{vals, Adc as Regs};
13use crate::{interrupt, Peri};
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
61#[derive(Clone, Copy, PartialEq, Eq, Debug)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63/// Reference voltage (Vref) selection for the ADC channels.
64pub enum Vrsel {
65 /// VDDA reference
66 VddaVssa = 0,
67
68 /// External reference from pin
69 ExtrefVrefm = 1,
70
71 /// Internal reference
72 IntrefVssa = 2,
73
74 /// VDDA and VREFM connected to VREF+ and VREF- of ADC
75 #[cfg(adc_neg_vref)]
76 VddaVrefm = 3,
77
78 /// INTREF and VREFM connected to VREF+ and VREF- of ADC
79 #[cfg(adc_neg_vref)]
80 IntrefVrefm = 4,
81}
82
83/// ADC configuration.
84#[derive(Copy, Clone)]
85#[non_exhaustive]
86pub struct Config {
87 /// Resolution of the ADC conversion. The number of bits used to represent an ADC measurement.
88 pub resolution: Resolution,
89 /// ADC voltage reference selection.
90 ///
91 /// This value is used when reading a single channel. When reading a sequence
92 /// the vr_select is provided per channel.
93 pub vr_select: Vrsel,
94 /// The sample time in number of ADC sample clock cycles.
95 pub sample_time: u16,
96}
97
98impl Default for Config {
99 fn default() -> Self {
100 Self {
101 resolution: Resolution::BIT12,
102 vr_select: Vrsel::VddaVssa,
103 sample_time: 50,
104 }
105 }
106}
107
108/// ADC (Analog to Digial Converter) Driver.
109pub struct Adc<'d, T: Instance, M: Mode> {
110 #[allow(unused)]
111 adc: crate::Peri<'d, T>,
112 info: &'static Info,
113 state: &'static State,
114 config: Config,
115 _phantom: PhantomData<M>,
116}
117
118impl<'d, T: Instance> Adc<'d, T, Blocking> {
119 /// A new blocking ADC driver instance.
120 pub fn new_blocking(peri: Peri<'d, T>, config: Config) -> Self {
121 let mut this = Self {
122 adc: peri,
123 info: T::info(),
124 state: T::state(),
125 config,
126 _phantom: PhantomData,
127 };
128 this.setup();
129 this
130 }
131}
132
133impl<'d, T: Instance> Adc<'d, T, Async> {
134 /// A new asynchronous ADC driver instance.
135 pub fn new_async(
136 peri: Peri<'d, T>,
137 config: Config,
138 _irqs: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
139 ) -> Self {
140 let mut this = Self {
141 adc: peri,
142 info: T::info(),
143 state: T::state(),
144 config,
145 _phantom: PhantomData,
146 };
147 this.setup();
148 unsafe {
149 this.info.interrupt.enable();
150 }
151 this
152 }
153}
154
155impl<'d, T: Instance, M: Mode> Adc<'d, T, M> {
156 const SINGLE_CHANNEL: u8 = 0;
157
158 fn setup(&mut self) {
159 let config = &self.config;
160 assert!(
161 (config.vr_select as u8) < ADC_VRSEL,
162 "Reference voltage selection out of bounds"
163 );
164 // Reset adc
165 self.info.regs.gprcm(0).rstctl().write(|reg| {
166 reg.set_resetstkyclr(true);
167 reg.set_resetassert(true);
168 reg.set_key(vals::ResetKey::KEY);
169 });
170
171 // Power up adc
172 self.info.regs.gprcm(0).pwren().modify(|reg| {
173 reg.set_enable(true);
174 reg.set_key(vals::PwrenKey::KEY);
175 });
176
177 // Wait for cycles similar to TI power setup
178 cortex_m::asm::delay(16);
179
180 // Set clock config
181 self.info.regs.gprcm(0).clkcfg().modify(|reg| {
182 reg.set_key(vals::ClkcfgKey::KEY);
183 reg.set_sampclk(vals::Sampclk::SYSOSC);
184 });
185 self.info.regs.ctl0().modify(|reg| {
186 reg.set_sclkdiv(vals::Sclkdiv::DIV_BY_4);
187 });
188 self.info.regs.clkfreq().modify(|reg| {
189 reg.set_frange(vals::Frange::RANGE24TO32);
190 });
191
192 // Init single conversion with software trigger and auto sampling
193 //
194 // We use sequence to support sequence operation in the future, but only set up a single
195 // channel
196 self.info.regs.ctl1().modify(|reg| {
197 reg.set_conseq(vals::Conseq::SEQUENCE);
198 reg.set_sampmode(vals::Sampmode::AUTO);
199 reg.set_trigsrc(vals::Trigsrc::SOFTWARE);
200 });
201 let res = match config.resolution {
202 Resolution::BIT12 => vals::Res::BIT_12,
203 Resolution::BIT10 => vals::Res::BIT_10,
204 Resolution::BIT8 => vals::Res::BIT_8,
205 };
206 self.info.regs.ctl2().modify(|reg| {
207 // Startadd detemines the channel used in single mode.
208 reg.set_startadd(Self::SINGLE_CHANNEL);
209 reg.set_endadd(Self::SINGLE_CHANNEL);
210 reg.set_res(res);
211 reg.set_df(false);
212 });
213
214 // Set the sample time used by all channels for now
215 self.info.regs.scomp0().modify(|reg| {
216 reg.set_val(config.sample_time);
217 });
218 }
219
220 fn setup_blocking_channel(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) {
221 channel.setup();
222
223 // CTL0.ENC must be 0 to write the MEMCTL register
224 while self.info.regs.ctl0().read().enc() {
225 // Wait until the ADC is not in conversion mode
226 }
227
228 // Conversion mem config
229 let vrsel = vals::Vrsel::from_bits(self.config.vr_select as u8);
230 self.info.regs.memctl(Self::SINGLE_CHANNEL as usize).modify(|reg| {
231 reg.set_chansel(channel.channel());
232 reg.set_vrsel(vrsel);
233 reg.set_stime(vals::Stime::SEL_SCOMP0);
234 reg.set_avgen(false);
235 reg.set_bcsen(false);
236 reg.set_trig(vals::Trig::AUTO_NEXT);
237 reg.set_wincomp(false);
238 });
239 self.info.regs.ctl2().modify(|reg| {
240 // Set end address to the number of used channels
241 reg.set_endadd(Self::SINGLE_CHANNEL);
242 });
243 }
244
245 fn enable_conversion(&mut self) {
246 // Enable conversion
247 self.info.regs.ctl0().modify(|reg| {
248 reg.set_enc(true);
249 });
250 }
251
252 fn start_conversion(&mut self) {
253 // Start conversion
254 self.info.regs.ctl1().modify(|reg| {
255 reg.set_sc(vals::Sc::START);
256 });
257 }
258
259 fn conversion_result(&mut self, channel_id: usize) -> u16 {
260 // Read result
261 self.info.regs.memres(channel_id).read().data()
262 }
263
264 /// Read one ADC channel in blocking mode using the config provided at initialization.
265 pub fn blocking_read(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) -> u16 {
266 self.setup_blocking_channel(channel);
267 self.enable_conversion();
268 self.start_conversion();
269
270 while self.info.regs.ctl0().read().enc() {}
271
272 self.conversion_result(Self::SINGLE_CHANNEL as usize)
273 }
274}
275
276impl<'d, T: Instance> Adc<'d, T, Async> {
277 /// Maximum length allowed for [`Self::read_sequence`].
278 pub const MAX_SEQUENCE_LEN: usize = ADC_MEMCTL as usize;
279
280 async fn wait_for_conversion(&self) {
281 let info = self.info;
282 let state = self.state;
283 poll_fn(move |cx| {
284 state.waker.register(cx.waker());
285
286 if !info.regs.ctl0().read().enc() {
287 Poll::Ready(())
288 } else {
289 Poll::Pending
290 }
291 })
292 .await;
293 }
294
295 fn setup_async_channel(&self, id: usize, channel: &Peri<'d, impl AdcChannel<T>>, vrsel: Vrsel) {
296 let vrsel = vals::Vrsel::from_bits(vrsel as u8);
297 // Conversion mem config
298 self.info.regs.memctl(id).modify(|reg| {
299 reg.set_chansel(channel.channel());
300 reg.set_vrsel(vrsel);
301 reg.set_stime(vals::Stime::SEL_SCOMP0);
302 reg.set_avgen(false);
303 reg.set_bcsen(false);
304 reg.set_trig(vals::Trig::AUTO_NEXT);
305 reg.set_wincomp(false);
306 });
307
308 // Clear interrupt status
309 self.info.regs.cpu_int(0).iclr().write(|reg| {
310 reg.set_memresifg(id, true);
311 });
312 // Enable interrupt
313 self.info.regs.cpu_int(0).imask().modify(|reg| {
314 reg.set_memresifg(id, true);
315 });
316 }
317
318 /// Read one ADC channel asynchronously using the config provided at initialization.
319 pub async fn read_channel(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) -> u16 {
320 channel.setup();
321
322 // CTL0.ENC must be 0 to write the MEMCTL register
323 self.wait_for_conversion().await;
324
325 self.info.regs.ctl2().modify(|reg| {
326 // Set end address to the number of used channels
327 reg.set_endadd(Self::SINGLE_CHANNEL);
328 });
329
330 self.setup_async_channel(Self::SINGLE_CHANNEL as usize, channel, self.config.vr_select);
331
332 self.enable_conversion();
333 self.start_conversion();
334 self.wait_for_conversion().await;
335
336 self.conversion_result(Self::SINGLE_CHANNEL as usize)
337 }
338
339 /// Read one or multiple ADC channels using the Vrsel provided per channel.
340 ///
341 /// `sequence` iterator and `readings` must have the same length.
342 ///
343 /// Example
344 /// ```rust,ignore
345 /// use embassy_mspm0::adc::{Adc, AdcChannel, Vrsel};
346 ///
347 /// let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs);
348 /// let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA20.into(), Vrsel::VddaVssa)];
349 /// let mut readings = [0u16; 2];
350 ///
351 /// adc.read_sequence(sequence.into_iter(), &mut readings).await;
352 /// defmt::info!("Measurements: {}", readings);
353 /// ```
354 pub async fn read_sequence<'a>(
355 &mut self,
356 sequence: impl ExactSizeIterator<Item = (&'a Peri<'d, AnyAdcChannel<T>>, Vrsel)>,
357 readings: &mut [u16],
358 ) where
359 'd: 'a,
360 {
361 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
362 assert!(
363 sequence.len() == readings.len(),
364 "Sequence length must be equal to readings length"
365 );
366 assert!(
367 sequence.len() <= Self::MAX_SEQUENCE_LEN,
368 "Asynchronous read sequence cannot be more than {} in length",
369 Self::MAX_SEQUENCE_LEN
370 );
371
372 // CTL0.ENC must be 0 to write the MEMCTL register
373 self.wait_for_conversion().await;
374
375 self.info.regs.ctl2().modify(|reg| {
376 // Set end address to the number of used channels
377 reg.set_endadd((sequence.len() - 1) as u8);
378 });
379
380 for (i, (channel, vrsel)) in sequence.enumerate() {
381 self.setup_async_channel(i, channel, vrsel);
382 }
383 self.enable_conversion();
384 self.start_conversion();
385 self.wait_for_conversion().await;
386
387 for (i, r) in readings.iter_mut().enumerate() {
388 *r = self.conversion_result(i);
389 }
390 }
391}
392
393/// Peripheral instance trait.
394#[allow(private_bounds)]
395pub trait Instance: PeripheralType + SealedInstance {
396 type Interrupt: crate::interrupt::typelevel::Interrupt;
397}
398
399/// Peripheral state.
400pub(crate) struct State {
401 waker: AtomicWaker,
402}
403
404impl State {
405 pub const fn new() -> Self {
406 Self {
407 waker: AtomicWaker::new(),
408 }
409 }
410}
411
412/// Peripheral information.
413pub(crate) struct Info {
414 pub(crate) regs: Regs,
415 pub(crate) interrupt: Interrupt,
416}
417
418/// Peripheral instance trait.
419pub(crate) trait SealedInstance {
420 fn info() -> &'static Info;
421 fn state() -> &'static State;
422}
423
424macro_rules! impl_adc_instance {
425 ($instance: ident) => {
426 impl crate::adc::SealedInstance for crate::peripherals::$instance {
427 fn info() -> &'static crate::adc::Info {
428 use crate::adc::Info;
429 use crate::interrupt::typelevel::Interrupt;
430
431 static INFO: Info = Info {
432 regs: crate::pac::$instance,
433 interrupt: crate::interrupt::typelevel::$instance::IRQ,
434 };
435 &INFO
436 }
437
438 fn state() -> &'static crate::adc::State {
439 use crate::adc::State;
440
441 static STATE: State = State::new();
442 &STATE
443 }
444 }
445
446 impl crate::adc::Instance for crate::peripherals::$instance {
447 type Interrupt = crate::interrupt::typelevel::$instance;
448 }
449 };
450}
451
452/// A type-erased channel for a given ADC instance.
453///
454/// This is useful in scenarios where you need the ADC channels to have the same type, such as
455/// storing them in an array.
456pub struct AnyAdcChannel<T> {
457 pub(crate) channel: u8,
458 pub(crate) _phantom: PhantomData<T>,
459}
460
461impl_peripheral!(AnyAdcChannel<T: Instance>);
462impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {}
463impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> {
464 fn channel(&self) -> u8 {
465 self.channel
466 }
467}
468
469impl<T> AnyAdcChannel<T> {
470 #[allow(unused)]
471 pub(crate) fn get_hw_channel(&self) -> u8 {
472 self.channel
473 }
474}
475
476/// ADC channel.
477#[allow(private_bounds)]
478pub trait AdcChannel<T>: PeripheralType + Into<AnyAdcChannel<T>> + SealedAdcChannel<T> + Sized {}
479
480pub(crate) trait SealedAdcChannel<T> {
481 fn setup(&self) {}
482
483 fn channel(&self) -> u8;
484}
485
486macro_rules! impl_adc_pin {
487 ($inst: ident, $pin: ident, $ch: expr) => {
488 impl crate::adc::AdcChannel<peripherals::$inst> for crate::peripherals::$pin {}
489 impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::peripherals::$pin {
490 fn setup(&self) {
491 crate::gpio::SealedPin::set_as_analog(self);
492 }
493
494 fn channel(&self) -> u8 {
495 $ch
496 }
497 }
498
499 impl From<crate::peripherals::$pin> for crate::adc::AnyAdcChannel<peripherals::$inst> {
500 fn from(val: crate::peripherals::$pin) -> Self {
501 crate::adc::SealedAdcChannel::<peripherals::$inst>::setup(&val);
502
503 Self {
504 channel: crate::adc::SealedAdcChannel::<peripherals::$inst>::channel(&val),
505 _phantom: core::marker::PhantomData,
506 }
507 }
508 }
509 };
510}
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;
diff --git a/examples/mspm0g3507/src/bin/adc.rs b/examples/mspm0g3507/src/bin/adc.rs
new file mode 100644
index 000000000..ceccc7c02
--- /dev/null
+++ b/examples/mspm0g3507/src/bin/adc.rs
@@ -0,0 +1,39 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_mspm0::adc::{self, Adc, Vrsel};
7use embassy_mspm0::{bind_interrupts, peripherals, Config};
8use embassy_time::Timer;
9use {defmt_rtt as _, panic_halt as _};
10
11bind_interrupts!(struct Irqs {
12 ADC0 => adc::InterruptHandler<peripherals::ADC0>;
13});
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) -> ! {
17 info!("Hello world!");
18 let p = embassy_mspm0::init(Config::default());
19
20 // Configure adc with sequence 0 to 1
21 let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs);
22 let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PB20.into(), Vrsel::VddaVssa)];
23 let mut readings = [0u16; 2];
24
25 loop {
26 let r = adc.read_channel(&p.PA27).await;
27 info!("Raw adc PA27: {}", r);
28 // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be
29 // approximated to voltage (~0.0008 per step).
30 let mut x = r as u32;
31 x = x * 8;
32 info!("Adc voltage PA27: {},{:#04}", x / 10_000, x % 10_000);
33 // Read a sequence of channels
34 adc.read_sequence(sequence.into_iter(), &mut readings).await;
35 info!("Raw adc sequence: {}", readings);
36
37 Timer::after_millis(400).await;
38 }
39}
diff --git a/examples/mspm0l1306/src/bin/adc.rs b/examples/mspm0l1306/src/bin/adc.rs
new file mode 100644
index 000000000..2806b98cc
--- /dev/null
+++ b/examples/mspm0l1306/src/bin/adc.rs
@@ -0,0 +1,39 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_mspm0::adc::{self, Adc, Vrsel};
7use embassy_mspm0::{bind_interrupts, peripherals, Config};
8use embassy_time::Timer;
9use {defmt_rtt as _, panic_halt as _};
10
11bind_interrupts!(struct Irqs {
12 ADC0 => adc::InterruptHandler<peripherals::ADC0>;
13});
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) -> ! {
17 info!("Hello world!");
18 let p = embassy_mspm0::init(Config::default());
19
20 // Configure adc with sequence 0 to 1
21 let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs);
22 let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA20.into(), Vrsel::VddaVssa)];
23 let mut readings = [0u16; 2];
24
25 loop {
26 let r = adc.read_channel(&p.PA27).await;
27 info!("Raw adc PA27: {}", r);
28 // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be
29 // approximated to voltage (~0.0008 per step).
30 let mut x = r as u32;
31 x = x * 8;
32 info!("Adc voltage PA27: {},{:#04}", x / 10_000, x % 10_000);
33 // Read a sequence of channels
34 adc.read_sequence(sequence.into_iter(), &mut readings).await;
35 info!("Raw adc sequence: {}", readings);
36
37 Timer::after_millis(400).await;
38 }
39}