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 } rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" -stm32-metapac = { version = "16" } -#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-db71f6aa03b7db26548b461d3844fc404d40c98c" } +#stm32-metapac = { version = "16" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609" } vcell = "0.1.3" nb = "1.0.0" @@ -101,8 +101,8 @@ proptest-state-machine = "0.3.0" proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-db71f6aa03b7db26548b461d3844fc404d40c98c", default-features = false, features = ["metadata"] } +#stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609", default-features = false, features = ["metadata"] } [features] 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() { "Bank1" } else if region.name.starts_with("BANK_2") { "Bank2" + } else if region.name == "OTP" { + "Otp" } else { continue; } 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 @@ +use pac::adc::vals::Scandir; +#[allow(unused)] +use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; +use pac::adccommon::vals::Presc; + +use super::{ + blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, +}; +use crate::dma::Transfer; +use crate::time::Hertz; +use crate::{pac, rcc, Peripheral}; + +/// Default VREF voltage used for sample conversion to millivolts. +pub const VREF_DEFAULT_MV: u32 = 3300; +/// VREF voltage used for factory calibration of VREFINTCAL register. +pub const VREF_CALIB_MV: u32 = 3300; + +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25); + +const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; + +const TEMP_CHANNEL: u8 = 9; +const VREF_CHANNEL: u8 = 10; + +const NUM_HW_CHANNELS: u8 = 22; +const CHSELR_SQ_SIZE: usize = 8; +const CHSELR_SQ_MAX_CHANNEL: u8 = 14; +const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; + +// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, +// this currently cannot be modeled with stm32-data, +// so these are available from the software on all ADCs. +/// Internal voltage reference channel. +pub struct VrefInt; +impl AdcChannel for VrefInt {} +impl SealedAdcChannel for VrefInt { + fn channel(&self) -> u8 { + VREF_CHANNEL + } +} + +/// Internal temperature channel. +pub struct Temperature; +impl AdcChannel for Temperature {} +impl SealedAdcChannel for Temperature { + fn channel(&self) -> u8 { + TEMP_CHANNEL + } +} + +#[derive(Debug)] +pub enum Prescaler { + NotDivided, + DividedBy2, + DividedBy4, + DividedBy6, + DividedBy8, + DividedBy10, + DividedBy12, + DividedBy16, + DividedBy32, + DividedBy64, + DividedBy128, + DividedBy256, +} + +impl Prescaler { + fn from_ker_ck(frequency: Hertz) -> Self { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Self::NotDivided, + 1 => Self::DividedBy2, + 2..=3 => Self::DividedBy4, + 4..=5 => Self::DividedBy6, + 6..=7 => Self::DividedBy8, + 8..=9 => Self::DividedBy10, + 10..=11 => Self::DividedBy12, + _ => unimplemented!(), + } + } + + #[allow(unused)] + fn divisor(&self) -> u32 { + match self { + Prescaler::NotDivided => 1, + Prescaler::DividedBy2 => 2, + Prescaler::DividedBy4 => 4, + Prescaler::DividedBy6 => 6, + Prescaler::DividedBy8 => 8, + Prescaler::DividedBy10 => 10, + Prescaler::DividedBy12 => 12, + Prescaler::DividedBy16 => 16, + Prescaler::DividedBy32 => 32, + Prescaler::DividedBy64 => 64, + Prescaler::DividedBy128 => 128, + Prescaler::DividedBy256 => 256, + } + } + + fn presc(&self) -> Presc { + match self { + Prescaler::NotDivided => Presc::DIV1, + Prescaler::DividedBy2 => Presc::DIV2, + Prescaler::DividedBy4 => Presc::DIV4, + Prescaler::DividedBy6 => Presc::DIV6, + Prescaler::DividedBy8 => Presc::DIV8, + Prescaler::DividedBy10 => Presc::DIV10, + Prescaler::DividedBy12 => Presc::DIV12, + Prescaler::DividedBy16 => Presc::DIV16, + Prescaler::DividedBy32 => Presc::DIV32, + Prescaler::DividedBy64 => Presc::DIV64, + Prescaler::DividedBy128 => Presc::DIV128, + Prescaler::DividedBy256 => Presc::DIV256, + } + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Prescaler { + fn format(&self, fmt: defmt::Formatter) { + match self { + Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), + Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), + Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), + Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), + Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), + Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), + Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), + Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), + Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), + Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), + Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), + Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), + } + } +} + +/// Number of samples used for averaging. +/// TODO: Implement hardware averaging setting. +#[allow(unused)] +pub enum Averaging { + Disabled, + Samples2, + Samples4, + Samples8, + Samples16, + Samples32, + Samples64, + Samples128, + Samples256, + Samples512, + Samples1024, +} + +impl<'d, T: Instance> Adc<'d, T> { + /// Create a new ADC driver. + pub fn new(adc: impl Peripheral

