diff options
| author | Timofei Korostelev <[email protected]> | 2025-02-17 21:40:35 -0800 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2025-03-20 01:58:44 +0100 |
| commit | 8c6fa83006a22c77740552161f816e294ec166b3 (patch) | |
| tree | e027d4b0c135933291e23e40bb17c0bce63ea4b7 | |
| parent | 9407ac67d3b7df7511bce7dbd73814e861851622 (diff) | |
Added ADC support for STM32C0.
| -rw-r--r-- | embassy-stm32/src/adc/c0.rs | 468 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/mod.rs | 20 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/c0.rs | 3 | ||||
| -rw-r--r-- | examples/stm32c0/src/bin/adc.rs | 57 |
4 files changed, 543 insertions, 5 deletions
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs new file mode 100644 index 000000000..84763ad4f --- /dev/null +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -0,0 +1,468 @@ | |||
| 1 | use pac::adc::vals::Scandir; | ||
| 2 | #[allow(unused)] | ||
| 3 | use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; | ||
| 4 | use pac::adccommon::vals::Presc; | ||
| 5 | |||
| 6 | use super::{ | ||
| 7 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | ||
| 8 | }; | ||
| 9 | use crate::dma::Transfer; | ||
| 10 | use crate::time::Hertz; | ||
| 11 | use crate::{pac, rcc, Peripheral}; | ||
| 12 | |||
| 13 | /// Default VREF voltage used for sample conversion to millivolts. | ||
| 14 | pub const VREF_DEFAULT_MV: u32 = 3300; | ||
| 15 | /// VREF voltage used for factory calibration of VREFINTCAL register. | ||
| 16 | pub const VREF_CALIB_MV: u32 = 3300; | ||
| 17 | |||
| 18 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25); | ||
| 19 | |||
| 20 | const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; | ||
| 21 | |||
| 22 | const TEMP_CHANNEL: u8 = 9; | ||
| 23 | const VREF_CHANNEL: u8 = 10; | ||
| 24 | |||
| 25 | const NUM_HW_CHANNELS: u8 = 22; | ||
| 26 | const CHSELR_SQ_SIZE: usize = 8; | ||
| 27 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; | ||
| 28 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; | ||
| 29 | |||
| 30 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, | ||
| 31 | // this currently cannot be modeled with stm32-data, | ||
| 32 | // so these are available from the software on all ADCs. | ||
| 33 | /// Internal voltage reference channel. | ||
| 34 | pub struct VrefInt; | ||
| 35 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 36 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 37 | fn channel(&self) -> u8 { | ||
| 38 | VREF_CHANNEL | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Internal temperature channel. | ||
| 43 | pub struct Temperature; | ||
| 44 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 45 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 46 | fn channel(&self) -> u8 { | ||
| 47 | TEMP_CHANNEL | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | #[derive(Debug)] | ||
| 52 | pub enum Prescaler { | ||
| 53 | NotDivided, | ||
| 54 | DividedBy2, | ||
| 55 | DividedBy4, | ||
| 56 | DividedBy6, | ||
| 57 | DividedBy8, | ||
| 58 | DividedBy10, | ||
| 59 | DividedBy12, | ||
| 60 | DividedBy16, | ||
| 61 | DividedBy32, | ||
| 62 | DividedBy64, | ||
| 63 | DividedBy128, | ||
| 64 | DividedBy256, | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Prescaler { | ||
| 68 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 69 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 70 | match raw_prescaler { | ||
| 71 | 0 => Self::NotDivided, | ||
| 72 | 1 => Self::DividedBy2, | ||
| 73 | 2..=3 => Self::DividedBy4, | ||
| 74 | 4..=5 => Self::DividedBy6, | ||
| 75 | 6..=7 => Self::DividedBy8, | ||
| 76 | 8..=9 => Self::DividedBy10, | ||
| 77 | 10..=11 => Self::DividedBy12, | ||
| 78 | _ => unimplemented!(), | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | #[allow(unused)] | ||
| 83 | fn divisor(&self) -> u32 { | ||
| 84 | match self { | ||
| 85 | Prescaler::NotDivided => 1, | ||
| 86 | Prescaler::DividedBy2 => 2, | ||
| 87 | Prescaler::DividedBy4 => 4, | ||
| 88 | Prescaler::DividedBy6 => 6, | ||
| 89 | Prescaler::DividedBy8 => 8, | ||
| 90 | Prescaler::DividedBy10 => 10, | ||
| 91 | Prescaler::DividedBy12 => 12, | ||
| 92 | Prescaler::DividedBy16 => 16, | ||
| 93 | Prescaler::DividedBy32 => 32, | ||
| 94 | Prescaler::DividedBy64 => 64, | ||
| 95 | Prescaler::DividedBy128 => 128, | ||
| 96 | Prescaler::DividedBy256 => 256, | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | fn presc(&self) -> Presc { | ||
| 101 | match self { | ||
| 102 | Prescaler::NotDivided => Presc::DIV1, | ||
| 103 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 104 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 105 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 106 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 107 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 108 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 109 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 110 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 111 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 112 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 113 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | #[cfg(feature = "defmt")] | ||
| 119 | impl<'a> defmt::Format for Prescaler { | ||
| 120 | fn format(&self, fmt: defmt::Formatter) { | ||
| 121 | match self { | ||
| 122 | Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), | ||
| 123 | Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), | ||
| 124 | Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), | ||
| 125 | Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), | ||
| 126 | Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), | ||
| 127 | Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), | ||
| 128 | Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), | ||
| 129 | Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), | ||
| 130 | Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), | ||
| 131 | Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), | ||
| 132 | Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), | ||
| 133 | Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Number of samples used for averaging. | ||
| 139 | /// TODO: Implement hardware averaging setting. | ||
| 140 | #[allow(unused)] | ||
| 141 | pub enum Averaging { | ||
| 142 | Disabled, | ||
| 143 | Samples2, | ||
| 144 | Samples4, | ||
| 145 | Samples8, | ||
| 146 | Samples16, | ||
| 147 | Samples32, | ||
| 148 | Samples64, | ||
| 149 | Samples128, | ||
| 150 | Samples256, | ||
| 151 | Samples512, | ||
| 152 | Samples1024, | ||
| 153 | } | ||
| 154 | |||
| 155 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 156 | /// Create a new ADC driver. | ||
| 157 | pub fn new(adc: impl Peripheral<P = T> + 'd, sample_time: SampleTime, resolution: Resolution) -> Self { | ||
| 158 | embassy_hal_internal::into_ref!(adc); | ||
| 159 | rcc::enable_and_reset::<T>(); | ||
| 160 | |||
| 161 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | ||
| 162 | |||
| 163 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | ||
| 164 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | ||
| 165 | |||
| 166 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | ||
| 167 | debug!("ADC frequency set to {} Hz", frequency.0); | ||
| 168 | |||
| 169 | if frequency > MAX_ADC_CLK_FREQ { | ||
| 170 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | ||
| 171 | } | ||
| 172 | |||
| 173 | let mut s = Self { | ||
| 174 | adc, | ||
| 175 | sample_time: SampleTime::from_bits(0), | ||
| 176 | }; | ||
| 177 | |||
| 178 | s.power_up(); | ||
| 179 | |||
| 180 | s.set_resolution(resolution); | ||
| 181 | |||
| 182 | s.calibrate(); | ||
| 183 | |||
| 184 | s.enable(); | ||
| 185 | |||
| 186 | s.configure_default(); | ||
| 187 | |||
| 188 | s.set_sample_time_all_channels(sample_time); | ||
| 189 | |||
| 190 | s | ||
| 191 | } | ||
| 192 | |||
| 193 | fn power_up(&mut self) { | ||
| 194 | T::regs().cr().modify(|reg| { | ||
| 195 | reg.set_advregen(true); | ||
| 196 | }); | ||
| 197 | |||
| 198 | // "The software must wait for the ADC voltage regulator startup time." | ||
| 199 | // See datasheet for the value. | ||
| 200 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | ||
| 201 | } | ||
| 202 | |||
| 203 | fn calibrate(&mut self) { | ||
| 204 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. | ||
| 205 | let autoff_value = T::regs().cfgr1().read().autoff(); | ||
| 206 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | ||
| 207 | |||
| 208 | T::regs().cr().modify(|w| w.set_adcal(true)); | ||
| 209 | |||
| 210 | // "ADCAL bit stays at 1 during all the calibration sequence." | ||
| 211 | // "It is then cleared by hardware as soon the calibration completes." | ||
| 212 | while T::regs().cr().read().adcal() {} | ||
| 213 | |||
| 214 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | ||
| 215 | |||
| 216 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | ||
| 217 | } | ||
| 218 | |||
| 219 | fn enable(&mut self) { | ||
| 220 | T::regs().isr().modify(|w| w.set_adrdy(true)); | ||
| 221 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 222 | // ADRDY is "ADC ready". Wait until it will be True. | ||
| 223 | while !T::regs().isr().read().adrdy() {} | ||
| 224 | } | ||
| 225 | |||
| 226 | fn configure_default(&mut self) { | ||
| 227 | // single conversion mode, software trigger | ||
| 228 | T::regs().cfgr1().modify(|w| { | ||
| 229 | w.set_cont(false); | ||
| 230 | w.set_exten(Exten::DISABLED); | ||
| 231 | w.set_align(Align::RIGHT); | ||
| 232 | }); | ||
| 233 | } | ||
| 234 | |||
| 235 | /// Enable reading the voltage reference internal channel. | ||
| 236 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 237 | T::common_regs().ccr().modify(|reg| { | ||
| 238 | reg.set_vrefen(true); | ||
| 239 | }); | ||
| 240 | |||
| 241 | VrefInt {} | ||
| 242 | } | ||
| 243 | |||
| 244 | /// Enable reading the temperature internal channel. | ||
| 245 | pub fn enable_temperature(&self) -> Temperature { | ||
| 246 | debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); | ||
| 247 | T::common_regs().ccr().modify(|reg| { | ||
| 248 | reg.set_tsen(true); | ||
| 249 | }); | ||
| 250 | |||
| 251 | Temperature {} | ||
| 252 | } | ||
| 253 | |||
| 254 | /// Set the ADC sample time. | ||
| 255 | /// Shall only be called when ADC is not converting. | ||
| 256 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { | ||
| 257 | self.sample_time = sample_time; | ||
| 258 | |||
| 259 | // Set all channels to use SMP1 field as source. | ||
| 260 | T::regs().smpr().modify(|w| { | ||
| 261 | w.smpsel(0); | ||
| 262 | w.set_smp1(sample_time); | ||
| 263 | }); | ||
| 264 | } | ||
| 265 | |||
| 266 | /// Set the ADC resolution. | ||
| 267 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 268 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 269 | } | ||
| 270 | |||
| 271 | /// Perform a single conversion. | ||
| 272 | fn convert(&mut self) -> u16 { | ||
| 273 | // Set single conversion mode. | ||
| 274 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 275 | |||
| 276 | // Start conversion | ||
| 277 | T::regs().cr().modify(|reg| { | ||
| 278 | reg.set_adstart(true); | ||
| 279 | }); | ||
| 280 | |||
| 281 | // Waiting for End Of Conversion (EOC). | ||
| 282 | while !T::regs().isr().read().eoc() {} | ||
| 283 | |||
| 284 | T::regs().dr().read().data() as u16 | ||
| 285 | } | ||
| 286 | |||
| 287 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 288 | Self::configure_channel(channel); | ||
| 289 | T::regs().cfgr1().write(|reg| { | ||
| 290 | reg.set_chselrmod(false); | ||
| 291 | reg.set_align(Align::RIGHT); | ||
| 292 | }); | ||
| 293 | self.convert() | ||
| 294 | } | ||
| 295 | |||
| 296 | fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator<Item = &'a mut AnyAdcChannel<T>>) { | ||
| 297 | assert!( | ||
| 298 | channel_sequence.len() <= CHSELR_SQ_SIZE, | ||
| 299 | "Seqenced read set cannot be more than {} in size.", | ||
| 300 | CHSELR_SQ_SIZE | ||
| 301 | ); | ||
| 302 | let mut last_sq_set: usize = 0; | ||
| 303 | T::regs().chselr_sq().write(|w| { | ||
| 304 | for (i, channel) in channel_sequence.enumerate() { | ||
| 305 | assert!( | ||
| 306 | channel.channel() <= CHSELR_SQ_MAX_CHANNEL, | ||
| 307 | "Sequencer only support HW channels smaller than {}.", | ||
| 308 | CHSELR_SQ_MAX_CHANNEL | ||
| 309 | ); | ||
| 310 | w.set_sq(i, channel.channel()); | ||
| 311 | last_sq_set = i; | ||
| 312 | } | ||
| 313 | |||
| 314 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 315 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 316 | } | ||
| 317 | }); | ||
| 318 | |||
| 319 | Self::apply_channel_conf() | ||
| 320 | } | ||
| 321 | |||
| 322 | async fn dma_convert(&mut self, rx_dma: &mut impl RxDma<T>, readings: &mut [u16]) { | ||
| 323 | // Enable overrun control, so no new DMA requests will be generated until | ||
| 324 | // previous DR values is read. | ||
| 325 | T::regs().isr().modify(|reg| { | ||
| 326 | reg.set_ovr(true); | ||
| 327 | }); | ||
| 328 | |||
| 329 | // Set continuous mode with oneshot dma. | ||
| 330 | T::regs().cfgr1().modify(|reg| { | ||
| 331 | reg.set_discen(false); | ||
| 332 | reg.set_cont(true); | ||
| 333 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); | ||
| 334 | reg.set_dmaen(true); | ||
| 335 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 336 | }); | ||
| 337 | |||
| 338 | let request = rx_dma.request(); | ||
| 339 | let transfer = unsafe { | ||
| 340 | Transfer::new_read( | ||
| 341 | rx_dma, | ||
| 342 | request, | ||
| 343 | T::regs().dr().as_ptr() as *mut u16, | ||
| 344 | readings, | ||
| 345 | Default::default(), | ||
| 346 | ) | ||
| 347 | }; | ||
| 348 | |||
| 349 | // Start conversion. | ||
| 350 | T::regs().cr().modify(|reg| { | ||
| 351 | reg.set_adstart(true); | ||
| 352 | }); | ||
| 353 | |||
| 354 | // Wait for conversion sequence to finish. | ||
| 355 | transfer.await; | ||
| 356 | |||
| 357 | // Ensure conversions are finished. | ||
| 358 | Self::cancel_conversions(); | ||
| 359 | |||
| 360 | // Reset configuration. | ||
| 361 | T::regs().cfgr1().modify(|reg| { | ||
| 362 | reg.set_cont(false); | ||
| 363 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 364 | reg.set_dmaen(false); | ||
| 365 | }); | ||
| 366 | } | ||
| 367 | |||
| 368 | /// Read one or multiple ADC channels using DMA in hardware order. | ||
| 369 | /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. | ||
| 370 | /// Readings won't be in the same order as in the `set`! | ||
| 371 | /// | ||
| 372 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 373 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 374 | /// TODO(chudsaviet): externalize generic code and merge with read(). | ||
| 375 | pub async fn read_in_hw_order( | ||
| 376 | &mut self, | ||
| 377 | rx_dma: &mut impl RxDma<T>, | ||
| 378 | hw_channel_selection: u32, | ||
| 379 | scandir: Scandir, | ||
| 380 | readings: &mut [u16], | ||
| 381 | ) { | ||
| 382 | assert!( | ||
| 383 | hw_channel_selection != 0, | ||
| 384 | "Some bits in `hw_channel_selection` shall be set." | ||
| 385 | ); | ||
| 386 | assert!( | ||
| 387 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 388 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 389 | NUM_HW_CHANNELS | ||
| 390 | ); | ||
| 391 | // To check for correct readings slice size, we shall solve Hamming weight problem, | ||
| 392 | // which is either slow or memory consuming. | ||
| 393 | // Since we have limited resources, we don't do it here. | ||
| 394 | // Not doing this have a great potential for a bug through. | ||
| 395 | |||
| 396 | // Ensure no conversions are ongoing. | ||
| 397 | Self::cancel_conversions(); | ||
| 398 | |||
| 399 | T::regs().cfgr1().modify(|reg| { | ||
| 400 | reg.set_chselrmod(false); | ||
| 401 | reg.set_scandir(scandir); | ||
| 402 | reg.set_align(Align::RIGHT); | ||
| 403 | }); | ||
| 404 | |||
| 405 | // Set required channels for multi-convert. | ||
| 406 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 407 | |||
| 408 | Self::apply_channel_conf(); | ||
| 409 | |||
| 410 | self.dma_convert(rx_dma, readings).await | ||
| 411 | } | ||
| 412 | |||
| 413 | // Read ADC channels in specified order using DMA (CHSELRMOD = 1). | ||
| 414 | // In STM32C0, only lower 14 ADC channels can be read this way. | ||
| 415 | // For other channels, use `read_in_hw_order()` or blocking read. | ||
| 416 | pub async fn read( | ||
| 417 | &mut self, | ||
| 418 | rx_dma: &mut impl RxDma<T>, | ||
| 419 | channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 420 | readings: &mut [u16], | ||
| 421 | ) { | ||
| 422 | assert!( | ||
| 423 | channel_sequence.len() != 0, | ||
| 424 | "Asynchronous read channel sequence cannot be empty." | ||
| 425 | ); | ||
| 426 | assert!( | ||
| 427 | channel_sequence.len() == readings.len(), | ||
| 428 | "Channel sequence length must be equal to readings length." | ||
| 429 | ); | ||
| 430 | |||
| 431 | // Ensure no conversions are ongoing. | ||
| 432 | Self::cancel_conversions(); | ||
| 433 | |||
| 434 | T::regs().cfgr1().modify(|reg| { | ||
| 435 | reg.set_chselrmod(true); | ||
| 436 | reg.set_align(Align::RIGHT); | ||
| 437 | }); | ||
| 438 | |||
| 439 | Self::setup_channel_sequencer(channel_sequence); | ||
| 440 | |||
| 441 | self.dma_convert(rx_dma, readings).await | ||
| 442 | } | ||
| 443 | |||
| 444 | fn configure_channel(channel: &mut impl AdcChannel<T>) { | ||
| 445 | channel.setup(); | ||
| 446 | // write() because we want all other bits to be set to 0. | ||
| 447 | T::regs() | ||
| 448 | .chselr() | ||
| 449 | .write(|w| w.set_chsel(channel.channel().into(), true)); | ||
| 450 | |||
| 451 | Self::apply_channel_conf(); | ||
| 452 | } | ||
| 453 | |||
| 454 | fn apply_channel_conf() { | ||
| 455 | // Trigger and wait for the channel selection procedure to complete. | ||
| 456 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 457 | while !T::regs().isr().read().ccrdy() {} | ||
| 458 | } | ||
| 459 | |||
| 460 | fn cancel_conversions() { | ||
| 461 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 462 | T::regs().cr().modify(|reg| { | ||
| 463 | reg.set_adstp(Adstp::STOP); | ||
| 464 | }); | ||
| 465 | while T::regs().cr().read().adstart() {} | ||
| 466 | } | ||
| 467 | } | ||
| 468 | } | ||
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 36898b8f9..31a08b6eb 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")] | 14 | #[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")] |
| 15 | #[cfg_attr(any(adc_v4, adc_u5), path = "v4.rs")] | 15 | #[cfg_attr(any(adc_v4, adc_u5), path = "v4.rs")] |
| 16 | #[cfg_attr(adc_g4, path = "g4.rs")] | 16 | #[cfg_attr(adc_g4, path = "g4.rs")] |
| 17 | #[cfg_attr(adc_c0, path = "c0.rs")] | ||
| 17 | mod _version; | 18 | mod _version; |
| 18 | 19 | ||
| 19 | use core::marker::PhantomData; | 20 | use core::marker::PhantomData; |
| @@ -71,7 +72,7 @@ trait SealedInstance { | |||
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | pub(crate) trait SealedAdcChannel<T> { | 74 | pub(crate) trait SealedAdcChannel<T> { |
| 74 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] | 75 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] |
| 75 | fn setup(&mut self) {} | 76 | fn setup(&mut self) {} |
| 76 | 77 | ||
| 77 | #[allow(unused)] | 78 | #[allow(unused)] |
| @@ -106,7 +107,8 @@ pub(crate) fn blocking_delay_us(us: u32) { | |||
| 106 | adc_g0, | 107 | adc_g0, |
| 107 | adc_u0, | 108 | adc_u0, |
| 108 | adc_h5, | 109 | adc_h5, |
| 109 | adc_u5 | 110 | adc_u5, |
| 111 | adc_c0 | ||
| 110 | )))] | 112 | )))] |
| 111 | #[allow(private_bounds)] | 113 | #[allow(private_bounds)] |
| 112 | pub trait Instance: SealedInstance + crate::Peripheral<P = Self> { | 114 | pub trait Instance: SealedInstance + crate::Peripheral<P = Self> { |
| @@ -126,7 +128,8 @@ pub trait Instance: SealedInstance + crate::Peripheral<P = Self> { | |||
| 126 | adc_g0, | 128 | adc_g0, |
| 127 | adc_u0, | 129 | adc_u0, |
| 128 | adc_h5, | 130 | adc_h5, |
| 129 | adc_u5 | 131 | adc_u5, |
| 132 | adc_c0 | ||
| 130 | ))] | 133 | ))] |
| 131 | #[allow(private_bounds)] | 134 | #[allow(private_bounds)] |
| 132 | pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral { | 135 | pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral { |
| @@ -164,6 +167,13 @@ impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | |||
| 164 | } | 167 | } |
| 165 | } | 168 | } |
| 166 | 169 | ||
| 170 | impl<T> AnyAdcChannel<T> { | ||
| 171 | #[allow(unused)] | ||
| 172 | pub fn get_hw_channel(&self) -> u8 { | ||
| 173 | self.channel | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 167 | #[cfg(adc_u5)] | 177 | #[cfg(adc_u5)] |
| 168 | foreach_adc!( | 178 | foreach_adc!( |
| 169 | (ADC4, $common_inst:ident, $clock:ident) => { | 179 | (ADC4, $common_inst:ident, $clock:ident) => { |
| @@ -225,7 +235,7 @@ macro_rules! impl_adc_pin { | |||
| 225 | ($inst:ident, $pin:ident, $ch:expr) => { | 235 | ($inst:ident, $pin:ident, $ch:expr) => { |
| 226 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::peripherals::$pin {} | 236 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::peripherals::$pin {} |
| 227 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::peripherals::$pin { | 237 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::peripherals::$pin { |
| 228 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] | 238 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] |
| 229 | fn setup(&mut self) { | 239 | fn setup(&mut self) { |
| 230 | <Self as crate::gpio::SealedPin>::set_as_analog(self); | 240 | <Self as crate::gpio::SealedPin>::set_as_analog(self); |
| 231 | } | 241 | } |
| @@ -254,7 +264,7 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { | |||
| 254 | Resolution::BITS12 => (1 << 12) - 1, | 264 | Resolution::BITS12 => (1 << 12) - 1, |
| 255 | Resolution::BITS10 => (1 << 10) - 1, | 265 | Resolution::BITS10 => (1 << 10) - 1, |
| 256 | Resolution::BITS8 => (1 << 8) - 1, | 266 | Resolution::BITS8 => (1 << 8) - 1, |
| 257 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] | 267 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] |
| 258 | Resolution::BITS6 => (1 << 6) - 1, | 268 | Resolution::BITS6 => (1 << 6) - 1, |
| 259 | #[allow(unreachable_patterns)] | 269 | #[allow(unreachable_patterns)] |
| 260 | _ => core::unreachable!(), | 270 | _ => core::unreachable!(), |
diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 977b2e7a2..04cbe83ed 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs | |||
| @@ -180,6 +180,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 180 | lsi: None, | 180 | lsi: None, |
| 181 | lse: None, | 181 | lse: None, |
| 182 | ); | 182 | ); |
| 183 | |||
| 184 | RCC.ccipr() | ||
| 185 | .modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); | ||
| 183 | } | 186 | } |
| 184 | 187 | ||
| 185 | mod max { | 188 | mod max { |
diff --git a/examples/stm32c0/src/bin/adc.rs b/examples/stm32c0/src/bin/adc.rs new file mode 100644 index 000000000..10481f4d2 --- /dev/null +++ b/examples/stm32c0/src/bin/adc.rs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::adc::vals::Scandir; | ||
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; | ||
| 8 | use embassy_stm32::peripherals::ADC1; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let config = Default::default(); | ||
| 15 | let p = embassy_stm32::init(config); | ||
| 16 | |||
| 17 | info!("ADC STM32C0 example."); | ||
| 18 | |||
| 19 | // We need to set certain sample time to be able to read temp sensor. | ||
| 20 | let mut adc = Adc::new(p.ADC1, SampleTime::CYCLES12_5, Resolution::BITS12); | ||
| 21 | let mut temp = adc.enable_temperature().degrade_adc(); | ||
| 22 | let mut vref = adc.enable_vrefint().degrade_adc(); | ||
| 23 | let mut pin0 = p.PA0.degrade_adc(); | ||
| 24 | |||
| 25 | let mut dma = p.DMA1_CH1; | ||
| 26 | let mut read_buffer: [u16; 3] = [0; 3]; | ||
| 27 | |||
| 28 | loop { | ||
| 29 | info!("============================"); | ||
| 30 | let blocking_temp = adc.blocking_read(&mut temp); | ||
| 31 | let blocking_vref = adc.blocking_read(&mut vref); | ||
| 32 | let blocing_pin0 = adc.blocking_read(&mut pin0); | ||
| 33 | info!( | ||
| 34 | "Blocking ADC read: vref = {}, temp = {}, pin0 = {}.", | ||
| 35 | blocking_vref, blocking_temp, blocing_pin0 | ||
| 36 | ); | ||
| 37 | |||
| 38 | let channels_seqence: [&mut AnyAdcChannel<ADC1>; 3] = [&mut vref, &mut temp, &mut pin0]; | ||
| 39 | adc.read(&mut dma, channels_seqence.into_iter(), &mut read_buffer).await; | ||
| 40 | // Values are ordered according to hardware ADC channel number! | ||
| 41 | info!( | ||
| 42 | "DMA ADC read in set: vref = {}, temp = {}, pin0 = {}.", | ||
| 43 | read_buffer[0], read_buffer[1], read_buffer[2] | ||
| 44 | ); | ||
| 45 | |||
| 46 | let hw_channel_selection: u32 = | ||
| 47 | (1 << temp.get_hw_channel()) + (1 << vref.get_hw_channel()) + (1 << pin0.get_hw_channel()); | ||
| 48 | adc.read_in_hw_order(&mut dma, hw_channel_selection, Scandir::UP, &mut read_buffer) | ||
| 49 | .await; | ||
| 50 | info!( | ||
| 51 | "DMA ADC read in hardware order: vref = {}, temp = {}, pin0 = {}.", | ||
| 52 | read_buffer[2], read_buffer[1], read_buffer[0] | ||
| 53 | ); | ||
| 54 | |||
| 55 | Timer::after_millis(2000).await; | ||
| 56 | } | ||
| 57 | } | ||
