Merge pull request #3981 from embassy-rs/update-stm32-data-234
update stm32-data, add C0 adc, add OTP memory regions.
This commit is contained in:
		
						commit
						3e03d693a7
					
				| @ -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"] | ||||
|  | ||||
| @ -296,6 +296,8 @@ fn main() { | ||||
|                 "Bank1" | ||||
|             } else if region.name.starts_with("BANK_2") { | ||||
|                 "Bank2" | ||||
|             } else if region.name == "OTP" { | ||||
|                 "Otp" | ||||
|             } else { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
							
								
								
									
										468
									
								
								embassy-stm32/src/adc/c0.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										468
									
								
								embassy-stm32/src/adc/c0.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<T: Instance> AdcChannel<T> for VrefInt {} | ||||
| impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||||
|     fn channel(&self) -> u8 { | ||||
|         VREF_CHANNEL | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Internal temperature channel.
 | ||||
| pub struct Temperature; | ||||
| impl<T: Instance> AdcChannel<T> for Temperature {} | ||||
| impl<T: Instance> SealedAdcChannel<T> 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<P = T> + 'd, sample_time: SampleTime, resolution: Resolution) -> Self { | ||||
|         embassy_hal_internal::into_ref!(adc); | ||||
|         rcc::enable_and_reset::<T>(); | ||||
| 
 | ||||
|         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<T>) -> 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<Item = &'a mut AnyAdcChannel<T>>) { | ||||
|         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<T>, 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<T>, | ||||
|         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<T>, | ||||
|         channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||||
|         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<T>) { | ||||
|         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() {} | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<T> { | ||||
|     #[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<P = Self> { | ||||
| @ -126,7 +128,8 @@ pub trait Instance: SealedInstance + crate::Peripheral<P = Self> { | ||||
|     adc_g0, | ||||
|     adc_u0, | ||||
|     adc_h5, | ||||
|     adc_u5 | ||||
|     adc_u5, | ||||
|     adc_c0 | ||||
| ))] | ||||
| #[allow(private_bounds)] | ||||
| pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral { | ||||
| @ -164,6 +167,13 @@ impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T> AnyAdcChannel<T> { | ||||
|     #[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<peripherals::$inst> for crate::peripherals::$pin {} | ||||
|         impl crate::adc::SealedAdcChannel<peripherals::$inst> 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) { | ||||
|                 <Self as crate::gpio::SealedPin>::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!(), | ||||
|  | ||||
| @ -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), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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")] | ||||
|  | ||||
| @ -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!(), | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
							
								
								
									
										57
									
								
								examples/stm32c0/src/bin/adc.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								examples/stm32c0/src/bin/adc.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<ADC1>; 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; | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user