+ 'd, sample_time: SampleTime, resolution: Resolution) -> Self { + embassy_hal_internal::into_ref!(adc); + rcc::enable_and_reset::(); + + T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); + + let prescaler = Prescaler::from_ker_ck(T::frequency()); + T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + debug!("ADC frequency set to {} Hz", frequency.0); + + if frequency > MAX_ADC_CLK_FREQ { + 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 ); + } + + let mut s = Self { + adc, + sample_time: SampleTime::from_bits(0), + }; + + s.power_up(); + + s.set_resolution(resolution); + + s.calibrate(); + + s.enable(); + + s.configure_default(); + + s.set_sample_time_all_channels(sample_time); + + s + } + + fn power_up(&mut self) { + T::regs().cr().modify(|reg| { + reg.set_advregen(true); + }); + + // "The software must wait for the ADC voltage regulator startup time." + // See datasheet for the value. + blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); + } + + fn calibrate(&mut self) { + // We have to make sure AUTOFF is OFF, but keep its value after calibration. + let autoff_value = T::regs().cfgr1().read().autoff(); + T::regs().cfgr1().modify(|w| w.set_autoff(false)); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + // "ADCAL bit stays at 1 during all the calibration sequence." + // "It is then cleared by hardware as soon the calibration completes." + while T::regs().cr().read().adcal() {} + + debug!("ADC calibration value: {}.", T::regs().dr().read().data()); + + T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); + } + + fn enable(&mut self) { + T::regs().isr().modify(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + // ADRDY is "ADC ready". Wait until it will be True. + while !T::regs().isr().read().adrdy() {} + } + + fn configure_default(&mut self) { + // single conversion mode, software trigger + T::regs().cfgr1().modify(|w| { + w.set_cont(false); + w.set_exten(Exten::DISABLED); + w.set_align(Align::RIGHT); + }); + } + + /// Enable reading the voltage reference internal channel. + pub fn enable_vrefint(&self) -> VrefInt { + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); + + VrefInt {} + } + + /// Enable reading the temperature internal channel. + pub fn enable_temperature(&self) -> Temperature { + debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); + T::common_regs().ccr().modify(|reg| { + reg.set_tsen(true); + }); + + Temperature {} + } + + /// Set the ADC sample time. + /// Shall only be called when ADC is not converting. + pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + + // Set all channels to use SMP1 field as source. + T::regs().smpr().modify(|w| { + w.smpsel(0); + w.set_smp1(sample_time); + }); + } + + /// Set the ADC resolution. + pub fn set_resolution(&mut self, resolution: Resolution) { + T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); + } + + /// Perform a single conversion. + fn convert(&mut self) -> u16 { + // Set single conversion mode. + T::regs().cfgr1().modify(|w| w.set_cont(false)); + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + // Waiting for End Of Conversion (EOC). + while !T::regs().isr().read().eoc() {} + + T::regs().dr().read().data() as u16 + } + + pub fn blocking_read(&mut self, channel: &mut impl AdcChannel) -> u16 { + Self::configure_channel(channel); + T::regs().cfgr1().write(|reg| { + reg.set_chselrmod(false); + reg.set_align(Align::RIGHT); + }); + self.convert() + } + + fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator>) { + assert!( + channel_sequence.len() <= CHSELR_SQ_SIZE, + "Seqenced read set cannot be more than {} in size.", + CHSELR_SQ_SIZE + ); + let mut last_sq_set: usize = 0; + T::regs().chselr_sq().write(|w| { + for (i, channel) in channel_sequence.enumerate() { + assert!( + channel.channel() <= CHSELR_SQ_MAX_CHANNEL, + "Sequencer only support HW channels smaller than {}.", + CHSELR_SQ_MAX_CHANNEL + ); + w.set_sq(i, channel.channel()); + last_sq_set = i; + } + + for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { + w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); + } + }); + + Self::apply_channel_conf() + } + + async fn dma_convert(&mut self, rx_dma: &mut impl RxDma, readings: &mut [u16]) { + // Enable overrun control, so no new DMA requests will be generated until + // previous DR values is read. + T::regs().isr().modify(|reg| { + reg.set_ovr(true); + }); + + // Set continuous mode with oneshot dma. + T::regs().cfgr1().modify(|reg| { + reg.set_discen(false); + reg.set_cont(true); + reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); + reg.set_dmaen(true); + reg.set_ovrmod(Ovrmod::PRESERVE); + }); + + let request = rx_dma.request(); + let transfer = unsafe { + Transfer::new_read( + rx_dma, + request, + T::regs().dr().as_ptr() as *mut u16, + readings, + Default::default(), + ) + }; + + // Start conversion. + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + // Wait for conversion sequence to finish. + transfer.await; + + // Ensure conversions are finished. + Self::cancel_conversions(); + + // Reset configuration. + T::regs().cfgr1().modify(|reg| { + reg.set_cont(false); + reg.set_dmacfg(Dmacfg::from_bits(0)); + reg.set_dmaen(false); + }); + } + + /// Read one or multiple ADC channels using DMA in hardware order. + /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. + /// Readings won't be in the same order as in the `set`! + /// + /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use + /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). + /// TODO(chudsaviet): externalize generic code and merge with read(). + pub async fn read_in_hw_order( + &mut self, + rx_dma: &mut impl RxDma, + hw_channel_selection: u32, + scandir: Scandir, + readings: &mut [u16], + ) { + assert!( + hw_channel_selection != 0, + "Some bits in `hw_channel_selection` shall be set." + ); + assert!( + (hw_channel_selection >> NUM_HW_CHANNELS) == 0, + "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", + NUM_HW_CHANNELS + ); + // To check for correct readings slice size, we shall solve Hamming weight problem, + // which is either slow or memory consuming. + // Since we have limited resources, we don't do it here. + // Not doing this have a great potential for a bug through. + + // Ensure no conversions are ongoing. + Self::cancel_conversions(); + + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(false); + reg.set_scandir(scandir); + reg.set_align(Align::RIGHT); + }); + + // Set required channels for multi-convert. + unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } + + Self::apply_channel_conf(); + + self.dma_convert(rx_dma, readings).await + } + + // Read ADC channels in specified order using DMA (CHSELRMOD = 1). + // In STM32C0, only lower 14 ADC channels can be read this way. + // For other channels, use `read_in_hw_order()` or blocking read. + pub async fn read( + &mut self, + rx_dma: &mut impl RxDma, + channel_sequence: impl ExactSizeIterator>, + readings: &mut [u16], + ) { + assert!( + channel_sequence.len() != 0, + "Asynchronous read channel sequence cannot be empty." + ); + assert!( + channel_sequence.len() == readings.len(), + "Channel sequence length must be equal to readings length." + ); + + // Ensure no conversions are ongoing. + Self::cancel_conversions(); + + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(true); + reg.set_align(Align::RIGHT); + }); + + Self::setup_channel_sequencer(channel_sequence); + + self.dma_convert(rx_dma, readings).await + } + + fn configure_channel(channel: &mut impl AdcChannel) { + channel.setup(); + // write() because we want all other bits to be set to 0. + T::regs() + .chselr() + .write(|w| w.set_chsel(channel.channel().into(), true)); + + Self::apply_channel_conf(); + } + + fn apply_channel_conf() { + // Trigger and wait for the channel selection procedure to complete. + T::regs().isr().modify(|w| w.set_ccrdy(false)); + while !T::regs().isr().read().ccrdy() {} + } + + fn cancel_conversions() { + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(Adstp::STOP); + }); + while T::regs().cr().read().adstart() {} + } + } +} 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 @@ #[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")] #[cfg_attr(any(adc_v4, adc_u5), path = "v4.rs")] #[cfg_attr(adc_g4, path = "g4.rs")] +#[cfg_attr(adc_c0, path = "c0.rs")] mod _version; use core::marker::PhantomData; @@ -71,7 +72,7 @@ trait SealedInstance { } pub(crate) trait SealedAdcChannel { - #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] fn setup(&mut self) {} #[allow(unused)] @@ -106,7 +107,8 @@ pub(crate) fn blocking_delay_us(us: u32) { adc_g0, adc_u0, adc_h5, - adc_u5 + adc_u5, + adc_c0 )))] #[allow(private_bounds)] pub trait Instance: SealedInstance + crate::Peripheral

