diff options
Diffstat (limited to 'embassy-mspm0')
| -rw-r--r-- | embassy-mspm0/build.rs | 59 | ||||
| -rw-r--r-- | embassy-mspm0/src/adc.rs | 483 | ||||
| -rw-r--r-- | embassy-mspm0/src/lib.rs | 1 |
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 | ||
| 224 | fn 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)] |
| 224 | struct Singleton { | 278 | struct 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 | |||
| 3 | use crate::interrupt; | ||
| 4 | use crate::interrupt::{Interrupt, InterruptExt}; | ||
| 5 | use crate::mode::{Async, Blocking, Mode}; | ||
| 6 | use core::future::poll_fn; | ||
| 7 | use core::marker::PhantomData; | ||
| 8 | use core::task::Poll; | ||
| 9 | |||
| 10 | use crate::pac::adc::{vals, Adc as Regs}; | ||
| 11 | use crate::Peri; | ||
| 12 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | ||
| 13 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 14 | |||
| 15 | /// Interrupt handler. | ||
| 16 | pub struct InterruptHandler<T: Instance> { | ||
| 17 | _phantom: PhantomData<T>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<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 | ||
| 33 | const ADC_VRSEL: u8 = crate::_generated::ADC_VRSEL; | ||
| 34 | const 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. | ||
| 39 | pub enum Resolution { | ||
| 40 | /// 12-bits resolution | ||
| 41 | BIT12, | ||
| 42 | |||
| 43 | /// 10-bits resolution | ||
| 44 | BIT10, | ||
| 45 | |||
| 46 | /// 8-bits resolution | ||
| 47 | BIT8, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl 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 | pub use crate::_generated::Vrsel; | ||
| 62 | |||
| 63 | /// ADC configuration. | ||
| 64 | pub 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. | ||
| 77 | pub 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 | |||
| 86 | impl<'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 | |||
| 101 | impl<'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 | |||
| 125 | impl<'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 | |||
| 246 | impl<'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)] | ||
| 368 | pub trait Instance: SealedInstance + PeripheralType { | ||
| 369 | type Interrupt: crate::interrupt::typelevel::Interrupt; | ||
| 370 | } | ||
| 371 | |||
| 372 | /// Peripheral state. | ||
| 373 | pub(crate) struct State { | ||
| 374 | waker: AtomicWaker, | ||
| 375 | } | ||
| 376 | |||
| 377 | impl State { | ||
| 378 | pub const fn new() -> Self { | ||
| 379 | Self { | ||
| 380 | waker: AtomicWaker::new(), | ||
| 381 | } | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | /// Peripheral information. | ||
| 386 | pub(crate) struct Info { | ||
| 387 | pub(crate) regs: Regs, | ||
| 388 | pub(crate) interrupt: Interrupt, | ||
| 389 | } | ||
| 390 | |||
| 391 | /// Peripheral instance trait. | ||
| 392 | pub(crate) trait SealedInstance { | ||
| 393 | fn info() -> &'static Info; | ||
| 394 | fn state() -> &'static State; | ||
| 395 | } | ||
| 396 | |||
| 397 | macro_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. | ||
| 429 | pub struct AnyAdcChannel<T> { | ||
| 430 | channel: u8, | ||
| 431 | _phantom: PhantomData<T>, | ||
| 432 | } | ||
| 433 | |||
| 434 | impl_peripheral!(AnyAdcChannel<T: Instance>); | ||
| 435 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} | ||
| 436 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | ||
| 437 | fn channel(&self) -> u8 { | ||
| 438 | self.channel | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | impl<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)] | ||
| 451 | pub 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 | |||
| 464 | pub(crate) trait SealedAdcChannel<T> { | ||
| 465 | fn setup(&mut self) {} | ||
| 466 | |||
| 467 | fn channel(&self) -> u8; | ||
| 468 | } | ||
| 469 | |||
| 470 | macro_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 |
| 14 | mod macros; | 14 | mod macros; |
| 15 | 15 | ||
| 16 | pub mod adc; | ||
| 16 | pub mod dma; | 17 | pub mod dma; |
| 17 | pub mod gpio; | 18 | pub mod gpio; |
| 18 | pub mod i2c; | 19 | pub mod i2c; |
