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