{ @@ -126,7 +128,8 @@ pub trait Instance: SealedInstance + crate::Peripheral

{ adc_g0, adc_u0, adc_h5, - adc_u5 + adc_u5, + adc_c0 ))] #[allow(private_bounds)] pub trait Instance: SealedInstance + crate::Peripheral

+ crate::rcc::RccPeripheral { @@ -164,6 +167,13 @@ impl SealedAdcChannel for AnyAdcChannel { } } +impl AnyAdcChannel { + #[allow(unused)] + pub fn get_hw_channel(&self) -> u8 { + self.channel + } +} + #[cfg(adc_u5)] foreach_adc!( (ADC4, $common_inst:ident, $clock:ident) => { @@ -225,7 +235,7 @@ macro_rules! impl_adc_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::adc::AdcChannel for crate::peripherals::$pin {} impl crate::adc::SealedAdcChannel for crate::peripherals::$pin { - #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] fn setup(&mut self) { ::set_as_analog(self); } @@ -254,7 +264,7 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { Resolution::BITS12 => (1 << 12) - 1, Resolution::BITS10 => (1 << 10) - 1, Resolution::BITS8 => (1 << 8) - 1, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] Resolution::BITS6 => (1 << 6) - 1, #[allow(unreachable_patterns)] _ => 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 { use embassy_hal_internal::PeripheralRef; - use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; + use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; use crate::_generated::FLASH_SIZE; use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; @@ -62,6 +62,7 @@ mod alt_regions { pub bank2_region1: AltBank2Region1<'d, MODE>, pub bank2_region2: AltBank2Region2<'d, MODE>, pub bank2_region3: AltBank2Region3<'d, MODE>, + pub otp_region: OTPRegion<'d, MODE>, } impl<'d> Flash<'d> { @@ -78,6 +79,7 @@ mod alt_regions { bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), } } @@ -94,6 +96,7 @@ mod alt_regions { bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), } } } 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 r.set_bksel(match sector.bank { crate::flash::FlashBank::Bank1 => stm32_metapac::flash::vals::NscrBksel::B_0X0, crate::flash::FlashBank::Bank2 => stm32_metapac::flash::vals::NscrBksel::B_0X1, + _ => unreachable!(), }); r.set_snb(sector.index_in_bank); 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]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + assert!(sector.bank != FlashBank::Otp); assert!(sector.index_in_bank < 8); while busy() {} @@ -67,6 +68,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E (FlashBank::Bank2, true) => Bksel::BANK1, (FlashBank::Bank2, false) => Bksel::BANK2, (FlashBank::Bank1, true) => Bksel::BANK2, + _ => unreachable!(), }); w.set_snb(sector.index_in_bank); 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 { Bank1 = 0, /// Bank 2 Bank2 = 1, + /// OTP region, + Otp, } #[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 w.set_bker(match sector.bank { FlashBank::Bank1 => pac::flash::vals::SeccrBker::B_0X0, FlashBank::Bank2 => pac::flash::vals::SeccrBker::B_0X1, + _ => unreachable!(), }); }); #[cfg(not(feature = "trustzone-secure"))] @@ -85,6 +86,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_bker(match sector.bank { FlashBank::Bank1 => pac::flash::vals::NscrBker::B_0X0, FlashBank::Bank2 => pac::flash::vals::NscrBker::B_0X1, + _ => unreachable!(), }); }); 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> { }); T::REGS.cr().modify(|w| { - w.set_fthres(vals::Threshold(config.fifo_threshold.into())); + w.set_fthres(vals::Threshold::from_bits(config.fifo_threshold.into())); }); // Wait for busy flag to clear @@ -643,7 +643,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { }); T::REGS.cr().modify(|w| { - w.set_fthres(vals::Threshold(config.fifo_threshold.into())); + w.set_fthres(vals::Threshold::from_bits(config.fifo_threshold.into())); }); // 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) { lsi: None, lse: None, ); + + RCC.ccipr() + .modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); } 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 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::vals::Scandir; +use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; +use embassy_stm32::peripherals::ADC1; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let config = Default::default(); + let p = embassy_stm32::init(config); + + info!("ADC STM32C0 example."); + + // We need to set certain sample time to be able to read temp sensor. + let mut adc = Adc::new(p.ADC1, SampleTime::CYCLES12_5, Resolution::BITS12); + let mut temp = adc.enable_temperature().degrade_adc(); + let mut vref = adc.enable_vrefint().degrade_adc(); + let mut pin0 = p.PA0.degrade_adc(); + + let mut dma = p.DMA1_CH1; + let mut read_buffer: [u16; 3] = [0; 3]; + + loop { + info!("============================"); + let blocking_temp = adc.blocking_read(&mut temp); + let blocking_vref = adc.blocking_read(&mut vref); + let blocing_pin0 = adc.blocking_read(&mut pin0); + info!( + "Blocking ADC read: vref = {}, temp = {}, pin0 = {}.", + blocking_vref, blocking_temp, blocing_pin0 + ); + + let channels_seqence: [&mut AnyAdcChannel; 3] = [&mut vref, &mut temp, &mut pin0]; + adc.read(&mut dma, channels_seqence.into_iter(), &mut read_buffer).await; + // Values are ordered according to hardware ADC channel number! + info!( + "DMA ADC read in set: vref = {}, temp = {}, pin0 = {}.", + read_buffer[0], read_buffer[1], read_buffer[2] + ); + + let hw_channel_selection: u32 = + (1 << temp.get_hw_channel()) + (1 << vref.get_hw_channel()) + (1 << pin0.get_hw_channel()); + adc.read_in_hw_order(&mut dma, hw_channel_selection, Scandir::UP, &mut read_buffer) + .await; + info!( + "DMA ADC read in hardware order: vref = {}, temp = {}, pin0 = {}.", + read_buffer[2], read_buffer[1], read_buffer[0] + ); + + Timer::after_millis(2000).await; + } +}