diff options
| -rw-r--r-- | embassy-stm32/Cargo.toml | 8 | ||||
| -rw-r--r-- | embassy-stm32/build.rs | 2 | ||||
| -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/flash/f4.rs | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/h5.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/h50.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/mod.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/flash/u5.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/ospi/mod.rs | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/c0.rs | 3 | ||||
| -rw-r--r-- | examples/stm32c0/src/bin/adc.rs | 57 |
12 files changed, 562 insertions, 12 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 068c5e230..8204a0fea 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -72,8 +72,8 @@ futures-util = { version = "0.3.30", default-features = false } | |||
| 72 | rand_core = "0.6.3" | 72 | rand_core = "0.6.3" |
| 73 | sdio-host = "0.5.0" | 73 | sdio-host = "0.5.0" |
| 74 | critical-section = "1.1" | 74 | critical-section = "1.1" |
| 75 | stm32-metapac = { version = "16" } | 75 | #stm32-metapac = { version = "16" } |
| 76 | #stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-db71f6aa03b7db26548b461d3844fc404d40c98c" } | 76 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609" } |
| 77 | 77 | ||
| 78 | vcell = "0.1.3" | 78 | vcell = "0.1.3" |
| 79 | nb = "1.0.0" | 79 | nb = "1.0.0" |
| @@ -101,8 +101,8 @@ proptest-state-machine = "0.3.0" | |||
| 101 | proc-macro2 = "1.0.36" | 101 | proc-macro2 = "1.0.36" |
| 102 | quote = "1.0.15" | 102 | quote = "1.0.15" |
| 103 | 103 | ||
| 104 | stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} | 104 | #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} |
| 105 | #stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-db71f6aa03b7db26548b461d3844fc404d40c98c", default-features = false, features = ["metadata"] } | 105 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609", default-features = false, features = ["metadata"] } |
| 106 | 106 | ||
| 107 | [features] | 107 | [features] |
| 108 | default = ["rt"] | 108 | default = ["rt"] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index b35fd0300..eb0437bc2 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -296,6 +296,8 @@ fn main() { | |||
| 296 | "Bank1" | 296 | "Bank1" |
| 297 | } else if region.name.starts_with("BANK_2") { | 297 | } else if region.name.starts_with("BANK_2") { |
| 298 | "Bank2" | 298 | "Bank2" |
| 299 | } else if region.name == "OTP" { | ||
| 300 | "Otp" | ||
| 299 | } else { | 301 | } else { |
| 300 | continue; | 302 | continue; |
| 301 | } | 303 | } |
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/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index d818c77d0..86afdce8a 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs | |||
| @@ -15,7 +15,7 @@ mod alt_regions { | |||
| 15 | 15 | ||
| 16 | use embassy_hal_internal::PeripheralRef; | 16 | use embassy_hal_internal::PeripheralRef; |
| 17 | 17 | ||
| 18 | use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; | 18 | use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; |
| 19 | use crate::_generated::FLASH_SIZE; | 19 | use crate::_generated::FLASH_SIZE; |
| 20 | use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; | 20 | use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; |
| 21 | use crate::peripherals::FLASH; | 21 | use crate::peripherals::FLASH; |
| @@ -62,6 +62,7 @@ mod alt_regions { | |||
| 62 | pub bank2_region1: AltBank2Region1<'d, MODE>, | 62 | pub bank2_region1: AltBank2Region1<'d, MODE>, |
| 63 | pub bank2_region2: AltBank2Region2<'d, MODE>, | 63 | pub bank2_region2: AltBank2Region2<'d, MODE>, |
| 64 | pub bank2_region3: AltBank2Region3<'d, MODE>, | 64 | pub bank2_region3: AltBank2Region3<'d, MODE>, |
| 65 | pub otp_region: OTPRegion<'d, MODE>, | ||
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | impl<'d> Flash<'d> { | 68 | impl<'d> Flash<'d> { |
| @@ -78,6 +79,7 @@ mod alt_regions { | |||
| 78 | bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), | 79 | bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), |
| 79 | bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), | 80 | bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), |
| 80 | bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), | 81 | bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), |
| 82 | otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 81 | } | 83 | } |
| 82 | } | 84 | } |
| 83 | 85 | ||
| @@ -94,6 +96,7 @@ mod alt_regions { | |||
| 94 | bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), | 96 | bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), |
| 95 | bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), | 97 | bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), |
| 96 | bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), | 98 | bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), |
| 99 | otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 97 | } | 100 | } |
| 98 | } | 101 | } |
| 99 | } | 102 | } |
diff --git a/embassy-stm32/src/flash/h5.rs b/embassy-stm32/src/flash/h5.rs index 9e131ca2b..d95de2e38 100644 --- a/embassy-stm32/src/flash/h5.rs +++ b/embassy-stm32/src/flash/h5.rs | |||
| @@ -114,6 +114,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 114 | r.set_bksel(match sector.bank { | 114 | r.set_bksel(match sector.bank { |
| 115 | crate::flash::FlashBank::Bank1 => stm32_metapac::flash::vals::NscrBksel::B_0X0, | 115 | crate::flash::FlashBank::Bank1 => stm32_metapac::flash::vals::NscrBksel::B_0X0, |
| 116 | crate::flash::FlashBank::Bank2 => stm32_metapac::flash::vals::NscrBksel::B_0X1, | 116 | crate::flash::FlashBank::Bank2 => stm32_metapac::flash::vals::NscrBksel::B_0X1, |
| 117 | _ => unreachable!(), | ||
| 117 | }); | 118 | }); |
| 118 | r.set_snb(sector.index_in_bank); | 119 | r.set_snb(sector.index_in_bank); |
| 119 | r.set_ser(true); | 120 | r.set_ser(true); |
diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index 82e77d130..74cd6cc03 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs | |||
| @@ -55,6 +55,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 57 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 58 | assert!(sector.bank != FlashBank::Otp); | ||
| 58 | assert!(sector.index_in_bank < 8); | 59 | assert!(sector.index_in_bank < 8); |
| 59 | 60 | ||
| 60 | while busy() {} | 61 | while busy() {} |
| @@ -67,6 +68,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 67 | (FlashBank::Bank2, true) => Bksel::BANK1, | 68 | (FlashBank::Bank2, true) => Bksel::BANK1, |
| 68 | (FlashBank::Bank2, false) => Bksel::BANK2, | 69 | (FlashBank::Bank2, false) => Bksel::BANK2, |
| 69 | (FlashBank::Bank1, true) => Bksel::BANK2, | 70 | (FlashBank::Bank1, true) => Bksel::BANK2, |
| 71 | _ => unreachable!(), | ||
| 70 | }); | 72 | }); |
| 71 | w.set_snb(sector.index_in_bank); | 73 | w.set_snb(sector.index_in_bank); |
| 72 | w.set_ser(true); | 74 | w.set_ser(true); |
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index b564de093..c7488c8ef 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs | |||
| @@ -88,6 +88,8 @@ pub enum FlashBank { | |||
| 88 | Bank1 = 0, | 88 | Bank1 = 0, |
| 89 | /// Bank 2 | 89 | /// Bank 2 |
| 90 | Bank2 = 1, | 90 | Bank2 = 1, |
| 91 | /// OTP region, | ||
| 92 | Otp, | ||
| 91 | } | 93 | } |
| 92 | 94 | ||
| 93 | #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb), path = "l.rs")] | 95 | #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb), path = "l.rs")] |
diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index e5af4f1f7..dad698316 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs | |||
| @@ -75,6 +75,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 75 | w.set_bker(match sector.bank { | 75 | w.set_bker(match sector.bank { |
| 76 | FlashBank::Bank1 => pac::flash::vals::SeccrBker::B_0X0, | 76 | FlashBank::Bank1 => pac::flash::vals::SeccrBker::B_0X0, |
| 77 | FlashBank::Bank2 => pac::flash::vals::SeccrBker::B_0X1, | 77 | FlashBank::Bank2 => pac::flash::vals::SeccrBker::B_0X1, |
| 78 | _ => unreachable!(), | ||
| 78 | }); | 79 | }); |
| 79 | }); | 80 | }); |
| 80 | #[cfg(not(feature = "trustzone-secure"))] | 81 | #[cfg(not(feature = "trustzone-secure"))] |
| @@ -85,6 +86,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 85 | w.set_bker(match sector.bank { | 86 | w.set_bker(match sector.bank { |
| 86 | FlashBank::Bank1 => pac::flash::vals::NscrBker::B_0X0, | 87 | FlashBank::Bank1 => pac::flash::vals::NscrBker::B_0X0, |
| 87 | FlashBank::Bank2 => pac::flash::vals::NscrBker::B_0X1, | 88 | FlashBank::Bank2 => pac::flash::vals::NscrBker::B_0X1, |
| 89 | _ => unreachable!(), | ||
| 88 | }); | 90 | }); |
| 89 | }); | 91 | }); |
| 90 | 92 | ||
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index e35d51c91..5dff3c4c3 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs | |||
| @@ -372,7 +372,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 372 | }); | 372 | }); |
| 373 | 373 | ||
| 374 | T::REGS.cr().modify(|w| { | 374 | T::REGS.cr().modify(|w| { |
| 375 | w.set_fthres(vals::Threshold(config.fifo_threshold.into())); | 375 | w.set_fthres(vals::Threshold::from_bits(config.fifo_threshold.into())); |
| 376 | }); | 376 | }); |
| 377 | 377 | ||
| 378 | // Wait for busy flag to clear | 378 | // Wait for busy flag to clear |
| @@ -643,7 +643,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 643 | }); | 643 | }); |
| 644 | 644 | ||
| 645 | T::REGS.cr().modify(|w| { | 645 | T::REGS.cr().modify(|w| { |
| 646 | w.set_fthres(vals::Threshold(config.fifo_threshold.into())); | 646 | w.set_fthres(vals::Threshold::from_bits(config.fifo_threshold.into())); |
| 647 | }); | 647 | }); |
| 648 | 648 | ||
| 649 | // Wait for busy flag to clear | 649 | // Wait for busy flag to clear |
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 | } | ||
