diff options
| author | xoviat <[email protected]> | 2025-11-24 21:28:58 -0600 |
|---|---|---|
| committer | xoviat <[email protected]> | 2025-11-24 21:28:58 -0600 |
| commit | ee14305d152273254571d2a5a8192fad26f3ab73 (patch) | |
| tree | 80ee08b08ddb82fe712da50b0a0859d4ded6b9b9 /embassy-stm32/src | |
| parent | 461681028681930e50f41ee00154ac3e1886ebca (diff) | |
| parent | 5ffb3698541674d57fddb22044ac0f06397c6113 (diff) | |
Merge branch 'main' of https://github.com/embassy-rs/embassy into xspi
Diffstat (limited to 'embassy-stm32/src')
120 files changed, 8490 insertions, 4246 deletions
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 255dc7956..453513309 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs | |||
| @@ -4,8 +4,8 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR | |||
| 4 | #[cfg(stm32wba)] | 4 | #[cfg(stm32wba)] |
| 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; | 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; |
| 6 | 6 | ||
| 7 | use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; | 7 | use super::blocking_delay_us; |
| 8 | use crate::dma::Transfer; | 8 | use crate::adc::ConversionMode; |
| 9 | #[cfg(stm32u5)] | 9 | #[cfg(stm32u5)] |
| 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; | 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; |
| 11 | #[cfg(stm32wba)] | 11 | #[cfg(stm32wba)] |
| @@ -15,7 +15,7 @@ pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4S | |||
| 15 | #[cfg(stm32wba)] | 15 | #[cfg(stm32wba)] |
| 16 | pub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime}; | 16 | pub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime}; |
| 17 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 18 | use crate::{pac, rcc, Peri}; | 18 | use crate::{Peri, pac, rcc}; |
| 19 | 19 | ||
| 20 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | 20 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); |
| 21 | 21 | ||
| @@ -24,56 +24,24 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 25 | pub const VREF_CALIB_MV: u32 = 3300; | 25 | pub const VREF_CALIB_MV: u32 = 3300; |
| 26 | 26 | ||
| 27 | const VREF_CHANNEL: u8 = 0; | 27 | impl<'d, T: Instance> super::SealedSpecialConverter<super::VrefInt> for Adc4<'d, T> { |
| 28 | const VCORE_CHANNEL: u8 = 12; | 28 | const CHANNEL: u8 = 0; |
| 29 | const TEMP_CHANNEL: u8 = 13; | ||
| 30 | const VBAT_CHANNEL: u8 = 14; | ||
| 31 | const DAC_CHANNEL: u8 = 21; | ||
| 32 | |||
| 33 | // 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 | ||
| 34 | /// Internal voltage reference channel. | ||
| 35 | pub struct VrefInt; | ||
| 36 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 37 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 38 | fn channel(&self) -> u8 { | ||
| 39 | VREF_CHANNEL | ||
| 40 | } | ||
| 41 | } | 29 | } |
| 42 | 30 | ||
| 43 | /// Internal temperature channel. | 31 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Temperature> for Adc4<'d, T> { |
| 44 | pub struct Temperature; | 32 | const CHANNEL: u8 = 13; |
| 45 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 46 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 47 | fn channel(&self) -> u8 { | ||
| 48 | TEMP_CHANNEL | ||
| 49 | } | ||
| 50 | } | 33 | } |
| 51 | 34 | ||
| 52 | /// Internal battery voltage channel. | 35 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Vcore> for Adc4<'d, T> { |
| 53 | pub struct Vbat; | 36 | const CHANNEL: u8 = 12; |
| 54 | impl<T: Instance> AdcChannel<T> for Vbat {} | ||
| 55 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | ||
| 56 | fn channel(&self) -> u8 { | ||
| 57 | VBAT_CHANNEL | ||
| 58 | } | ||
| 59 | } | 37 | } |
| 60 | 38 | ||
| 61 | /// Internal DAC channel. | 39 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Vbat> for Adc4<'d, T> { |
| 62 | pub struct Dac; | 40 | const CHANNEL: u8 = 14; |
| 63 | impl<T: Instance> AdcChannel<T> for Dac {} | ||
| 64 | impl<T: Instance> SealedAdcChannel<T> for Dac { | ||
| 65 | fn channel(&self) -> u8 { | ||
| 66 | DAC_CHANNEL | ||
| 67 | } | ||
| 68 | } | 41 | } |
| 69 | 42 | ||
| 70 | /// Internal Vcore channel. | 43 | impl<'d, T: Instance> super::SealedSpecialConverter<super::Dac> for Adc4<'d, T> { |
| 71 | pub struct Vcore; | 44 | const CHANNEL: u8 = 21; |
| 72 | impl<T: Instance> AdcChannel<T> for Vcore {} | ||
| 73 | impl<T: Instance> SealedAdcChannel<T> for Vcore { | ||
| 74 | fn channel(&self) -> u8 { | ||
| 75 | VCORE_CHANNEL | ||
| 76 | } | ||
| 77 | } | 45 | } |
| 78 | 46 | ||
| 79 | #[derive(Copy, Clone)] | 47 | #[derive(Copy, Clone)] |
| @@ -108,71 +76,17 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { | |||
| 108 | } | 76 | } |
| 109 | } | 77 | } |
| 110 | 78 | ||
| 111 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 79 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 112 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 80 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 113 | #[allow(unused)] | 81 | match raw_prescaler { |
| 114 | enum Prescaler { | 82 | 0 => Presc::DIV1, |
| 115 | NotDivided, | 83 | 1 => Presc::DIV2, |
| 116 | DividedBy2, | 84 | 2..=3 => Presc::DIV4, |
| 117 | DividedBy4, | 85 | 4..=5 => Presc::DIV6, |
| 118 | DividedBy6, | 86 | 6..=7 => Presc::DIV8, |
| 119 | DividedBy8, | 87 | 8..=9 => Presc::DIV10, |
| 120 | DividedBy10, | 88 | 10..=11 => Presc::DIV12, |
| 121 | DividedBy12, | 89 | _ => unimplemented!(), |
| 122 | DividedBy16, | ||
| 123 | DividedBy32, | ||
| 124 | DividedBy64, | ||
| 125 | DividedBy128, | ||
| 126 | DividedBy256, | ||
| 127 | } | ||
| 128 | |||
| 129 | impl Prescaler { | ||
| 130 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 131 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 132 | match raw_prescaler { | ||
| 133 | 0 => Self::NotDivided, | ||
| 134 | 1 => Self::DividedBy2, | ||
| 135 | 2..=3 => Self::DividedBy4, | ||
| 136 | 4..=5 => Self::DividedBy6, | ||
| 137 | 6..=7 => Self::DividedBy8, | ||
| 138 | 8..=9 => Self::DividedBy10, | ||
| 139 | 10..=11 => Self::DividedBy12, | ||
| 140 | _ => unimplemented!(), | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | fn divisor(&self) -> u32 { | ||
| 145 | match self { | ||
| 146 | Prescaler::NotDivided => 1, | ||
| 147 | Prescaler::DividedBy2 => 2, | ||
| 148 | Prescaler::DividedBy4 => 4, | ||
| 149 | Prescaler::DividedBy6 => 6, | ||
| 150 | Prescaler::DividedBy8 => 8, | ||
| 151 | Prescaler::DividedBy10 => 10, | ||
| 152 | Prescaler::DividedBy12 => 12, | ||
| 153 | Prescaler::DividedBy16 => 16, | ||
| 154 | Prescaler::DividedBy32 => 32, | ||
| 155 | Prescaler::DividedBy64 => 64, | ||
| 156 | Prescaler::DividedBy128 => 128, | ||
| 157 | Prescaler::DividedBy256 => 256, | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | fn presc(&self) -> Presc { | ||
| 162 | match self { | ||
| 163 | Prescaler::NotDivided => Presc::DIV1, | ||
| 164 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 165 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 166 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 167 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 168 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 169 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 170 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 171 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 172 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 173 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 174 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 175 | } | ||
| 176 | } | 90 | } |
| 177 | } | 91 | } |
| 178 | 92 | ||
| @@ -185,6 +99,127 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri | |||
| 185 | type Interrupt: crate::interrupt::typelevel::Interrupt; | 99 | type Interrupt: crate::interrupt::typelevel::Interrupt; |
| 186 | } | 100 | } |
| 187 | 101 | ||
| 102 | foreach_adc!( | ||
| 103 | (ADC4, $common_inst:ident, $clock:ident) => { | ||
| 104 | use crate::peripherals::ADC4; | ||
| 105 | |||
| 106 | impl super::BasicAnyInstance for ADC4 { | ||
| 107 | type SampleTime = SampleTime; | ||
| 108 | } | ||
| 109 | |||
| 110 | impl super::SealedAnyInstance for ADC4 { | ||
| 111 | fn dr() -> *mut u16 { | ||
| 112 | ADC4::regs().dr().as_ptr() as *mut u16 | ||
| 113 | } | ||
| 114 | |||
| 115 | fn enable() { | ||
| 116 | if !ADC4::regs().cr().read().aden() || !ADC4::regs().isr().read().adrdy() { | ||
| 117 | ADC4::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 118 | ADC4::regs().cr().modify(|w| w.set_aden(true)); | ||
| 119 | while !ADC4::regs().isr().read().adrdy() {} | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | fn start() { | ||
| 124 | // Start conversion | ||
| 125 | ADC4::regs().cr().modify(|reg| { | ||
| 126 | reg.set_adstart(true); | ||
| 127 | }); | ||
| 128 | } | ||
| 129 | |||
| 130 | fn stop() { | ||
| 131 | let cr = ADC4::regs().cr().read(); | ||
| 132 | if cr.adstart() { | ||
| 133 | ADC4::regs().cr().modify(|w| w.set_adstp(true)); | ||
| 134 | while ADC4::regs().cr().read().adstart() {} | ||
| 135 | } | ||
| 136 | |||
| 137 | if cr.aden() || cr.adstart() { | ||
| 138 | ADC4::regs().cr().modify(|w| w.set_addis(true)); | ||
| 139 | while ADC4::regs().cr().read().aden() {} | ||
| 140 | } | ||
| 141 | |||
| 142 | // Reset configuration. | ||
| 143 | ADC4::regs().cfgr1().modify(|reg| { | ||
| 144 | reg.set_dmaen(false); | ||
| 145 | }); | ||
| 146 | } | ||
| 147 | |||
| 148 | fn configure_dma(conversion_mode: ConversionMode) { | ||
| 149 | match conversion_mode { | ||
| 150 | ConversionMode::Singular => { | ||
| 151 | ADC4::regs().isr().modify(|reg| { | ||
| 152 | reg.set_ovr(true); | ||
| 153 | reg.set_eos(true); | ||
| 154 | reg.set_eoc(true); | ||
| 155 | }); | ||
| 156 | |||
| 157 | ADC4::regs().cfgr1().modify(|reg| { | ||
| 158 | reg.set_dmaen(true); | ||
| 159 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 160 | #[cfg(stm32u5)] | ||
| 161 | reg.set_chselrmod(false); | ||
| 162 | #[cfg(stm32wba)] | ||
| 163 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 164 | }); | ||
| 165 | } | ||
| 166 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 167 | _ => unreachable!(), | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 172 | let mut prev_channel: i16 = -1; | ||
| 173 | #[cfg(stm32wba)] | ||
| 174 | ADC4::regs().chselr().write_value(Chselr(0_u32)); | ||
| 175 | #[cfg(stm32u5)] | ||
| 176 | ADC4::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 177 | for (_i, ((channel, _), sample_time)) in sequence.enumerate() { | ||
| 178 | ADC4::regs().smpr().modify(|w| { | ||
| 179 | w.set_smp(_i, sample_time); | ||
| 180 | }); | ||
| 181 | |||
| 182 | let channel_num = channel; | ||
| 183 | if channel_num as i16 <= prev_channel { | ||
| 184 | return; | ||
| 185 | }; | ||
| 186 | prev_channel = channel_num as i16; | ||
| 187 | |||
| 188 | #[cfg(stm32wba)] | ||
| 189 | ADC4::regs().chselr().modify(|w| { | ||
| 190 | w.set_chsel0(channel as usize, true); | ||
| 191 | }); | ||
| 192 | #[cfg(stm32u5)] | ||
| 193 | ADC4::regs().chselrmod0().modify(|w| { | ||
| 194 | w.set_chsel(channel as usize, true); | ||
| 195 | }); | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | fn convert() -> u16 { | ||
| 200 | // Reset interrupts | ||
| 201 | ADC4::regs().isr().modify(|reg| { | ||
| 202 | reg.set_eos(true); | ||
| 203 | reg.set_eoc(true); | ||
| 204 | }); | ||
| 205 | |||
| 206 | // Start conversion | ||
| 207 | ADC4::regs().cr().modify(|reg| { | ||
| 208 | reg.set_adstart(true); | ||
| 209 | }); | ||
| 210 | |||
| 211 | while !ADC4::regs().isr().read().eos() { | ||
| 212 | // spin | ||
| 213 | } | ||
| 214 | |||
| 215 | ADC4::regs().dr().read().0 as u16 | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | impl super::AnyInstance for ADC4 {} | ||
| 220 | }; | ||
| 221 | ); | ||
| 222 | |||
| 188 | pub struct Adc4<'d, T: Instance> { | 223 | pub struct Adc4<'d, T: Instance> { |
| 189 | #[allow(unused)] | 224 | #[allow(unused)] |
| 190 | adc: crate::Peri<'d, T>, | 225 | adc: crate::Peri<'d, T>, |
| @@ -196,35 +231,24 @@ pub enum Adc4Error { | |||
| 196 | DMAError, | 231 | DMAError, |
| 197 | } | 232 | } |
| 198 | 233 | ||
| 199 | impl<'d, T: Instance> Adc4<'d, T> { | 234 | impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { |
| 200 | /// Create a new ADC driver. | 235 | /// Create a new ADC driver. |
| 201 | pub fn new(adc: Peri<'d, T>) -> Self { | 236 | pub fn new_adc4(adc: Peri<'d, T>) -> Self { |
| 202 | rcc::enable_and_reset::<T>(); | 237 | rcc::enable_and_reset::<T>(); |
| 203 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 238 | let prescaler = from_ker_ck(T::frequency()); |
| 204 | 239 | ||
| 205 | T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 240 | T::regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 206 | 241 | ||
| 207 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 242 | let frequency = T::frequency() / prescaler; |
| 208 | info!("ADC4 frequency set to {}", frequency); | 243 | info!("ADC4 frequency set to {}", frequency); |
| 209 | 244 | ||
| 210 | if frequency > MAX_ADC_CLK_FREQ { | 245 | if frequency > MAX_ADC_CLK_FREQ { |
| 211 | panic!("Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | 246 | panic!( |
| 247 | "Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 248 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 249 | ); | ||
| 212 | } | 250 | } |
| 213 | 251 | ||
| 214 | let mut s = Self { adc }; | ||
| 215 | |||
| 216 | s.power_up(); | ||
| 217 | |||
| 218 | s.calibrate(); | ||
| 219 | blocking_delay_us(1); | ||
| 220 | |||
| 221 | s.enable(); | ||
| 222 | s.configure(); | ||
| 223 | |||
| 224 | s | ||
| 225 | } | ||
| 226 | |||
| 227 | fn power_up(&mut self) { | ||
| 228 | T::regs().isr().modify(|w| { | 252 | T::regs().isr().modify(|w| { |
| 229 | w.set_ldordy(true); | 253 | w.set_ldordy(true); |
| 230 | }); | 254 | }); |
| @@ -236,22 +260,15 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 236 | T::regs().isr().modify(|w| { | 260 | T::regs().isr().modify(|w| { |
| 237 | w.set_ldordy(true); | 261 | w.set_ldordy(true); |
| 238 | }); | 262 | }); |
| 239 | } | ||
| 240 | 263 | ||
| 241 | fn calibrate(&mut self) { | ||
| 242 | T::regs().cr().modify(|w| w.set_adcal(true)); | 264 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 243 | while T::regs().cr().read().adcal() {} | 265 | while T::regs().cr().read().adcal() {} |
| 244 | T::regs().isr().modify(|w| w.set_eocal(true)); | 266 | T::regs().isr().modify(|w| w.set_eocal(true)); |
| 245 | } | ||
| 246 | 267 | ||
| 247 | fn enable(&mut self) { | 268 | blocking_delay_us(1); |
| 248 | T::regs().isr().write(|w| w.set_adrdy(true)); | 269 | |
| 249 | T::regs().cr().modify(|w| w.set_aden(true)); | 270 | T::enable(); |
| 250 | while !T::regs().isr().read().adrdy() {} | ||
| 251 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 252 | } | ||
| 253 | 271 | ||
| 254 | fn configure(&mut self) { | ||
| 255 | // single conversion mode, software trigger | 272 | // single conversion mode, software trigger |
| 256 | T::regs().cfgr1().modify(|w| { | 273 | T::regs().cfgr1().modify(|w| { |
| 257 | #[cfg(stm32u5)] | 274 | #[cfg(stm32u5)] |
| @@ -277,73 +294,63 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 277 | w.set_smpsel(i, Smpsel::SMP1); | 294 | w.set_smpsel(i, Smpsel::SMP1); |
| 278 | } | 295 | } |
| 279 | }); | 296 | }); |
| 297 | |||
| 298 | Self { adc } | ||
| 280 | } | 299 | } |
| 281 | 300 | ||
| 282 | /// Enable reading the voltage reference internal channel. | 301 | /// Enable reading the voltage reference internal channel. |
| 283 | pub fn enable_vrefint(&self) -> VrefInt { | 302 | pub fn enable_vrefint_adc4(&self) -> super::VrefInt { |
| 284 | T::regs().ccr().modify(|w| { | 303 | T::regs().ccr().modify(|w| { |
| 285 | w.set_vrefen(true); | 304 | w.set_vrefen(true); |
| 286 | }); | 305 | }); |
| 287 | 306 | ||
| 288 | VrefInt {} | 307 | super::VrefInt {} |
| 289 | } | 308 | } |
| 290 | 309 | ||
| 291 | /// Enable reading the temperature internal channel. | 310 | /// Enable reading the temperature internal channel. |
| 292 | pub fn enable_temperature(&self) -> Temperature { | 311 | pub fn enable_temperature_adc4(&self) -> super::Temperature { |
| 293 | T::regs().ccr().modify(|w| { | 312 | T::regs().ccr().modify(|w| { |
| 294 | w.set_vsensesel(true); | 313 | w.set_vsensesel(true); |
| 295 | }); | 314 | }); |
| 296 | 315 | ||
| 297 | Temperature {} | 316 | super::Temperature {} |
| 298 | } | 317 | } |
| 299 | 318 | ||
| 300 | /// Enable reading the vbat internal channel. | 319 | /// Enable reading the vbat internal channel. |
| 301 | #[cfg(stm32u5)] | 320 | #[cfg(stm32u5)] |
| 302 | pub fn enable_vbat(&self) -> Vbat { | 321 | pub fn enable_vbat_adc4(&self) -> super::Vbat { |
| 303 | T::regs().ccr().modify(|w| { | 322 | T::regs().ccr().modify(|w| { |
| 304 | w.set_vbaten(true); | 323 | w.set_vbaten(true); |
| 305 | }); | 324 | }); |
| 306 | 325 | ||
| 307 | Vbat {} | 326 | super::Vbat {} |
| 308 | } | 327 | } |
| 309 | 328 | ||
| 310 | /// Enable reading the vbat internal channel. | 329 | /// Enable reading the vbat internal channel. |
| 311 | pub fn enable_vcore(&self) -> Vcore { | 330 | pub fn enable_vcore_adc4(&self) -> super::Vcore { |
| 312 | Vcore {} | 331 | super::Vcore {} |
| 313 | } | 332 | } |
| 314 | 333 | ||
| 315 | /// Enable reading the vbat internal channel. | 334 | /// Enable reading the vbat internal channel. |
| 316 | #[cfg(stm32u5)] | 335 | #[cfg(stm32u5)] |
| 317 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | 336 | pub fn enable_dac_channel_adc4(&self, dac: DacChannel) -> super::Dac { |
| 318 | let mux; | 337 | let mux; |
| 319 | match dac { | 338 | match dac { |
| 320 | DacChannel::OUT1 => mux = false, | 339 | DacChannel::OUT1 => mux = false, |
| 321 | DacChannel::OUT2 => mux = true, | 340 | DacChannel::OUT2 => mux = true, |
| 322 | } | 341 | } |
| 323 | T::regs().or().modify(|w| w.set_chn21sel(mux)); | 342 | T::regs().or().modify(|w| w.set_chn21sel(mux)); |
| 324 | Dac {} | 343 | super::Dac {} |
| 325 | } | ||
| 326 | |||
| 327 | /// Set the ADC sample time. | ||
| 328 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 329 | T::regs().smpr().modify(|w| { | ||
| 330 | w.set_smp(0, sample_time); | ||
| 331 | }); | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Get the ADC sample time. | ||
| 335 | pub fn sample_time(&self) -> SampleTime { | ||
| 336 | T::regs().smpr().read().smp(0) | ||
| 337 | } | 344 | } |
| 338 | 345 | ||
| 339 | /// Set the ADC resolution. | 346 | /// Set the ADC resolution. |
| 340 | pub fn set_resolution(&mut self, resolution: Resolution) { | 347 | pub fn set_resolution_adc4(&mut self, resolution: Resolution) { |
| 341 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); | 348 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); |
| 342 | } | 349 | } |
| 343 | 350 | ||
| 344 | /// Set hardware averaging. | 351 | /// Set hardware averaging. |
| 345 | #[cfg(stm32u5)] | 352 | #[cfg(stm32u5)] |
| 346 | pub fn set_averaging(&mut self, averaging: Averaging) { | 353 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 347 | let (enable, samples, right_shift) = match averaging { | 354 | let (enable, samples, right_shift) = match averaging { |
| 348 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), | 355 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), |
| 349 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), | 356 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), |
| @@ -363,7 +370,7 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 363 | }) | 370 | }) |
| 364 | } | 371 | } |
| 365 | #[cfg(stm32wba)] | 372 | #[cfg(stm32wba)] |
| 366 | pub fn set_averaging(&mut self, averaging: Averaging) { | 373 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 367 | let (enable, samples, right_shift) = match averaging { | 374 | let (enable, samples, right_shift) = match averaging { |
| 368 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), | 375 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), |
| 369 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), | 376 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), |
| @@ -382,164 +389,4 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 382 | w.set_ovse(enable) | 389 | w.set_ovse(enable) |
| 383 | }) | 390 | }) |
| 384 | } | 391 | } |
| 385 | |||
| 386 | /// Read an ADC channel. | ||
| 387 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 388 | channel.setup(); | ||
| 389 | |||
| 390 | // Select channel | ||
| 391 | #[cfg(stm32wba)] | ||
| 392 | { | ||
| 393 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 394 | T::regs().chselr().modify(|w| { | ||
| 395 | w.set_chsel0(channel.channel() as usize, true); | ||
| 396 | }); | ||
| 397 | } | ||
| 398 | #[cfg(stm32u5)] | ||
| 399 | { | ||
| 400 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 401 | T::regs().chselrmod0().modify(|w| { | ||
| 402 | w.set_chsel(channel.channel() as usize, true); | ||
| 403 | }); | ||
| 404 | } | ||
| 405 | |||
| 406 | // Reset interrupts | ||
| 407 | T::regs().isr().modify(|reg| { | ||
| 408 | reg.set_eos(true); | ||
| 409 | reg.set_eoc(true); | ||
| 410 | }); | ||
| 411 | |||
| 412 | // Start conversion | ||
| 413 | T::regs().cr().modify(|reg| { | ||
| 414 | reg.set_adstart(true); | ||
| 415 | }); | ||
| 416 | |||
| 417 | while !T::regs().isr().read().eos() { | ||
| 418 | // spin | ||
| 419 | } | ||
| 420 | |||
| 421 | T::regs().dr().read().0 as u16 | ||
| 422 | } | ||
| 423 | |||
| 424 | /// Read one or multiple ADC channels using DMA. | ||
| 425 | /// | ||
| 426 | /// `sequence` iterator and `readings` must have the same length. | ||
| 427 | /// The channels in `sequence` must be in ascending order. | ||
| 428 | /// | ||
| 429 | /// Example | ||
| 430 | /// ```rust,ignore | ||
| 431 | /// use embassy_stm32::adc::adc4; | ||
| 432 | /// use embassy_stm32::adc::AdcChannel; | ||
| 433 | /// | ||
| 434 | /// let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 435 | /// let mut adc4_pin1 = p.PC1; | ||
| 436 | /// let mut adc4_pin2 = p.PC0; | ||
| 437 | /// let mut.into()d41 = adc4_pin1.into(); | ||
| 438 | /// let mut.into()d42 = adc4_pin2.into(); | ||
| 439 | /// let mut measurements = [0u16; 2]; | ||
| 440 | /// // not that the channels must be in ascending order | ||
| 441 | /// adc4.read( | ||
| 442 | /// &mut p.GPDMA1_CH1, | ||
| 443 | /// [ | ||
| 444 | /// &mut.into()d42, | ||
| 445 | /// &mut.into()d41, | ||
| 446 | /// ] | ||
| 447 | /// .into_iter(), | ||
| 448 | /// &mut measurements, | ||
| 449 | /// ).await.unwrap(); | ||
| 450 | /// ``` | ||
| 451 | pub async fn read( | ||
| 452 | &mut self, | ||
| 453 | rx_dma: Peri<'_, impl RxDma4<T>>, | ||
| 454 | sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 455 | readings: &mut [u16], | ||
| 456 | ) -> Result<(), Adc4Error> { | ||
| 457 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 458 | assert!( | ||
| 459 | sequence.len() == readings.len(), | ||
| 460 | "Sequence length must be equal to readings length" | ||
| 461 | ); | ||
| 462 | |||
| 463 | // Ensure no conversions are ongoing | ||
| 464 | Self::cancel_conversions(); | ||
| 465 | |||
| 466 | T::regs().isr().modify(|reg| { | ||
| 467 | reg.set_ovr(true); | ||
| 468 | reg.set_eos(true); | ||
| 469 | reg.set_eoc(true); | ||
| 470 | }); | ||
| 471 | |||
| 472 | T::regs().cfgr1().modify(|reg| { | ||
| 473 | reg.set_dmaen(true); | ||
| 474 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 475 | #[cfg(stm32u5)] | ||
| 476 | reg.set_chselrmod(false); | ||
| 477 | #[cfg(stm32wba)] | ||
| 478 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 479 | }); | ||
| 480 | |||
| 481 | // Verify and activate sequence | ||
| 482 | let mut prev_channel: i16 = -1; | ||
| 483 | #[cfg(stm32wba)] | ||
| 484 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 485 | #[cfg(stm32u5)] | ||
| 486 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 487 | for channel in sequence { | ||
| 488 | let channel_num = channel.channel; | ||
| 489 | if channel_num as i16 <= prev_channel { | ||
| 490 | return Err(Adc4Error::InvalidSequence); | ||
| 491 | }; | ||
| 492 | prev_channel = channel_num as i16; | ||
| 493 | |||
| 494 | #[cfg(stm32wba)] | ||
| 495 | T::regs().chselr().modify(|w| { | ||
| 496 | w.set_chsel0(channel.channel as usize, true); | ||
| 497 | }); | ||
| 498 | #[cfg(stm32u5)] | ||
| 499 | T::regs().chselrmod0().modify(|w| { | ||
| 500 | w.set_chsel(channel.channel as usize, true); | ||
| 501 | }); | ||
| 502 | } | ||
| 503 | |||
| 504 | let request = rx_dma.request(); | ||
| 505 | let transfer = unsafe { | ||
| 506 | Transfer::new_read( | ||
| 507 | rx_dma, | ||
| 508 | request, | ||
| 509 | T::regs().dr().as_ptr() as *mut u16, | ||
| 510 | readings, | ||
| 511 | Default::default(), | ||
| 512 | ) | ||
| 513 | }; | ||
| 514 | |||
| 515 | // Start conversion | ||
| 516 | T::regs().cr().modify(|reg| { | ||
| 517 | reg.set_adstart(true); | ||
| 518 | }); | ||
| 519 | |||
| 520 | transfer.await; | ||
| 521 | |||
| 522 | // Ensure conversions are finished. | ||
| 523 | Self::cancel_conversions(); | ||
| 524 | |||
| 525 | // Reset configuration. | ||
| 526 | T::regs().cfgr1().modify(|reg| { | ||
| 527 | reg.set_dmaen(false); | ||
| 528 | }); | ||
| 529 | |||
| 530 | if T::regs().isr().read().ovr() { | ||
| 531 | Err(Adc4Error::DMAError) | ||
| 532 | } else { | ||
| 533 | Ok(()) | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | fn cancel_conversions() { | ||
| 538 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 539 | T::regs().cr().modify(|reg| { | ||
| 540 | reg.set_adstp(true); | ||
| 541 | }); | ||
| 542 | while T::regs().cr().read().adstart() {} | ||
| 543 | } | ||
| 544 | } | ||
| 545 | } | 392 | } |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index f2837a8f1..3e109e429 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::{AnyInstance, 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,200 @@ 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<T: Instance> super::SealedAnyInstance for T { |
| 68 | fn from_ker_ck(frequency: Hertz) -> Self { | 47 | fn dr() -> *mut u16 { |
| 69 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 48 | T::regs().dr().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() { |
| 83 | fn divisor(&self) -> u32 { | 52 | T::regs().isr().modify(|w| w.set_adrdy(true)); |
| 84 | match self { | 53 | T::regs().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 !T::regs().isr().read().adrdy() {} |
| 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 | } | 56 | } |
| 99 | 57 | ||
| 100 | fn presc(&self) -> Presc { | 58 | fn start() { |
| 101 | match self { | 59 | // Start conversion |
| 102 | Prescaler::NotDivided => Presc::DIV1, | 60 | T::regs().cr().modify(|reg| { |
| 103 | Prescaler::DividedBy2 => Presc::DIV2, | 61 | reg.set_adstart(true); |
| 104 | Prescaler::DividedBy4 => Presc::DIV4, | 62 | }); |
| 105 | Prescaler::DividedBy6 => Presc::DIV6, | 63 | } |
| 106 | Prescaler::DividedBy8 => Presc::DIV8, | 64 | |
| 107 | Prescaler::DividedBy10 => Presc::DIV10, | 65 | fn stop() { |
| 108 | Prescaler::DividedBy12 => Presc::DIV12, | 66 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 109 | Prescaler::DividedBy16 => Presc::DIV16, | 67 | T::regs().cr().modify(|reg| { |
| 110 | Prescaler::DividedBy32 => Presc::DIV32, | 68 | reg.set_adstp(Adstp::STOP); |
| 111 | Prescaler::DividedBy64 => Presc::DIV64, | 69 | }); |
| 112 | Prescaler::DividedBy128 => Presc::DIV128, | 70 | while T::regs().cr().read().adstart() {} |
| 113 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 114 | } | 71 | } |
| 72 | |||
| 73 | // Reset configuration. | ||
| 74 | T::regs().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(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 | T::regs().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 | T::regs().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(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 | T::regs().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); |
| 172 | } | 133 | } |
| 134 | }); | ||
| 173 | 135 | ||
| 174 | let mut s = Self { | 136 | if needs_hw { |
| 175 | adc, | 137 | assert!( |
| 176 | sample_time: SampleTime::from_bits(0), | 138 | sequence_len <= CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, |
| 177 | }; | 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 { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 150 | } | ||
| 178 | 151 | ||
| 179 | s.power_up(); | 152 | T::regs().smpr().modify(|w| { |
| 153 | w.smpsel(0); | ||
| 154 | w.set_smp1(sample_time); | ||
| 155 | }); | ||
| 180 | 156 | ||
| 181 | s.set_resolution(resolution); | 157 | T::regs().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 | }); | ||
| 182 | 162 | ||
| 183 | s.calibrate(); | 163 | // Trigger and wait for the channel selection procedure to complete. |
| 164 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 165 | while !T::regs().isr().read().ccrdy() {} | ||
| 166 | } | ||
| 184 | 167 | ||
| 185 | s.enable(); | 168 | fn convert() -> u16 { |
| 169 | // Set single conversion mode. | ||
| 170 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 186 | 171 | ||
| 187 | s.configure_default(); | 172 | // Start conversion |
| 173 | T::regs().cr().modify(|reg| { | ||
| 174 | reg.set_adstart(true); | ||
| 175 | }); | ||
| 188 | 176 | ||
| 189 | s.set_sample_time_all_channels(sample_time); | 177 | // Waiting for End Of Conversion (EOC). |
| 178 | while !T::regs().isr().read().eoc() {} | ||
| 190 | 179 | ||
| 191 | s | 180 | T::regs().dr().read().data() as u16 |
| 192 | } | 181 | } |
| 182 | } | ||
| 183 | |||
| 184 | impl<'d, T: AnyInstance> Adc<'d, T> { | ||
| 185 | /// Create a new ADC driver. | ||
| 186 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { | ||
| 187 | rcc::enable_and_reset::<T>(); | ||
| 188 | |||
| 189 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | ||
| 190 | |||
| 191 | let prescaler = from_ker_ck(T::frequency()); | ||
| 192 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); | ||
| 193 | |||
| 194 | let frequency = T::frequency() / prescaler; | ||
| 195 | debug!("ADC frequency set to {}", frequency); | ||
| 196 | |||
| 197 | if frequency > MAX_ADC_CLK_FREQ { | ||
| 198 | panic!( | ||
| 199 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 200 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 201 | ); | ||
| 202 | } | ||
| 193 | 203 | ||
| 194 | fn power_up(&mut self) { | ||
| 195 | T::regs().cr().modify(|reg| { | 204 | T::regs().cr().modify(|reg| { |
| 196 | reg.set_advregen(true); | 205 | reg.set_advregen(true); |
| 197 | }); | 206 | }); |
| 198 | 207 | ||
| 199 | // "The software must wait for the ADC voltage regulator startup time." | 208 | // "The software must wait for the ADC voltage regulator startup time." |
| 200 | // See datasheet for the value. | 209 | // See datasheet for the value. |
| 201 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | 210 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US as u64 + 1); |
| 202 | } | 211 | |
| 212 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 203 | 213 | ||
| 204 | fn calibrate(&mut self) { | ||
| 205 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. | 214 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. |
| 206 | let autoff_value = T::regs().cfgr1().read().autoff(); | 215 | let autoff_value = T::regs().cfgr1().read().autoff(); |
| 207 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | 216 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); |
| @@ -215,255 +224,35 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 215 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | 224 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); |
| 216 | 225 | ||
| 217 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | 226 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); |
| 218 | } | ||
| 219 | 227 | ||
| 220 | fn enable(&mut self) { | 228 | T::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 | 229 | ||
| 227 | fn configure_default(&mut self) { | ||
| 228 | // single conversion mode, software trigger | 230 | // single conversion mode, software trigger |
| 229 | T::regs().cfgr1().modify(|w| { | 231 | T::regs().cfgr1().modify(|w| { |
| 230 | w.set_cont(false); | 232 | w.set_cont(false); |
| 231 | w.set_exten(Exten::DISABLED); | 233 | w.set_exten(Exten::DISABLED); |
| 232 | w.set_align(Align::RIGHT); | 234 | w.set_align(Align::RIGHT); |
| 233 | }); | 235 | }); |
| 236 | |||
| 237 | Self { adc } | ||
| 234 | } | 238 | } |
| 235 | 239 | ||
| 236 | /// Enable reading the voltage reference internal channel. | 240 | /// Enable reading the voltage reference internal channel. |
| 237 | pub fn enable_vrefint(&self) -> VrefInt { | 241 | pub fn enable_vrefint(&self) -> super::VrefInt { |
| 238 | T::common_regs().ccr().modify(|reg| { | 242 | T::common_regs().ccr().modify(|reg| { |
| 239 | reg.set_vrefen(true); | 243 | reg.set_vrefen(true); |
| 240 | }); | 244 | }); |
| 241 | 245 | ||
| 242 | VrefInt {} | 246 | super::VrefInt {} |
| 243 | } | 247 | } |
| 244 | 248 | ||
| 245 | /// Enable reading the temperature internal channel. | 249 | /// Enable reading the temperature internal channel. |
| 246 | pub fn enable_temperature(&self) -> Temperature { | 250 | 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!"); | 251 | debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); |
| 248 | T::common_regs().ccr().modify(|reg| { | 252 | T::common_regs().ccr().modify(|reg| { |
| 249 | reg.set_tsen(true); | 253 | reg.set_tsen(true); |
| 250 | }); | 254 | }); |
| 251 | 255 | ||
| 252 | Temperature {} | 256 | 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 | } | 257 | } |
| 469 | } | 258 | } |
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 3cdc9d8fb..d6c6f480b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -3,11 +3,11 @@ use core::marker::PhantomData; | |||
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use super::blocking_delay_us; | 5 | use super::blocking_delay_us; |
| 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime, VrefInt}; |
| 7 | use crate::interrupt::typelevel::Interrupt; | 7 | use crate::interrupt::typelevel::Interrupt; |
| 8 | use crate::interrupt::{self}; | 8 | use crate::interrupt::{self}; |
| 9 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 10 | use crate::{rcc, Peri}; | 10 | use crate::{Peri, rcc}; |
| 11 | 11 | ||
| 12 | pub const VDDA_CALIB_MV: u32 = 3300; | 12 | pub const VDDA_CALIB_MV: u32 = 3300; |
| 13 | pub const ADC_MAX: u32 = (1 << 12) - 1; | 13 | pub const ADC_MAX: u32 = (1 << 12) - 1; |
| @@ -28,20 +28,12 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | pub struct Vref; | 31 | impl<T: Instance> super::SealedSpecialConverter<VrefInt> for T { |
| 32 | impl<T: Instance> AdcChannel<T> for Vref {} | 32 | const CHANNEL: u8 = 17; |
| 33 | impl<T: Instance> super::SealedAdcChannel<T> for Vref { | ||
| 34 | fn channel(&self) -> u8 { | ||
| 35 | 17 | ||
| 36 | } | ||
| 37 | } | 33 | } |
| 38 | 34 | ||
| 39 | pub struct Temperature; | 35 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 40 | impl<T: Instance> AdcChannel<T> for Temperature {} | 36 | const CHANNEL: u8 = 16; |
| 41 | impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | ||
| 42 | fn channel(&self) -> u8 { | ||
| 43 | 16 | ||
| 44 | } | ||
| 45 | } | 37 | } |
| 46 | 38 | ||
| 47 | impl<'d, T: Instance> Adc<'d, T> { | 39 | impl<'d, T: Instance> Adc<'d, T> { |
| @@ -51,7 +43,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 51 | 43 | ||
| 52 | // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) | 44 | // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) |
| 53 | // for at least two ADC clock cycles. | 45 | // for at least two ADC clock cycles. |
| 54 | blocking_delay_us((1_000_000 * 2) / Self::freq().0 + 1); | 46 | blocking_delay_us((1_000_000 * 2) / Self::freq().0 as u64 + 1); |
| 55 | 47 | ||
| 56 | // Reset calibration | 48 | // Reset calibration |
| 57 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); | 49 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); |
| @@ -66,15 +58,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 66 | } | 58 | } |
| 67 | 59 | ||
| 68 | // One cycle after calibration | 60 | // One cycle after calibration |
| 69 | blocking_delay_us((1_000_000 * 1) / Self::freq().0 + 1); | 61 | blocking_delay_us((1_000_000 * 1) / Self::freq().0 as u64 + 1); |
| 70 | 62 | ||
| 71 | T::Interrupt::unpend(); | 63 | T::Interrupt::unpend(); |
| 72 | unsafe { T::Interrupt::enable() }; | 64 | unsafe { T::Interrupt::enable() }; |
| 73 | 65 | ||
| 74 | Self { | 66 | Self { adc } |
| 75 | adc, | ||
| 76 | sample_time: SampleTime::from_bits(0), | ||
| 77 | } | ||
| 78 | } | 67 | } |
| 79 | 68 | ||
| 80 | fn freq() -> Hertz { | 69 | fn freq() -> Hertz { |
| @@ -94,22 +83,18 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 94 | } | 83 | } |
| 95 | } | 84 | } |
| 96 | 85 | ||
| 97 | pub fn enable_vref(&self) -> Vref { | 86 | pub fn enable_vref(&self) -> super::VrefInt { |
| 98 | T::regs().cr2().modify(|reg| { | 87 | T::regs().cr2().modify(|reg| { |
| 99 | reg.set_tsvrefe(true); | 88 | reg.set_tsvrefe(true); |
| 100 | }); | 89 | }); |
| 101 | Vref {} | 90 | super::VrefInt {} |
| 102 | } | 91 | } |
| 103 | 92 | ||
| 104 | pub fn enable_temperature(&self) -> Temperature { | 93 | pub fn enable_temperature(&self) -> super::Temperature { |
| 105 | T::regs().cr2().modify(|reg| { | 94 | T::regs().cr2().modify(|reg| { |
| 106 | reg.set_tsvrefe(true); | 95 | reg.set_tsvrefe(true); |
| 107 | }); | 96 | }); |
| 108 | Temperature {} | 97 | super::Temperature {} |
| 109 | } | ||
| 110 | |||
| 111 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 112 | self.sample_time = sample_time; | ||
| 113 | } | 98 | } |
| 114 | 99 | ||
| 115 | /// Perform a single conversion. | 100 | /// Perform a single conversion. |
| @@ -134,8 +119,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 134 | T::regs().dr().read().0 as u16 | 119 | T::regs().dr().read().0 as u16 |
| 135 | } | 120 | } |
| 136 | 121 | ||
| 137 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 122 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 138 | Self::set_channel_sample_time(channel.channel(), self.sample_time); | 123 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| 139 | T::regs().cr1().modify(|reg| { | 124 | T::regs().cr1().modify(|reg| { |
| 140 | reg.set_scan(false); | 125 | reg.set_scan(false); |
| 141 | reg.set_discen(false); | 126 | reg.set_discen(false); |
diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index 3aeb6f2c7..29bfdac97 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -3,10 +3,10 @@ use core::marker::PhantomData; | |||
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use super::blocking_delay_us; | 5 | use super::blocking_delay_us; |
| 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime, VrefInt}; |
| 7 | use crate::interrupt::typelevel::Interrupt; | 7 | use crate::interrupt::typelevel::Interrupt; |
| 8 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 9 | use crate::{interrupt, rcc, Peri}; | 9 | use crate::{Peri, interrupt, rcc}; |
| 10 | 10 | ||
| 11 | pub const VDDA_CALIB_MV: u32 = 3300; | 11 | pub const VDDA_CALIB_MV: u32 = 3300; |
| 12 | pub const ADC_MAX: u32 = (1 << 12) - 1; | 12 | pub const ADC_MAX: u32 = (1 << 12) - 1; |
| @@ -29,27 +29,12 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | pub struct Vref; | 32 | impl<T: Instance> super::SealedSpecialConverter<VrefInt> for T { |
| 33 | impl<T: Instance> AdcChannel<T> for Vref {} | 33 | const CHANNEL: u8 = 18; |
| 34 | impl<T: Instance> super::SealedAdcChannel<T> for Vref { | ||
| 35 | fn channel(&self) -> u8 { | ||
| 36 | 18 | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | impl Vref { | ||
| 41 | /// The value that vref would be if vdda was at 3300mv | ||
| 42 | pub fn value(&self) -> u16 { | ||
| 43 | crate::pac::VREFINTCAL.data().read() | ||
| 44 | } | ||
| 45 | } | 34 | } |
| 46 | 35 | ||
| 47 | pub struct Temperature; | 36 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 48 | impl<T: Instance> AdcChannel<T> for Temperature {} | 37 | const CHANNEL: u8 = 16; |
| 49 | impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | ||
| 50 | fn channel(&self) -> u8 { | ||
| 51 | 16 | ||
| 52 | } | ||
| 53 | } | 38 | } |
| 54 | 39 | ||
| 55 | impl<'d, T: Instance> Adc<'d, T> { | 40 | impl<'d, T: Instance> Adc<'d, T> { |
| @@ -77,7 +62,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 77 | while T::regs().cr().read().adcal() {} | 62 | while T::regs().cr().read().adcal() {} |
| 78 | 63 | ||
| 79 | // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223). | 64 | // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223). |
| 80 | blocking_delay_us((1_000_000 * 4) / Self::freq().0 + 1); | 65 | blocking_delay_us((1_000_000 * 4) / Self::freq().0 as u64 + 1); |
| 81 | 66 | ||
| 82 | // Enable the adc | 67 | // Enable the adc |
| 83 | T::regs().cr().modify(|w| w.set_aden(true)); | 68 | T::regs().cr().modify(|w| w.set_aden(true)); |
| @@ -90,10 +75,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 90 | T::Interrupt::enable(); | 75 | T::Interrupt::enable(); |
| 91 | } | 76 | } |
| 92 | 77 | ||
| 93 | Self { | 78 | Self { adc } |
| 94 | adc, | ||
| 95 | sample_time: SampleTime::from_bits(0), | ||
| 96 | } | ||
| 97 | } | 79 | } |
| 98 | 80 | ||
| 99 | fn freq() -> Hertz { | 81 | fn freq() -> Hertz { |
| @@ -112,20 +94,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 112 | } | 94 | } |
| 113 | } | 95 | } |
| 114 | 96 | ||
| 115 | pub fn enable_vref(&self) -> Vref { | 97 | pub fn enable_vref(&self) -> super::VrefInt { |
| 116 | T::common_regs().ccr().modify(|w| w.set_vrefen(true)); | 98 | T::common_regs().ccr().modify(|w| w.set_vrefen(true)); |
| 117 | 99 | ||
| 118 | Vref {} | 100 | super::VrefInt {} |
| 119 | } | 101 | } |
| 120 | 102 | ||
| 121 | pub fn enable_temperature(&self) -> Temperature { | 103 | pub fn enable_temperature(&self) -> super::Temperature { |
| 122 | T::common_regs().ccr().modify(|w| w.set_tsen(true)); | 104 | T::common_regs().ccr().modify(|w| w.set_tsen(true)); |
| 123 | 105 | ||
| 124 | Temperature {} | 106 | super::Temperature {} |
| 125 | } | ||
| 126 | |||
| 127 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 128 | self.sample_time = sample_time; | ||
| 129 | } | 107 | } |
| 130 | 108 | ||
| 131 | /// Perform a single conversion. | 109 | /// Perform a single conversion. |
| @@ -150,8 +128,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 150 | T::regs().dr().read().rdata() | 128 | T::regs().dr().read().rdata() |
| 151 | } | 129 | } |
| 152 | 130 | ||
| 153 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 131 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 154 | Self::set_channel_sample_time(channel.channel(), self.sample_time); | 132 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| 155 | 133 | ||
| 156 | // Configure the channel to sample | 134 | // Configure the channel to sample |
| 157 | T::regs().sqr1().write(|w| w.set_sq(0, channel.channel())); | 135 | T::regs().sqr1().write(|w| w.set_sq(0, channel.channel())); |
diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index 84613078c..919ac3cc0 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs | |||
| @@ -9,7 +9,7 @@ use super::Resolution; | |||
| 9 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 9 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; |
| 10 | use crate::interrupt::typelevel::Interrupt; | 10 | use crate::interrupt::typelevel::Interrupt; |
| 11 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 12 | use crate::{interrupt, rcc, Peri}; | 12 | use crate::{Peri, interrupt, rcc}; |
| 13 | 13 | ||
| 14 | const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; | 14 | const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; |
| 15 | 15 | ||
| @@ -79,7 +79,7 @@ impl<T: Instance> Vref<T> { | |||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration { | 81 | pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration { |
| 82 | let vref_val = adc.read(self).await; | 82 | let vref_val = adc.read(self, SampleTime::from(0)).await; |
| 83 | Calibration { | 83 | Calibration { |
| 84 | vref_cal: self.calibrated_value(), | 84 | vref_cal: self.calibrated_value(), |
| 85 | vref_val, | 85 | vref_val, |
| @@ -270,7 +270,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 270 | } | 270 | } |
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 273 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 274 | self.set_sample_time(channel, sample_time).await; | ||
| 274 | self.set_sample_sequence(&[channel.channel()]).await; | 275 | self.set_sample_sequence(&[channel.channel()]).await; |
| 275 | self.convert().await | 276 | self.convert().await |
| 276 | } | 277 | } |
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 43498966f..1767a3bb3 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,175 +1,278 @@ | |||
| 1 | #[cfg(stm32g4)] | ||
| 2 | use pac::adc::regs::Difsel as DifselReg; | ||
| 3 | #[allow(unused)] | ||
| 4 | #[cfg(stm32g4)] | ||
| 5 | pub use pac::adc::vals::{Adcaldif, Adstp, Difsel, Dmacfg, Dmaen, Exten, Rovsm, Trovs}; | ||
| 1 | #[allow(unused)] | 6 | #[allow(unused)] |
| 2 | #[cfg(stm32h7)] | 7 | #[cfg(stm32h7)] |
| 3 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 8 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| 4 | #[allow(unused)] | 9 | pub use pac::adccommon::vals::{Dual, Presc}; |
| 5 | #[cfg(stm32g4)] | ||
| 6 | use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; | ||
| 7 | use pac::adccommon::vals::Presc; | ||
| 8 | use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | ||
| 9 | 10 | ||
| 10 | use super::{blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; | 11 | use super::{ |
| 11 | use crate::adc::SealedAdcChannel; | 12 | Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, |
| 12 | use crate::dma::Transfer; | 13 | blocking_delay_us, |
| 14 | }; | ||
| 15 | use crate::adc::{AnyInstance, SealedAdcChannel}; | ||
| 13 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 14 | use crate::{pac, rcc, Peri}; | 17 | use crate::{Peri, pac, rcc}; |
| 18 | |||
| 19 | mod injected; | ||
| 20 | pub use injected::InjectedAdc; | ||
| 15 | 21 | ||
| 16 | /// Default VREF voltage used for sample conversion to millivolts. | 22 | /// Default VREF voltage used for sample conversion to millivolts. |
| 17 | pub const VREF_DEFAULT_MV: u32 = 3300; | 23 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| 18 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 24 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 19 | pub const VREF_CALIB_MV: u32 = 3300; | 25 | pub const VREF_CALIB_MV: u32 = 3300; |
| 20 | 26 | ||
| 27 | const NR_INJECTED_RANKS: usize = 4; | ||
| 28 | |||
| 21 | /// Max single ADC operation clock frequency | 29 | /// Max single ADC operation clock frequency |
| 22 | #[cfg(stm32g4)] | 30 | #[cfg(stm32g4)] |
| 23 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | 31 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); |
| 24 | #[cfg(stm32h7)] | 32 | #[cfg(stm32h7)] |
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 33 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 26 | 34 | ||
| 27 | // 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 | 35 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 28 | /// Internal voltage reference channel. | 36 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 29 | pub struct VrefInt; | 37 | match raw_prescaler { |
| 30 | impl<T: Instance + VrefChannel> AdcChannel<T> for VrefInt {} | 38 | 0 => Presc::DIV1, |
| 31 | impl<T: Instance + VrefChannel> super::SealedAdcChannel<T> for VrefInt { | 39 | 1 => Presc::DIV2, |
| 32 | fn channel(&self) -> u8 { | 40 | 2..=3 => Presc::DIV4, |
| 33 | T::CHANNEL | 41 | 4..=5 => Presc::DIV6, |
| 42 | 6..=7 => Presc::DIV8, | ||
| 43 | 8..=9 => Presc::DIV10, | ||
| 44 | 10..=11 => Presc::DIV12, | ||
| 45 | _ => unimplemented!(), | ||
| 34 | } | 46 | } |
| 35 | } | 47 | } |
| 36 | 48 | ||
| 37 | /// Internal temperature channel. | 49 | /// ADC configuration |
| 38 | pub struct Temperature; | 50 | #[derive(Default)] |
| 39 | impl<T: Instance + TemperatureChannel> AdcChannel<T> for Temperature {} | 51 | pub struct AdcConfig { |
| 40 | impl<T: Instance + TemperatureChannel> super::SealedAdcChannel<T> for Temperature { | 52 | pub dual_mode: Option<Dual>, |
| 41 | fn channel(&self) -> u8 { | 53 | pub resolution: Option<Resolution>, |
| 42 | T::CHANNEL | 54 | #[cfg(stm32g4)] |
| 43 | } | 55 | pub oversampling_shift: Option<u8>, |
| 56 | #[cfg(stm32g4)] | ||
| 57 | pub oversampling_ratio: Option<u8>, | ||
| 58 | #[cfg(stm32g4)] | ||
| 59 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | ||
| 44 | } | 60 | } |
| 45 | 61 | ||
| 46 | /// Internal battery voltage channel. | 62 | // Trigger source for ADC conversions¨ |
| 47 | pub struct Vbat; | 63 | #[derive(Copy, Clone)] |
| 48 | impl<T: Instance + VBatChannel> AdcChannel<T> for Vbat {} | 64 | pub struct ConversionTrigger { |
| 49 | impl<T: Instance + VBatChannel> super::SealedAdcChannel<T> for Vbat { | 65 | // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers |
| 50 | fn channel(&self) -> u8 { | 66 | // Note that Injected and Regular channels uses different mappings |
| 51 | T::CHANNEL | 67 | pub channel: u8, |
| 52 | } | 68 | pub edge: Exten, |
| 53 | } | 69 | } |
| 54 | 70 | ||
| 55 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 71 | impl<T: Instance> super::SealedAnyInstance for T { |
| 56 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 72 | fn dr() -> *mut u16 { |
| 57 | #[allow(unused)] | 73 | T::regs().dr().as_ptr() as *mut u16 |
| 58 | enum Prescaler { | 74 | } |
| 59 | NotDivided, | 75 | |
| 60 | DividedBy2, | 76 | fn enable() { |
| 61 | DividedBy4, | 77 | // Make sure bits are off |
| 62 | DividedBy6, | 78 | while T::regs().cr().read().addis() { |
| 63 | DividedBy8, | 79 | // spin |
| 64 | DividedBy10, | 80 | } |
| 65 | DividedBy12, | 81 | |
| 66 | DividedBy16, | 82 | if !T::regs().cr().read().aden() { |
| 67 | DividedBy32, | 83 | // Enable ADC |
| 68 | DividedBy64, | 84 | T::regs().isr().modify(|reg| { |
| 69 | DividedBy128, | 85 | reg.set_adrdy(true); |
| 70 | DividedBy256, | 86 | }); |
| 71 | } | 87 | T::regs().cr().modify(|reg| { |
| 88 | reg.set_aden(true); | ||
| 89 | }); | ||
| 72 | 90 | ||
| 73 | impl Prescaler { | 91 | while !T::regs().isr().read().adrdy() { |
| 74 | fn from_ker_ck(frequency: Hertz) -> Self { | 92 | // spin |
| 75 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 93 | } |
| 76 | match raw_prescaler { | ||
| 77 | 0 => Self::NotDivided, | ||
| 78 | 1 => Self::DividedBy2, | ||
| 79 | 2..=3 => Self::DividedBy4, | ||
| 80 | 4..=5 => Self::DividedBy6, | ||
| 81 | 6..=7 => Self::DividedBy8, | ||
| 82 | 8..=9 => Self::DividedBy10, | ||
| 83 | 10..=11 => Self::DividedBy12, | ||
| 84 | _ => unimplemented!(), | ||
| 85 | } | 94 | } |
| 86 | } | 95 | } |
| 87 | 96 | ||
| 88 | fn divisor(&self) -> u32 { | 97 | fn start() { |
| 89 | match self { | 98 | T::regs().cr().modify(|reg| { |
| 90 | Prescaler::NotDivided => 1, | 99 | reg.set_adstart(true); |
| 91 | Prescaler::DividedBy2 => 2, | 100 | }); |
| 92 | Prescaler::DividedBy4 => 4, | 101 | } |
| 93 | Prescaler::DividedBy6 => 6, | 102 | |
| 94 | Prescaler::DividedBy8 => 8, | 103 | fn stop() { |
| 95 | Prescaler::DividedBy10 => 10, | 104 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 96 | Prescaler::DividedBy12 => 12, | 105 | T::regs().cr().modify(|reg| { |
| 97 | Prescaler::DividedBy16 => 16, | 106 | reg.set_adstp(Adstp::STOP); |
| 98 | Prescaler::DividedBy32 => 32, | 107 | }); |
| 99 | Prescaler::DividedBy64 => 64, | 108 | // The software must poll ADSTART until the bit is reset before assuming the |
| 100 | Prescaler::DividedBy128 => 128, | 109 | // ADC is completely stopped |
| 101 | Prescaler::DividedBy256 => 256, | 110 | while T::regs().cr().read().adstart() {} |
| 102 | } | 111 | } |
| 112 | |||
| 113 | // Disable dma control and continuous conversion, if enabled | ||
| 114 | T::regs().cfgr().modify(|reg| { | ||
| 115 | reg.set_cont(false); | ||
| 116 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 117 | }); | ||
| 103 | } | 118 | } |
| 104 | 119 | ||
| 105 | fn presc(&self) -> Presc { | 120 | fn convert() -> u16 { |
| 106 | match self { | 121 | T::regs().isr().modify(|reg| { |
| 107 | Prescaler::NotDivided => Presc::DIV1, | 122 | reg.set_eos(true); |
| 108 | Prescaler::DividedBy2 => Presc::DIV2, | 123 | reg.set_eoc(true); |
| 109 | Prescaler::DividedBy4 => Presc::DIV4, | 124 | }); |
| 110 | Prescaler::DividedBy6 => Presc::DIV6, | 125 | |
| 111 | Prescaler::DividedBy8 => Presc::DIV8, | 126 | // Start conversion |
| 112 | Prescaler::DividedBy10 => Presc::DIV10, | 127 | T::regs().cr().modify(|reg| { |
| 113 | Prescaler::DividedBy12 => Presc::DIV12, | 128 | reg.set_adstart(true); |
| 114 | Prescaler::DividedBy16 => Presc::DIV16, | 129 | }); |
| 115 | Prescaler::DividedBy32 => Presc::DIV32, | 130 | |
| 116 | Prescaler::DividedBy64 => Presc::DIV64, | 131 | while !T::regs().isr().read().eos() { |
| 117 | Prescaler::DividedBy128 => Presc::DIV128, | 132 | // spin |
| 118 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 119 | } | 133 | } |
| 134 | |||
| 135 | T::regs().dr().read().0 as u16 | ||
| 120 | } | 136 | } |
| 121 | } | ||
| 122 | 137 | ||
| 123 | impl<'d, T: Instance> Adc<'d, T> { | 138 | fn configure_dma(conversion_mode: ConversionMode) { |
| 124 | /// Create a new ADC driver. | 139 | T::regs().isr().modify(|reg| { |
| 125 | pub fn new(adc: Peri<'d, T>) -> Self { | 140 | reg.set_ovr(true); |
| 126 | rcc::enable_and_reset::<T>(); | 141 | }); |
| 127 | 142 | ||
| 128 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 143 | T::regs().cfgr().modify(|reg| { |
| 144 | reg.set_discen(false); // Convert all channels for each trigger | ||
| 145 | reg.set_dmacfg(match conversion_mode { | ||
| 146 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 147 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 148 | }); | ||
| 149 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 150 | }); | ||
| 129 | 151 | ||
| 130 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 152 | if let ConversionMode::Repeated(mode) = conversion_mode { |
| 153 | match mode { | ||
| 154 | RegularConversionMode::Continuous => { | ||
| 155 | T::regs().cfgr().modify(|reg| { | ||
| 156 | reg.set_cont(true); | ||
| 157 | }); | ||
| 158 | } | ||
| 159 | RegularConversionMode::Triggered(trigger) => { | ||
| 160 | T::regs().cfgr().modify(|r| { | ||
| 161 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 162 | }); | ||
| 131 | 163 | ||
| 132 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 164 | T::regs().cfgr().modify(|r| { |
| 133 | trace!("ADC frequency set to {}", frequency); | 165 | r.set_extsel(trigger.channel); |
| 166 | r.set_exten(trigger.edge); | ||
| 167 | }); | ||
| 134 | 168 | ||
| 135 | if frequency > MAX_ADC_CLK_FREQ { | 169 | // Regular conversions uses DMA so no need to generate interrupt |
| 136 | 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 ); | 170 | T::regs().ier().modify(|r| r.set_eosie(false)); |
| 171 | } | ||
| 172 | } | ||
| 137 | } | 173 | } |
| 174 | } | ||
| 175 | |||
| 176 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 177 | T::regs().cr().modify(|w| w.set_aden(false)); | ||
| 138 | 178 | ||
| 139 | let mut s = Self { | 179 | // Set sequence length |
| 140 | adc, | 180 | T::regs().sqr1().modify(|w| { |
| 141 | sample_time: SampleTime::from_bits(0), | 181 | w.set_l(sequence.len() as u8 - 1); |
| 142 | }; | 182 | }); |
| 143 | s.power_up(); | ||
| 144 | s.configure_differential_inputs(); | ||
| 145 | 183 | ||
| 146 | s.calibrate(); | 184 | #[cfg(stm32g4)] |
| 147 | blocking_delay_us(1); | 185 | let mut difsel = DifselReg::default(); |
| 186 | let mut smpr = T::regs().smpr().read(); | ||
| 187 | let mut smpr2 = T::regs().smpr2().read(); | ||
| 188 | let mut sqr1 = T::regs().sqr1().read(); | ||
| 189 | let mut sqr2 = T::regs().sqr2().read(); | ||
| 190 | let mut sqr3 = T::regs().sqr3().read(); | ||
| 191 | let mut sqr4 = T::regs().sqr4().read(); | ||
| 148 | 192 | ||
| 149 | s.enable(); | 193 | // Configure channels and ranks |
| 150 | s.configure(); | 194 | for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { |
| 195 | let sample_time = sample_time.into(); | ||
| 196 | if ch <= 9 { | ||
| 197 | smpr.set_smp(ch as _, sample_time); | ||
| 198 | } else { | ||
| 199 | smpr2.set_smp((ch - 10) as _, sample_time); | ||
| 200 | } | ||
| 151 | 201 | ||
| 152 | s | 202 | match _i { |
| 203 | 0..=3 => { | ||
| 204 | sqr1.set_sq(_i, ch); | ||
| 205 | } | ||
| 206 | 4..=8 => { | ||
| 207 | sqr2.set_sq(_i - 4, ch); | ||
| 208 | } | ||
| 209 | 9..=13 => { | ||
| 210 | sqr3.set_sq(_i - 9, ch); | ||
| 211 | } | ||
| 212 | 14..=15 => { | ||
| 213 | sqr4.set_sq(_i - 14, ch); | ||
| 214 | } | ||
| 215 | _ => unreachable!(), | ||
| 216 | } | ||
| 217 | |||
| 218 | #[cfg(stm32g4)] | ||
| 219 | { | ||
| 220 | if ch < 18 { | ||
| 221 | difsel.set_difsel( | ||
| 222 | ch.into(), | ||
| 223 | if is_differential { | ||
| 224 | Difsel::DIFFERENTIAL | ||
| 225 | } else { | ||
| 226 | Difsel::SINGLE_ENDED | ||
| 227 | }, | ||
| 228 | ); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | T::regs().smpr().write_value(smpr); | ||
| 234 | T::regs().smpr2().write_value(smpr2); | ||
| 235 | T::regs().sqr1().write_value(sqr1); | ||
| 236 | T::regs().sqr2().write_value(sqr2); | ||
| 237 | T::regs().sqr3().write_value(sqr3); | ||
| 238 | T::regs().sqr4().write_value(sqr4); | ||
| 239 | #[cfg(stm32g4)] | ||
| 240 | T::regs().difsel().write_value(difsel); | ||
| 153 | } | 241 | } |
| 242 | } | ||
| 243 | |||
| 244 | impl<'d, T: Instance + AnyInstance> Adc<'d, T> { | ||
| 245 | /// Create a new ADC driver. | ||
| 246 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 247 | rcc::enable_and_reset::<T>(); | ||
| 248 | |||
| 249 | let prescaler = from_ker_ck(T::frequency()); | ||
| 250 | |||
| 251 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); | ||
| 252 | |||
| 253 | let frequency = T::frequency() / prescaler; | ||
| 254 | trace!("ADC frequency set to {}", frequency); | ||
| 255 | |||
| 256 | if frequency > MAX_ADC_CLK_FREQ { | ||
| 257 | panic!( | ||
| 258 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 259 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 260 | ); | ||
| 261 | } | ||
| 154 | 262 | ||
| 155 | fn power_up(&mut self) { | ||
| 156 | T::regs().cr().modify(|reg| { | 263 | T::regs().cr().modify(|reg| { |
| 157 | reg.set_deeppwd(false); | 264 | reg.set_deeppwd(false); |
| 158 | reg.set_advregen(true); | 265 | reg.set_advregen(true); |
| 159 | }); | 266 | }); |
| 160 | 267 | ||
| 161 | blocking_delay_us(20); | 268 | blocking_delay_us(20); |
| 162 | } | ||
| 163 | 269 | ||
| 164 | fn configure_differential_inputs(&mut self) { | ||
| 165 | T::regs().difsel().modify(|w| { | 270 | T::regs().difsel().modify(|w| { |
| 166 | for n in 0..18 { | 271 | for n in 0..18 { |
| 167 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 272 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 168 | } | 273 | } |
| 169 | }); | 274 | }); |
| 170 | } | ||
| 171 | 275 | ||
| 172 | fn calibrate(&mut self) { | ||
| 173 | T::regs().cr().modify(|w| { | 276 | T::regs().cr().modify(|w| { |
| 174 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 277 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| 175 | }); | 278 | }); |
| @@ -189,120 +292,79 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 189 | while T::regs().cr().read().adcal() {} | 292 | while T::regs().cr().read().adcal() {} |
| 190 | 293 | ||
| 191 | blocking_delay_us(20); | 294 | blocking_delay_us(20); |
| 192 | } | ||
| 193 | |||
| 194 | fn enable(&mut self) { | ||
| 195 | // Make sure bits are off | ||
| 196 | while T::regs().cr().read().addis() { | ||
| 197 | // spin | ||
| 198 | } | ||
| 199 | |||
| 200 | if !T::regs().cr().read().aden() { | ||
| 201 | // Enable ADC | ||
| 202 | T::regs().isr().modify(|reg| { | ||
| 203 | reg.set_adrdy(true); | ||
| 204 | }); | ||
| 205 | T::regs().cr().modify(|reg| { | ||
| 206 | reg.set_aden(true); | ||
| 207 | }); | ||
| 208 | 295 | ||
| 209 | while !T::regs().isr().read().adrdy() { | 296 | T::enable(); |
| 210 | // spin | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | 297 | ||
| 215 | fn configure(&mut self) { | ||
| 216 | // single conversion mode, software trigger | 298 | // single conversion mode, software trigger |
| 217 | T::regs().cfgr().modify(|w| { | 299 | T::regs().cfgr().modify(|w| { |
| 218 | w.set_cont(false); | 300 | w.set_cont(false); |
| 219 | w.set_exten(Exten::DISABLED); | 301 | w.set_exten(Exten::DISABLED); |
| 220 | }); | 302 | }); |
| 303 | |||
| 304 | if let Some(dual) = config.dual_mode { | ||
| 305 | T::common_regs().ccr().modify(|reg| { | ||
| 306 | reg.set_dual(dual); | ||
| 307 | }) | ||
| 308 | } | ||
| 309 | |||
| 310 | if let Some(resolution) = config.resolution { | ||
| 311 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 312 | } | ||
| 313 | |||
| 314 | #[cfg(stm32g4)] | ||
| 315 | if let Some(shift) = config.oversampling_shift { | ||
| 316 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 317 | } | ||
| 318 | |||
| 319 | #[cfg(stm32g4)] | ||
| 320 | if let Some(ratio) = config.oversampling_ratio { | ||
| 321 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 322 | } | ||
| 323 | |||
| 324 | #[cfg(stm32g4)] | ||
| 325 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { | ||
| 326 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 327 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 328 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 329 | } | ||
| 330 | |||
| 331 | Self { adc } | ||
| 221 | } | 332 | } |
| 222 | 333 | ||
| 223 | /// Enable reading the voltage reference internal channel. | 334 | /// Enable reading the voltage reference internal channel. |
| 224 | pub fn enable_vrefint(&self) -> VrefInt | 335 | pub fn enable_vrefint(&self) -> super::VrefInt |
| 225 | where | 336 | where |
| 226 | T: VrefChannel, | 337 | T: super::SpecialConverter<super::VrefInt>, |
| 227 | { | 338 | { |
| 228 | T::common_regs().ccr().modify(|reg| { | 339 | T::common_regs().ccr().modify(|reg| { |
| 229 | reg.set_vrefen(true); | 340 | reg.set_vrefen(true); |
| 230 | }); | 341 | }); |
| 231 | 342 | ||
| 232 | VrefInt {} | 343 | super::VrefInt {} |
| 233 | } | 344 | } |
| 234 | 345 | ||
| 235 | /// Enable reading the temperature internal channel. | 346 | /// Enable reading the temperature internal channel. |
| 236 | pub fn enable_temperature(&self) -> Temperature | 347 | pub fn enable_temperature(&self) -> super::Temperature |
| 237 | where | 348 | where |
| 238 | T: TemperatureChannel, | 349 | T: super::SpecialConverter<super::Temperature>, |
| 239 | { | 350 | { |
| 240 | T::common_regs().ccr().modify(|reg| { | 351 | T::common_regs().ccr().modify(|reg| { |
| 241 | reg.set_vsenseen(true); | 352 | reg.set_vsenseen(true); |
| 242 | }); | 353 | }); |
| 243 | 354 | ||
| 244 | Temperature {} | 355 | super::Temperature {} |
| 245 | } | 356 | } |
| 246 | 357 | ||
| 247 | /// Enable reading the vbat internal channel. | 358 | /// Enable reading the vbat internal channel. |
| 248 | pub fn enable_vbat(&self) -> Vbat | 359 | pub fn enable_vbat(&self) -> super::Vbat |
| 249 | where | 360 | where |
| 250 | T: VBatChannel, | 361 | T: super::SpecialConverter<super::Vbat>, |
| 251 | { | 362 | { |
| 252 | T::common_regs().ccr().modify(|reg| { | 363 | T::common_regs().ccr().modify(|reg| { |
| 253 | reg.set_vbaten(true); | 364 | reg.set_vbaten(true); |
| 254 | }); | 365 | }); |
| 255 | 366 | ||
| 256 | Vbat {} | 367 | super::Vbat {} |
| 257 | } | ||
| 258 | |||
| 259 | /// Enable differential channel. | ||
| 260 | /// Caution: | ||
| 261 | /// : When configuring the channel “i” in differential input mode, its negative input voltage VINN[i] | ||
| 262 | /// is connected to another channel. As a consequence, this channel is no longer usable in | ||
| 263 | /// single-ended mode or in differential mode and must never be configured to be converted. | ||
| 264 | /// Some channels are shared between ADC1/ADC2/ADC3/ADC4/ADC5: this can make the | ||
| 265 | /// channel on the other ADC unusable. The only exception is when ADC master and the slave | ||
| 266 | /// operate in interleaved mode. | ||
| 267 | #[cfg(stm32g4)] | ||
| 268 | pub fn set_differential_channel(&mut self, ch: usize, enable: bool) { | ||
| 269 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc | ||
| 270 | T::regs().difsel().modify(|w| { | ||
| 271 | w.set_difsel( | ||
| 272 | ch, | ||
| 273 | if enable { | ||
| 274 | Difsel::DIFFERENTIAL | ||
| 275 | } else { | ||
| 276 | Difsel::SINGLE_ENDED | ||
| 277 | }, | ||
| 278 | ); | ||
| 279 | }); | ||
| 280 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 281 | } | ||
| 282 | |||
| 283 | #[cfg(stm32g4)] | ||
| 284 | pub fn set_differential(&mut self, channel: &mut impl AdcChannel<T>, enable: bool) { | ||
| 285 | self.set_differential_channel(channel.channel() as usize, enable); | ||
| 286 | } | ||
| 287 | |||
| 288 | /// Set oversampling shift. | ||
| 289 | #[cfg(stm32g4)] | ||
| 290 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 291 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Set oversampling ratio. | ||
| 295 | #[cfg(stm32g4)] | ||
| 296 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 297 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 298 | } | ||
| 299 | |||
| 300 | /// Enable oversampling in regular mode. | ||
| 301 | #[cfg(stm32g4)] | ||
| 302 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 303 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 304 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 305 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 306 | } | 368 | } |
| 307 | 369 | ||
| 308 | // Reads that are not implemented as INJECTED in "blocking_read" | 370 | // Reads that are not implemented as INJECTED in "blocking_read" |
| @@ -318,260 +380,215 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 318 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | 380 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); |
| 319 | // } | 381 | // } |
| 320 | 382 | ||
| 321 | /// Set the ADC sample time. | 383 | /// Configures the ADC for injected conversions. |
| 322 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 323 | self.sample_time = sample_time; | ||
| 324 | } | ||
| 325 | |||
| 326 | /// Set the ADC resolution. | ||
| 327 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 328 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Perform a single conversion. | ||
| 332 | fn convert(&mut self) -> u16 { | ||
| 333 | T::regs().isr().modify(|reg| { | ||
| 334 | reg.set_eos(true); | ||
| 335 | reg.set_eoc(true); | ||
| 336 | }); | ||
| 337 | |||
| 338 | // Start conversion | ||
| 339 | T::regs().cr().modify(|reg| { | ||
| 340 | reg.set_adstart(true); | ||
| 341 | }); | ||
| 342 | |||
| 343 | while !T::regs().isr().read().eos() { | ||
| 344 | // spin | ||
| 345 | } | ||
| 346 | |||
| 347 | T::regs().dr().read().0 as u16 | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Read an ADC pin. | ||
| 351 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 352 | channel.setup(); | ||
| 353 | |||
| 354 | self.read_channel(channel) | ||
| 355 | } | ||
| 356 | |||
| 357 | /// Read one or multiple ADC channels using DMA. | ||
| 358 | /// | 384 | /// |
| 359 | /// `sequence` iterator and `readings` must have the same length. | 385 | /// Injected conversions are separate from the regular conversion sequence and are typically |
| 386 | /// triggered by software or an external event. This method sets up a fixed-length sequence of | ||
| 387 | /// injected channels with specified sample times, the trigger source, and whether the end-of-sequence | ||
| 388 | /// interrupt should be enabled. | ||
| 360 | /// | 389 | /// |
| 361 | /// Example | 390 | /// # Parameters |
| 362 | /// ```rust,ignore | 391 | /// - `sequence`: An array of tuples containing the ADC channels and their sample times. The length |
| 363 | /// use embassy_stm32::adc::{Adc, AdcChannel} | 392 | /// `N` determines the number of injected ranks to configure (maximum 4 for STM32). |
| 393 | /// - `trigger`: The trigger source that starts the injected conversion sequence. | ||
| 394 | /// - `interrupt`: If `true`, enables the end-of-sequence (JEOS) interrupt for injected conversions. | ||
| 364 | /// | 395 | /// |
| 365 | /// let mut adc = Adc::new(p.ADC1); | 396 | /// # Returns |
| 366 | /// let mut adc_pin0 = p.PA0.into(); | 397 | /// An `InjectedAdc<T, N>` instance that represents the configured injected sequence. The returned |
| 367 | /// let mut adc_pin1 = p.PA1.into(); | 398 | /// type encodes the sequence length `N` in its type, ensuring that reads return exactly `N` samples. |
| 368 | /// let mut measurements = [0u16; 2]; | ||
| 369 | /// | 399 | /// |
| 370 | /// adc.read( | 400 | /// # Panics |
| 371 | /// p.DMA1_CH2.reborrow(), | 401 | /// This function will panic if: |
| 372 | /// [ | 402 | /// - `sequence` is empty. |
| 373 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | 403 | /// - `sequence` length exceeds the maximum number of injected ranks (`NR_INJECTED_RANKS`). |
| 374 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | 404 | /// |
| 375 | /// ] | 405 | /// # Notes |
| 376 | /// .into_iter(), | 406 | /// - Injected conversions can run independently of regular ADC conversions. |
| 377 | /// &mut measurements, | 407 | /// - The order of channels in `sequence` determines the rank order in the injected sequence. |
| 378 | /// ) | 408 | /// - Accessing samples beyond `N` will result in a panic; use the returned type |
| 379 | /// .await; | 409 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. |
| 380 | /// defmt::info!("measurements: {}", measurements); | 410 | pub fn setup_injected_conversions<'a, const N: usize>( |
| 381 | /// ``` | 411 | self, |
| 382 | pub async fn read( | 412 | sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 383 | &mut self, | 413 | trigger: ConversionTrigger, |
| 384 | rx_dma: Peri<'_, impl RxDma<T>>, | 414 | interrupt: bool, |
| 385 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | 415 | ) -> InjectedAdc<T, N> { |
| 386 | readings: &mut [u16], | 416 | assert!(N != 0, "Read sequence cannot be empty"); |
| 387 | ) { | ||
| 388 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 389 | assert!( | ||
| 390 | sequence.len() == readings.len(), | ||
| 391 | "Sequence length must be equal to readings length" | ||
| 392 | ); | ||
| 393 | assert!( | 417 | assert!( |
| 394 | sequence.len() <= 16, | 418 | N <= NR_INJECTED_RANKS, |
| 395 | "Asynchronous read sequence cannot be more than 16 in length" | 419 | "Read sequence cannot be more than {} in length", |
| 420 | NR_INJECTED_RANKS | ||
| 396 | ); | 421 | ); |
| 397 | 422 | ||
| 398 | // Ensure no conversions are ongoing and ADC is enabled. | 423 | T::enable(); |
| 399 | Self::cancel_conversions(); | ||
| 400 | self.enable(); | ||
| 401 | |||
| 402 | // Set sequence length | ||
| 403 | T::regs().sqr1().modify(|w| { | ||
| 404 | w.set_l(sequence.len() as u8 - 1); | ||
| 405 | }); | ||
| 406 | 424 | ||
| 407 | // Configure channels and ranks | 425 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); |
| 408 | for (_i, (channel, sample_time)) in sequence.enumerate() { | ||
| 409 | Self::configure_channel(channel, sample_time); | ||
| 410 | 426 | ||
| 411 | match _i { | 427 | for (n, (channel, sample_time)) in sequence.into_iter().enumerate() { |
| 412 | 0..=3 => { | 428 | let sample_time = sample_time.into(); |
| 413 | T::regs().sqr1().modify(|w| { | 429 | if channel.channel() <= 9 { |
| 414 | w.set_sq(_i, channel.channel()); | 430 | T::regs() |
| 415 | }); | 431 | .smpr() |
| 416 | } | 432 | .modify(|reg| reg.set_smp(channel.channel() as _, sample_time)); |
| 417 | 4..=8 => { | 433 | } else { |
| 418 | T::regs().sqr2().modify(|w| { | 434 | T::regs() |
| 419 | w.set_sq(_i - 4, channel.channel()); | 435 | .smpr2() |
| 420 | }); | 436 | .modify(|reg| reg.set_smp((channel.channel() - 10) as _, sample_time)); |
| 421 | } | ||
| 422 | 9..=13 => { | ||
| 423 | T::regs().sqr3().modify(|w| { | ||
| 424 | w.set_sq(_i - 9, channel.channel()); | ||
| 425 | }); | ||
| 426 | } | ||
| 427 | 14..=15 => { | ||
| 428 | T::regs().sqr4().modify(|w| { | ||
| 429 | w.set_sq(_i - 14, channel.channel()); | ||
| 430 | }); | ||
| 431 | } | ||
| 432 | _ => unreachable!(), | ||
| 433 | } | 437 | } |
| 434 | } | ||
| 435 | |||
| 436 | // Set continuous mode with oneshot dma. | ||
| 437 | // Clear overrun flag before starting transfer. | ||
| 438 | T::regs().isr().modify(|reg| { | ||
| 439 | reg.set_ovr(true); | ||
| 440 | }); | ||
| 441 | |||
| 442 | T::regs().cfgr().modify(|reg| { | ||
| 443 | reg.set_discen(false); | ||
| 444 | reg.set_cont(true); | ||
| 445 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 446 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 447 | }); | ||
| 448 | |||
| 449 | let request = rx_dma.request(); | ||
| 450 | let transfer = unsafe { | ||
| 451 | Transfer::new_read( | ||
| 452 | rx_dma, | ||
| 453 | request, | ||
| 454 | T::regs().dr().as_ptr() as *mut u16, | ||
| 455 | readings, | ||
| 456 | Default::default(), | ||
| 457 | ) | ||
| 458 | }; | ||
| 459 | 438 | ||
| 460 | // Start conversion | 439 | let idx = match n { |
| 461 | T::regs().cr().modify(|reg| { | 440 | 0..=3 => n, |
| 462 | reg.set_adstart(true); | 441 | 4..=8 => n - 4, |
| 463 | }); | 442 | 9..=13 => n - 9, |
| 443 | 14..=15 => n - 14, | ||
| 444 | _ => unreachable!(), | ||
| 445 | }; | ||
| 464 | 446 | ||
| 465 | // Wait for conversion sequence to finish. | 447 | T::regs().jsqr().modify(|w| w.set_jsq(idx, channel.channel())); |
| 466 | transfer.await; | 448 | } |
| 467 | 449 | ||
| 468 | // Ensure conversions are finished. | 450 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); |
| 469 | Self::cancel_conversions(); | ||
| 470 | 451 | ||
| 471 | // Reset configuration. | 452 | // Set external trigger for injected conversion sequence |
| 472 | T::regs().cfgr().modify(|reg| { | 453 | // Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 473 | reg.set_cont(false); | 454 | T::regs().jsqr().modify(|r| { |
| 455 | r.set_jextsel(trigger.channel); | ||
| 456 | r.set_jexten(trigger.edge); | ||
| 474 | }); | 457 | }); |
| 475 | } | ||
| 476 | |||
| 477 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 478 | // Configure channel | ||
| 479 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 480 | } | ||
| 481 | 458 | ||
| 482 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 459 | // Enable end of injected sequence interrupt |
| 483 | Self::configure_channel(channel, self.sample_time); | 460 | T::regs().ier().modify(|r| r.set_jeosie(interrupt)); |
| 484 | #[cfg(stm32h7)] | ||
| 485 | { | ||
| 486 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 487 | T::regs() | ||
| 488 | .pcsel() | ||
| 489 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 490 | } | ||
| 491 | 461 | ||
| 492 | T::regs().sqr1().write(|reg| { | 462 | Self::start_injected_conversions(); |
| 493 | reg.set_sq(0, channel.channel()); | ||
| 494 | reg.set_l(0); | ||
| 495 | }); | ||
| 496 | 463 | ||
| 497 | self.convert() | 464 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels |
| 498 | } | 465 | } |
| 499 | 466 | ||
| 500 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | 467 | /// Configures ADC for both regular conversions with a ring-buffered DMA and injected conversions. |
| 501 | let sample_time = sample_time.into(); | 468 | /// |
| 502 | if ch <= 9 { | 469 | /// # Parameters |
| 503 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | 470 | /// - `dma`: The DMA peripheral to use for the ring-buffered ADC transfers. |
| 504 | } else { | 471 | /// - `dma_buf`: The buffer to store DMA-transferred samples for regular conversions. |
| 505 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | 472 | /// - `regular_sequence`: The sequence of channels and their sample times for regular conversions. |
| 473 | /// - `regular_conversion_mode`: The mode for regular conversions (e.g., continuous or triggered). | ||
| 474 | /// - `injected_sequence`: An array of channels and sample times for injected conversions (length `N`). | ||
| 475 | /// - `injected_trigger`: The trigger source for injected conversions. | ||
| 476 | /// - `injected_interrupt`: Whether to enable the end-of-sequence interrupt for injected conversions. | ||
| 477 | /// | ||
| 478 | /// Injected conversions are typically used with interrupts. If ADC1 and ADC2 are used in dual mode, | ||
| 479 | /// it is recommended to enable interrupts only for the ADC whose sequence takes the longest to complete. | ||
| 480 | /// | ||
| 481 | /// # Returns | ||
| 482 | /// A tuple containing: | ||
| 483 | /// 1. `RingBufferedAdc<'a, T>` — the configured ADC for regular conversions using DMA. | ||
| 484 | /// 2. `InjectedAdc<T, N>` — the configured ADC for injected conversions. | ||
| 485 | /// | ||
| 486 | /// # Safety | ||
| 487 | /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the | ||
| 488 | /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently. | ||
| 489 | /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way. | ||
| 490 | pub fn into_ring_buffered_and_injected<'a, const N: usize>( | ||
| 491 | self, | ||
| 492 | dma: Peri<'a, impl RxDma<T>>, | ||
| 493 | dma_buf: &'a mut [u16], | ||
| 494 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, | ||
| 495 | regular_conversion_mode: RegularConversionMode, | ||
| 496 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], | ||
| 497 | injected_trigger: ConversionTrigger, | ||
| 498 | injected_interrupt: bool, | ||
| 499 | ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | ||
| 500 | unsafe { | ||
| 501 | ( | ||
| 502 | Self { | ||
| 503 | adc: self.adc.clone_unchecked(), | ||
| 504 | } | ||
| 505 | .into_ring_buffered(dma, dma_buf, regular_sequence, regular_conversion_mode), | ||
| 506 | Self { | ||
| 507 | adc: self.adc.clone_unchecked(), | ||
| 508 | } | ||
| 509 | .setup_injected_conversions(injected_sequence, injected_trigger, injected_interrupt), | ||
| 510 | ) | ||
| 506 | } | 511 | } |
| 507 | } | 512 | } |
| 508 | 513 | ||
| 509 | fn cancel_conversions() { | 514 | /// Stop injected conversions |
| 515 | pub(super) fn stop_injected_conversions() { | ||
| 510 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 516 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 511 | T::regs().cr().modify(|reg| { | 517 | T::regs().cr().modify(|reg| { |
| 512 | reg.set_adstp(Adstp::STOP); | 518 | reg.set_jadstp(Adstp::STOP); |
| 513 | }); | 519 | }); |
| 514 | while T::regs().cr().read().adstart() {} | 520 | // The software must poll JADSTART until the bit is reset before assuming the |
| 521 | // ADC is completely stopped | ||
| 522 | while T::regs().cr().read().jadstart() {} | ||
| 515 | } | 523 | } |
| 516 | } | 524 | } |
| 517 | } | ||
| 518 | 525 | ||
| 519 | /// Implemented for ADCs that have a Temperature channel | 526 | /// Start injected ADC conversion |
| 520 | pub trait TemperatureChannel { | 527 | pub(super) fn start_injected_conversions() { |
| 521 | const CHANNEL: u8; | 528 | T::regs().cr().modify(|reg| { |
| 522 | } | 529 | reg.set_jadstart(true); |
| 523 | /// Implemented for ADCs that have a Vref channel | 530 | }); |
| 524 | pub trait VrefChannel { | 531 | } |
| 525 | const CHANNEL: u8; | ||
| 526 | } | 532 | } |
| 527 | /// Implemented for ADCs that have a VBat channel | 533 | |
| 528 | pub trait VBatChannel { | 534 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
| 529 | const CHANNEL: u8; | 535 | /// Read sampled data from all injected ADC injected ranks |
| 536 | /// Clear the JEOS flag to allow a new injected sequence | ||
| 537 | pub(super) fn read_injected_data() -> [u16; N] { | ||
| 538 | let mut data = [0u16; N]; | ||
| 539 | for i in 0..N { | ||
| 540 | data[i] = T::regs().jdr(i).read().jdata(); | ||
| 541 | } | ||
| 542 | |||
| 543 | // Clear JEOS by writing 1 | ||
| 544 | T::regs().isr().modify(|r| r.set_jeos(true)); | ||
| 545 | data | ||
| 546 | } | ||
| 530 | } | 547 | } |
| 531 | 548 | ||
| 532 | #[cfg(stm32g4)] | 549 | #[cfg(stm32g4)] |
| 533 | mod g4 { | 550 | mod g4 { |
| 534 | pub use super::*; | 551 | use crate::adc::{SealedSpecialConverter, Temperature, Vbat, VrefInt}; |
| 535 | 552 | ||
| 536 | impl TemperatureChannel for crate::peripherals::ADC1 { | 553 | impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC1 { |
| 537 | const CHANNEL: u8 = 16; | 554 | const CHANNEL: u8 = 16; |
| 538 | } | 555 | } |
| 539 | 556 | ||
| 540 | impl VrefChannel for crate::peripherals::ADC1 { | 557 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC1 { |
| 541 | const CHANNEL: u8 = 18; | 558 | const CHANNEL: u8 = 18; |
| 542 | } | 559 | } |
| 543 | 560 | ||
| 544 | impl VBatChannel for crate::peripherals::ADC1 { | 561 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC1 { |
| 545 | const CHANNEL: u8 = 17; | 562 | const CHANNEL: u8 = 17; |
| 546 | } | 563 | } |
| 547 | 564 | ||
| 548 | #[cfg(peri_adc3_common)] | 565 | #[cfg(peri_adc3_common)] |
| 549 | impl VrefChannel for crate::peripherals::ADC3 { | 566 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC3 { |
| 550 | const CHANNEL: u8 = 18; | 567 | const CHANNEL: u8 = 18; |
| 551 | } | 568 | } |
| 552 | 569 | ||
| 553 | #[cfg(peri_adc3_common)] | 570 | #[cfg(peri_adc3_common)] |
| 554 | impl VBatChannel for crate::peripherals::ADC3 { | 571 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC3 { |
| 555 | const CHANNEL: u8 = 17; | 572 | const CHANNEL: u8 = 17; |
| 556 | } | 573 | } |
| 557 | 574 | ||
| 558 | #[cfg(not(stm32g4x1))] | 575 | #[cfg(not(stm32g4x1))] |
| 559 | impl VrefChannel for crate::peripherals::ADC4 { | 576 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC4 { |
| 560 | const CHANNEL: u8 = 18; | 577 | const CHANNEL: u8 = 18; |
| 561 | } | 578 | } |
| 562 | 579 | ||
| 563 | #[cfg(not(stm32g4x1))] | 580 | #[cfg(not(stm32g4x1))] |
| 564 | impl TemperatureChannel for crate::peripherals::ADC5 { | 581 | impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC5 { |
| 565 | const CHANNEL: u8 = 4; | 582 | const CHANNEL: u8 = 4; |
| 566 | } | 583 | } |
| 567 | 584 | ||
| 568 | #[cfg(not(stm32g4x1))] | 585 | #[cfg(not(stm32g4x1))] |
| 569 | impl VrefChannel for crate::peripherals::ADC5 { | 586 | impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC5 { |
| 570 | const CHANNEL: u8 = 18; | 587 | const CHANNEL: u8 = 18; |
| 571 | } | 588 | } |
| 572 | 589 | ||
| 573 | #[cfg(not(stm32g4x1))] | 590 | #[cfg(not(stm32g4x1))] |
| 574 | impl VBatChannel for crate::peripherals::ADC5 { | 591 | impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC5 { |
| 575 | const CHANNEL: u8 = 17; | 592 | const CHANNEL: u8 = 17; |
| 576 | } | 593 | } |
| 577 | } | 594 | } |
| @@ -579,13 +596,13 @@ mod g4 { | |||
| 579 | // TODO this should look at each ADC individually and impl the correct channels | 596 | // TODO this should look at each ADC individually and impl the correct channels |
| 580 | #[cfg(stm32h7)] | 597 | #[cfg(stm32h7)] |
| 581 | mod h7 { | 598 | mod h7 { |
| 582 | impl<T: Instance> TemperatureChannel for T { | 599 | impl<T: Instance> SealedSpecialConverter<Temperature> for T { |
| 583 | const CHANNEL: u8 = 18; | 600 | const CHANNEL: u8 = 18; |
| 584 | } | 601 | } |
| 585 | impl<T: Instance> VrefChannel for T { | 602 | impl<T: Instance> SealedSpecialConverter<VrefInt> for T { |
| 586 | const CHANNEL: u8 = 19; | 603 | const CHANNEL: u8 = 19; |
| 587 | } | 604 | } |
| 588 | impl<T: Instance> VBatChannel for T { | 605 | impl<T: Instance> SealedSpecialConverter<Vbat> for T { |
| 589 | // TODO this should be 14 for H7a/b/35 | 606 | // TODO this should be 14 for H7a/b/35 |
| 590 | const CHANNEL: u8 = 17; | 607 | const CHANNEL: u8 = 17; |
| 591 | } | 608 | } |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs new file mode 100644 index 000000000..ccaa5d1b2 --- /dev/null +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 4 | #[allow(unused_imports)] | ||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use super::{AnyAdcChannel, SampleTime}; | ||
| 8 | #[allow(unused_imports)] | ||
| 9 | use crate::adc::Instance; | ||
| 10 | use crate::adc::{Adc, AnyInstance}; | ||
| 11 | |||
| 12 | /// Injected ADC sequence with owned channels. | ||
| 13 | pub struct InjectedAdc<T: Instance, const N: usize> { | ||
| 14 | _channels: [(AnyAdcChannel<T>, SampleTime); N], | ||
| 15 | _phantom: PhantomData<T>, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | ||
| 19 | pub(crate) fn new(channels: [(AnyAdcChannel<T>, SampleTime); N]) -> Self { | ||
| 20 | Self { | ||
| 21 | _channels: channels, | ||
| 22 | _phantom: PhantomData, | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | pub fn stop_injected_conversions(&mut self) { | ||
| 27 | Adc::<T>::stop_injected_conversions() | ||
| 28 | } | ||
| 29 | |||
| 30 | pub fn start_injected_conversions(&mut self) { | ||
| 31 | Adc::<T>::start_injected_conversions() | ||
| 32 | } | ||
| 33 | |||
| 34 | pub fn read_injected_samples(&mut self) -> [u16; N] { | ||
| 35 | InjectedAdc::<T, N>::read_injected_data() | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<T: Instance + AnyInstance, const N: usize> Drop for InjectedAdc<T, N> { | ||
| 40 | fn drop(&mut self) { | ||
| 41 | T::stop(); | ||
| 42 | compiler_fence(Ordering::SeqCst); | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ea986f4cf..6d53d9b91 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -17,38 +17,38 @@ | |||
| 17 | #[cfg_attr(adc_c0, path = "c0.rs")] | 17 | #[cfg_attr(adc_c0, path = "c0.rs")] |
| 18 | mod _version; | 18 | mod _version; |
| 19 | 19 | ||
| 20 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 21 | mod ringbuffered; | ||
| 22 | |||
| 20 | use core::marker::PhantomData; | 23 | use core::marker::PhantomData; |
| 21 | 24 | ||
| 22 | #[allow(unused)] | 25 | #[allow(unused)] |
| 23 | #[cfg(not(any(adc_f3v3, adc_wba)))] | 26 | #[cfg(not(any(adc_f3v3, adc_wba)))] |
| 24 | pub use _version::*; | 27 | pub use _version::*; |
| 25 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 28 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 26 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 29 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 27 | use embassy_sync::waitqueue::AtomicWaker; | 30 | use embassy_sync::waitqueue::AtomicWaker; |
| 31 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 32 | pub use ringbuffered::RingBufferedAdc; | ||
| 28 | 33 | ||
| 29 | #[cfg(any(adc_u5, adc_wba))] | 34 | #[cfg(any(adc_u5, adc_wba))] |
| 30 | #[path = "adc4.rs"] | 35 | #[path = "adc4.rs"] |
| 31 | pub mod adc4; | 36 | pub mod adc4; |
| 32 | 37 | ||
| 38 | #[allow(unused)] | ||
| 39 | pub(self) use crate::block_for_us as blocking_delay_us; | ||
| 33 | pub use crate::pac::adc::vals; | 40 | pub use crate::pac::adc::vals; |
| 34 | #[cfg(not(any(adc_f1, adc_f3v3)))] | 41 | #[cfg(not(any(adc_f1, adc_f3v3)))] |
| 35 | pub use crate::pac::adc::vals::Res as Resolution; | 42 | pub use crate::pac::adc::vals::Res as Resolution; |
| 36 | pub use crate::pac::adc::vals::SampleTime; | 43 | pub use crate::pac::adc::vals::SampleTime; |
| 37 | use crate::peripherals; | 44 | use crate::peripherals; |
| 38 | 45 | ||
| 39 | #[cfg(not(adc_wba))] | 46 | dma_trait!(RxDma, AnyInstance); |
| 40 | dma_trait!(RxDma, Instance); | ||
| 41 | #[cfg(adc_u5)] | ||
| 42 | dma_trait!(RxDma4, adc4::Instance); | ||
| 43 | #[cfg(adc_wba)] | ||
| 44 | dma_trait!(RxDma4, adc4::Instance); | ||
| 45 | 47 | ||
| 46 | /// Analog to Digital driver. | 48 | /// Analog to Digital driver. |
| 47 | pub struct Adc<'d, T: Instance> { | 49 | pub struct Adc<'d, T: AnyInstance> { |
| 48 | #[allow(unused)] | 50 | #[allow(unused)] |
| 49 | adc: crate::Peri<'d, T>, | 51 | adc: crate::Peri<'d, T>, |
| 50 | #[cfg(not(any(adc_f3v3, adc_f3v2, adc_wba)))] | ||
| 51 | sample_time: SampleTime, | ||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] | 54 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| @@ -82,22 +82,318 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 82 | 82 | ||
| 83 | #[allow(unused)] | 83 | #[allow(unused)] |
| 84 | fn channel(&self) -> u8; | 84 | fn channel(&self) -> u8; |
| 85 | |||
| 86 | #[allow(unused)] | ||
| 87 | fn is_differential(&self) -> bool { | ||
| 88 | false | ||
| 89 | } | ||
| 85 | } | 90 | } |
| 86 | 91 | ||
| 87 | /// Performs a busy-wait delay for a specified number of microseconds. | 92 | // Temporary patch for ADCs that have not implemented the standard iface yet |
| 88 | #[allow(unused)] | 93 | #[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] |
| 89 | pub(crate) fn blocking_delay_us(us: u32) { | 94 | trait_set::trait_set! { |
| 90 | #[cfg(feature = "time")] | 95 | pub trait AnyInstance = Instance; |
| 91 | embassy_time::block_for(embassy_time::Duration::from_micros(us as u64)); | 96 | } |
| 92 | #[cfg(not(feature = "time"))] | 97 | |
| 93 | { | 98 | #[cfg(any( |
| 94 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | 99 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 |
| 95 | let us = us as u64; | 100 | ))] |
| 96 | let cycles = freq * us / 1_000_000; | 101 | pub trait BasicAnyInstance { |
| 97 | cortex_m::asm::delay(cycles as u32); | 102 | type SampleTime; |
| 103 | } | ||
| 104 | |||
| 105 | #[cfg(any( | ||
| 106 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 107 | ))] | ||
| 108 | pub(self) trait SealedAnyInstance: BasicAnyInstance { | ||
| 109 | fn enable(); | ||
| 110 | fn start(); | ||
| 111 | fn stop(); | ||
| 112 | fn convert() -> u16; | ||
| 113 | fn configure_dma(conversion_mode: ConversionMode); | ||
| 114 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); | ||
| 115 | #[allow(dead_code)] | ||
| 116 | fn dr() -> *mut u16; | ||
| 117 | } | ||
| 118 | |||
| 119 | // On chips without ADC4, AnyInstance is an Instance | ||
| 120 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4, adc_c0))] | ||
| 121 | #[allow(private_bounds)] | ||
| 122 | pub trait AnyInstance: SealedAnyInstance + Instance {} | ||
| 123 | |||
| 124 | // On chips with ADC4, AnyInstance is an Instance or adc4::Instance | ||
| 125 | #[cfg(any(adc_v4, adc_u5, adc_wba))] | ||
| 126 | #[allow(private_bounds)] | ||
| 127 | pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} | ||
| 128 | |||
| 129 | // Implement AnyInstance automatically for SealedAnyInstance | ||
| 130 | #[cfg(any( | ||
| 131 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 132 | ))] | ||
| 133 | impl<T: SealedAnyInstance + Instance> BasicAnyInstance for T { | ||
| 134 | type SampleTime = SampleTime; | ||
| 135 | } | ||
| 136 | |||
| 137 | #[cfg(any( | ||
| 138 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 139 | ))] | ||
| 140 | impl<T: SealedAnyInstance + Instance> AnyInstance for T {} | ||
| 141 | |||
| 142 | #[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] | ||
| 143 | /// Number of samples used for averaging. | ||
| 144 | #[derive(Copy, Clone, Debug)] | ||
| 145 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 146 | pub enum Averaging { | ||
| 147 | Disabled, | ||
| 148 | Samples2, | ||
| 149 | Samples4, | ||
| 150 | Samples8, | ||
| 151 | Samples16, | ||
| 152 | Samples32, | ||
| 153 | Samples64, | ||
| 154 | Samples128, | ||
| 155 | Samples256, | ||
| 156 | #[cfg(any(adc_c0, adc_v4, adc_u5))] | ||
| 157 | Samples512, | ||
| 158 | #[cfg(any(adc_c0, adc_v4, adc_u5))] | ||
| 159 | Samples1024, | ||
| 160 | } | ||
| 161 | |||
| 162 | #[cfg(any( | ||
| 163 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0 | ||
| 164 | ))] | ||
| 165 | pub(crate) enum ConversionMode { | ||
| 166 | // Should match the cfg on "read" below | ||
| 167 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 168 | Singular, | ||
| 169 | // Should match the cfg on "into_ring_buffered" below | ||
| 170 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 171 | Repeated(RegularConversionMode), | ||
| 172 | } | ||
| 173 | |||
| 174 | // Should match the cfg on "into_ring_buffered" below | ||
| 175 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 176 | // Conversion mode for regular ADC channels | ||
| 177 | #[derive(Copy, Clone)] | ||
| 178 | pub enum RegularConversionMode { | ||
| 179 | // Samples as fast as possible | ||
| 180 | Continuous, | ||
| 181 | #[cfg(adc_g4)] | ||
| 182 | // Sample at rate determined by external trigger | ||
| 183 | Triggered(ConversionTrigger), | ||
| 184 | } | ||
| 185 | |||
| 186 | impl<'d, T: AnyInstance> Adc<'d, T> { | ||
| 187 | #[cfg(any( | ||
| 188 | adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba, adc_c0 | ||
| 189 | ))] | ||
| 190 | /// Read an ADC pin. | ||
| 191 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: T::SampleTime) -> u16 { | ||
| 192 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | ||
| 193 | channel.setup(); | ||
| 194 | |||
| 195 | // Ensure no conversions are ongoing | ||
| 196 | T::stop(); | ||
| 197 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))] | ||
| 198 | T::enable(); | ||
| 199 | T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 200 | |||
| 201 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 202 | // | ||
| 203 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 204 | #[cfg(any(adc_g4, adc_h5))] | ||
| 205 | T::enable(); | ||
| 206 | |||
| 207 | T::convert() | ||
| 208 | } | ||
| 209 | |||
| 210 | #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 211 | /// Read one or multiple ADC regular channels using DMA. | ||
| 212 | /// | ||
| 213 | /// `sequence` iterator and `readings` must have the same length. | ||
| 214 | /// | ||
| 215 | /// Example | ||
| 216 | /// ```rust,ignore | ||
| 217 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 218 | /// | ||
| 219 | /// let mut adc = Adc::new(p.ADC1); | ||
| 220 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 221 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 222 | /// let mut measurements = [0u16; 2]; | ||
| 223 | /// | ||
| 224 | /// adc.read( | ||
| 225 | /// p.DMA1_CH2.reborrow(), | ||
| 226 | /// [ | ||
| 227 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 228 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 229 | /// ] | ||
| 230 | /// .into_iter(), | ||
| 231 | /// &mut measurements, | ||
| 232 | /// ) | ||
| 233 | /// .await; | ||
| 234 | /// defmt::info!("measurements: {}", measurements); | ||
| 235 | /// ``` | ||
| 236 | /// | ||
| 237 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 238 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 239 | /// | ||
| 240 | /// Note: Depending on hardware limitations, this method may require channels to be passed | ||
| 241 | /// in order or require the sequence to have the same sample time for all channnels, depending | ||
| 242 | /// on the number and properties of the channels in the sequence. This method will panic if | ||
| 243 | /// the hardware cannot deliver the requested configuration. | ||
| 244 | pub async fn read( | ||
| 245 | &mut self, | ||
| 246 | rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, | ||
| 247 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, T::SampleTime)>, | ||
| 248 | readings: &mut [u16], | ||
| 249 | ) { | ||
| 250 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 251 | assert!( | ||
| 252 | sequence.len() == readings.len(), | ||
| 253 | "Sequence length must be equal to readings length" | ||
| 254 | ); | ||
| 255 | assert!( | ||
| 256 | sequence.len() <= 16, | ||
| 257 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 258 | ); | ||
| 259 | |||
| 260 | // Ensure no conversions are ongoing | ||
| 261 | T::stop(); | ||
| 262 | #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 263 | T::enable(); | ||
| 264 | |||
| 265 | T::configure_sequence( | ||
| 266 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 267 | ); | ||
| 268 | |||
| 269 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 270 | // | ||
| 271 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 272 | #[cfg(any(adc_g4, adc_h5))] | ||
| 273 | T::enable(); | ||
| 274 | T::configure_dma(ConversionMode::Singular); | ||
| 275 | |||
| 276 | let request = rx_dma.request(); | ||
| 277 | let transfer = | ||
| 278 | unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::dr(), readings, Default::default()) }; | ||
| 279 | |||
| 280 | T::start(); | ||
| 281 | |||
| 282 | // Wait for conversion sequence to finish. | ||
| 283 | transfer.await; | ||
| 284 | |||
| 285 | // Ensure conversions are finished. | ||
| 286 | T::stop(); | ||
| 287 | } | ||
| 288 | |||
| 289 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 290 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 291 | /// | ||
| 292 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 293 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 294 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 295 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 296 | /// defines the period at which the buffer should be read. | ||
| 297 | /// | ||
| 298 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 299 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 300 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 301 | /// the buffer length should be `3 * 40 = 120`. | ||
| 302 | /// | ||
| 303 | /// # Parameters | ||
| 304 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 305 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 306 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 307 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 308 | /// | ||
| 309 | /// # Returns | ||
| 310 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 311 | /// | ||
| 312 | /// Note: Depending on hardware limitations, this method may require channels to be passed | ||
| 313 | /// in order or require the sequence to have the same sample time for all channnels, depending | ||
| 314 | /// on the number and properties of the channels in the sequence. This method will panic if | ||
| 315 | /// the hardware cannot deliver the requested configuration. | ||
| 316 | pub fn into_ring_buffered<'a>( | ||
| 317 | self, | ||
| 318 | dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>, | ||
| 319 | dma_buf: &'a mut [u16], | ||
| 320 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, | ||
| 321 | mode: RegularConversionMode, | ||
| 322 | ) -> RingBufferedAdc<'a, T> { | ||
| 323 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 324 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 325 | assert!( | ||
| 326 | sequence.len() <= 16, | ||
| 327 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 328 | ); | ||
| 329 | // Ensure no conversions are ongoing | ||
| 330 | T::stop(); | ||
| 331 | #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] | ||
| 332 | T::enable(); | ||
| 333 | |||
| 334 | T::configure_sequence( | ||
| 335 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 336 | ); | ||
| 337 | |||
| 338 | // On chips with differential channels, enable after configure_sequence to allow setting differential channels | ||
| 339 | // | ||
| 340 | // TODO: If hardware allows, enable after configure_sequence on all chips | ||
| 341 | #[cfg(any(adc_g4, adc_h5))] | ||
| 342 | T::enable(); | ||
| 343 | T::configure_dma(ConversionMode::Repeated(mode)); | ||
| 344 | |||
| 345 | core::mem::forget(self); | ||
| 346 | |||
| 347 | RingBufferedAdc::new(dma, dma_buf) | ||
| 98 | } | 348 | } |
| 99 | } | 349 | } |
| 100 | 350 | ||
| 351 | pub(self) trait SpecialChannel {} | ||
| 352 | |||
| 353 | /// Implemented for ADCs that have a special channel | ||
| 354 | trait SealedSpecialConverter<T: SpecialChannel + Sized> { | ||
| 355 | const CHANNEL: u8; | ||
| 356 | } | ||
| 357 | |||
| 358 | #[allow(private_bounds)] | ||
| 359 | pub trait SpecialConverter<T: SpecialChannel + Sized>: SealedSpecialConverter<T> {} | ||
| 360 | |||
| 361 | impl<C: SpecialChannel + Sized, T: SealedSpecialConverter<C>> SpecialConverter<C> for T {} | ||
| 362 | |||
| 363 | impl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> AdcChannel<T> for C {} | ||
| 364 | impl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> SealedAdcChannel<T> for C { | ||
| 365 | fn channel(&self) -> u8 { | ||
| 366 | T::CHANNEL | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | pub struct VrefInt; | ||
| 371 | impl SpecialChannel for VrefInt {} | ||
| 372 | |||
| 373 | impl VrefInt { | ||
| 374 | #[cfg(any(adc_f3v1, adc_f3v2))] | ||
| 375 | /// The value that vref would be if vdda was at 3300mv | ||
| 376 | pub fn calibrated_value(&self) -> u16 { | ||
| 377 | crate::pac::VREFINTCAL.data().read() | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | /// Internal temperature channel. | ||
| 382 | pub struct Temperature; | ||
| 383 | impl SpecialChannel for Temperature {} | ||
| 384 | |||
| 385 | /// Internal battery voltage channel. | ||
| 386 | pub struct Vbat; | ||
| 387 | impl SpecialChannel for Vbat {} | ||
| 388 | |||
| 389 | /// Vcore channel. | ||
| 390 | pub struct Vcore; | ||
| 391 | impl SpecialChannel for Vcore {} | ||
| 392 | |||
| 393 | /// Internal dac channel. | ||
| 394 | pub struct Dac; | ||
| 395 | impl SpecialChannel for Dac {} | ||
| 396 | |||
| 101 | /// ADC instance. | 397 | /// ADC instance. |
| 102 | #[cfg(not(any( | 398 | #[cfg(not(any( |
| 103 | adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, | 399 | adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, |
| @@ -127,6 +423,7 @@ pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | |||
| 127 | 423 | ||
| 128 | AnyAdcChannel { | 424 | AnyAdcChannel { |
| 129 | channel: self.channel(), | 425 | channel: self.channel(), |
| 426 | is_differential: self.is_differential(), | ||
| 130 | _phantom: PhantomData, | 427 | _phantom: PhantomData, |
| 131 | } | 428 | } |
| 132 | } | 429 | } |
| @@ -138,14 +435,19 @@ pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | |||
| 138 | /// storing them in an array. | 435 | /// storing them in an array. |
| 139 | pub struct AnyAdcChannel<T> { | 436 | pub struct AnyAdcChannel<T> { |
| 140 | channel: u8, | 437 | channel: u8, |
| 438 | is_differential: bool, | ||
| 141 | _phantom: PhantomData<T>, | 439 | _phantom: PhantomData<T>, |
| 142 | } | 440 | } |
| 143 | impl_peripheral!(AnyAdcChannel<T: Instance>); | 441 | impl_peripheral!(AnyAdcChannel<T: AnyInstance>); |
| 144 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} | 442 | impl<T: AnyInstance> AdcChannel<T> for AnyAdcChannel<T> {} |
| 145 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | 443 | impl<T: AnyInstance> SealedAdcChannel<T> for AnyAdcChannel<T> { |
| 146 | fn channel(&self) -> u8 { | 444 | fn channel(&self) -> u8 { |
| 147 | self.channel | 445 | self.channel |
| 148 | } | 446 | } |
| 447 | |||
| 448 | fn is_differential(&self) -> bool { | ||
| 449 | self.is_differential | ||
| 450 | } | ||
| 149 | } | 451 | } |
| 150 | 452 | ||
| 151 | impl<T> AnyAdcChannel<T> { | 453 | impl<T> AnyAdcChannel<T> { |
| @@ -264,6 +566,39 @@ macro_rules! impl_adc_pin { | |||
| 264 | }; | 566 | }; |
| 265 | } | 567 | } |
| 266 | 568 | ||
| 569 | #[allow(unused_macros)] | ||
| 570 | macro_rules! impl_adc_pair { | ||
| 571 | ($inst:ident, $pin:ident, $npin:ident, $ch:expr) => { | ||
| 572 | impl crate::adc::AdcChannel<peripherals::$inst> | ||
| 573 | for ( | ||
| 574 | crate::Peri<'_, crate::peripherals::$pin>, | ||
| 575 | crate::Peri<'_, crate::peripherals::$npin>, | ||
| 576 | ) | ||
| 577 | { | ||
| 578 | } | ||
| 579 | impl crate::adc::SealedAdcChannel<peripherals::$inst> | ||
| 580 | for ( | ||
| 581 | crate::Peri<'_, crate::peripherals::$pin>, | ||
| 582 | crate::Peri<'_, crate::peripherals::$npin>, | ||
| 583 | ) | ||
| 584 | { | ||
| 585 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] | ||
| 586 | fn setup(&mut self) { | ||
| 587 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(&mut self.0); | ||
| 588 | <crate::peripherals::$npin as crate::gpio::SealedPin>::set_as_analog(&mut self.1); | ||
| 589 | } | ||
| 590 | |||
| 591 | fn channel(&self) -> u8 { | ||
| 592 | $ch | ||
| 593 | } | ||
| 594 | |||
| 595 | fn is_differential(&self) -> bool { | ||
| 596 | true | ||
| 597 | } | ||
| 598 | } | ||
| 599 | }; | ||
| 600 | } | ||
| 601 | |||
| 267 | /// Get the maximum reading value for this resolution. | 602 | /// Get the maximum reading value for this resolution. |
| 268 | /// | 603 | /// |
| 269 | /// This is `2**n - 1`. | 604 | /// This is `2**n - 1`. |
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs new file mode 100644 index 000000000..5437866d3 --- /dev/null +++ b/embassy-stm32/src/adc/ringbuffered.rs | |||
| @@ -0,0 +1,180 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 4 | #[allow(unused_imports)] | ||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use crate::adc::AnyInstance; | ||
| 8 | #[allow(unused_imports)] | ||
| 9 | use crate::adc::{Instance, RxDma}; | ||
| 10 | #[allow(unused_imports)] | ||
| 11 | use crate::dma::{ReadableRingBuffer, TransferOptions}; | ||
| 12 | use crate::rcc; | ||
| 13 | |||
| 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 15 | pub struct OverrunError; | ||
| 16 | |||
| 17 | pub struct RingBufferedAdc<'d, T: Instance> { | ||
| 18 | _phantom: PhantomData<T>, | ||
| 19 | ring_buf: ReadableRingBuffer<'d, u16>, | ||
| 20 | } | ||
| 21 | |||
| 22 | impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { | ||
| 23 | pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self { | ||
| 24 | //dma side setup | ||
| 25 | let opts = TransferOptions { | ||
| 26 | half_transfer_ir: true, | ||
| 27 | circular: true, | ||
| 28 | ..Default::default() | ||
| 29 | }; | ||
| 30 | |||
| 31 | // Safety: we forget the struct before this function returns. | ||
| 32 | let request = dma.request(); | ||
| 33 | |||
| 34 | let ring_buf = | ||
| 35 | unsafe { ReadableRingBuffer::new(dma, request, T::regs().dr().as_ptr() as *mut u16, dma_buf, opts) }; | ||
| 36 | |||
| 37 | Self { | ||
| 38 | _phantom: PhantomData, | ||
| 39 | ring_buf, | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | ||
| 44 | pub fn start(&mut self) { | ||
| 45 | compiler_fence(Ordering::SeqCst); | ||
| 46 | self.ring_buf.start(); | ||
| 47 | |||
| 48 | T::start(); | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn stop(&mut self) { | ||
| 52 | self.ring_buf.request_pause(); | ||
| 53 | |||
| 54 | compiler_fence(Ordering::SeqCst); | ||
| 55 | } | ||
| 56 | |||
| 57 | pub fn clear(&mut self) { | ||
| 58 | self.ring_buf.clear(); | ||
| 59 | } | ||
| 60 | |||
| 61 | /// Reads measurements from the DMA ring buffer. | ||
| 62 | /// | ||
| 63 | /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. | ||
| 64 | /// The length of the `measurements` array should be exactly half of the DMA buffer length. | ||
| 65 | /// Because interrupts are only generated if half or full DMA transfer completes. | ||
| 66 | /// | ||
| 67 | /// Each call to `read` will populate the `measurements` array in the same order as the channels | ||
| 68 | /// defined with `sequence`. There will be many sequences worth of measurements in this array | ||
| 69 | /// because it only returns if at least half of the DMA buffer is filled. For example if 2 | ||
| 70 | /// channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`. | ||
| 71 | /// | ||
| 72 | /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly | ||
| 73 | /// running tasks. Otherwise, you'll see constant Overrun errors occurring, this means that | ||
| 74 | /// you're sampling too quickly for the task to handle, and you may need to increase the buffer size. | ||
| 75 | /// Example: | ||
| 76 | /// ```rust,ignore | ||
| 77 | /// const DMA_BUF_LEN: usize = 120; | ||
| 78 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 79 | /// | ||
| 80 | /// let mut adc = Adc::new(p.ADC1); | ||
| 81 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 82 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 83 | /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; | ||
| 84 | /// | ||
| 85 | /// let mut ring_buffered_adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( | ||
| 86 | /// p.DMA2_CH0, | ||
| 87 | /// adc_dma_buf, [ | ||
| 88 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 89 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 90 | /// ].into_iter()); | ||
| 91 | /// | ||
| 92 | /// | ||
| 93 | /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; | ||
| 94 | /// loop { | ||
| 95 | /// match ring_buffered_adc.read(&mut measurements).await { | ||
| 96 | /// Ok(_) => { | ||
| 97 | /// defmt::info!("adc1: {}", measurements); | ||
| 98 | /// } | ||
| 99 | /// Err(e) => { | ||
| 100 | /// defmt::warn!("Error: {:?}", e); | ||
| 101 | /// } | ||
| 102 | /// } | ||
| 103 | /// } | ||
| 104 | /// ``` | ||
| 105 | /// | ||
| 106 | /// | ||
| 107 | /// [`teardown_adc`]: #method.teardown_adc | ||
| 108 | /// [`start_continuous_sampling`]: #method.start_continuous_sampling | ||
| 109 | pub async fn read(&mut self, measurements: &mut [u16]) -> Result<usize, OverrunError> { | ||
| 110 | assert_eq!( | ||
| 111 | self.ring_buf.capacity() / 2, | ||
| 112 | measurements.len(), | ||
| 113 | "Buffer size must be half the size of the ring buffer" | ||
| 114 | ); | ||
| 115 | |||
| 116 | if !self.ring_buf.is_running() { | ||
| 117 | self.start(); | ||
| 118 | } | ||
| 119 | |||
| 120 | #[cfg(adc_v2)] | ||
| 121 | { | ||
| 122 | // Clear overrun flag if set. | ||
| 123 | if T::regs().sr().read().ovr() { | ||
| 124 | self.stop(); | ||
| 125 | |||
| 126 | return Err(OverrunError); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Read bytes that are readily available in the ring buffer. | ||
| 134 | /// If no bytes are currently available in the buffer the call waits until the some | ||
| 135 | /// bytes are available (at least one byte and at most half the buffer size) | ||
| 136 | /// | ||
| 137 | /// Background receive is started if `start_continuous_sampling()` has not been previously called. | ||
| 138 | /// | ||
| 139 | /// Receive in the background is terminated if an error is returned. | ||
| 140 | /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`. | ||
| 141 | pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> { | ||
| 142 | if !self.ring_buf.is_running() { | ||
| 143 | self.start(); | ||
| 144 | } | ||
| 145 | |||
| 146 | #[cfg(adc_v2)] | ||
| 147 | { | ||
| 148 | // Clear overrun flag if set. | ||
| 149 | if T::regs().sr().read().ovr() { | ||
| 150 | self.stop(); | ||
| 151 | |||
| 152 | return Err(OverrunError); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | loop { | ||
| 156 | match self.ring_buf.read(buf) { | ||
| 157 | Ok((0, _)) => {} | ||
| 158 | Ok((len, _)) => { | ||
| 159 | return Ok(len); | ||
| 160 | } | ||
| 161 | Err(_) => { | ||
| 162 | self.ring_buf.request_pause(); | ||
| 163 | |||
| 164 | return Err(OverrunError); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | impl<T: Instance + AnyInstance> Drop for RingBufferedAdc<'_, T> { | ||
| 172 | fn drop(&mut self) { | ||
| 173 | T::stop(); | ||
| 174 | |||
| 175 | compiler_fence(Ordering::SeqCst); | ||
| 176 | |||
| 177 | self.ring_buf.request_pause(); | ||
| 178 | rcc::disable::<T>(); | ||
| 179 | } | ||
| 180 | } | ||
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs deleted file mode 100644 index 6f69e8486..000000000 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ /dev/null | |||
| @@ -1,432 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::mem; | ||
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 4 | |||
| 5 | use stm32_metapac::adc::vals::SampleTime; | ||
| 6 | |||
| 7 | use crate::adc::{Adc, AdcChannel, Instance, RxDma}; | ||
| 8 | use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; | ||
| 9 | use crate::pac::adc::vals; | ||
| 10 | use crate::{rcc, Peri}; | ||
| 11 | |||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | pub struct OverrunError; | ||
| 14 | |||
| 15 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | ||
| 16 | r.sr().modify(|regs| { | ||
| 17 | regs.set_eoc(false); | ||
| 18 | regs.set_ovr(false); | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | #[derive(PartialOrd, PartialEq, Debug, Clone, Copy)] | ||
| 23 | pub enum Sequence { | ||
| 24 | One, | ||
| 25 | Two, | ||
| 26 | Three, | ||
| 27 | Four, | ||
| 28 | Five, | ||
| 29 | Six, | ||
| 30 | Seven, | ||
| 31 | Eight, | ||
| 32 | Nine, | ||
| 33 | Ten, | ||
| 34 | Eleven, | ||
| 35 | Twelve, | ||
| 36 | Thirteen, | ||
| 37 | Fourteen, | ||
| 38 | Fifteen, | ||
| 39 | Sixteen, | ||
| 40 | } | ||
| 41 | |||
| 42 | impl From<Sequence> for u8 { | ||
| 43 | fn from(s: Sequence) -> u8 { | ||
| 44 | match s { | ||
| 45 | Sequence::One => 0, | ||
| 46 | Sequence::Two => 1, | ||
| 47 | Sequence::Three => 2, | ||
| 48 | Sequence::Four => 3, | ||
| 49 | Sequence::Five => 4, | ||
| 50 | Sequence::Six => 5, | ||
| 51 | Sequence::Seven => 6, | ||
| 52 | Sequence::Eight => 7, | ||
| 53 | Sequence::Nine => 8, | ||
| 54 | Sequence::Ten => 9, | ||
| 55 | Sequence::Eleven => 10, | ||
| 56 | Sequence::Twelve => 11, | ||
| 57 | Sequence::Thirteen => 12, | ||
| 58 | Sequence::Fourteen => 13, | ||
| 59 | Sequence::Fifteen => 14, | ||
| 60 | Sequence::Sixteen => 15, | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | impl From<u8> for Sequence { | ||
| 66 | fn from(val: u8) -> Self { | ||
| 67 | match val { | ||
| 68 | 0 => Sequence::One, | ||
| 69 | 1 => Sequence::Two, | ||
| 70 | 2 => Sequence::Three, | ||
| 71 | 3 => Sequence::Four, | ||
| 72 | 4 => Sequence::Five, | ||
| 73 | 5 => Sequence::Six, | ||
| 74 | 6 => Sequence::Seven, | ||
| 75 | 7 => Sequence::Eight, | ||
| 76 | 8 => Sequence::Nine, | ||
| 77 | 9 => Sequence::Ten, | ||
| 78 | 10 => Sequence::Eleven, | ||
| 79 | 11 => Sequence::Twelve, | ||
| 80 | 12 => Sequence::Thirteen, | ||
| 81 | 13 => Sequence::Fourteen, | ||
| 82 | 14 => Sequence::Fifteen, | ||
| 83 | 15 => Sequence::Sixteen, | ||
| 84 | _ => panic!("Invalid sequence number"), | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | pub struct RingBufferedAdc<'d, T: Instance> { | ||
| 90 | _phantom: PhantomData<T>, | ||
| 91 | ring_buf: ReadableRingBuffer<'d, u16>, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 95 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 96 | /// | ||
| 97 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 98 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 99 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 100 | /// | ||
| 101 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 102 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 103 | /// | ||
| 104 | /// [`read`]: #method.read | ||
| 105 | pub fn into_ring_buffered(self, dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> RingBufferedAdc<'d, T> { | ||
| 106 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 107 | |||
| 108 | let opts: crate::dma::TransferOptions = TransferOptions { | ||
| 109 | half_transfer_ir: true, | ||
| 110 | priority: Priority::VeryHigh, | ||
| 111 | ..Default::default() | ||
| 112 | }; | ||
| 113 | |||
| 114 | // Safety: we forget the struct before this function returns. | ||
| 115 | let rx_src = T::regs().dr().as_ptr() as *mut u16; | ||
| 116 | let request = dma.request(); | ||
| 117 | |||
| 118 | let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) }; | ||
| 119 | |||
| 120 | // Don't disable the clock | ||
| 121 | mem::forget(self); | ||
| 122 | |||
| 123 | RingBufferedAdc { | ||
| 124 | _phantom: PhantomData, | ||
| 125 | ring_buf, | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | impl<'d, T: Instance> RingBufferedAdc<'d, T> { | ||
| 131 | fn is_on() -> bool { | ||
| 132 | T::regs().cr2().read().adon() | ||
| 133 | } | ||
| 134 | |||
| 135 | fn stop_adc() { | ||
| 136 | T::regs().cr2().modify(|reg| { | ||
| 137 | reg.set_adon(false); | ||
| 138 | }); | ||
| 139 | } | ||
| 140 | |||
| 141 | fn start_adc() { | ||
| 142 | T::regs().cr2().modify(|reg| { | ||
| 143 | reg.set_adon(true); | ||
| 144 | }); | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Sets the channel sample time | ||
| 148 | /// | ||
| 149 | /// ## SAFETY: | ||
| 150 | /// - ADON == 0 i.e ADC must not be enabled when this is called. | ||
| 151 | unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 152 | if ch <= 9 { | ||
| 153 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 154 | } else { | ||
| 155 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) { | ||
| 160 | let ch_iter = ch.iter(); | ||
| 161 | for idx in ch_iter { | ||
| 162 | unsafe { | ||
| 163 | Self::set_channel_sample_time(*idx, sample_time); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | pub fn set_sample_sequence( | ||
| 169 | &mut self, | ||
| 170 | sequence: Sequence, | ||
| 171 | channel: &mut impl AdcChannel<T>, | ||
| 172 | sample_time: SampleTime, | ||
| 173 | ) { | ||
| 174 | let was_on = Self::is_on(); | ||
| 175 | if !was_on { | ||
| 176 | Self::start_adc(); | ||
| 177 | } | ||
| 178 | |||
| 179 | // Check the sequence is long enough | ||
| 180 | T::regs().sqr1().modify(|r| { | ||
| 181 | let prev: Sequence = r.l().into(); | ||
| 182 | if prev < sequence { | ||
| 183 | let new_l: Sequence = sequence; | ||
| 184 | trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8); | ||
| 185 | r.set_l(sequence.into()) | ||
| 186 | } else { | ||
| 187 | r.set_l(prev.into()) | ||
| 188 | } | ||
| 189 | }); | ||
| 190 | |||
| 191 | // Set this GPIO as an analog input. | ||
| 192 | channel.setup(); | ||
| 193 | |||
| 194 | // Set the channel in the right sequence field. | ||
| 195 | match sequence { | ||
| 196 | Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())), | ||
| 197 | Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())), | ||
| 198 | Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())), | ||
| 199 | Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())), | ||
| 200 | Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())), | ||
| 201 | Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())), | ||
| 202 | Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(0, channel.channel())), | ||
| 203 | Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(1, channel.channel())), | ||
| 204 | Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(2, channel.channel())), | ||
| 205 | Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(3, channel.channel())), | ||
| 206 | Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(4, channel.channel())), | ||
| 207 | Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(5, channel.channel())), | ||
| 208 | Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(0, channel.channel())), | ||
| 209 | Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(1, channel.channel())), | ||
| 210 | Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(2, channel.channel())), | ||
| 211 | Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(3, channel.channel())), | ||
| 212 | }; | ||
| 213 | |||
| 214 | if !was_on { | ||
| 215 | Self::stop_adc(); | ||
| 216 | } | ||
| 217 | |||
| 218 | self.set_channels_sample_time(&[channel.channel()], sample_time); | ||
| 219 | |||
| 220 | Self::start_adc(); | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | ||
| 224 | pub fn start(&mut self) -> Result<(), OverrunError> { | ||
| 225 | self.setup_adc(); | ||
| 226 | self.ring_buf.clear(); | ||
| 227 | |||
| 228 | Ok(()) | ||
| 229 | } | ||
| 230 | |||
| 231 | fn stop(&mut self, err: OverrunError) -> Result<usize, OverrunError> { | ||
| 232 | self.teardown_adc(); | ||
| 233 | Err(err) | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Stops DMA transfer. | ||
| 237 | /// It does not turn off ADC. | ||
| 238 | /// Calling `start` restarts continuous DMA transfer. | ||
| 239 | /// | ||
| 240 | /// [`start`]: #method.start | ||
| 241 | pub fn teardown_adc(&mut self) { | ||
| 242 | // Stop the DMA transfer | ||
| 243 | self.ring_buf.request_pause(); | ||
| 244 | |||
| 245 | let r = T::regs(); | ||
| 246 | |||
| 247 | // Stop ADC | ||
| 248 | r.cr2().modify(|reg| { | ||
| 249 | // Stop ADC | ||
| 250 | reg.set_swstart(false); | ||
| 251 | // Stop DMA | ||
| 252 | reg.set_dma(false); | ||
| 253 | }); | ||
| 254 | |||
| 255 | r.cr1().modify(|w| { | ||
| 256 | // Disable interrupt for end of conversion | ||
| 257 | w.set_eocie(false); | ||
| 258 | // Disable interrupt for overrun | ||
| 259 | w.set_ovrie(false); | ||
| 260 | }); | ||
| 261 | |||
| 262 | clear_interrupt_flags(r); | ||
| 263 | |||
| 264 | compiler_fence(Ordering::SeqCst); | ||
| 265 | } | ||
| 266 | |||
| 267 | fn setup_adc(&mut self) { | ||
| 268 | compiler_fence(Ordering::SeqCst); | ||
| 269 | |||
| 270 | self.ring_buf.start(); | ||
| 271 | |||
| 272 | let r = T::regs(); | ||
| 273 | |||
| 274 | // Enable ADC | ||
| 275 | let was_on = Self::is_on(); | ||
| 276 | if !was_on { | ||
| 277 | r.cr2().modify(|reg| { | ||
| 278 | reg.set_adon(false); | ||
| 279 | reg.set_swstart(false); | ||
| 280 | }); | ||
| 281 | } | ||
| 282 | |||
| 283 | // Clear all interrupts | ||
| 284 | r.sr().modify(|regs| { | ||
| 285 | regs.set_eoc(false); | ||
| 286 | regs.set_ovr(false); | ||
| 287 | regs.set_strt(false); | ||
| 288 | }); | ||
| 289 | |||
| 290 | r.cr1().modify(|w| { | ||
| 291 | // Enable interrupt for end of conversion | ||
| 292 | w.set_eocie(true); | ||
| 293 | // Enable interrupt for overrun | ||
| 294 | w.set_ovrie(true); | ||
| 295 | // Scanning converisons of multiple channels | ||
| 296 | w.set_scan(true); | ||
| 297 | // Continuous conversion mode | ||
| 298 | w.set_discen(false); | ||
| 299 | }); | ||
| 300 | |||
| 301 | r.cr2().modify(|w| { | ||
| 302 | // Enable DMA mode | ||
| 303 | w.set_dma(true); | ||
| 304 | // Enable continuous conversions | ||
| 305 | w.set_cont(true); | ||
| 306 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 307 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 308 | // EOC flag is set at the end of each conversion. | ||
| 309 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 310 | }); | ||
| 311 | |||
| 312 | // Begin ADC conversions | ||
| 313 | T::regs().cr2().modify(|reg| { | ||
| 314 | reg.set_adon(true); | ||
| 315 | reg.set_swstart(true); | ||
| 316 | }); | ||
| 317 | |||
| 318 | super::blocking_delay_us(3); | ||
| 319 | } | ||
| 320 | |||
| 321 | /// Read bytes that are readily available in the ring buffer. | ||
| 322 | /// If no bytes are currently available in the buffer the call waits until the some | ||
| 323 | /// bytes are available (at least one byte and at most half the buffer size) | ||
| 324 | /// | ||
| 325 | /// Background receive is started if `start()` has not been previously called. | ||
| 326 | /// | ||
| 327 | /// Receive in the background is terminated if an error is returned. | ||
| 328 | /// It must then manually be started again by calling `start()` or by re-calling `read()`. | ||
| 329 | pub fn blocking_read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> { | ||
| 330 | let r = T::regs(); | ||
| 331 | |||
| 332 | // Start background receive if it was not already started | ||
| 333 | if !r.cr2().read().dma() { | ||
| 334 | self.start()?; | ||
| 335 | } | ||
| 336 | |||
| 337 | // Clear overrun flag if set. | ||
| 338 | if r.sr().read().ovr() { | ||
| 339 | return self.stop(OverrunError); | ||
| 340 | } | ||
| 341 | |||
| 342 | loop { | ||
| 343 | match self.ring_buf.read(buf) { | ||
| 344 | Ok((0, _)) => {} | ||
| 345 | Ok((len, _)) => { | ||
| 346 | return Ok(len); | ||
| 347 | } | ||
| 348 | Err(_) => { | ||
| 349 | return self.stop(OverrunError); | ||
| 350 | } | ||
| 351 | } | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | /// Reads measurements from the DMA ring buffer. | ||
| 356 | /// | ||
| 357 | /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. | ||
| 358 | /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes. | ||
| 359 | /// | ||
| 360 | /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`. | ||
| 361 | /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled. | ||
| 362 | /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`. | ||
| 363 | /// | ||
| 364 | /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read` again. | ||
| 365 | /// | ||
| 366 | /// By default, the ADC fills the DMA buffer as quickly as possible. To control the sample rate, call `teardown_adc` after each readout, and then start the DMA again at the desired interval. | ||
| 367 | /// Note that even if using `teardown_adc` to control the sample rate, with each call to `read`, measurements equivalent to half the size of the DMA buffer are still collected. | ||
| 368 | /// | ||
| 369 | /// Example: | ||
| 370 | /// ```rust,ignore | ||
| 371 | /// const DMA_BUF_LEN: usize = 120; | ||
| 372 | /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; | ||
| 373 | /// let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_dma_buf); | ||
| 374 | /// | ||
| 375 | /// adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); | ||
| 376 | /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112); | ||
| 377 | /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112); | ||
| 378 | /// | ||
| 379 | /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; | ||
| 380 | /// loop { | ||
| 381 | /// match adc.read(&mut measurements).await { | ||
| 382 | /// Ok(_) => { | ||
| 383 | /// defmt::info!("adc1: {}", measurements); | ||
| 384 | /// // Only needed to manually control sample rate. | ||
| 385 | /// adc.teardown_adc(); | ||
| 386 | /// } | ||
| 387 | /// Err(e) => { | ||
| 388 | /// defmt::warn!("Error: {:?}", e); | ||
| 389 | /// // DMA overrun, next call to `read` restarts ADC. | ||
| 390 | /// } | ||
| 391 | /// } | ||
| 392 | /// | ||
| 393 | /// // Manually control sample rate. | ||
| 394 | /// Timer::after_millis(100).await; | ||
| 395 | /// } | ||
| 396 | /// ``` | ||
| 397 | /// | ||
| 398 | /// | ||
| 399 | /// [`set_sample_sequence`]: #method.set_sample_sequence | ||
| 400 | /// [`teardown_adc`]: #method.teardown_adc | ||
| 401 | /// [`start`]: #method.start | ||
| 402 | pub async fn read<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> { | ||
| 403 | assert_eq!( | ||
| 404 | self.ring_buf.capacity() / 2, | ||
| 405 | N, | ||
| 406 | "Buffer size must be half the size of the ring buffer" | ||
| 407 | ); | ||
| 408 | |||
| 409 | let r = T::regs(); | ||
| 410 | |||
| 411 | // Start background receive if it was not already started | ||
| 412 | if !r.cr2().read().dma() { | ||
| 413 | self.start()?; | ||
| 414 | } | ||
| 415 | |||
| 416 | // Clear overrun flag if set. | ||
| 417 | if r.sr().read().ovr() { | ||
| 418 | return self.stop(OverrunError); | ||
| 419 | } | ||
| 420 | match self.ring_buf.read_exact(measurements).await { | ||
| 421 | Ok(len) => Ok(len), | ||
| 422 | Err(_) => self.stop(OverrunError), | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | ||
| 428 | fn drop(&mut self) { | ||
| 429 | self.teardown_adc(); | ||
| 430 | rcc::disable::<T>(); | ||
| 431 | } | ||
| 432 | } | ||
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index d09374876..58c30935f 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -5,11 +5,12 @@ use core::task::Poll; | |||
| 5 | #[cfg(adc_l0)] | 5 | #[cfg(adc_l0)] |
| 6 | use stm32_metapac::adc::vals::Ckmode; | 6 | use stm32_metapac::adc::vals::Ckmode; |
| 7 | 7 | ||
| 8 | use super::blocking_delay_us; | 8 | #[cfg(not(adc_l0))] |
| 9 | use super::Vbat; | ||
| 10 | use super::{Temperature, VrefInt, blocking_delay_us}; | ||
| 9 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; | 11 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; |
| 10 | use crate::interrupt::typelevel::Interrupt; | 12 | use crate::interrupt::typelevel::Interrupt; |
| 11 | use crate::peripherals::ADC1; | 13 | use crate::{Peri, interrupt, rcc}; |
| 12 | use crate::{interrupt, rcc, Peri}; | ||
| 13 | 14 | ||
| 14 | mod watchdog_v1; | 15 | mod watchdog_v1; |
| 15 | pub use watchdog_v1::WatchdogChannels; | 16 | pub use watchdog_v1::WatchdogChannels; |
| @@ -42,36 +43,23 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | #[cfg(not(adc_l0))] | 45 | #[cfg(not(adc_l0))] |
| 45 | pub struct Vbat; | 46 | impl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC1 { |
| 46 | 47 | const CHANNEL: u8 = 18; | |
| 47 | #[cfg(not(adc_l0))] | 48 | } |
| 48 | impl AdcChannel<ADC1> for Vbat {} | ||
| 49 | 49 | ||
| 50 | #[cfg(not(adc_l0))] | 50 | #[cfg(not(adc_l0))] |
| 51 | impl super::SealedAdcChannel<ADC1> for Vbat { | 51 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 { |
| 52 | fn channel(&self) -> u8 { | 52 | const CHANNEL: u8 = 17; |
| 53 | 18 | ||
| 54 | } | ||
| 55 | } | 53 | } |
| 56 | 54 | ||
| 57 | pub struct Vref; | 55 | #[cfg(adc_l0)] |
| 58 | impl AdcChannel<ADC1> for Vref {} | 56 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 { |
| 59 | impl super::SealedAdcChannel<ADC1> for Vref { | 57 | const CHANNEL: u8 = 18; |
| 60 | fn channel(&self) -> u8 { | ||
| 61 | 17 | ||
| 62 | } | ||
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | pub struct Temperature; | 60 | #[cfg(not(adc_l0))] |
| 66 | impl AdcChannel<ADC1> for Temperature {} | 61 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 { |
| 67 | impl super::SealedAdcChannel<ADC1> for Temperature { | 62 | const CHANNEL: u8 = 16; |
| 68 | fn channel(&self) -> u8 { | ||
| 69 | if cfg!(adc_l0) { | ||
| 70 | 18 | ||
| 71 | } else { | ||
| 72 | 16 | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | 63 | } |
| 76 | 64 | ||
| 77 | impl<'d, T: Instance> Adc<'d, T> { | 65 | impl<'d, T: Instance> Adc<'d, T> { |
| @@ -118,10 +106,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 118 | T::Interrupt::enable(); | 106 | T::Interrupt::enable(); |
| 119 | } | 107 | } |
| 120 | 108 | ||
| 121 | Self { | 109 | Self { adc } |
| 122 | adc, | ||
| 123 | sample_time: SampleTime::from_bits(0), | ||
| 124 | } | ||
| 125 | } | 110 | } |
| 126 | 111 | ||
| 127 | #[cfg(not(adc_l0))] | 112 | #[cfg(not(adc_l0))] |
| @@ -134,12 +119,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 134 | Vbat | 119 | Vbat |
| 135 | } | 120 | } |
| 136 | 121 | ||
| 137 | pub fn enable_vref(&self) -> Vref { | 122 | pub fn enable_vref(&self) -> VrefInt { |
| 138 | // Table 28. Embedded internal reference voltage | 123 | // Table 28. Embedded internal reference voltage |
| 139 | // tstart = 10μs | 124 | // tstart = 10μs |
| 140 | T::regs().ccr().modify(|reg| reg.set_vrefen(true)); | 125 | T::regs().ccr().modify(|reg| reg.set_vrefen(true)); |
| 141 | blocking_delay_us(10); | 126 | blocking_delay_us(10); |
| 142 | Vref | 127 | VrefInt |
| 143 | } | 128 | } |
| 144 | 129 | ||
| 145 | pub fn enable_temperature(&self) -> Temperature { | 130 | pub fn enable_temperature(&self) -> Temperature { |
| @@ -153,10 +138,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 153 | Temperature | 138 | Temperature |
| 154 | } | 139 | } |
| 155 | 140 | ||
| 156 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 157 | self.sample_time = sample_time; | ||
| 158 | } | ||
| 159 | |||
| 160 | pub fn set_resolution(&mut self, resolution: Resolution) { | 141 | pub fn set_resolution(&mut self, resolution: Resolution) { |
| 161 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | 142 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); |
| 162 | } | 143 | } |
| @@ -167,12 +148,13 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 167 | T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode)); | 148 | T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode)); |
| 168 | } | 149 | } |
| 169 | 150 | ||
| 170 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 151 | pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { |
| 171 | let ch_num = channel.channel(); | 152 | let ch_num = channel.channel(); |
| 172 | channel.setup(); | 153 | channel.setup(); |
| 173 | 154 | ||
| 174 | // A.7.5 Single conversion sequence code example - Software trigger | 155 | // A.7.5 Single conversion sequence code example - Software trigger |
| 175 | T::regs().chselr().write(|reg| reg.set_chsel_x(ch_num as usize, true)); | 156 | T::regs().chselr().write(|reg| reg.set_chsel_x(ch_num as usize, true)); |
| 157 | T::regs().smpr().modify(|reg| reg.set_smp(sample_time.into())); | ||
| 176 | 158 | ||
| 177 | self.convert().await | 159 | self.convert().await |
| 178 | } | 160 | } |
| @@ -183,7 +165,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 183 | reg.set_eosmp(true); | 165 | reg.set_eosmp(true); |
| 184 | }); | 166 | }); |
| 185 | 167 | ||
| 186 | T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); | ||
| 187 | T::regs().ier().modify(|w| w.set_eocie(true)); | 168 | T::regs().ier().modify(|w| w.set_eocie(true)); |
| 188 | T::regs().cr().modify(|reg| reg.set_adstart(true)); | 169 | T::regs().cr().modify(|reg| reg.set_adstart(true)); |
| 189 | 170 | ||
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e94a25b24..3c4431ae0 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,23 +1,40 @@ | |||
| 1 | use super::blocking_delay_us; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 2 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; | 2 | |
| 3 | use crate::peripherals::ADC1; | 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 4 | use crate::adc::{Adc, Instance, Resolution, SampleTime}; | ||
| 5 | use crate::pac::adc::vals; | ||
| 6 | pub use crate::pac::adccommon::vals::Adcpre; | ||
| 4 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 5 | use crate::{rcc, Peri}; | 8 | use crate::{Peri, rcc}; |
| 6 | 9 | ||
| 7 | mod ringbuffered_v2; | 10 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 8 | pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; | 11 | r.sr().modify(|regs| { |
| 12 | regs.set_eoc(false); | ||
| 13 | regs.set_ovr(false); | ||
| 14 | }); | ||
| 15 | } | ||
| 9 | 16 | ||
| 10 | /// Default VREF voltage used for sample conversion to millivolts. | 17 | /// Default VREF voltage used for sample conversion to millivolts. |
| 11 | pub const VREF_DEFAULT_MV: u32 = 3300; | 18 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| 12 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 19 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 13 | pub const VREF_CALIB_MV: u32 = 3300; | 20 | pub const VREF_CALIB_MV: u32 = 3300; |
| 14 | 21 | ||
| 15 | pub struct VrefInt; | 22 | impl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 { |
| 16 | impl AdcChannel<ADC1> for VrefInt {} | 23 | const CHANNEL: u8 = 17; |
| 17 | impl super::SealedAdcChannel<ADC1> for VrefInt { | 24 | } |
| 18 | fn channel(&self) -> u8 { | 25 | |
| 19 | 17 | 26 | #[cfg(any(stm32f2, stm32f40x, stm32f41x))] |
| 20 | } | 27 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 { |
| 28 | const CHANNEL: u8 = 16; | ||
| 29 | } | ||
| 30 | |||
| 31 | #[cfg(not(any(stm32f2, stm32f40x, stm32f41x)))] | ||
| 32 | impl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 { | ||
| 33 | const CHANNEL: u8 = 18; | ||
| 34 | } | ||
| 35 | |||
| 36 | impl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC1 { | ||
| 37 | const CHANNEL: u8 = 18; | ||
| 21 | } | 38 | } |
| 22 | 39 | ||
| 23 | impl VrefInt { | 40 | impl VrefInt { |
| @@ -27,20 +44,6 @@ impl VrefInt { | |||
| 27 | } | 44 | } |
| 28 | } | 45 | } |
| 29 | 46 | ||
| 30 | pub struct Temperature; | ||
| 31 | impl AdcChannel<ADC1> for Temperature {} | ||
| 32 | impl super::SealedAdcChannel<ADC1> for Temperature { | ||
| 33 | fn channel(&self) -> u8 { | ||
| 34 | cfg_if::cfg_if! { | ||
| 35 | if #[cfg(any(stm32f2, stm32f40x, stm32f41x))] { | ||
| 36 | 16 | ||
| 37 | } else { | ||
| 38 | 18 | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | impl Temperature { | 47 | impl Temperature { |
| 45 | /// Time needed for temperature sensor readings to stabilize | 48 | /// Time needed for temperature sensor readings to stabilize |
| 46 | pub fn start_time_us() -> u32 { | 49 | pub fn start_time_us() -> u32 { |
| @@ -48,76 +51,176 @@ impl Temperature { | |||
| 48 | } | 51 | } |
| 49 | } | 52 | } |
| 50 | 53 | ||
| 51 | pub struct Vbat; | 54 | fn from_pclk2(freq: Hertz) -> Adcpre { |
| 52 | impl AdcChannel<ADC1> for Vbat {} | 55 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). |
| 53 | impl super::SealedAdcChannel<ADC1> for Vbat { | 56 | #[cfg(stm32f2)] |
| 54 | fn channel(&self) -> u8 { | 57 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); |
| 55 | 18 | 58 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. |
| 59 | #[cfg(not(stm32f2))] | ||
| 60 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | ||
| 61 | let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0); | ||
| 62 | match raw_div { | ||
| 63 | 0..=1 => Adcpre::DIV2, | ||
| 64 | 2..=3 => Adcpre::DIV4, | ||
| 65 | 4..=5 => Adcpre::DIV6, | ||
| 66 | 6..=7 => Adcpre::DIV8, | ||
| 67 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), | ||
| 56 | } | 68 | } |
| 57 | } | 69 | } |
| 58 | 70 | ||
| 59 | enum Prescaler { | 71 | /// ADC configuration |
| 60 | Div2, | 72 | #[derive(Default)] |
| 61 | Div4, | 73 | pub struct AdcConfig { |
| 62 | Div6, | 74 | resolution: Option<Resolution>, |
| 63 | Div8, | ||
| 64 | } | 75 | } |
| 65 | 76 | ||
| 66 | impl Prescaler { | 77 | impl<T: Instance> super::SealedAnyInstance for T { |
| 67 | fn from_pclk2(freq: Hertz) -> Self { | 78 | fn dr() -> *mut u16 { |
| 68 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). | 79 | T::regs().dr().as_ptr() as *mut u16 |
| 69 | #[cfg(stm32f2)] | 80 | } |
| 70 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); | 81 | |
| 71 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. | 82 | fn enable() { |
| 72 | #[cfg(not(stm32f2))] | 83 | T::regs().cr2().modify(|reg| { |
| 73 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | 84 | reg.set_adon(true); |
| 74 | let raw_div = freq.0 / MAX_FREQUENCY.0; | 85 | }); |
| 75 | match raw_div { | 86 | |
| 76 | 0..=1 => Self::Div2, | 87 | blocking_delay_us(3); |
| 77 | 2..=3 => Self::Div4, | 88 | } |
| 78 | 4..=5 => Self::Div6, | 89 | |
| 79 | 6..=7 => Self::Div8, | 90 | fn start() { |
| 80 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), | 91 | // Begin ADC conversions |
| 81 | } | 92 | T::regs().cr2().modify(|reg| { |
| 93 | reg.set_swstart(true); | ||
| 94 | }); | ||
| 95 | } | ||
| 96 | |||
| 97 | fn stop() { | ||
| 98 | let r = T::regs(); | ||
| 99 | |||
| 100 | // Stop ADC | ||
| 101 | r.cr2().modify(|reg| { | ||
| 102 | // Stop ADC | ||
| 103 | reg.set_swstart(false); | ||
| 104 | // Stop ADC | ||
| 105 | reg.set_adon(false); | ||
| 106 | // Stop DMA | ||
| 107 | reg.set_dma(false); | ||
| 108 | }); | ||
| 109 | |||
| 110 | r.cr1().modify(|w| { | ||
| 111 | // Disable interrupt for end of conversion | ||
| 112 | w.set_eocie(false); | ||
| 113 | // Disable interrupt for overrun | ||
| 114 | w.set_ovrie(false); | ||
| 115 | }); | ||
| 116 | |||
| 117 | clear_interrupt_flags(r); | ||
| 118 | |||
| 119 | compiler_fence(Ordering::SeqCst); | ||
| 82 | } | 120 | } |
| 83 | 121 | ||
| 84 | fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { | 122 | fn convert() -> u16 { |
| 85 | match self { | 123 | // clear end of conversion flag |
| 86 | Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, | 124 | T::regs().sr().modify(|reg| { |
| 87 | Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, | 125 | reg.set_eoc(false); |
| 88 | Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, | 126 | }); |
| 89 | Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, | 127 | |
| 128 | // Start conversion | ||
| 129 | T::regs().cr2().modify(|reg| { | ||
| 130 | reg.set_swstart(true); | ||
| 131 | }); | ||
| 132 | |||
| 133 | while T::regs().sr().read().strt() == false { | ||
| 134 | // spin //wait for actual start | ||
| 135 | } | ||
| 136 | while T::regs().sr().read().eoc() == false { | ||
| 137 | // spin //wait for finish | ||
| 90 | } | 138 | } |
| 139 | |||
| 140 | T::regs().dr().read().0 as u16 | ||
| 91 | } | 141 | } |
| 92 | } | ||
| 93 | 142 | ||
| 94 | impl<'d, T> Adc<'d, T> | 143 | fn configure_dma(conversion_mode: ConversionMode) { |
| 95 | where | 144 | match conversion_mode { |
| 96 | T: Instance, | 145 | ConversionMode::Repeated(_) => { |
| 97 | { | 146 | let r = T::regs(); |
| 98 | pub fn new(adc: Peri<'d, T>) -> Self { | 147 | |
| 99 | rcc::enable_and_reset::<T>(); | 148 | // Clear all interrupts |
| 149 | r.sr().modify(|regs| { | ||
| 150 | regs.set_eoc(false); | ||
| 151 | regs.set_ovr(false); | ||
| 152 | regs.set_strt(false); | ||
| 153 | }); | ||
| 154 | |||
| 155 | r.cr1().modify(|w| { | ||
| 156 | // Enable interrupt for end of conversion | ||
| 157 | w.set_eocie(true); | ||
| 158 | // Enable interrupt for overrun | ||
| 159 | w.set_ovrie(true); | ||
| 160 | // Scanning converisons of multiple channels | ||
| 161 | w.set_scan(true); | ||
| 162 | // Continuous conversion mode | ||
| 163 | w.set_discen(false); | ||
| 164 | }); | ||
| 165 | |||
| 166 | r.cr2().modify(|w| { | ||
| 167 | // Enable DMA mode | ||
| 168 | w.set_dma(true); | ||
| 169 | // Enable continuous conversions | ||
| 170 | w.set_cont(true); | ||
| 171 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 172 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 173 | // EOC flag is set at the end of each conversion. | ||
| 174 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 175 | }); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 100 | 179 | ||
| 101 | let presc = Prescaler::from_pclk2(T::frequency()); | 180 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 102 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); | ||
| 103 | T::regs().cr2().modify(|reg| { | 181 | T::regs().cr2().modify(|reg| { |
| 104 | reg.set_adon(true); | 182 | reg.set_adon(true); |
| 105 | }); | 183 | }); |
| 106 | 184 | ||
| 107 | blocking_delay_us(3); | 185 | // Check the sequence is long enough |
| 186 | T::regs().sqr1().modify(|r| { | ||
| 187 | r.set_l((sequence.len() - 1).try_into().unwrap()); | ||
| 188 | }); | ||
| 108 | 189 | ||
| 109 | Self { | 190 | for (i, ((ch, _), sample_time)) in sequence.enumerate() { |
| 110 | adc, | 191 | // Set the channel in the right sequence field. |
| 111 | sample_time: SampleTime::from_bits(0), | 192 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); |
| 193 | |||
| 194 | let sample_time = sample_time.into(); | ||
| 195 | if ch <= 9 { | ||
| 196 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 197 | } else { | ||
| 198 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 199 | } | ||
| 112 | } | 200 | } |
| 113 | } | 201 | } |
| 202 | } | ||
| 114 | 203 | ||
| 115 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | 204 | impl<'d, T> Adc<'d, T> |
| 116 | self.sample_time = sample_time; | 205 | where |
| 206 | T: Instance + super::AnyInstance, | ||
| 207 | { | ||
| 208 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 209 | Self::new_with_config(adc, Default::default()) | ||
| 117 | } | 210 | } |
| 118 | 211 | ||
| 119 | pub fn set_resolution(&mut self, resolution: Resolution) { | 212 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 120 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | 213 | rcc::enable_and_reset::<T>(); |
| 214 | |||
| 215 | let presc = from_pclk2(T::frequency()); | ||
| 216 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); | ||
| 217 | T::enable(); | ||
| 218 | |||
| 219 | if let Some(resolution) = config.resolution { | ||
| 220 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 221 | } | ||
| 222 | |||
| 223 | Self { adc } | ||
| 121 | } | 224 | } |
| 122 | 225 | ||
| 123 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | 226 | /// Enables internal voltage reference and returns [VrefInt], which can be used in |
| @@ -152,52 +255,6 @@ where | |||
| 152 | 255 | ||
| 153 | Vbat {} | 256 | Vbat {} |
| 154 | } | 257 | } |
| 155 | |||
| 156 | /// Perform a single conversion. | ||
| 157 | fn convert(&mut self) -> u16 { | ||
| 158 | // clear end of conversion flag | ||
| 159 | T::regs().sr().modify(|reg| { | ||
| 160 | reg.set_eoc(false); | ||
| 161 | }); | ||
| 162 | |||
| 163 | // Start conversion | ||
| 164 | T::regs().cr2().modify(|reg| { | ||
| 165 | reg.set_swstart(true); | ||
| 166 | }); | ||
| 167 | |||
| 168 | while T::regs().sr().read().strt() == false { | ||
| 169 | // spin //wait for actual start | ||
| 170 | } | ||
| 171 | while T::regs().sr().read().eoc() == false { | ||
| 172 | // spin //wait for finish | ||
| 173 | } | ||
| 174 | |||
| 175 | T::regs().dr().read().0 as u16 | ||
| 176 | } | ||
| 177 | |||
| 178 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 179 | channel.setup(); | ||
| 180 | |||
| 181 | // Configure ADC | ||
| 182 | let channel = channel.channel(); | ||
| 183 | |||
| 184 | // Select channel | ||
| 185 | T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); | ||
| 186 | |||
| 187 | // Configure channel | ||
| 188 | Self::set_channel_sample_time(channel, self.sample_time); | ||
| 189 | |||
| 190 | self.convert() | ||
| 191 | } | ||
| 192 | |||
| 193 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 194 | let sample_time = sample_time.into(); | ||
| 195 | if ch <= 9 { | ||
| 196 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 197 | } else { | ||
| 198 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | } | 258 | } |
| 202 | 259 | ||
| 203 | impl<'d, T: Instance> Drop for Adc<'d, T> { | 260 | impl<'d, T: Instance> Drop for Adc<'d, T> { |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 16063ce4d..b270588c4 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,19 +1,19 @@ | |||
| 1 | use cfg_if::cfg_if; | 1 | use cfg_if::cfg_if; |
| 2 | #[cfg(adc_g0)] | 2 | #[cfg(adc_g0)] |
| 3 | use heapless::Vec; | 3 | use heapless::Vec; |
| 4 | use pac::adc::vals::Dmacfg; | ||
| 5 | #[cfg(adc_g0)] | 4 | #[cfg(adc_g0)] |
| 6 | use pac::adc::vals::{Ckmode, Smpsel}; | 5 | use pac::adc::vals::Ckmode; |
| 6 | use pac::adc::vals::Dmacfg; | ||
| 7 | #[cfg(adc_v3)] | 7 | #[cfg(adc_v3)] |
| 8 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; | 8 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; |
| 9 | #[cfg(adc_g0)] | 9 | #[cfg(adc_g0)] |
| 10 | pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | 10 | pub use pac::adc::vals::{Ovsr, Ovss, Presc}; |
| 11 | 11 | ||
| 12 | use super::{ | 12 | #[allow(unused_imports)] |
| 13 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 13 | use super::SealedAdcChannel; |
| 14 | }; | 14 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 15 | use crate::dma::Transfer; | 15 | use crate::adc::ConversionMode; |
| 16 | use crate::{pac, rcc, Peri}; | 16 | use crate::{Peri, pac, rcc}; |
| 17 | 17 | ||
| 18 | /// Default VREF voltage used for sample conversion to millivolts. | 18 | /// Default VREF voltage used for sample conversion to millivolts. |
| 19 | pub const VREF_DEFAULT_MV: u32 = 3300; | 19 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -25,70 +25,64 @@ pub const VREF_CALIB_MV: u32 = 3000; | |||
| 25 | // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable | 25 | // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable |
| 26 | const SAMPLE_TIMES_CAPACITY: usize = 2; | 26 | const SAMPLE_TIMES_CAPACITY: usize = 2; |
| 27 | 27 | ||
| 28 | pub struct VrefInt; | 28 | #[cfg(adc_g0)] |
| 29 | impl<T: Instance> AdcChannel<T> for VrefInt {} | 29 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 30 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | 30 | const CHANNEL: u8 = 13; |
| 31 | fn channel(&self) -> u8 { | 31 | } |
| 32 | cfg_if! { | 32 | #[cfg(any(adc_h5, adc_h7rs))] |
| 33 | if #[cfg(adc_g0)] { | 33 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 34 | let val = 13; | 34 | const CHANNEL: u8 = 17; |
| 35 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 35 | } |
| 36 | let val = 17; | 36 | #[cfg(adc_u0)] |
| 37 | } else if #[cfg(adc_u0)] { | 37 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 38 | let val = 12; | 38 | const CHANNEL: u8 = 12; |
| 39 | } else { | 39 | } |
| 40 | let val = 0; | 40 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 41 | } | 41 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 42 | } | 42 | const CHANNEL: u8 = 0; |
| 43 | val | ||
| 44 | } | ||
| 45 | } | 43 | } |
| 46 | 44 | ||
| 47 | pub struct Temperature; | 45 | #[cfg(adc_g0)] |
| 48 | impl<T: Instance> AdcChannel<T> for Temperature {} | 46 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 49 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | 47 | const CHANNEL: u8 = 12; |
| 50 | fn channel(&self) -> u8 { | 48 | } |
| 51 | cfg_if! { | 49 | #[cfg(any(adc_h5, adc_h7rs))] |
| 52 | if #[cfg(adc_g0)] { | 50 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 53 | let val = 12; | 51 | const CHANNEL: u8 = 16; |
| 54 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 52 | } |
| 55 | let val = 16; | 53 | #[cfg(adc_u0)] |
| 56 | } else if #[cfg(adc_u0)] { | 54 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 57 | let val = 11; | 55 | const CHANNEL: u8 = 11; |
| 58 | } else { | 56 | } |
| 59 | let val = 17; | 57 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 60 | } | 58 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 61 | } | 59 | const CHANNEL: u8 = 17; |
| 62 | val | ||
| 63 | } | ||
| 64 | } | 60 | } |
| 65 | 61 | ||
| 66 | pub struct Vbat; | 62 | #[cfg(adc_g0)] |
| 67 | impl<T: Instance> AdcChannel<T> for Vbat {} | 63 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 68 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | 64 | const CHANNEL: u8 = 14; |
| 69 | fn channel(&self) -> u8 { | 65 | } |
| 70 | cfg_if! { | 66 | #[cfg(any(adc_h5, adc_h7rs))] |
| 71 | if #[cfg(adc_g0)] { | 67 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 72 | let val = 14; | 68 | const CHANNEL: u8 = 16; |
| 73 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 69 | } |
| 74 | let val = 2; | 70 | #[cfg(adc_u0)] |
| 75 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 71 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 76 | let val = 13; | 72 | const CHANNEL: u8 = 13; |
| 77 | } else { | 73 | } |
| 78 | let val = 18; | 74 | #[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))] |
| 79 | } | 75 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 80 | } | 76 | const CHANNEL: u8 = 18; |
| 81 | val | ||
| 82 | } | ||
| 83 | } | 77 | } |
| 84 | 78 | ||
| 85 | cfg_if! { | 79 | cfg_if! { |
| 86 | if #[cfg(any(adc_h5, adc_h7rs))] { | 80 | if #[cfg(any(adc_h5, adc_h7rs))] { |
| 87 | pub struct VddCore; | 81 | pub struct VddCore; |
| 88 | impl<T: Instance> AdcChannel<T> for VddCore {} | 82 | impl<T: Instance> super::AdcChannel<T> for VddCore {} |
| 89 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { | 83 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { |
| 90 | fn channel(&self) -> u8 { | 84 | fn channel(&self) -> u8 { |
| 91 | 6 | 85 | 17 |
| 92 | } | 86 | } |
| 93 | } | 87 | } |
| 94 | } | 88 | } |
| @@ -97,7 +91,7 @@ cfg_if! { | |||
| 97 | cfg_if! { | 91 | cfg_if! { |
| 98 | if #[cfg(adc_u0)] { | 92 | if #[cfg(adc_u0)] { |
| 99 | pub struct DacOut; | 93 | pub struct DacOut; |
| 100 | impl<T: Instance> AdcChannel<T> for DacOut {} | 94 | impl<T: Instance> super::AdcChannel<T> for DacOut {} |
| 101 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { | 95 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { |
| 102 | fn channel(&self) -> u8 { | 96 | fn channel(&self) -> u8 { |
| 103 | 19 | 97 | 19 |
| @@ -106,21 +100,6 @@ cfg_if! { | |||
| 106 | } | 100 | } |
| 107 | } | 101 | } |
| 108 | 102 | ||
| 109 | /// Number of samples used for averaging. | ||
| 110 | #[derive(Copy, Clone, Debug)] | ||
| 111 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 112 | pub enum Averaging { | ||
| 113 | Disabled, | ||
| 114 | Samples2, | ||
| 115 | Samples4, | ||
| 116 | Samples8, | ||
| 117 | Samples16, | ||
| 118 | Samples32, | ||
| 119 | Samples64, | ||
| 120 | Samples128, | ||
| 121 | Samples256, | ||
| 122 | } | ||
| 123 | |||
| 124 | cfg_if! { if #[cfg(adc_g0)] { | 103 | cfg_if! { if #[cfg(adc_g0)] { |
| 125 | 104 | ||
| 126 | /// Synchronous PCLK prescaler | 105 | /// Synchronous PCLK prescaler |
| @@ -141,91 +120,39 @@ pub enum Clock { | |||
| 141 | 120 | ||
| 142 | }} | 121 | }} |
| 143 | 122 | ||
| 144 | impl<'d, T: Instance> Adc<'d, T> { | 123 | #[cfg(adc_u0)] |
| 145 | /// Enable the voltage regulator | 124 | type Ovss = u8; |
| 146 | fn init_regulator() { | 125 | #[cfg(adc_u0)] |
| 147 | rcc::enable_and_reset::<T>(); | 126 | type Ovsr = u8; |
| 148 | T::regs().cr().modify(|reg| { | 127 | #[cfg(adc_v3)] |
| 149 | #[cfg(not(any(adc_g0, adc_u0)))] | 128 | type Ovss = OversamplingShift; |
| 150 | reg.set_deeppwd(false); | 129 | #[cfg(adc_v3)] |
| 151 | reg.set_advregen(true); | 130 | type Ovsr = OversamplingRatio; |
| 152 | }); | 131 | |
| 153 | 132 | /// Adc configuration | |
| 154 | // If this is false then each ADC_CHSELR bit enables an input channel. | 133 | #[derive(Default)] |
| 155 | // This is the reset value, so has no effect. | 134 | pub struct AdcConfig { |
| 156 | #[cfg(any(adc_g0, adc_u0))] | 135 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 157 | T::regs().cfgr1().modify(|reg| { | 136 | pub oversampling_shift: Option<Ovss>, |
| 158 | reg.set_chselrmod(false); | 137 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 159 | }); | 138 | pub oversampling_ratio: Option<Ovsr>, |
| 160 | 139 | #[cfg(any(adc_u0, adc_g0))] | |
| 161 | blocking_delay_us(20); | 140 | pub oversampling_enable: Option<bool>, |
| 162 | } | 141 | #[cfg(adc_v3)] |
| 163 | 142 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | |
| 164 | /// Calibrate to remove conversion offset | ||
| 165 | fn init_calibrate() { | ||
| 166 | T::regs().cr().modify(|reg| { | ||
| 167 | reg.set_adcal(true); | ||
| 168 | }); | ||
| 169 | |||
| 170 | while T::regs().cr().read().adcal() { | ||
| 171 | // spin | ||
| 172 | } | ||
| 173 | |||
| 174 | blocking_delay_us(1); | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Initialize the ADC leaving any analog clock at reset value. | ||
| 178 | /// For G0 and WL, this is the async clock without prescaler. | ||
| 179 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 180 | Self::init_regulator(); | ||
| 181 | Self::init_calibrate(); | ||
| 182 | Self { | ||
| 183 | adc, | ||
| 184 | sample_time: SampleTime::from_bits(0), | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | #[cfg(adc_g0)] | 143 | #[cfg(adc_g0)] |
| 189 | /// Initialize ADC with explicit clock for the analog ADC | 144 | pub clock: Option<Clock>, |
| 190 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | 145 | pub resolution: Option<Resolution>, |
| 191 | Self::init_regulator(); | 146 | pub averaging: Option<Averaging>, |
| 192 | 147 | } | |
| 193 | #[cfg(any(stm32wl5x))] | ||
| 194 | { | ||
| 195 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 196 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 197 | match clock { | ||
| 198 | Clock::Async { div: _ } => { | ||
| 199 | assert!(async_clock_available); | ||
| 200 | } | ||
| 201 | Clock::Sync { div: _ } => { | ||
| 202 | if async_clock_available { | ||
| 203 | warn!("Not using configured ADC clock"); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | match clock { | ||
| 209 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 210 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 211 | reg.set_ckmode(match div { | ||
| 212 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 213 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 214 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 215 | }) | ||
| 216 | }), | ||
| 217 | } | ||
| 218 | |||
| 219 | Self::init_calibrate(); | ||
| 220 | 148 | ||
| 221 | Self { | 149 | impl<T: Instance> super::SealedAnyInstance for T { |
| 222 | adc, | 150 | fn dr() -> *mut u16 { |
| 223 | sample_time: SampleTime::from_bits(0), | 151 | T::regs().dr().as_ptr() as *mut u16 |
| 224 | } | ||
| 225 | } | 152 | } |
| 226 | 153 | ||
| 227 | // Enable ADC only when it is not already running. | 154 | // Enable ADC only when it is not already running. |
| 228 | fn enable(&mut self) { | 155 | fn enable() { |
| 229 | // Make sure bits are off | 156 | // Make sure bits are off |
| 230 | while T::regs().cr().read().addis() { | 157 | while T::regs().cr().read().addis() { |
| 231 | // spin | 158 | // spin |
| @@ -246,186 +173,95 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 246 | } | 173 | } |
| 247 | } | 174 | } |
| 248 | 175 | ||
| 249 | pub fn enable_vrefint(&self) -> VrefInt { | 176 | fn start() { |
| 250 | #[cfg(not(any(adc_g0, adc_u0)))] | 177 | T::regs().cr().modify(|reg| { |
| 251 | T::common_regs().ccr().modify(|reg| { | 178 | reg.set_adstart(true); |
| 252 | reg.set_vrefen(true); | ||
| 253 | }); | ||
| 254 | #[cfg(any(adc_g0, adc_u0))] | ||
| 255 | T::regs().ccr().modify(|reg| { | ||
| 256 | reg.set_vrefen(true); | ||
| 257 | }); | 179 | }); |
| 258 | |||
| 259 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||
| 260 | // to stabilize the internal voltage reference. | ||
| 261 | blocking_delay_us(15); | ||
| 262 | |||
| 263 | VrefInt {} | ||
| 264 | } | 180 | } |
| 265 | 181 | ||
| 266 | pub fn enable_temperature(&self) -> Temperature { | 182 | fn stop() { |
| 267 | cfg_if! { | 183 | // Ensure conversions are finished. |
| 268 | if #[cfg(any(adc_g0, adc_u0))] { | 184 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 269 | T::regs().ccr().modify(|reg| { | 185 | T::regs().cr().modify(|reg| { |
| 270 | reg.set_tsen(true); | 186 | reg.set_adstp(true); |
| 271 | }); | 187 | }); |
| 272 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 188 | while T::regs().cr().read().adstart() {} |
| 273 | T::common_regs().ccr().modify(|reg| { | ||
| 274 | reg.set_tsen(true); | ||
| 275 | }); | ||
| 276 | } else { | ||
| 277 | T::common_regs().ccr().modify(|reg| { | ||
| 278 | reg.set_ch17sel(true); | ||
| 279 | }); | ||
| 280 | } | ||
| 281 | } | 189 | } |
| 282 | 190 | ||
| 283 | Temperature {} | 191 | // Reset configuration. |
| 192 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 193 | T::regs().cfgr().modify(|reg| { | ||
| 194 | reg.set_cont(false); | ||
| 195 | reg.set_dmaen(false); | ||
| 196 | }); | ||
| 197 | #[cfg(any(adc_g0, adc_u0))] | ||
| 198 | T::regs().cfgr1().modify(|reg| { | ||
| 199 | reg.set_cont(false); | ||
| 200 | reg.set_dmaen(false); | ||
| 201 | }); | ||
| 284 | } | 202 | } |
| 285 | 203 | ||
| 286 | pub fn enable_vbat(&self) -> Vbat { | 204 | /// Perform a single conversion. |
| 287 | cfg_if! { | 205 | fn convert() -> u16 { |
| 288 | if #[cfg(any(adc_g0, adc_u0))] { | 206 | // Some models are affected by an erratum: |
| 289 | T::regs().ccr().modify(|reg| { | 207 | // If we perform conversions slower than 1 kHz, the first read ADC value can be |
| 290 | reg.set_vbaten(true); | 208 | // corrupted, so we discard it and measure again. |
| 291 | }); | 209 | // |
| 292 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 210 | // STM32L471xx: Section 2.7.3 |
| 293 | T::common_regs().ccr().modify(|reg| { | 211 | // STM32G4: Section 2.7.3 |
| 294 | reg.set_vbaten(true); | 212 | #[cfg(any(rcc_l4, rcc_g4))] |
| 295 | }); | 213 | let len = 2; |
| 296 | } else { | ||
| 297 | T::common_regs().ccr().modify(|reg| { | ||
| 298 | reg.set_ch18sel(true); | ||
| 299 | }); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | 214 | ||
| 303 | Vbat {} | 215 | #[cfg(not(any(rcc_l4, rcc_g4)))] |
| 304 | } | 216 | let len = 1; |
| 305 | 217 | ||
| 306 | /// Set the ADC sample time. | 218 | for _ in 0..len { |
| 307 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | 219 | T::regs().isr().modify(|reg| { |
| 308 | self.sample_time = sample_time; | 220 | reg.set_eos(true); |
| 309 | } | 221 | reg.set_eoc(true); |
| 222 | }); | ||
| 310 | 223 | ||
| 311 | /// Get the ADC sample time. | 224 | // Start conversion |
| 312 | pub fn sample_time(&self) -> SampleTime { | 225 | T::regs().cr().modify(|reg| { |
| 313 | self.sample_time | 226 | reg.set_adstart(true); |
| 314 | } | 227 | }); |
| 315 | 228 | ||
| 316 | /// Set the ADC resolution. | 229 | while !T::regs().isr().read().eos() { |
| 317 | pub fn set_resolution(&mut self, resolution: Resolution) { | 230 | // spin |
| 318 | #[cfg(not(any(adc_g0, adc_u0)))] | 231 | } |
| 319 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | 232 | } |
| 320 | #[cfg(any(adc_g0, adc_u0))] | ||
| 321 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 322 | } | ||
| 323 | 233 | ||
| 324 | pub fn set_averaging(&mut self, averaging: Averaging) { | 234 | T::regs().dr().read().0 as u16 |
| 325 | let (enable, samples, right_shift) = match averaging { | ||
| 326 | Averaging::Disabled => (false, 0, 0), | ||
| 327 | Averaging::Samples2 => (true, 0, 1), | ||
| 328 | Averaging::Samples4 => (true, 1, 2), | ||
| 329 | Averaging::Samples8 => (true, 2, 3), | ||
| 330 | Averaging::Samples16 => (true, 3, 4), | ||
| 331 | Averaging::Samples32 => (true, 4, 5), | ||
| 332 | Averaging::Samples64 => (true, 5, 6), | ||
| 333 | Averaging::Samples128 => (true, 6, 7), | ||
| 334 | Averaging::Samples256 => (true, 7, 8), | ||
| 335 | }; | ||
| 336 | T::regs().cfgr2().modify(|reg| { | ||
| 337 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 338 | reg.set_rovse(enable); | ||
| 339 | #[cfg(any(adc_g0, adc_u0))] | ||
| 340 | reg.set_ovse(enable); | ||
| 341 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 342 | reg.set_ovsr(samples.into()); | ||
| 343 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 344 | reg.set_ovsr(samples.into()); | ||
| 345 | reg.set_ovss(right_shift.into()); | ||
| 346 | }) | ||
| 347 | } | ||
| 348 | /* | ||
| 349 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 350 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 351 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 352 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 353 | + 30.0 | ||
| 354 | } | 235 | } |
| 355 | */ | ||
| 356 | 236 | ||
| 357 | /// Perform a single conversion. | 237 | fn configure_dma(conversion_mode: ConversionMode) { |
| 358 | fn convert(&mut self) -> u16 { | 238 | // Set continuous mode with oneshot dma. |
| 239 | // Clear overrun flag before starting transfer. | ||
| 359 | T::regs().isr().modify(|reg| { | 240 | T::regs().isr().modify(|reg| { |
| 360 | reg.set_eos(true); | 241 | reg.set_ovr(true); |
| 361 | reg.set_eoc(true); | ||
| 362 | }); | ||
| 363 | |||
| 364 | // Start conversion | ||
| 365 | T::regs().cr().modify(|reg| { | ||
| 366 | reg.set_adstart(true); | ||
| 367 | }); | 242 | }); |
| 368 | 243 | ||
| 369 | while !T::regs().isr().read().eos() { | 244 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 370 | // spin | 245 | let regs = T::regs().cfgr(); |
| 371 | } | ||
| 372 | 246 | ||
| 373 | T::regs().dr().read().0 as u16 | 247 | #[cfg(any(adc_g0, adc_u0))] |
| 374 | } | 248 | let regs = T::regs().cfgr1(); |
| 375 | 249 | ||
| 376 | /// Read an ADC channel. | 250 | regs.modify(|reg| { |
| 377 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 251 | reg.set_discen(false); |
| 378 | self.read_channel(channel) | 252 | reg.set_cont(true); |
| 253 | reg.set_dmacfg(match conversion_mode { | ||
| 254 | ConversionMode::Singular => Dmacfg::ONE_SHOT, | ||
| 255 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] | ||
| 256 | ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, | ||
| 257 | }); | ||
| 258 | reg.set_dmaen(true); | ||
| 259 | }); | ||
| 379 | } | 260 | } |
| 380 | 261 | ||
| 381 | /// Read one or multiple ADC channels using DMA. | 262 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 382 | /// | 263 | #[cfg(adc_h5)] |
| 383 | /// `readings` must have a length that is a multiple of the length of the | 264 | T::regs().cr().modify(|w| w.set_aden(false)); |
| 384 | /// `sequence` iterator. | ||
| 385 | /// | ||
| 386 | /// Note: The order of values in `readings` is defined by the pin ADC | ||
| 387 | /// channel number and not the pin order in `sequence`. | ||
| 388 | /// | ||
| 389 | /// Example | ||
| 390 | /// ```rust,ignore | ||
| 391 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 392 | /// | ||
| 393 | /// let mut adc = Adc::new(p.ADC1); | ||
| 394 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 395 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 396 | /// let mut measurements = [0u16; 2]; | ||
| 397 | /// | ||
| 398 | /// adc.read( | ||
| 399 | /// p.DMA1_CH2.reborrow(), | ||
| 400 | /// [ | ||
| 401 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 402 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 403 | /// ] | ||
| 404 | /// .into_iter(), | ||
| 405 | /// &mut measurements, | ||
| 406 | /// ) | ||
| 407 | /// .await; | ||
| 408 | /// defmt::info!("measurements: {}", measurements); | ||
| 409 | /// ``` | ||
| 410 | pub async fn read( | ||
| 411 | &mut self, | ||
| 412 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 413 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 414 | readings: &mut [u16], | ||
| 415 | ) { | ||
| 416 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 417 | assert!( | ||
| 418 | readings.len() % sequence.len() == 0, | ||
| 419 | "Readings length must be a multiple of sequence length" | ||
| 420 | ); | ||
| 421 | assert!( | ||
| 422 | sequence.len() <= 16, | ||
| 423 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 424 | ); | ||
| 425 | |||
| 426 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 427 | Self::cancel_conversions(); | ||
| 428 | self.enable(); | ||
| 429 | 265 | ||
| 430 | // Set sequence length | 266 | // Set sequence length |
| 431 | #[cfg(not(any(adc_g0, adc_u0)))] | 267 | #[cfg(not(any(adc_g0, adc_u0)))] |
| @@ -439,10 +275,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 439 | 275 | ||
| 440 | T::regs().chselr().write(|chselr| { | 276 | T::regs().chselr().write(|chselr| { |
| 441 | T::regs().smpr().write(|smpr| { | 277 | T::regs().smpr().write(|smpr| { |
| 442 | for (channel, sample_time) in sequence { | 278 | for ((channel, _), sample_time) in sequence { |
| 443 | chselr.set_chsel(channel.channel.into(), true); | 279 | chselr.set_chsel(channel.into(), true); |
| 444 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | 280 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { |
| 445 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | 281 | smpr.set_smpsel(channel.into(), (i as u8).into()); |
| 446 | } else { | 282 | } else { |
| 447 | smpr.set_sample_time(sample_times.len(), sample_time); | 283 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 448 | if let Err(_) = sample_times.push(sample_time) { | 284 | if let Err(_) = sample_times.push(sample_time) { |
| @@ -461,42 +297,86 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 461 | #[cfg(adc_u0)] | 297 | #[cfg(adc_u0)] |
| 462 | let mut channel_mask = 0; | 298 | let mut channel_mask = 0; |
| 463 | 299 | ||
| 300 | #[cfg(adc_h5)] | ||
| 301 | let mut difsel = 0u32; | ||
| 302 | |||
| 464 | // Configure channels and ranks | 303 | // Configure channels and ranks |
| 465 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 304 | for (_i, ((channel, _is_differential), sample_time)) in sequence.enumerate() { |
| 466 | Self::configure_channel(channel, sample_time); | 305 | // RM0492, RM0481, etc. |
| 306 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 307 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 308 | if channel == 0 { | ||
| 309 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 310 | } | ||
| 311 | |||
| 312 | // Configure channel | ||
| 313 | cfg_if! { | ||
| 314 | if #[cfg(adc_u0)] { | ||
| 315 | // On G0 and U6 all channels use the same sampling time. | ||
| 316 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | ||
| 317 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 318 | match channel { | ||
| 319 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 320 | _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), | ||
| 321 | } | ||
| 322 | } else { | ||
| 323 | let sample_time = sample_time.into(); | ||
| 324 | T::regs() | ||
| 325 | .smpr(channel as usize / 10) | ||
| 326 | .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | #[cfg(stm32h7)] | ||
| 331 | { | ||
| 332 | use crate::pac::adc::vals::Pcsel; | ||
| 333 | |||
| 334 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 335 | T::regs() | ||
| 336 | .pcsel() | ||
| 337 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 338 | } | ||
| 467 | 339 | ||
| 468 | // Each channel is sampled according to sequence | 340 | // Each channel is sampled according to sequence |
| 469 | #[cfg(not(any(adc_g0, adc_u0)))] | 341 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 470 | match _i { | 342 | match _i { |
| 471 | 0..=3 => { | 343 | 0..=3 => { |
| 472 | T::regs().sqr1().modify(|w| { | 344 | T::regs().sqr1().modify(|w| { |
| 473 | w.set_sq(_i, channel.channel()); | 345 | w.set_sq(_i, channel); |
| 474 | }); | 346 | }); |
| 475 | } | 347 | } |
| 476 | 4..=8 => { | 348 | 4..=8 => { |
| 477 | T::regs().sqr2().modify(|w| { | 349 | T::regs().sqr2().modify(|w| { |
| 478 | w.set_sq(_i - 4, channel.channel()); | 350 | w.set_sq(_i - 4, channel); |
| 479 | }); | 351 | }); |
| 480 | } | 352 | } |
| 481 | 9..=13 => { | 353 | 9..=13 => { |
| 482 | T::regs().sqr3().modify(|w| { | 354 | T::regs().sqr3().modify(|w| { |
| 483 | w.set_sq(_i - 9, channel.channel()); | 355 | w.set_sq(_i - 9, channel); |
| 484 | }); | 356 | }); |
| 485 | } | 357 | } |
| 486 | 14..=15 => { | 358 | 14..=15 => { |
| 487 | T::regs().sqr4().modify(|w| { | 359 | T::regs().sqr4().modify(|w| { |
| 488 | w.set_sq(_i - 14, channel.channel()); | 360 | w.set_sq(_i - 14, channel); |
| 489 | }); | 361 | }); |
| 490 | } | 362 | } |
| 491 | _ => unreachable!(), | 363 | _ => unreachable!(), |
| 492 | } | 364 | } |
| 493 | 365 | ||
| 366 | #[cfg(adc_h5)] | ||
| 367 | { | ||
| 368 | difsel |= (_is_differential as u32) << channel; | ||
| 369 | } | ||
| 370 | |||
| 494 | #[cfg(adc_u0)] | 371 | #[cfg(adc_u0)] |
| 495 | { | 372 | { |
| 496 | channel_mask |= 1 << channel.channel(); | 373 | channel_mask |= 1 << channel; |
| 497 | } | 374 | } |
| 498 | } | 375 | } |
| 499 | 376 | ||
| 377 | #[cfg(adc_h5)] | ||
| 378 | T::regs().difsel().write(|w| w.set_difsel(difsel)); | ||
| 379 | |||
| 500 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | 380 | // On G0 and U0 enabled channels are sampled from 0 to last channel. |
| 501 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | 381 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. |
| 502 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | 382 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. |
| @@ -505,181 +385,234 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 505 | reg.set_chsel(channel_mask); | 385 | reg.set_chsel(channel_mask); |
| 506 | }); | 386 | }); |
| 507 | } | 387 | } |
| 508 | // Set continuous mode with oneshot dma. | 388 | } |
| 509 | // Clear overrun flag before starting transfer. | 389 | } |
| 510 | T::regs().isr().modify(|reg| { | ||
| 511 | reg.set_ovr(true); | ||
| 512 | }); | ||
| 513 | 390 | ||
| 514 | #[cfg(not(any(adc_g0, adc_u0)))] | 391 | impl<'d, T: Instance> Adc<'d, T> { |
| 515 | T::regs().cfgr().modify(|reg| { | 392 | /// Enable the voltage regulator |
| 516 | reg.set_discen(false); | 393 | fn init_regulator() { |
| 517 | reg.set_cont(true); | 394 | rcc::enable_and_reset::<T>(); |
| 518 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 395 | T::regs().cr().modify(|reg| { |
| 519 | reg.set_dmaen(true); | 396 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 397 | reg.set_deeppwd(false); | ||
| 398 | reg.set_advregen(true); | ||
| 520 | }); | 399 | }); |
| 400 | |||
| 401 | // If this is false then each ADC_CHSELR bit enables an input channel. | ||
| 402 | // This is the reset value, so has no effect. | ||
| 521 | #[cfg(any(adc_g0, adc_u0))] | 403 | #[cfg(any(adc_g0, adc_u0))] |
| 522 | T::regs().cfgr1().modify(|reg| { | 404 | T::regs().cfgr1().modify(|reg| { |
| 523 | reg.set_discen(false); | 405 | reg.set_chselrmod(false); |
| 524 | reg.set_cont(true); | ||
| 525 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 526 | reg.set_dmaen(true); | ||
| 527 | }); | 406 | }); |
| 528 | 407 | ||
| 529 | let request = rx_dma.request(); | 408 | blocking_delay_us(20); |
| 530 | let transfer = unsafe { | 409 | } |
| 531 | Transfer::new_read( | ||
| 532 | rx_dma, | ||
| 533 | request, | ||
| 534 | T::regs().dr().as_ptr() as *mut u16, | ||
| 535 | readings, | ||
| 536 | Default::default(), | ||
| 537 | ) | ||
| 538 | }; | ||
| 539 | 410 | ||
| 540 | // Start conversion | 411 | /// Calibrate to remove conversion offset |
| 412 | fn init_calibrate() { | ||
| 541 | T::regs().cr().modify(|reg| { | 413 | T::regs().cr().modify(|reg| { |
| 542 | reg.set_adstart(true); | 414 | reg.set_adcal(true); |
| 543 | }); | 415 | }); |
| 544 | 416 | ||
| 545 | // Wait for conversion sequence to finish. | 417 | while T::regs().cr().read().adcal() { |
| 546 | transfer.await; | 418 | // spin |
| 547 | 419 | } | |
| 548 | // Ensure conversions are finished. | ||
| 549 | Self::cancel_conversions(); | ||
| 550 | 420 | ||
| 551 | // Reset configuration. | 421 | blocking_delay_us(1); |
| 552 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 553 | T::regs().cfgr().modify(|reg| { | ||
| 554 | reg.set_cont(false); | ||
| 555 | }); | ||
| 556 | #[cfg(any(adc_g0, adc_u0))] | ||
| 557 | T::regs().cfgr1().modify(|reg| { | ||
| 558 | reg.set_cont(false); | ||
| 559 | }); | ||
| 560 | } | 422 | } |
| 561 | 423 | ||
| 562 | #[cfg(not(adc_g0))] | 424 | /// Initialize the ADC leaving any analog clock at reset value. |
| 563 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 425 | /// For G0 and WL, this is the async clock without prescaler. |
| 564 | // RM0492, RM0481, etc. | 426 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 565 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 427 | Self::init_regulator(); |
| 566 | #[cfg(any(adc_h5, adc_h7rs))] | 428 | Self::init_calibrate(); |
| 567 | if channel.channel() == 0 { | 429 | Self { adc } |
| 568 | T::regs().or().modify(|reg| reg.set_op0(true)); | ||
| 569 | } | ||
| 570 | |||
| 571 | // Configure channel | ||
| 572 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 573 | } | 430 | } |
| 574 | 431 | ||
| 575 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 432 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 576 | self.enable(); | ||
| 577 | #[cfg(not(adc_g0))] | 433 | #[cfg(not(adc_g0))] |
| 578 | Self::configure_channel(channel, self.sample_time); | 434 | let s = Self::new(adc); |
| 435 | |||
| 579 | #[cfg(adc_g0)] | 436 | #[cfg(adc_g0)] |
| 580 | T::regs().smpr().write(|reg| { | 437 | let s = match config.clock { |
| 581 | reg.set_sample_time(0, self.sample_time); | 438 | Some(clock) => Self::new_with_clock(adc, clock), |
| 582 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | 439 | None => Self::new(adc), |
| 583 | }); | 440 | }; |
| 584 | // Select channel | 441 | |
| 585 | #[cfg(not(any(adc_g0, adc_u0)))] | 442 | #[cfg(any(adc_g0, adc_u0, adc_v3))] |
| 586 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 443 | if let Some(shift) = config.oversampling_shift { |
| 444 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 445 | } | ||
| 446 | |||
| 447 | #[cfg(any(adc_g0, adc_u0, adc_v3))] | ||
| 448 | if let Some(ratio) = config.oversampling_ratio { | ||
| 449 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 450 | } | ||
| 451 | |||
| 587 | #[cfg(any(adc_g0, adc_u0))] | 452 | #[cfg(any(adc_g0, adc_u0))] |
| 588 | T::regs().chselr().write(|reg| { | 453 | if let Some(enable) = config.oversampling_enable { |
| 589 | #[cfg(adc_g0)] | 454 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); |
| 590 | reg.set_chsel(channel.channel().into(), true); | 455 | } |
| 591 | #[cfg(adc_u0)] | ||
| 592 | reg.set_chsel(1 << channel.channel()); | ||
| 593 | }); | ||
| 594 | 456 | ||
| 595 | // Some models are affected by an erratum: | 457 | #[cfg(adc_v3)] |
| 596 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | 458 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { |
| 597 | // corrupted, so we discard it and measure again. | 459 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); |
| 598 | // | 460 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); |
| 599 | // STM32L471xx: Section 2.7.3 | 461 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); |
| 600 | // STM32G4: Section 2.7.3 | 462 | } |
| 601 | #[cfg(any(rcc_l4, rcc_g4))] | ||
| 602 | let _ = self.convert(); | ||
| 603 | let val = self.convert(); | ||
| 604 | 463 | ||
| 605 | T::regs().cr().modify(|reg| reg.set_addis(true)); | 464 | if let Some(resolution) = config.resolution { |
| 465 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 466 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 467 | #[cfg(any(adc_g0, adc_u0))] | ||
| 468 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 469 | } | ||
| 606 | 470 | ||
| 607 | // RM0492, RM0481, etc. | 471 | if let Some(averaging) = config.averaging { |
| 608 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 472 | let (enable, samples, right_shift) = match averaging { |
| 609 | #[cfg(any(adc_h5, adc_h7rs))] | 473 | Averaging::Disabled => (false, 0, 0), |
| 610 | if channel.channel() == 0 { | 474 | Averaging::Samples2 => (true, 0, 1), |
| 611 | T::regs().or().modify(|reg| reg.set_op0(false)); | 475 | Averaging::Samples4 => (true, 1, 2), |
| 476 | Averaging::Samples8 => (true, 2, 3), | ||
| 477 | Averaging::Samples16 => (true, 3, 4), | ||
| 478 | Averaging::Samples32 => (true, 4, 5), | ||
| 479 | Averaging::Samples64 => (true, 5, 6), | ||
| 480 | Averaging::Samples128 => (true, 6, 7), | ||
| 481 | Averaging::Samples256 => (true, 7, 8), | ||
| 482 | }; | ||
| 483 | T::regs().cfgr2().modify(|reg| { | ||
| 484 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 485 | reg.set_rovse(enable); | ||
| 486 | #[cfg(any(adc_g0, adc_u0))] | ||
| 487 | reg.set_ovse(enable); | ||
| 488 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 489 | reg.set_ovsr(samples.into()); | ||
| 490 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 491 | reg.set_ovsr(samples.into()); | ||
| 492 | reg.set_ovss(right_shift.into()); | ||
| 493 | }) | ||
| 612 | } | 494 | } |
| 613 | 495 | ||
| 614 | val | 496 | s |
| 615 | } | 497 | } |
| 616 | 498 | ||
| 617 | #[cfg(adc_g0)] | 499 | #[cfg(adc_g0)] |
| 618 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | 500 | /// Initialize ADC with explicit clock for the analog ADC |
| 619 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | 501 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { |
| 620 | } | 502 | Self::init_regulator(); |
| 621 | #[cfg(adc_u0)] | ||
| 622 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 623 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 624 | } | ||
| 625 | 503 | ||
| 626 | #[cfg(adc_g0)] | 504 | #[cfg(any(stm32wl5x))] |
| 627 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | 505 | { |
| 628 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 506 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual |
| 629 | } | 507 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; |
| 630 | #[cfg(adc_u0)] | 508 | match clock { |
| 631 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | 509 | Clock::Async { div: _ } => { |
| 632 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 510 | assert!(async_clock_available); |
| 633 | } | 511 | } |
| 512 | Clock::Sync { div: _ } => { | ||
| 513 | if async_clock_available { | ||
| 514 | warn!("Not using configured ADC clock"); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | } | ||
| 518 | } | ||
| 519 | match clock { | ||
| 520 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 521 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 522 | reg.set_ckmode(match div { | ||
| 523 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 524 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 525 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 526 | }) | ||
| 527 | }), | ||
| 528 | } | ||
| 634 | 529 | ||
| 635 | #[cfg(any(adc_g0, adc_u0))] | 530 | Self::init_calibrate(); |
| 636 | pub fn oversampling_enable(&mut self, enable: bool) { | ||
| 637 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | ||
| 638 | } | ||
| 639 | 531 | ||
| 640 | #[cfg(adc_v3)] | 532 | Self { adc } |
| 641 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 642 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 643 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 644 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 645 | } | 533 | } |
| 646 | 534 | ||
| 647 | #[cfg(adc_v3)] | 535 | pub fn enable_vrefint(&self) -> VrefInt { |
| 648 | pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { | 536 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 649 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 537 | T::common_regs().ccr().modify(|reg| { |
| 538 | reg.set_vrefen(true); | ||
| 539 | }); | ||
| 540 | #[cfg(any(adc_g0, adc_u0))] | ||
| 541 | T::regs().ccr().modify(|reg| { | ||
| 542 | reg.set_vrefen(true); | ||
| 543 | }); | ||
| 544 | |||
| 545 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||
| 546 | // to stabilize the internal voltage reference. | ||
| 547 | blocking_delay_us(15); | ||
| 548 | |||
| 549 | VrefInt {} | ||
| 650 | } | 550 | } |
| 651 | 551 | ||
| 652 | #[cfg(adc_v3)] | 552 | pub fn enable_temperature(&self) -> Temperature { |
| 653 | pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { | 553 | cfg_if! { |
| 654 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | 554 | if #[cfg(any(adc_g0, adc_u0))] { |
| 555 | T::regs().ccr().modify(|reg| { | ||
| 556 | reg.set_tsen(true); | ||
| 557 | }); | ||
| 558 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 559 | T::common_regs().ccr().modify(|reg| { | ||
| 560 | reg.set_tsen(true); | ||
| 561 | }); | ||
| 562 | } else { | ||
| 563 | T::common_regs().ccr().modify(|reg| { | ||
| 564 | reg.set_ch17sel(true); | ||
| 565 | }); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | |||
| 569 | Temperature {} | ||
| 655 | } | 570 | } |
| 656 | 571 | ||
| 657 | #[cfg(not(adc_g0))] | 572 | pub fn enable_vbat(&self) -> Vbat { |
| 658 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||
| 659 | cfg_if! { | 573 | cfg_if! { |
| 660 | if #[cfg(adc_u0)] { | 574 | if #[cfg(any(adc_g0, adc_u0))] { |
| 661 | // On G0 and U6 all channels use the same sampling time. | 575 | T::regs().ccr().modify(|reg| { |
| 662 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 576 | reg.set_vbaten(true); |
| 577 | }); | ||
| 663 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 578 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 664 | match _ch { | 579 | T::common_regs().ccr().modify(|reg| { |
| 665 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 580 | reg.set_vbaten(true); |
| 666 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 581 | }); |
| 667 | } | ||
| 668 | } else { | 582 | } else { |
| 669 | let sample_time = sample_time.into(); | 583 | T::common_regs().ccr().modify(|reg| { |
| 670 | T::regs() | 584 | reg.set_ch18sel(true); |
| 671 | .smpr(_ch as usize / 10) | 585 | }); |
| 672 | .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); | ||
| 673 | } | 586 | } |
| 674 | } | 587 | } |
| 588 | |||
| 589 | Vbat {} | ||
| 675 | } | 590 | } |
| 676 | 591 | ||
| 677 | fn cancel_conversions() { | 592 | pub fn disable_vbat(&self) { |
| 678 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 593 | cfg_if! { |
| 679 | T::regs().cr().modify(|reg| { | 594 | if #[cfg(any(adc_g0, adc_u0))] { |
| 680 | reg.set_adstp(true); | 595 | T::regs().ccr().modify(|reg| { |
| 681 | }); | 596 | reg.set_vbaten(false); |
| 682 | while T::regs().cr().read().adstart() {} | 597 | }); |
| 598 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 599 | T::common_regs().ccr().modify(|reg| { | ||
| 600 | reg.set_vbaten(false); | ||
| 601 | }); | ||
| 602 | } else { | ||
| 603 | T::common_regs().ccr().modify(|reg| { | ||
| 604 | reg.set_ch18sel(false); | ||
| 605 | }); | ||
| 606 | } | ||
| 683 | } | 607 | } |
| 684 | } | 608 | } |
| 609 | |||
| 610 | /* | ||
| 611 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 612 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 613 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 614 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 615 | + 30.0 | ||
| 616 | } | ||
| 617 | */ | ||
| 685 | } | 618 | } |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b66437e6e..a3d9e6176 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -4,12 +4,10 @@ use pac::adc::vals::{Adcaldif, Boost}; | |||
| 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; | 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; |
| 5 | use pac::adccommon::vals::Presc; | 5 | use pac::adccommon::vals::Presc; |
| 6 | 6 | ||
| 7 | use super::{ | 7 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 8 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 8 | use crate::adc::ConversionMode; |
| 9 | }; | ||
| 10 | use crate::dma::Transfer; | ||
| 11 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 12 | use crate::{pac, rcc, Peri}; | 10 | use crate::{Peri, pac, rcc}; |
| 13 | 11 | ||
| 14 | /// Default VREF voltage used for sample conversion to millivolts. | 12 | /// Default VREF voltage used for sample conversion to millivolts. |
| 15 | pub const VREF_DEFAULT_MV: u32 = 3300; | 13 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -25,153 +23,234 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | |||
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | 23 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); |
| 26 | 24 | ||
| 27 | #[cfg(stm32g4)] | 25 | #[cfg(stm32g4)] |
| 28 | const VREF_CHANNEL: u8 = 18; | 26 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 27 | const CHANNEL: u8 = 18; | ||
| 28 | } | ||
| 29 | #[cfg(stm32g4)] | 29 | #[cfg(stm32g4)] |
| 30 | const TEMP_CHANNEL: u8 = 16; | 30 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 31 | const CHANNEL: u8 = 16; | ||
| 32 | } | ||
| 31 | 33 | ||
| 32 | #[cfg(stm32h7)] | 34 | #[cfg(stm32h7)] |
| 33 | const VREF_CHANNEL: u8 = 19; | 35 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 36 | const CHANNEL: u8 = 19; | ||
| 37 | } | ||
| 34 | #[cfg(stm32h7)] | 38 | #[cfg(stm32h7)] |
| 35 | const TEMP_CHANNEL: u8 = 18; | 39 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 40 | const CHANNEL: u8 = 18; | ||
| 41 | } | ||
| 36 | 42 | ||
| 37 | // TODO this should be 14 for H7a/b/35 | 43 | // TODO this should be 14 for H7a/b/35 |
| 38 | #[cfg(not(stm32u5))] | 44 | #[cfg(not(stm32u5))] |
| 39 | const VBAT_CHANNEL: u8 = 17; | 45 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 46 | const CHANNEL: u8 = 17; | ||
| 47 | } | ||
| 40 | 48 | ||
| 41 | #[cfg(stm32u5)] | 49 | #[cfg(stm32u5)] |
| 42 | const VREF_CHANNEL: u8 = 0; | 50 | impl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T { |
| 51 | const CHANNEL: u8 = 0; | ||
| 52 | } | ||
| 43 | #[cfg(stm32u5)] | 53 | #[cfg(stm32u5)] |
| 44 | const TEMP_CHANNEL: u8 = 19; | 54 | impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { |
| 55 | const CHANNEL: u8 = 19; | ||
| 56 | } | ||
| 45 | #[cfg(stm32u5)] | 57 | #[cfg(stm32u5)] |
| 46 | const VBAT_CHANNEL: u8 = 18; | 58 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 47 | 59 | const CHANNEL: u8 = 18; | |
| 48 | // 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 | ||
| 49 | /// Internal voltage reference channel. | ||
| 50 | pub struct VrefInt; | ||
| 51 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 52 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 53 | fn channel(&self) -> u8 { | ||
| 54 | VREF_CHANNEL | ||
| 55 | } | ||
| 56 | } | 60 | } |
| 57 | 61 | ||
| 58 | /// Internal temperature channel. | 62 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 59 | pub struct Temperature; | 63 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 60 | impl<T: Instance> AdcChannel<T> for Temperature {} | 64 | match raw_prescaler { |
| 61 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | 65 | 0 => Presc::DIV1, |
| 62 | fn channel(&self) -> u8 { | 66 | 1 => Presc::DIV2, |
| 63 | TEMP_CHANNEL | 67 | 2..=3 => Presc::DIV4, |
| 68 | 4..=5 => Presc::DIV6, | ||
| 69 | 6..=7 => Presc::DIV8, | ||
| 70 | 8..=9 => Presc::DIV10, | ||
| 71 | 10..=11 => Presc::DIV12, | ||
| 72 | _ => unimplemented!(), | ||
| 64 | } | 73 | } |
| 65 | } | 74 | } |
| 66 | 75 | ||
| 67 | /// Internal battery voltage channel. | 76 | /// Adc configuration |
| 68 | pub struct Vbat; | 77 | #[derive(Default)] |
| 69 | impl<T: Instance> AdcChannel<T> for Vbat {} | 78 | pub struct AdcConfig { |
| 70 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | 79 | pub resolution: Option<Resolution>, |
| 71 | fn channel(&self) -> u8 { | 80 | pub averaging: Option<Averaging>, |
| 72 | VBAT_CHANNEL | ||
| 73 | } | ||
| 74 | } | 81 | } |
| 75 | 82 | ||
| 76 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 83 | impl<T: Instance> super::SealedAnyInstance for T { |
| 77 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 84 | fn dr() -> *mut u16 { |
| 78 | #[allow(unused)] | 85 | T::regs().dr().as_ptr() as *mut u16 |
| 79 | enum Prescaler { | 86 | } |
| 80 | NotDivided, | 87 | |
| 81 | DividedBy2, | 88 | fn enable() { |
| 82 | DividedBy4, | 89 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 83 | DividedBy6, | 90 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 84 | DividedBy8, | 91 | while !T::regs().isr().read().adrdy() {} |
| 85 | DividedBy10, | 92 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 86 | DividedBy12, | 93 | } |
| 87 | DividedBy16, | ||
| 88 | DividedBy32, | ||
| 89 | DividedBy64, | ||
| 90 | DividedBy128, | ||
| 91 | DividedBy256, | ||
| 92 | } | ||
| 93 | 94 | ||
| 94 | impl Prescaler { | 95 | fn start() { |
| 95 | fn from_ker_ck(frequency: Hertz) -> Self { | 96 | // Start conversion |
| 96 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 97 | T::regs().cr().modify(|reg| { |
| 97 | match raw_prescaler { | 98 | reg.set_adstart(true); |
| 98 | 0 => Self::NotDivided, | 99 | }); |
| 99 | 1 => Self::DividedBy2, | 100 | } |
| 100 | 2..=3 => Self::DividedBy4, | 101 | |
| 101 | 4..=5 => Self::DividedBy6, | 102 | fn stop() { |
| 102 | 6..=7 => Self::DividedBy8, | 103 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 103 | 8..=9 => Self::DividedBy10, | 104 | T::regs().cr().modify(|reg| { |
| 104 | 10..=11 => Self::DividedBy12, | 105 | reg.set_adstp(Adstp::STOP); |
| 105 | _ => unimplemented!(), | 106 | }); |
| 107 | while T::regs().cr().read().adstart() {} | ||
| 106 | } | 108 | } |
| 109 | |||
| 110 | // Reset configuration. | ||
| 111 | T::regs().cfgr().modify(|reg| { | ||
| 112 | reg.set_cont(false); | ||
| 113 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 114 | }); | ||
| 107 | } | 115 | } |
| 108 | 116 | ||
| 109 | fn divisor(&self) -> u32 { | 117 | fn convert() -> u16 { |
| 110 | match self { | 118 | T::regs().isr().modify(|reg| { |
| 111 | Prescaler::NotDivided => 1, | 119 | reg.set_eos(true); |
| 112 | Prescaler::DividedBy2 => 2, | 120 | reg.set_eoc(true); |
| 113 | Prescaler::DividedBy4 => 4, | 121 | }); |
| 114 | Prescaler::DividedBy6 => 6, | 122 | |
| 115 | Prescaler::DividedBy8 => 8, | 123 | // Start conversion |
| 116 | Prescaler::DividedBy10 => 10, | 124 | T::regs().cr().modify(|reg| { |
| 117 | Prescaler::DividedBy12 => 12, | 125 | reg.set_adstart(true); |
| 118 | Prescaler::DividedBy16 => 16, | 126 | }); |
| 119 | Prescaler::DividedBy32 => 32, | 127 | |
| 120 | Prescaler::DividedBy64 => 64, | 128 | while !T::regs().isr().read().eos() { |
| 121 | Prescaler::DividedBy128 => 128, | 129 | // spin |
| 122 | Prescaler::DividedBy256 => 256, | ||
| 123 | } | 130 | } |
| 131 | |||
| 132 | T::regs().dr().read().0 as u16 | ||
| 124 | } | 133 | } |
| 125 | 134 | ||
| 126 | fn presc(&self) -> Presc { | 135 | fn configure_dma(conversion_mode: ConversionMode) { |
| 127 | match self { | 136 | match conversion_mode { |
| 128 | Prescaler::NotDivided => Presc::DIV1, | 137 | ConversionMode::Singular => { |
| 129 | Prescaler::DividedBy2 => Presc::DIV2, | 138 | T::regs().isr().modify(|reg| { |
| 130 | Prescaler::DividedBy4 => Presc::DIV4, | 139 | reg.set_ovr(true); |
| 131 | Prescaler::DividedBy6 => Presc::DIV6, | 140 | }); |
| 132 | Prescaler::DividedBy8 => Presc::DIV8, | 141 | T::regs().cfgr().modify(|reg| { |
| 133 | Prescaler::DividedBy10 => Presc::DIV10, | 142 | reg.set_cont(true); |
| 134 | Prescaler::DividedBy12 => Presc::DIV12, | 143 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); |
| 135 | Prescaler::DividedBy16 => Presc::DIV16, | 144 | }); |
| 136 | Prescaler::DividedBy32 => Presc::DIV32, | 145 | } |
| 137 | Prescaler::DividedBy64 => Presc::DIV64, | 146 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] |
| 138 | Prescaler::DividedBy128 => Presc::DIV128, | 147 | _ => unreachable!(), |
| 139 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 140 | } | 148 | } |
| 141 | } | 149 | } |
| 142 | } | ||
| 143 | 150 | ||
| 144 | /// Number of samples used for averaging. | 151 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 145 | #[derive(Copy, Clone, Debug)] | 152 | // Set sequence length |
| 146 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 153 | T::regs().sqr1().modify(|w| { |
| 147 | pub enum Averaging { | 154 | w.set_l(sequence.len() as u8 - 1); |
| 148 | Disabled, | 155 | }); |
| 149 | Samples2, | 156 | |
| 150 | Samples4, | 157 | // Configure channels and ranks |
| 151 | Samples8, | 158 | for (i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 152 | Samples16, | 159 | let sample_time = sample_time.into(); |
| 153 | Samples32, | 160 | if channel <= 9 { |
| 154 | Samples64, | 161 | T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); |
| 155 | Samples128, | 162 | } else { |
| 156 | Samples256, | 163 | T::regs() |
| 157 | Samples512, | 164 | .smpr(1) |
| 158 | Samples1024, | 165 | .modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); |
| 166 | } | ||
| 167 | |||
| 168 | #[cfg(any(stm32h7, stm32u5))] | ||
| 169 | { | ||
| 170 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 171 | T::regs() | ||
| 172 | .pcsel() | ||
| 173 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 174 | } | ||
| 175 | |||
| 176 | match i { | ||
| 177 | 0..=3 => { | ||
| 178 | T::regs().sqr1().modify(|w| { | ||
| 179 | w.set_sq(i, channel); | ||
| 180 | }); | ||
| 181 | } | ||
| 182 | 4..=8 => { | ||
| 183 | T::regs().sqr2().modify(|w| { | ||
| 184 | w.set_sq(i - 4, channel); | ||
| 185 | }); | ||
| 186 | } | ||
| 187 | 9..=13 => { | ||
| 188 | T::regs().sqr3().modify(|w| { | ||
| 189 | w.set_sq(i - 9, channel); | ||
| 190 | }); | ||
| 191 | } | ||
| 192 | 14..=15 => { | ||
| 193 | T::regs().sqr4().modify(|w| { | ||
| 194 | w.set_sq(i - 14, channel); | ||
| 195 | }); | ||
| 196 | } | ||
| 197 | _ => unreachable!(), | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 159 | } | 201 | } |
| 160 | 202 | ||
| 161 | impl<'d, T: Instance> Adc<'d, T> { | 203 | impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { |
| 204 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { | ||
| 205 | let s = Self::new(adc); | ||
| 206 | |||
| 207 | // Set the ADC resolution. | ||
| 208 | if let Some(resolution) = config.resolution { | ||
| 209 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 210 | } | ||
| 211 | |||
| 212 | // Set hardware averaging. | ||
| 213 | if let Some(averaging) = config.averaging { | ||
| 214 | let (enable, samples, right_shift) = match averaging { | ||
| 215 | Averaging::Disabled => (false, 0, 0), | ||
| 216 | Averaging::Samples2 => (true, 1, 1), | ||
| 217 | Averaging::Samples4 => (true, 3, 2), | ||
| 218 | Averaging::Samples8 => (true, 7, 3), | ||
| 219 | Averaging::Samples16 => (true, 15, 4), | ||
| 220 | Averaging::Samples32 => (true, 31, 5), | ||
| 221 | Averaging::Samples64 => (true, 63, 6), | ||
| 222 | Averaging::Samples128 => (true, 127, 7), | ||
| 223 | Averaging::Samples256 => (true, 255, 8), | ||
| 224 | Averaging::Samples512 => (true, 511, 9), | ||
| 225 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 226 | }; | ||
| 227 | |||
| 228 | T::regs().cfgr2().modify(|reg| { | ||
| 229 | reg.set_rovse(enable); | ||
| 230 | reg.set_ovsr(samples); | ||
| 231 | reg.set_ovss(right_shift); | ||
| 232 | }) | ||
| 233 | } | ||
| 234 | |||
| 235 | s | ||
| 236 | } | ||
| 237 | |||
| 162 | /// Create a new ADC driver. | 238 | /// Create a new ADC driver. |
| 163 | pub fn new(adc: Peri<'d, T>) -> Self { | 239 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 164 | rcc::enable_and_reset::<T>(); | 240 | rcc::enable_and_reset::<T>(); |
| 165 | 241 | ||
| 166 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 242 | let prescaler = from_ker_ck(T::frequency()); |
| 167 | 243 | ||
| 168 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 244 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 169 | 245 | ||
| 170 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 246 | let frequency = T::frequency() / prescaler; |
| 171 | info!("ADC frequency set to {}", frequency); | 247 | info!("ADC frequency set to {}", frequency); |
| 172 | 248 | ||
| 173 | if frequency > MAX_ADC_CLK_FREQ { | 249 | if frequency > MAX_ADC_CLK_FREQ { |
| 174 | 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 ); | 250 | panic!( |
| 251 | "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", | ||
| 252 | MAX_ADC_CLK_FREQ.0 / 1_000_000 | ||
| 253 | ); | ||
| 175 | } | 254 | } |
| 176 | 255 | ||
| 177 | #[cfg(stm32h7)] | 256 | #[cfg(stm32h7)] |
| @@ -187,40 +266,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 187 | }; | 266 | }; |
| 188 | T::regs().cr().modify(|w| w.set_boost(boost)); | 267 | T::regs().cr().modify(|w| w.set_boost(boost)); |
| 189 | } | 268 | } |
| 190 | let mut s = Self { | ||
| 191 | adc, | ||
| 192 | sample_time: SampleTime::from_bits(0), | ||
| 193 | }; | ||
| 194 | s.power_up(); | ||
| 195 | s.configure_differential_inputs(); | ||
| 196 | |||
| 197 | s.calibrate(); | ||
| 198 | blocking_delay_us(1); | ||
| 199 | |||
| 200 | s.enable(); | ||
| 201 | s.configure(); | ||
| 202 | |||
| 203 | s | ||
| 204 | } | ||
| 205 | 269 | ||
| 206 | fn power_up(&mut self) { | ||
| 207 | T::regs().cr().modify(|reg| { | 270 | T::regs().cr().modify(|reg| { |
| 208 | reg.set_deeppwd(false); | 271 | reg.set_deeppwd(false); |
| 209 | reg.set_advregen(true); | 272 | reg.set_advregen(true); |
| 210 | }); | 273 | }); |
| 211 | 274 | ||
| 212 | blocking_delay_us(10); | 275 | blocking_delay_us(10); |
| 213 | } | ||
| 214 | 276 | ||
| 215 | fn configure_differential_inputs(&mut self) { | ||
| 216 | T::regs().difsel().modify(|w| { | 277 | T::regs().difsel().modify(|w| { |
| 217 | for n in 0..20 { | 278 | for n in 0..20 { |
| 218 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 279 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 219 | } | 280 | } |
| 220 | }); | 281 | }); |
| 221 | } | ||
| 222 | 282 | ||
| 223 | fn calibrate(&mut self) { | ||
| 224 | T::regs().cr().modify(|w| { | 283 | T::regs().cr().modify(|w| { |
| 225 | #[cfg(not(adc_u5))] | 284 | #[cfg(not(adc_u5))] |
| 226 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 285 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| @@ -230,21 +289,18 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 230 | T::regs().cr().modify(|w| w.set_adcal(true)); | 289 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 231 | 290 | ||
| 232 | while T::regs().cr().read().adcal() {} | 291 | while T::regs().cr().read().adcal() {} |
| 233 | } | ||
| 234 | 292 | ||
| 235 | fn enable(&mut self) { | 293 | blocking_delay_us(1); |
| 236 | T::regs().isr().write(|w| w.set_adrdy(true)); | 294 | |
| 237 | T::regs().cr().modify(|w| w.set_aden(true)); | 295 | T::enable(); |
| 238 | while !T::regs().isr().read().adrdy() {} | ||
| 239 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 240 | } | ||
| 241 | 296 | ||
| 242 | fn configure(&mut self) { | ||
| 243 | // single conversion mode, software trigger | 297 | // single conversion mode, software trigger |
| 244 | T::regs().cfgr().modify(|w| { | 298 | T::regs().cfgr().modify(|w| { |
| 245 | w.set_cont(false); | 299 | w.set_cont(false); |
| 246 | w.set_exten(Exten::DISABLED); | 300 | w.set_exten(Exten::DISABLED); |
| 247 | }); | 301 | }); |
| 302 | |||
| 303 | Self { adc } | ||
| 248 | } | 304 | } |
| 249 | 305 | ||
| 250 | /// Enable reading the voltage reference internal channel. | 306 | /// Enable reading the voltage reference internal channel. |
| @@ -273,228 +329,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 273 | 329 | ||
| 274 | Vbat {} | 330 | Vbat {} |
| 275 | } | 331 | } |
| 276 | |||
| 277 | /// Set the ADC sample time. | ||
| 278 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 279 | self.sample_time = sample_time; | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Get the ADC sample time. | ||
| 283 | pub fn sample_time(&self) -> SampleTime { | ||
| 284 | self.sample_time | ||
| 285 | } | ||
| 286 | |||
| 287 | /// Set the ADC resolution. | ||
| 288 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 289 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Set hardware averaging. | ||
| 293 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 294 | let (enable, samples, right_shift) = match averaging { | ||
| 295 | Averaging::Disabled => (false, 0, 0), | ||
| 296 | Averaging::Samples2 => (true, 1, 1), | ||
| 297 | Averaging::Samples4 => (true, 3, 2), | ||
| 298 | Averaging::Samples8 => (true, 7, 3), | ||
| 299 | Averaging::Samples16 => (true, 15, 4), | ||
| 300 | Averaging::Samples32 => (true, 31, 5), | ||
| 301 | Averaging::Samples64 => (true, 63, 6), | ||
| 302 | Averaging::Samples128 => (true, 127, 7), | ||
| 303 | Averaging::Samples256 => (true, 255, 8), | ||
| 304 | Averaging::Samples512 => (true, 511, 9), | ||
| 305 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 306 | }; | ||
| 307 | |||
| 308 | T::regs().cfgr2().modify(|reg| { | ||
| 309 | reg.set_rovse(enable); | ||
| 310 | reg.set_ovsr(samples); | ||
| 311 | reg.set_ovss(right_shift); | ||
| 312 | }) | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Perform a single conversion. | ||
| 316 | fn convert(&mut self) -> u16 { | ||
| 317 | T::regs().isr().modify(|reg| { | ||
| 318 | reg.set_eos(true); | ||
| 319 | reg.set_eoc(true); | ||
| 320 | }); | ||
| 321 | |||
| 322 | // Start conversion | ||
| 323 | T::regs().cr().modify(|reg| { | ||
| 324 | reg.set_adstart(true); | ||
| 325 | }); | ||
| 326 | |||
| 327 | while !T::regs().isr().read().eos() { | ||
| 328 | // spin | ||
| 329 | } | ||
| 330 | |||
| 331 | T::regs().dr().read().0 as u16 | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Read an ADC channel. | ||
| 335 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 336 | self.read_channel(channel) | ||
| 337 | } | ||
| 338 | |||
| 339 | /// Read one or multiple ADC channels using DMA. | ||
| 340 | /// | ||
| 341 | /// `sequence` iterator and `readings` must have the same length. | ||
| 342 | /// | ||
| 343 | /// Example | ||
| 344 | /// ```rust,ignore | ||
| 345 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 346 | /// | ||
| 347 | /// let mut adc = Adc::new(p.ADC1); | ||
| 348 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 349 | /// let mut adc_pin2 = p.PA2.into(); | ||
| 350 | /// let mut measurements = [0u16; 2]; | ||
| 351 | /// | ||
| 352 | /// adc.read( | ||
| 353 | /// p.DMA2_CH0.reborrow(), | ||
| 354 | /// [ | ||
| 355 | /// (&mut *adc_pin0, SampleTime::CYCLES112), | ||
| 356 | /// (&mut *adc_pin2, SampleTime::CYCLES112), | ||
| 357 | /// ] | ||
| 358 | /// .into_iter(), | ||
| 359 | /// &mut measurements, | ||
| 360 | /// ) | ||
| 361 | /// .await; | ||
| 362 | /// defmt::info!("measurements: {}", measurements); | ||
| 363 | /// ``` | ||
| 364 | pub async fn read( | ||
| 365 | &mut self, | ||
| 366 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 367 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 368 | readings: &mut [u16], | ||
| 369 | ) { | ||
| 370 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 371 | assert!( | ||
| 372 | sequence.len() == readings.len(), | ||
| 373 | "Sequence length must be equal to readings length" | ||
| 374 | ); | ||
| 375 | assert!( | ||
| 376 | sequence.len() <= 16, | ||
| 377 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 378 | ); | ||
| 379 | |||
| 380 | // Ensure no conversions are ongoing | ||
| 381 | Self::cancel_conversions(); | ||
| 382 | |||
| 383 | // Set sequence length | ||
| 384 | T::regs().sqr1().modify(|w| { | ||
| 385 | w.set_l(sequence.len() as u8 - 1); | ||
| 386 | }); | ||
| 387 | |||
| 388 | // Configure channels and ranks | ||
| 389 | for (i, (channel, sample_time)) in sequence.enumerate() { | ||
| 390 | Self::configure_channel(channel, sample_time); | ||
| 391 | match i { | ||
| 392 | 0..=3 => { | ||
| 393 | T::regs().sqr1().modify(|w| { | ||
| 394 | w.set_sq(i, channel.channel()); | ||
| 395 | }); | ||
| 396 | } | ||
| 397 | 4..=8 => { | ||
| 398 | T::regs().sqr2().modify(|w| { | ||
| 399 | w.set_sq(i - 4, channel.channel()); | ||
| 400 | }); | ||
| 401 | } | ||
| 402 | 9..=13 => { | ||
| 403 | T::regs().sqr3().modify(|w| { | ||
| 404 | w.set_sq(i - 9, channel.channel()); | ||
| 405 | }); | ||
| 406 | } | ||
| 407 | 14..=15 => { | ||
| 408 | T::regs().sqr4().modify(|w| { | ||
| 409 | w.set_sq(i - 14, channel.channel()); | ||
| 410 | }); | ||
| 411 | } | ||
| 412 | _ => unreachable!(), | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 416 | // Set continuous mode with oneshot dma. | ||
| 417 | // Clear overrun flag before starting transfer. | ||
| 418 | |||
| 419 | T::regs().isr().modify(|reg| { | ||
| 420 | reg.set_ovr(true); | ||
| 421 | }); | ||
| 422 | T::regs().cfgr().modify(|reg| { | ||
| 423 | reg.set_cont(true); | ||
| 424 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 425 | }); | ||
| 426 | |||
| 427 | let request = rx_dma.request(); | ||
| 428 | let transfer = unsafe { | ||
| 429 | Transfer::new_read( | ||
| 430 | rx_dma, | ||
| 431 | request, | ||
| 432 | T::regs().dr().as_ptr() as *mut u16, | ||
| 433 | readings, | ||
| 434 | Default::default(), | ||
| 435 | ) | ||
| 436 | }; | ||
| 437 | |||
| 438 | // Start conversion | ||
| 439 | T::regs().cr().modify(|reg| { | ||
| 440 | reg.set_adstart(true); | ||
| 441 | }); | ||
| 442 | |||
| 443 | // Wait for conversion sequence to finish. | ||
| 444 | transfer.await; | ||
| 445 | |||
| 446 | // Ensure conversions are finished. | ||
| 447 | Self::cancel_conversions(); | ||
| 448 | |||
| 449 | // Reset configuration. | ||
| 450 | T::regs().cfgr().modify(|reg| { | ||
| 451 | reg.set_cont(false); | ||
| 452 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 453 | }); | ||
| 454 | } | ||
| 455 | |||
| 456 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 457 | channel.setup(); | ||
| 458 | |||
| 459 | let channel = channel.channel(); | ||
| 460 | |||
| 461 | Self::set_channel_sample_time(channel, sample_time); | ||
| 462 | |||
| 463 | #[cfg(any(stm32h7, stm32u5))] | ||
| 464 | { | ||
| 465 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 466 | T::regs() | ||
| 467 | .pcsel() | ||
| 468 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 473 | Self::configure_channel(channel, self.sample_time); | ||
| 474 | |||
| 475 | T::regs().sqr1().modify(|reg| { | ||
| 476 | reg.set_sq(0, channel.channel()); | ||
| 477 | reg.set_l(0); | ||
| 478 | }); | ||
| 479 | |||
| 480 | self.convert() | ||
| 481 | } | ||
| 482 | |||
| 483 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 484 | let sample_time = sample_time.into(); | ||
| 485 | if ch <= 9 { | ||
| 486 | T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 487 | } else { | ||
| 488 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | fn cancel_conversions() { | ||
| 493 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 494 | T::regs().cr().modify(|reg| { | ||
| 495 | reg.set_adstp(Adstp::STOP); | ||
| 496 | }); | ||
| 497 | while T::regs().cr().read().adstart() {} | ||
| 498 | } | ||
| 499 | } | ||
| 500 | } | 332 | } |
diff --git a/embassy-stm32/src/adc/watchdog_v1.rs b/embassy-stm32/src/adc/watchdog_v1.rs index bbe8e1971..b12e0d333 100644 --- a/embassy-stm32/src/adc/watchdog_v1.rs +++ b/embassy-stm32/src/adc/watchdog_v1.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 2 | use core::task::Poll; | 2 | use core::task::Poll; |
| 3 | 3 | ||
| 4 | use stm32_metapac::adc::vals::{Align, Awdsgl, Res}; | 4 | use stm32_metapac::adc::vals::{Align, Awdsgl, Res, SampleTime}; |
| 5 | 5 | ||
| 6 | use crate::adc::{Adc, AdcChannel, Instance}; | 6 | use crate::adc::{Adc, AdcChannel, Instance}; |
| 7 | 7 | ||
| @@ -67,7 +67,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 67 | /// let v_high = adc.monitor_watchdog().await; | 67 | /// let v_high = adc.monitor_watchdog().await; |
| 68 | /// info!("ADC sample is high {}", v_high); | 68 | /// info!("ADC sample is high {}", v_high); |
| 69 | /// ``` | 69 | /// ``` |
| 70 | pub async fn monitor_watchdog(&mut self) -> u16 { | 70 | pub async fn monitor_watchdog(&mut self, sample_time: SampleTime) -> u16 { |
| 71 | assert!( | 71 | assert!( |
| 72 | match T::regs().cfgr1().read().awdsgl() { | 72 | match T::regs().cfgr1().read().awdsgl() { |
| 73 | Awdsgl::SINGLE_CHANNEL => T::regs().cfgr1().read().awdch() != 0, | 73 | Awdsgl::SINGLE_CHANNEL => T::regs().cfgr1().read().awdch() != 0, |
| @@ -76,7 +76,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 76 | "`set_channel` should be called before `monitor`", | 76 | "`set_channel` should be called before `monitor`", |
| 77 | ); | 77 | ); |
| 78 | assert!(T::regs().chselr().read().0 != 0); | 78 | assert!(T::regs().chselr().read().0 != 0); |
| 79 | T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); | 79 | T::regs().smpr().modify(|reg| reg.set_smp(sample_time.into())); |
| 80 | Self::start_awd(); | 80 | Self::start_awd(); |
| 81 | 81 | ||
| 82 | let sample = poll_fn(|cx| { | 82 | let sample = poll_fn(|cx| { |
diff --git a/embassy-stm32/src/backup_sram.rs b/embassy-stm32/src/backup_sram.rs new file mode 100644 index 000000000..31b373c6c --- /dev/null +++ b/embassy-stm32/src/backup_sram.rs | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | //! Battary backed SRAM | ||
| 2 | |||
| 3 | use core::slice; | ||
| 4 | |||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use crate::_generated::{BKPSRAM_BASE, BKPSRAM_SIZE}; | ||
| 8 | use crate::peripherals::BKPSRAM; | ||
| 9 | |||
| 10 | /// Struct used to initilize backup sram | ||
| 11 | pub struct BackupMemory {} | ||
| 12 | |||
| 13 | impl BackupMemory { | ||
| 14 | /// Setup battery backed sram | ||
| 15 | /// | ||
| 16 | /// Returns slice to sram and whether the sram was retained | ||
| 17 | pub fn new(_backup_sram: Peri<'static, BKPSRAM>) -> (&'static mut [u8], bool) { | ||
| 18 | // Assert bksram has been enabled in rcc | ||
| 19 | assert!(crate::pac::PWR.bdcr().read().bren() == crate::pac::pwr::vals::Retention::PRESERVED); | ||
| 20 | |||
| 21 | unsafe { | ||
| 22 | ( | ||
| 23 | slice::from_raw_parts_mut(BKPSRAM_BASE as *mut u8, BKPSRAM_SIZE), | ||
| 24 | critical_section::with(|_| crate::rcc::BKSRAM_RETAINED), | ||
| 25 | ) | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 8eb188560..507350c42 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs | |||
| @@ -5,8 +5,8 @@ use core::future::poll_fn; | |||
| 5 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 9 | use embassy_hal_internal::PeripheralType; | 8 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 11 | use embassy_sync::channel::Channel; | 11 | use embassy_sync::channel::Channel; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 12 | use embassy_sync::waitqueue::AtomicWaker; |
| @@ -22,7 +22,7 @@ use crate::can::enums::{BusError, RefCountOp, TryReadError}; | |||
| 22 | use crate::gpio::{AfType, OutputType, Pull, Speed}; | 22 | use crate::gpio::{AfType, OutputType, Pull, Speed}; |
| 23 | use crate::interrupt::typelevel::Interrupt; | 23 | use crate::interrupt::typelevel::Interrupt; |
| 24 | use crate::rcc::{self, RccPeripheral}; | 24 | use crate::rcc::{self, RccPeripheral}; |
| 25 | use crate::{interrupt, peripherals, Peri}; | 25 | use crate::{Peri, interrupt, peripherals}; |
| 26 | 26 | ||
| 27 | /// Interrupt handler. | 27 | /// Interrupt handler. |
| 28 | pub struct TxInterruptHandler<T: Instance> { | 28 | pub struct TxInterruptHandler<T: Instance> { |
| @@ -186,10 +186,10 @@ impl<'d> Can<'d> { | |||
| 186 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, | 186 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 187 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, | 187 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 188 | _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> | 188 | _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> |
| 189 | + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> | 189 | + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> |
| 190 | + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> | 190 | + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> |
| 191 | + interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>> | 191 | + interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>> |
| 192 | + 'd, | 192 | + 'd, |
| 193 | ) -> Self { | 193 | ) -> Self { |
| 194 | let info = T::info(); | 194 | let info = T::info(); |
| 195 | let regs = &T::info().regs; | 195 | let regs = &T::info().regs; |
diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config.rs index c6a66b469..4fe634ce4 100644 --- a/embassy-stm32/src/can/fd/config.rs +++ b/embassy-stm32/src/can/fd/config.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! Configuration for FDCAN Module | 1 | //! Configuration for FDCAN Module |
| 2 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | 2 | // Note: This file is copied and modified from fdcan crate by Richard Meadows |
| 3 | 3 | ||
| 4 | use core::num::{NonZeroU16, NonZeroU8}; | 4 | use core::num::{NonZeroU8, NonZeroU16}; |
| 5 | 5 | ||
| 6 | /// Configures the bit timings. | 6 | /// Configures the bit timings. |
| 7 | /// | 7 | /// |
| @@ -360,6 +360,8 @@ pub struct FdCanConfig { | |||
| 360 | pub global_filter: GlobalFilter, | 360 | pub global_filter: GlobalFilter, |
| 361 | /// TX buffer mode (FIFO or priority queue) | 361 | /// TX buffer mode (FIFO or priority queue) |
| 362 | pub tx_buffer_mode: TxBufferMode, | 362 | pub tx_buffer_mode: TxBufferMode, |
| 363 | /// Automatic recovery from bus off state | ||
| 364 | pub automatic_bus_off_recovery: bool, | ||
| 363 | } | 365 | } |
| 364 | 366 | ||
| 365 | impl FdCanConfig { | 367 | impl FdCanConfig { |
| @@ -456,6 +458,16 @@ impl FdCanConfig { | |||
| 456 | self.tx_buffer_mode = txbm; | 458 | self.tx_buffer_mode = txbm; |
| 457 | self | 459 | self |
| 458 | } | 460 | } |
| 461 | |||
| 462 | /// Enables or disables automatic recovery from bus off state | ||
| 463 | /// | ||
| 464 | /// Automatic recovery is performed by clearing the INIT bit in the CCCR register if | ||
| 465 | /// the BO bit is active in the IR register in the IT0 interrupt. | ||
| 466 | #[inline] | ||
| 467 | pub const fn set_automatic_bus_off_recovery(mut self, enabled: bool) -> Self { | ||
| 468 | self.automatic_bus_off_recovery = enabled; | ||
| 469 | self | ||
| 470 | } | ||
| 459 | } | 471 | } |
| 460 | 472 | ||
| 461 | impl Default for FdCanConfig { | 473 | impl Default for FdCanConfig { |
| @@ -474,6 +486,7 @@ impl Default for FdCanConfig { | |||
| 474 | timestamp_source: TimestampSource::None, | 486 | timestamp_source: TimestampSource::None, |
| 475 | global_filter: GlobalFilter::default(), | 487 | global_filter: GlobalFilter::default(), |
| 476 | tx_buffer_mode: TxBufferMode::Priority, | 488 | tx_buffer_mode: TxBufferMode::Priority, |
| 489 | automatic_bus_off_recovery: true, | ||
| 477 | } | 490 | } |
| 478 | } | 491 | } |
| 479 | } | 492 | } |
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index d8f71e03e..9883aff57 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs | |||
| @@ -3,8 +3,8 @@ use core::future::poll_fn; | |||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 7 | use embassy_hal_internal::PeripheralType; | 6 | use embassy_hal_internal::PeripheralType; |
| 7 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 9 | use embassy_sync::channel::Channel; | 9 | use embassy_sync::channel::Channel; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| @@ -13,7 +13,7 @@ use crate::can::fd::peripheral::Registers; | |||
| 13 | use crate::gpio::{AfType, OutputType, Pull, SealedPin as _, Speed}; | 13 | use crate::gpio::{AfType, OutputType, Pull, SealedPin as _, Speed}; |
| 14 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::rcc::{self, RccPeripheral}; | 15 | use crate::rcc::{self, RccPeripheral}; |
| 16 | use crate::{interrupt, peripherals, Peri}; | 16 | use crate::{Peri, interrupt, peripherals}; |
| 17 | 17 | ||
| 18 | pub(crate) mod fd; | 18 | pub(crate) mod fd; |
| 19 | 19 | ||
| @@ -53,7 +53,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 53 | regs.ir().write(|w| w.set_tefn(true)); | 53 | regs.ir().write(|w| w.set_tefn(true)); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | T::info().state.lock(|s| { | 56 | let recover_from_bo = T::info().state.lock(|s| { |
| 57 | let state = s.borrow_mut(); | 57 | let state = s.borrow_mut(); |
| 58 | match &state.tx_mode { | 58 | match &state.tx_mode { |
| 59 | TxMode::NonBuffered(waker) => waker.wake(), | 59 | TxMode::NonBuffered(waker) => waker.wake(), |
| @@ -85,11 +85,15 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 85 | if ir.rfn(1) { | 85 | if ir.rfn(1) { |
| 86 | state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick); | 86 | state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick); |
| 87 | } | 87 | } |
| 88 | |||
| 89 | state.automatic_bus_off_recovery | ||
| 88 | }); | 90 | }); |
| 89 | 91 | ||
| 90 | if ir.bo() { | 92 | if ir.bo() { |
| 91 | regs.ir().write(|w| w.set_bo(true)); | 93 | regs.ir().write(|w| w.set_bo(true)); |
| 92 | if regs.psr().read().bo() { | 94 | if let Some(true) = recover_from_bo |
| 95 | && regs.psr().read().bo() | ||
| 96 | { | ||
| 93 | // Initiate bus-off recovery sequence by resetting CCCR.INIT | 97 | // Initiate bus-off recovery sequence by resetting CCCR.INIT |
| 94 | regs.cccr().modify(|w| w.set_init(false)); | 98 | regs.cccr().modify(|w| w.set_init(false)); |
| 95 | } | 99 | } |
| @@ -182,8 +186,8 @@ impl<'d> CanConfigurator<'d> { | |||
| 182 | rx: Peri<'d, impl RxPin<T>>, | 186 | rx: Peri<'d, impl RxPin<T>>, |
| 183 | tx: Peri<'d, impl TxPin<T>>, | 187 | tx: Peri<'d, impl TxPin<T>>, |
| 184 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> | 188 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> |
| 185 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> | 189 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> |
| 186 | + 'd, | 190 | + 'd, |
| 187 | ) -> CanConfigurator<'d> { | 191 | ) -> CanConfigurator<'d> { |
| 188 | set_as_af!(rx, AfType::input(Pull::None)); | 192 | set_as_af!(rx, AfType::input(Pull::None)); |
| 189 | set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 193 | set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| @@ -263,7 +267,9 @@ impl<'d> CanConfigurator<'d> { | |||
| 263 | pub fn start(self, mode: OperatingMode) -> Can<'d> { | 267 | pub fn start(self, mode: OperatingMode) -> Can<'d> { |
| 264 | let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); | 268 | let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); |
| 265 | self.info.state.lock(|s| { | 269 | self.info.state.lock(|s| { |
| 266 | s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; | 270 | let mut state = s.borrow_mut(); |
| 271 | state.ns_per_timer_tick = ns_per_timer_tick; | ||
| 272 | state.automatic_bus_off_recovery = Some(self.config.automatic_bus_off_recovery); | ||
| 267 | }); | 273 | }); |
| 268 | self.info.regs.into_mode(self.config, mode); | 274 | self.info.regs.into_mode(self.config, mode); |
| 269 | Can { | 275 | Can { |
| @@ -459,7 +465,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, | |||
| 459 | pub async fn write(&mut self, frame: Frame) { | 465 | pub async fn write(&mut self, frame: Frame) { |
| 460 | self.tx_buf.send(frame).await; | 466 | self.tx_buf.send(frame).await; |
| 461 | self.info.interrupt0.pend(); // Wake for Tx | 467 | self.info.interrupt0.pend(); // Wake for Tx |
| 462 | //T::IT0Interrupt::pend(); // Wake for Tx | 468 | //T::IT0Interrupt::pend(); // Wake for Tx |
| 463 | } | 469 | } |
| 464 | 470 | ||
| 465 | /// Async read frame from RX buffer. | 471 | /// Async read frame from RX buffer. |
| @@ -548,7 +554,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' | |||
| 548 | pub async fn write(&mut self, frame: FdFrame) { | 554 | pub async fn write(&mut self, frame: FdFrame) { |
| 549 | self.tx_buf.send(frame).await; | 555 | self.tx_buf.send(frame).await; |
| 550 | self.info.interrupt0.pend(); // Wake for Tx | 556 | self.info.interrupt0.pend(); // Wake for Tx |
| 551 | //T::IT0Interrupt::pend(); // Wake for Tx | 557 | //T::IT0Interrupt::pend(); // Wake for Tx |
| 552 | } | 558 | } |
| 553 | 559 | ||
| 554 | /// Async read frame from RX buffer. | 560 | /// Async read frame from RX buffer. |
| @@ -861,7 +867,7 @@ struct State { | |||
| 861 | sender_instance_count: usize, | 867 | sender_instance_count: usize, |
| 862 | tx_pin_port: Option<u8>, | 868 | tx_pin_port: Option<u8>, |
| 863 | rx_pin_port: Option<u8>, | 869 | rx_pin_port: Option<u8>, |
| 864 | 870 | automatic_bus_off_recovery: Option<bool>, // controlled by CanConfigurator::start() | |
| 865 | pub err_waker: AtomicWaker, | 871 | pub err_waker: AtomicWaker, |
| 866 | } | 872 | } |
| 867 | 873 | ||
| @@ -876,6 +882,7 @@ impl State { | |||
| 876 | sender_instance_count: 0, | 882 | sender_instance_count: 0, |
| 877 | tx_pin_port: None, | 883 | tx_pin_port: None, |
| 878 | rx_pin_port: None, | 884 | rx_pin_port: None, |
| 885 | automatic_bus_off_recovery: None, | ||
| 879 | } | 886 | } |
| 880 | } | 887 | } |
| 881 | } | 888 | } |
diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs index fcdbbad62..6d7f0c16a 100644 --- a/embassy-stm32/src/can/util.rs +++ b/embassy-stm32/src/can/util.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | //! Utility functions shared between CAN controller types. | 1 | //! Utility functions shared between CAN controller types. |
| 2 | 2 | ||
| 3 | use core::num::{NonZeroU16, NonZeroU8}; | 3 | use core::num::{NonZeroU8, NonZeroU16}; |
| 4 | 4 | ||
| 5 | /// Shared struct to represent bit timings used by calc_can_timings. | 5 | /// Shared struct to represent bit timings used by calc_can_timings. |
| 6 | #[derive(Clone, Copy, Debug)] | 6 | #[derive(Clone, Copy, Debug)] |
diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index 13e5263de..836228599 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use crate::pac::CRC as PAC_CRC; | 1 | use crate::pac::CRC as PAC_CRC; |
| 2 | use crate::peripherals::CRC; | 2 | use crate::peripherals::CRC; |
| 3 | use crate::{rcc, Peri}; | 3 | use crate::{Peri, rcc}; |
| 4 | 4 | ||
| 5 | /// CRC driver. | 5 | /// CRC driver. |
| 6 | pub struct Crc<'d> { | 6 | pub struct Crc<'d> { |
diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index d834d0971..a566a2e04 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use crate::pac::crc::vals; | ||
| 2 | use crate::pac::CRC as PAC_CRC; | 1 | use crate::pac::CRC as PAC_CRC; |
| 2 | use crate::pac::crc::vals; | ||
| 3 | use crate::peripherals::CRC; | 3 | use crate::peripherals::CRC; |
| 4 | use crate::{rcc, Peri}; | 4 | use crate::{Peri, rcc}; |
| 5 | 5 | ||
| 6 | /// CRC driver. | 6 | /// CRC driver. |
| 7 | pub struct Crc<'d> { | 7 | pub struct Crc<'d> { |
diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index 0173b2b5d..4f1115fb7 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs | |||
| @@ -1236,7 +1236,10 @@ impl<'d, T: Instance, M: Mode> Cryp<'d, T, M> { | |||
| 1236 | } | 1236 | } |
| 1237 | if C::REQUIRES_PADDING { | 1237 | if C::REQUIRES_PADDING { |
| 1238 | if last_block_remainder != 0 { | 1238 | if last_block_remainder != 0 { |
| 1239 | panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); | 1239 | panic!( |
| 1240 | "Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", | ||
| 1241 | C::BLOCK_SIZE | ||
| 1242 | ); | ||
| 1240 | } | 1243 | } |
| 1241 | } | 1244 | } |
| 1242 | if last_block { | 1245 | if last_block { |
| @@ -1703,7 +1706,10 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { | |||
| 1703 | } | 1706 | } |
| 1704 | if C::REQUIRES_PADDING { | 1707 | if C::REQUIRES_PADDING { |
| 1705 | if last_block_remainder != 0 { | 1708 | if last_block_remainder != 0 { |
| 1706 | panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); | 1709 | panic!( |
| 1710 | "Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", | ||
| 1711 | C::BLOCK_SIZE | ||
| 1712 | ); | ||
| 1707 | } | 1713 | } |
| 1708 | } | 1714 | } |
| 1709 | if last_block { | 1715 | if last_block { |
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 08e001337..d74d4a4be 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -8,7 +8,7 @@ use crate::mode::{Async, Blocking, Mode as PeriMode}; | |||
| 8 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | 8 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] |
| 9 | use crate::pac::dac; | 9 | use crate::pac::dac; |
| 10 | use crate::rcc::{self, RccPeripheral}; | 10 | use crate::rcc::{self, RccPeripheral}; |
| 11 | use crate::{peripherals, Peri}; | 11 | use crate::{Peri, peripherals}; |
| 12 | 12 | ||
| 13 | mod tsel; | 13 | mod tsel; |
| 14 | use embassy_hal_internal::PeripheralType; | 14 | use embassy_hal_internal::PeripheralType; |
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index bd03f1e00..dcae9f298 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs | |||
| @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 9 | use crate::dma::Transfer; | 9 | use crate::dma::Transfer; |
| 10 | use crate::gpio::{AfType, Pull}; | 10 | use crate::gpio::{AfType, Pull}; |
| 11 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| 12 | use crate::{interrupt, rcc, Peri}; | 12 | use crate::{Peri, interrupt, rcc}; |
| 13 | 13 | ||
| 14 | /// Interrupt handler. | 14 | /// Interrupt handler. |
| 15 | pub struct InterruptHandler<T: Instance> { | 15 | pub struct InterruptHandler<T: Instance> { |
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 73ecab070..b46ae2813 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use core::future::{poll_fn, Future}; | 1 | use core::future::{Future, poll_fn}; |
| 2 | use core::pin::Pin; | 2 | use core::pin::Pin; |
| 3 | use core::sync::atomic::{fence, AtomicUsize, Ordering}; | 3 | use core::sync::atomic::{AtomicUsize, Ordering, fence}; |
| 4 | use core::task::{Context, Poll, Waker}; | 4 | use core::task::{Context, Poll, Waker}; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::Peri; | 6 | use embassy_hal_internal::Peri; |
| @@ -8,7 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 8 | 8 | ||
| 9 | use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | 9 | use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 10 | use super::word::{Word, WordSize}; | 10 | use super::word::{Word, WordSize}; |
| 11 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | 11 | use super::{AnyChannel, BusyChannel, Channel, Dir, Request, STATE}; |
| 12 | use crate::interrupt::typelevel::Interrupt; | 12 | use crate::interrupt::typelevel::Interrupt; |
| 13 | use crate::{interrupt, pac}; | 13 | use crate::{interrupt, pac}; |
| 14 | 14 | ||
| @@ -602,7 +602,7 @@ impl AnyChannel { | |||
| 602 | /// DMA transfer. | 602 | /// DMA transfer. |
| 603 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 603 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 604 | pub struct Transfer<'a> { | 604 | pub struct Transfer<'a> { |
| 605 | channel: Peri<'a, AnyChannel>, | 605 | channel: BusyChannel<'a>, |
| 606 | } | 606 | } |
| 607 | 607 | ||
| 608 | impl<'a> Transfer<'a> { | 608 | impl<'a> Transfer<'a> { |
| @@ -713,7 +713,9 @@ impl<'a> Transfer<'a> { | |||
| 713 | _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, | 713 | _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, |
| 714 | ); | 714 | ); |
| 715 | channel.start(); | 715 | channel.start(); |
| 716 | Self { channel } | 716 | Self { |
| 717 | channel: BusyChannel::new(channel), | ||
| 718 | } | ||
| 717 | } | 719 | } |
| 718 | 720 | ||
| 719 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 721 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| @@ -816,7 +818,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | |||
| 816 | 818 | ||
| 817 | /// Ringbuffer for receiving data using DMA circular mode. | 819 | /// Ringbuffer for receiving data using DMA circular mode. |
| 818 | pub struct ReadableRingBuffer<'a, W: Word> { | 820 | pub struct ReadableRingBuffer<'a, W: Word> { |
| 819 | channel: Peri<'a, AnyChannel>, | 821 | channel: BusyChannel<'a>, |
| 820 | ringbuf: ReadableDmaRingBuffer<'a, W>, | 822 | ringbuf: ReadableDmaRingBuffer<'a, W>, |
| 821 | } | 823 | } |
| 822 | 824 | ||
| @@ -853,7 +855,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 853 | ); | 855 | ); |
| 854 | 856 | ||
| 855 | Self { | 857 | Self { |
| 856 | channel, | 858 | channel: BusyChannel::new(channel), |
| 857 | ringbuf: ReadableDmaRingBuffer::new(buffer), | 859 | ringbuf: ReadableDmaRingBuffer::new(buffer), |
| 858 | } | 860 | } |
| 859 | } | 861 | } |
| @@ -972,7 +974,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | |||
| 972 | 974 | ||
| 973 | /// Ringbuffer for writing data using DMA circular mode. | 975 | /// Ringbuffer for writing data using DMA circular mode. |
| 974 | pub struct WritableRingBuffer<'a, W: Word> { | 976 | pub struct WritableRingBuffer<'a, W: Word> { |
| 975 | channel: Peri<'a, AnyChannel>, | 977 | channel: BusyChannel<'a>, |
| 976 | ringbuf: WritableDmaRingBuffer<'a, W>, | 978 | ringbuf: WritableDmaRingBuffer<'a, W>, |
| 977 | } | 979 | } |
| 978 | 980 | ||
| @@ -1009,7 +1011,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 1009 | ); | 1011 | ); |
| 1010 | 1012 | ||
| 1011 | Self { | 1013 | Self { |
| 1012 | channel, | 1014 | channel: BusyChannel::new(channel), |
| 1013 | ringbuf: WritableDmaRingBuffer::new(buffer), | 1015 | ringbuf: WritableDmaRingBuffer::new(buffer), |
| 1014 | } | 1016 | } |
| 1015 | } | 1017 | } |
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 4a14c2a8e..383c74a78 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | use core::future::Future; | 3 | use core::future::Future; |
| 4 | use core::pin::Pin; | 4 | use core::pin::Pin; |
| 5 | use core::sync::atomic::{fence, AtomicUsize, Ordering}; | 5 | use core::sync::atomic::{AtomicUsize, Ordering, fence}; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::Peri; | 8 | use embassy_hal_internal::Peri; |
| @@ -11,6 +11,7 @@ use linked_list::Table; | |||
| 11 | 11 | ||
| 12 | use super::word::{Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | 13 | use super::{AnyChannel, Channel, Dir, Request, STATE}; |
| 14 | use crate::dma::BusyChannel; | ||
| 14 | use crate::interrupt::typelevel::Interrupt; | 15 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::pac; | 16 | use crate::pac; |
| 16 | use crate::pac::gpdma::vals; | 17 | use crate::pac::gpdma::vals; |
| @@ -136,6 +137,7 @@ pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: c | |||
| 136 | 137 | ||
| 137 | impl AnyChannel { | 138 | impl AnyChannel { |
| 138 | /// Safety: Must be called with a matching set of parameters for a valid dma channel | 139 | /// Safety: Must be called with a matching set of parameters for a valid dma channel |
| 140 | #[cfg(not(stm32n6))] | ||
| 139 | pub(crate) unsafe fn on_irq(&self) { | 141 | pub(crate) unsafe fn on_irq(&self) { |
| 140 | let info = self.info(); | 142 | let info = self.info(); |
| 141 | #[cfg(feature = "_dual-core")] | 143 | #[cfg(feature = "_dual-core")] |
| @@ -407,7 +409,7 @@ impl AnyChannel { | |||
| 407 | /// Linked-list DMA transfer. | 409 | /// Linked-list DMA transfer. |
| 408 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 410 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 409 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { | 411 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { |
| 410 | channel: Peri<'a, AnyChannel>, | 412 | channel: BusyChannel<'a>, |
| 411 | } | 413 | } |
| 412 | 414 | ||
| 413 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | 415 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { |
| @@ -428,7 +430,9 @@ impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | |||
| 428 | channel.configure_linked_list(&table, options); | 430 | channel.configure_linked_list(&table, options); |
| 429 | channel.start(); | 431 | channel.start(); |
| 430 | 432 | ||
| 431 | Self { channel } | 433 | Self { |
| 434 | channel: BusyChannel::new(channel), | ||
| 435 | } | ||
| 432 | } | 436 | } |
| 433 | 437 | ||
| 434 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 438 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| @@ -504,7 +508,7 @@ impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> | |||
| 504 | /// DMA transfer. | 508 | /// DMA transfer. |
| 505 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 509 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 506 | pub struct Transfer<'a> { | 510 | pub struct Transfer<'a> { |
| 507 | channel: Peri<'a, AnyChannel>, | 511 | channel: BusyChannel<'a>, |
| 508 | } | 512 | } |
| 509 | 513 | ||
| 510 | impl<'a> Transfer<'a> { | 514 | impl<'a> Transfer<'a> { |
| @@ -624,7 +628,9 @@ impl<'a> Transfer<'a> { | |||
| 624 | ); | 628 | ); |
| 625 | channel.start(); | 629 | channel.start(); |
| 626 | 630 | ||
| 627 | Self { channel } | 631 | Self { |
| 632 | channel: BusyChannel::new(channel), | ||
| 633 | } | ||
| 628 | } | 634 | } |
| 629 | 635 | ||
| 630 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 636 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index 9ee52193b..54e4d5f71 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs | |||
| @@ -3,16 +3,16 @@ | |||
| 3 | //! FIXME: Add request_pause functionality? | 3 | //! FIXME: Add request_pause functionality? |
| 4 | //! FIXME: Stop the DMA, if a user does not queue new transfers (chain of linked-list items ends automatically). | 4 | //! FIXME: Stop the DMA, if a user does not queue new transfers (chain of linked-list items ends automatically). |
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::sync::atomic::{fence, Ordering}; | 6 | use core::sync::atomic::{Ordering, fence}; |
| 7 | use core::task::Waker; | 7 | use core::task::Waker; |
| 8 | 8 | ||
| 9 | use embassy_hal_internal::Peri; | 9 | use embassy_hal_internal::Peri; |
| 10 | 10 | ||
| 11 | use super::{AnyChannel, TransferOptions, STATE}; | 11 | use super::{AnyChannel, STATE, TransferOptions}; |
| 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; | 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; |
| 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 14 | use crate::dma::word::Word; | 14 | use crate::dma::word::Word; |
| 15 | use crate::dma::{Channel, Dir, Request}; | 15 | use crate::dma::{BusyChannel, Channel, Dir, Request}; |
| 16 | 16 | ||
| 17 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); | 17 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); |
| 18 | 18 | ||
| @@ -49,7 +49,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | |||
| 49 | 49 | ||
| 50 | /// Ringbuffer for receiving data using GPDMA linked-list mode. | 50 | /// Ringbuffer for receiving data using GPDMA linked-list mode. |
| 51 | pub struct ReadableRingBuffer<'a, W: Word> { | 51 | pub struct ReadableRingBuffer<'a, W: Word> { |
| 52 | channel: Peri<'a, AnyChannel>, | 52 | channel: BusyChannel<'a>, |
| 53 | ringbuf: ReadableDmaRingBuffer<'a, W>, | 53 | ringbuf: ReadableDmaRingBuffer<'a, W>, |
| 54 | table: Table<2>, | 54 | table: Table<2>, |
| 55 | options: TransferOptions, | 55 | options: TransferOptions, |
| @@ -70,7 +70,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 70 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); | 70 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); |
| 71 | 71 | ||
| 72 | Self { | 72 | Self { |
| 73 | channel, | 73 | channel: BusyChannel::new(channel), |
| 74 | ringbuf: ReadableDmaRingBuffer::new(buffer), | 74 | ringbuf: ReadableDmaRingBuffer::new(buffer), |
| 75 | table, | 75 | table, |
| 76 | options, | 76 | options, |
| @@ -189,7 +189,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | |||
| 189 | 189 | ||
| 190 | /// Ringbuffer for writing data using GPDMA linked-list mode. | 190 | /// Ringbuffer for writing data using GPDMA linked-list mode. |
| 191 | pub struct WritableRingBuffer<'a, W: Word> { | 191 | pub struct WritableRingBuffer<'a, W: Word> { |
| 192 | channel: Peri<'a, AnyChannel>, | 192 | channel: BusyChannel<'a>, |
| 193 | ringbuf: WritableDmaRingBuffer<'a, W>, | 193 | ringbuf: WritableDmaRingBuffer<'a, W>, |
| 194 | table: Table<2>, | 194 | table: Table<2>, |
| 195 | options: TransferOptions, | 195 | options: TransferOptions, |
| @@ -210,7 +210,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 210 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); | 210 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); |
| 211 | 211 | ||
| 212 | Self { | 212 | Self { |
| 213 | channel, | 213 | channel: BusyChannel::new(channel), |
| 214 | ringbuf: WritableDmaRingBuffer::new(buffer), | 214 | ringbuf: WritableDmaRingBuffer::new(buffer), |
| 215 | table, | 215 | table, |
| 216 | options, | 216 | options, |
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 5989bfd7c..4becc2d87 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -3,11 +3,14 @@ | |||
| 3 | 3 | ||
| 4 | #[cfg(any(bdma, dma))] | 4 | #[cfg(any(bdma, dma))] |
| 5 | mod dma_bdma; | 5 | mod dma_bdma; |
| 6 | use core::ops; | ||
| 7 | |||
| 6 | #[cfg(any(bdma, dma))] | 8 | #[cfg(any(bdma, dma))] |
| 7 | pub use dma_bdma::*; | 9 | pub use dma_bdma::*; |
| 8 | 10 | ||
| 9 | #[cfg(gpdma)] | 11 | #[cfg(gpdma)] |
| 10 | pub(crate) mod gpdma; | 12 | pub(crate) mod gpdma; |
| 13 | use embassy_hal_internal::Peri; | ||
| 11 | #[cfg(gpdma)] | 14 | #[cfg(gpdma)] |
| 12 | pub use gpdma::ringbuffered::*; | 15 | pub use gpdma::ringbuffered::*; |
| 13 | #[cfg(gpdma)] | 16 | #[cfg(gpdma)] |
| @@ -24,7 +27,7 @@ pub(crate) use util::*; | |||
| 24 | pub(crate) mod ringbuffer; | 27 | pub(crate) mod ringbuffer; |
| 25 | pub mod word; | 28 | pub mod word; |
| 26 | 29 | ||
| 27 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 30 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 28 | 31 | ||
| 29 | use crate::interrupt; | 32 | use crate::interrupt; |
| 30 | 33 | ||
| @@ -46,9 +49,13 @@ pub type Request = u8; | |||
| 46 | pub type Request = (); | 49 | pub type Request = (); |
| 47 | 50 | ||
| 48 | pub(crate) trait SealedChannel { | 51 | pub(crate) trait SealedChannel { |
| 52 | #[cfg(not(stm32n6))] | ||
| 49 | fn id(&self) -> u8; | 53 | fn id(&self) -> u8; |
| 54 | #[cfg(feature = "low-power")] | ||
| 55 | fn stop_mode(&self) -> crate::rcc::StopMode; | ||
| 50 | } | 56 | } |
| 51 | 57 | ||
| 58 | #[cfg(not(stm32n6))] | ||
| 52 | pub(crate) trait ChannelInterrupt { | 59 | pub(crate) trait ChannelInterrupt { |
| 53 | #[cfg_attr(not(feature = "rt"), allow(unused))] | 60 | #[cfg_attr(not(feature = "rt"), allow(unused))] |
| 54 | unsafe fn on_irq(); | 61 | unsafe fn on_irq(); |
| @@ -58,16 +65,27 @@ pub(crate) trait ChannelInterrupt { | |||
| 58 | #[allow(private_bounds)] | 65 | #[allow(private_bounds)] |
| 59 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} | 66 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} |
| 60 | 67 | ||
| 68 | #[cfg(not(stm32n6))] | ||
| 61 | macro_rules! dma_channel_impl { | 69 | macro_rules! dma_channel_impl { |
| 62 | ($channel_peri:ident, $index:expr) => { | 70 | ($channel_peri:ident, $index:expr, $stop_mode:ident) => { |
| 63 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { | 71 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { |
| 64 | fn id(&self) -> u8 { | 72 | fn id(&self) -> u8 { |
| 65 | $index | 73 | $index |
| 66 | } | 74 | } |
| 75 | |||
| 76 | #[cfg(feature = "low-power")] | ||
| 77 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 78 | crate::rcc::StopMode::$stop_mode | ||
| 79 | } | ||
| 67 | } | 80 | } |
| 68 | impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { | 81 | impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { |
| 69 | unsafe fn on_irq() { | 82 | unsafe fn on_irq() { |
| 70 | crate::dma::AnyChannel { id: $index }.on_irq(); | 83 | crate::dma::AnyChannel { |
| 84 | id: $index, | ||
| 85 | #[cfg(feature = "low-power")] | ||
| 86 | stop_mode: crate::rcc::StopMode::$stop_mode, | ||
| 87 | } | ||
| 88 | .on_irq(); | ||
| 71 | } | 89 | } |
| 72 | } | 90 | } |
| 73 | 91 | ||
| @@ -77,15 +95,57 @@ macro_rules! dma_channel_impl { | |||
| 77 | fn from(val: crate::peripherals::$channel_peri) -> Self { | 95 | fn from(val: crate::peripherals::$channel_peri) -> Self { |
| 78 | Self { | 96 | Self { |
| 79 | id: crate::dma::SealedChannel::id(&val), | 97 | id: crate::dma::SealedChannel::id(&val), |
| 98 | #[cfg(feature = "low-power")] | ||
| 99 | stop_mode: crate::dma::SealedChannel::stop_mode(&val), | ||
| 80 | } | 100 | } |
| 81 | } | 101 | } |
| 82 | } | 102 | } |
| 83 | }; | 103 | }; |
| 84 | } | 104 | } |
| 85 | 105 | ||
| 106 | pub(crate) struct BusyChannel<'a> { | ||
| 107 | channel: Peri<'a, AnyChannel>, | ||
| 108 | } | ||
| 109 | |||
| 110 | impl<'a> BusyChannel<'a> { | ||
| 111 | pub fn new(channel: Peri<'a, AnyChannel>) -> Self { | ||
| 112 | #[cfg(feature = "low-power")] | ||
| 113 | critical_section::with(|cs| { | ||
| 114 | crate::rcc::increment_stop_refcount(cs, channel.stop_mode); | ||
| 115 | }); | ||
| 116 | |||
| 117 | Self { channel } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | impl<'a> Drop for BusyChannel<'a> { | ||
| 122 | fn drop(&mut self) { | ||
| 123 | #[cfg(feature = "low-power")] | ||
| 124 | critical_section::with(|cs| { | ||
| 125 | crate::rcc::decrement_stop_refcount(cs, self.stop_mode); | ||
| 126 | }); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | impl<'a> ops::Deref for BusyChannel<'a> { | ||
| 131 | type Target = Peri<'a, AnyChannel>; | ||
| 132 | |||
| 133 | fn deref(&self) -> &Self::Target { | ||
| 134 | &self.channel | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | impl<'a> ops::DerefMut for BusyChannel<'a> { | ||
| 139 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 140 | &mut self.channel | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 86 | /// Type-erased DMA channel. | 144 | /// Type-erased DMA channel. |
| 87 | pub struct AnyChannel { | 145 | pub struct AnyChannel { |
| 88 | pub(crate) id: u8, | 146 | pub(crate) id: u8, |
| 147 | #[cfg(feature = "low-power")] | ||
| 148 | pub(crate) stop_mode: crate::rcc::StopMode, | ||
| 89 | } | 149 | } |
| 90 | impl_peripheral!(AnyChannel); | 150 | impl_peripheral!(AnyChannel); |
| 91 | 151 | ||
| @@ -96,9 +156,15 @@ impl AnyChannel { | |||
| 96 | } | 156 | } |
| 97 | 157 | ||
| 98 | impl SealedChannel for AnyChannel { | 158 | impl SealedChannel for AnyChannel { |
| 159 | #[cfg(not(stm32n6))] | ||
| 99 | fn id(&self) -> u8 { | 160 | fn id(&self) -> u8 { |
| 100 | self.id | 161 | self.id |
| 101 | } | 162 | } |
| 163 | |||
| 164 | #[cfg(feature = "low-power")] | ||
| 165 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 166 | self.stop_mode | ||
| 167 | } | ||
| 102 | } | 168 | } |
| 103 | impl Channel for AnyChannel {} | 169 | impl Channel for AnyChannel {} |
| 104 | 170 | ||
diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs index 661fb1728..eff5b4058 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs | |||
| @@ -2,7 +2,7 @@ use std::task::Waker; | |||
| 2 | 2 | ||
| 3 | use proptest::prop_oneof; | 3 | use proptest::prop_oneof; |
| 4 | use proptest::strategy::{self, BoxedStrategy, Strategy as _}; | 4 | use proptest::strategy::{self, BoxedStrategy, Strategy as _}; |
| 5 | use proptest_state_machine::{prop_state_machine, ReferenceStateMachine, StateMachineTest}; | 5 | use proptest_state_machine::{ReferenceStateMachine, StateMachineTest, prop_state_machine}; |
| 6 | 6 | ||
| 7 | use super::*; | 7 | use super::*; |
| 8 | 8 | ||
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index deda956af..b8945820c 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -5,17 +5,10 @@ use core::marker::PhantomData; | |||
| 5 | use embassy_hal_internal::PeripheralType; | 5 | use embassy_hal_internal::PeripheralType; |
| 6 | 6 | ||
| 7 | //use crate::gpio::{AnyPin, SealedPin}; | 7 | //use crate::gpio::{AnyPin, SealedPin}; |
| 8 | use crate::block_for_us; | ||
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 9 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 9 | use crate::rcc::{self, RccPeripheral}; | 10 | use crate::rcc::{self, RccPeripheral}; |
| 10 | use crate::{peripherals, Peri}; | 11 | use crate::{Peri, peripherals}; |
| 11 | |||
| 12 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 13 | pub fn blocking_delay_ms(ms: u32) { | ||
| 14 | #[cfg(feature = "time")] | ||
| 15 | embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); | ||
| 16 | #[cfg(not(feature = "time"))] | ||
| 17 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms); | ||
| 18 | } | ||
| 19 | 12 | ||
| 20 | /// PacketTypes extracted from CubeMX | 13 | /// PacketTypes extracted from CubeMX |
| 21 | #[repr(u8)] | 14 | #[repr(u8)] |
| @@ -121,17 +114,15 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 121 | 114 | ||
| 122 | /// DCS or Generic short/long write command | 115 | /// DCS or Generic short/long write command |
| 123 | pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { | 116 | pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { |
| 124 | assert!(data.len() > 0); | 117 | match data.len() { |
| 125 | 118 | 0 => self.short_write(channel_id, PacketType::DcsShortPktWriteP0, address, 0), | |
| 126 | if data.len() == 1 { | 119 | 1 => self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]), |
| 127 | self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]) | 120 | _ => self.long_write( |
| 128 | } else { | ||
| 129 | self.long_write( | ||
| 130 | channel_id, | 121 | channel_id, |
| 131 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... | 122 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... |
| 132 | address, | 123 | address, |
| 133 | data, | 124 | data, |
| 134 | ) | 125 | ), |
| 135 | } | 126 | } |
| 136 | } | 127 | } |
| 137 | 128 | ||
| @@ -336,7 +327,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 336 | if T::regs().gpsr().read().cmdfe() { | 327 | if T::regs().gpsr().read().cmdfe() { |
| 337 | return Ok(()); | 328 | return Ok(()); |
| 338 | } | 329 | } |
| 339 | blocking_delay_ms(1); | 330 | block_for_us(1_000); |
| 340 | } | 331 | } |
| 341 | Err(Error::FifoTimeout) | 332 | Err(Error::FifoTimeout) |
| 342 | } | 333 | } |
| @@ -347,7 +338,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 347 | if !T::regs().gpsr().read().cmdff() { | 338 | if !T::regs().gpsr().read().cmdff() { |
| 348 | return Ok(()); | 339 | return Ok(()); |
| 349 | } | 340 | } |
| 350 | blocking_delay_ms(1); | 341 | block_for_us(1_000); |
| 351 | } | 342 | } |
| 352 | Err(Error::FifoTimeout) | 343 | Err(Error::FifoTimeout) |
| 353 | } | 344 | } |
| @@ -358,7 +349,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 358 | if !self.read_busy() { | 349 | if !self.read_busy() { |
| 359 | return Ok(()); | 350 | return Ok(()); |
| 360 | } | 351 | } |
| 361 | blocking_delay_ms(1); | 352 | block_for_us(1_000); |
| 362 | } | 353 | } |
| 363 | Err(Error::ReadTimeout) | 354 | Err(Error::ReadTimeout) |
| 364 | } | 355 | } |
| @@ -369,7 +360,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 369 | if !T::regs().gpsr().read().prdfe() { | 360 | if !T::regs().gpsr().read().prdfe() { |
| 370 | return Ok(()); | 361 | return Ok(()); |
| 371 | } | 362 | } |
| 372 | blocking_delay_ms(1); | 363 | block_for_us(1_000); |
| 373 | } | 364 | } |
| 374 | Err(Error::FifoTimeout) | 365 | Err(Error::FifoTimeout) |
| 375 | } | 366 | } |
diff --git a/embassy-stm32/src/dts/mod.rs b/embassy-stm32/src/dts/mod.rs index 1f39c8db5..a75ae0560 100644 --- a/embassy-stm32/src/dts/mod.rs +++ b/embassy-stm32/src/dts/mod.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! Digital Temperature Sensor (DTS) | 1 | //! Digital Temperature Sensor (DTS) |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 5 | use core::task::Poll; | 5 | use core::task::Poll; |
| 6 | 6 | ||
| 7 | use embassy_hal_internal::Peri; | 7 | use embassy_hal_internal::Peri; |
diff --git a/embassy-stm32/src/dts/tsel.rs b/embassy-stm32/src/dts/tsel.rs index 99eab6dd8..79c697c8d 100644 --- a/embassy-stm32/src/dts/tsel.rs +++ b/embassy-stm32/src/dts/tsel.rs | |||
| @@ -49,3 +49,20 @@ pub enum TriggerSel { | |||
| 49 | /// EXTI13 | 49 | /// EXTI13 |
| 50 | Exti13 = 4, | 50 | Exti13 = 4, |
| 51 | } | 51 | } |
| 52 | |||
| 53 | /// Trigger selection for N6 | ||
| 54 | #[cfg(stm32n6)] | ||
| 55 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 56 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 57 | pub enum TriggerSel { | ||
| 58 | /// Software triggering. Performs continuous measurements. | ||
| 59 | Software = 0, | ||
| 60 | /// LPTIM4 OUT | ||
| 61 | Lptim4 = 1, | ||
| 62 | /// LPTIM2 CH1 | ||
| 63 | Lptim2 = 2, | ||
| 64 | /// LPTIM3 CH1 | ||
| 65 | Lptim3 = 3, | ||
| 66 | /// EXTI13 | ||
| 67 | Exti13 = 4, | ||
| 68 | } | ||
diff --git a/embassy-stm32/src/eth/generic_phy.rs b/embassy-stm32/src/eth/generic_phy.rs index 774beef80..0a5f41de0 100644 --- a/embassy-stm32/src/eth/generic_phy.rs +++ b/embassy-stm32/src/eth/generic_phy.rs | |||
| @@ -8,6 +8,7 @@ use embassy_time::{Duration, Timer}; | |||
| 8 | use futures_util::FutureExt; | 8 | use futures_util::FutureExt; |
| 9 | 9 | ||
| 10 | use super::{Phy, StationManagement}; | 10 | use super::{Phy, StationManagement}; |
| 11 | use crate::block_for_us as blocking_delay_us; | ||
| 11 | 12 | ||
| 12 | #[allow(dead_code)] | 13 | #[allow(dead_code)] |
| 13 | mod phy_consts { | 14 | mod phy_consts { |
| @@ -43,21 +44,23 @@ mod phy_consts { | |||
| 43 | use self::phy_consts::*; | 44 | use self::phy_consts::*; |
| 44 | 45 | ||
| 45 | /// Generic SMI Ethernet PHY implementation | 46 | /// Generic SMI Ethernet PHY implementation |
| 46 | pub struct GenericPhy { | 47 | pub struct GenericPhy<SM: StationManagement> { |
| 47 | phy_addr: u8, | 48 | phy_addr: u8, |
| 49 | sm: SM, | ||
| 48 | #[cfg(feature = "time")] | 50 | #[cfg(feature = "time")] |
| 49 | poll_interval: Duration, | 51 | poll_interval: Duration, |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | impl GenericPhy { | 54 | impl<SM: StationManagement> GenericPhy<SM> { |
| 53 | /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication | 55 | /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication |
| 54 | /// | 56 | /// |
| 55 | /// # Panics | 57 | /// # Panics |
| 56 | /// `phy_addr` must be in range `0..32` | 58 | /// `phy_addr` must be in range `0..32` |
| 57 | pub fn new(phy_addr: u8) -> Self { | 59 | pub fn new(sm: SM, phy_addr: u8) -> Self { |
| 58 | assert!(phy_addr < 32); | 60 | assert!(phy_addr < 32); |
| 59 | Self { | 61 | Self { |
| 60 | phy_addr, | 62 | phy_addr, |
| 63 | sm, | ||
| 61 | #[cfg(feature = "time")] | 64 | #[cfg(feature = "time")] |
| 62 | poll_interval: Duration::from_millis(500), | 65 | poll_interval: Duration::from_millis(500), |
| 63 | } | 66 | } |
| @@ -67,8 +70,9 @@ impl GenericPhy { | |||
| 67 | /// | 70 | /// |
| 68 | /// # Panics | 71 | /// # Panics |
| 69 | /// Initialization panics if PHY didn't respond on any address | 72 | /// Initialization panics if PHY didn't respond on any address |
| 70 | pub fn new_auto() -> Self { | 73 | pub fn new_auto(sm: SM) -> Self { |
| 71 | Self { | 74 | Self { |
| 75 | sm, | ||
| 72 | phy_addr: 0xFF, | 76 | phy_addr: 0xFF, |
| 73 | #[cfg(feature = "time")] | 77 | #[cfg(feature = "time")] |
| 74 | poll_interval: Duration::from_millis(500), | 78 | poll_interval: Duration::from_millis(500), |
| @@ -76,27 +80,14 @@ impl GenericPhy { | |||
| 76 | } | 80 | } |
| 77 | } | 81 | } |
| 78 | 82 | ||
| 79 | // TODO: Factor out to shared functionality | 83 | impl<SM: StationManagement> Phy for GenericPhy<SM> { |
| 80 | fn blocking_delay_us(us: u32) { | 84 | fn phy_reset(&mut self) { |
| 81 | #[cfg(feature = "time")] | ||
| 82 | embassy_time::block_for(Duration::from_micros(us as u64)); | ||
| 83 | #[cfg(not(feature = "time"))] | ||
| 84 | { | ||
| 85 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | ||
| 86 | let us = us as u64; | ||
| 87 | let cycles = freq * us / 1_000_000; | ||
| 88 | cortex_m::asm::delay(cycles as u32); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | impl Phy for GenericPhy { | ||
| 93 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) { | ||
| 94 | // Detect SMI address | 85 | // Detect SMI address |
| 95 | if self.phy_addr == 0xFF { | 86 | if self.phy_addr == 0xFF { |
| 96 | for addr in 0..32 { | 87 | for addr in 0..32 { |
| 97 | sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); | 88 | self.sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); |
| 98 | for _ in 0..10 { | 89 | for _ in 0..10 { |
| 99 | if sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { | 90 | if self.sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { |
| 100 | trace!("Found ETH PHY on address {}", addr); | 91 | trace!("Found ETH PHY on address {}", addr); |
| 101 | self.phy_addr = addr; | 92 | self.phy_addr = addr; |
| 102 | return; | 93 | return; |
| @@ -108,30 +99,30 @@ impl Phy for GenericPhy { | |||
| 108 | panic!("PHY did not respond"); | 99 | panic!("PHY did not respond"); |
| 109 | } | 100 | } |
| 110 | 101 | ||
| 111 | sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); | 102 | self.sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); |
| 112 | while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} | 103 | while self.sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} |
| 113 | } | 104 | } |
| 114 | 105 | ||
| 115 | fn phy_init<S: StationManagement>(&mut self, sm: &mut S) { | 106 | fn phy_init(&mut self) { |
| 116 | // Clear WU CSR | 107 | // Clear WU CSR |
| 117 | self.smi_write_ext(sm, PHY_REG_WUCSR, 0); | 108 | self.smi_write_ext(PHY_REG_WUCSR, 0); |
| 118 | 109 | ||
| 119 | // Enable auto-negotiation | 110 | // Enable auto-negotiation |
| 120 | sm.smi_write( | 111 | self.sm.smi_write( |
| 121 | self.phy_addr, | 112 | self.phy_addr, |
| 122 | PHY_REG_BCR, | 113 | PHY_REG_BCR, |
| 123 | PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, | 114 | PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, |
| 124 | ); | 115 | ); |
| 125 | } | 116 | } |
| 126 | 117 | ||
| 127 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool { | 118 | fn poll_link(&mut self, cx: &mut Context) -> bool { |
| 128 | #[cfg(not(feature = "time"))] | 119 | #[cfg(not(feature = "time"))] |
| 129 | cx.waker().wake_by_ref(); | 120 | cx.waker().wake_by_ref(); |
| 130 | 121 | ||
| 131 | #[cfg(feature = "time")] | 122 | #[cfg(feature = "time")] |
| 132 | let _ = Timer::after(self.poll_interval).poll_unpin(cx); | 123 | let _ = Timer::after(self.poll_interval).poll_unpin(cx); |
| 133 | 124 | ||
| 134 | let bsr = sm.smi_read(self.phy_addr, PHY_REG_BSR); | 125 | let bsr = self.sm.smi_read(self.phy_addr, PHY_REG_BSR); |
| 135 | 126 | ||
| 136 | // No link without autonegotiate | 127 | // No link without autonegotiate |
| 137 | if bsr & PHY_REG_BSR_ANDONE == 0 { | 128 | if bsr & PHY_REG_BSR_ANDONE == 0 { |
| @@ -148,7 +139,7 @@ impl Phy for GenericPhy { | |||
| 148 | } | 139 | } |
| 149 | 140 | ||
| 150 | /// Public functions for the PHY | 141 | /// Public functions for the PHY |
| 151 | impl GenericPhy { | 142 | impl<SM: StationManagement> GenericPhy<SM> { |
| 152 | /// Set the SMI polling interval. | 143 | /// Set the SMI polling interval. |
| 153 | #[cfg(feature = "time")] | 144 | #[cfg(feature = "time")] |
| 154 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { | 145 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { |
| @@ -156,10 +147,15 @@ impl GenericPhy { | |||
| 156 | } | 147 | } |
| 157 | 148 | ||
| 158 | // Writes a value to an extended PHY register in MMD address space | 149 | // Writes a value to an extended PHY register in MMD address space |
| 159 | fn smi_write_ext<S: StationManagement>(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { | 150 | fn smi_write_ext(&mut self, reg_addr: u16, reg_data: u16) { |
| 160 | sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address | 151 | self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address |
| 161 | sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); | 152 | self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); |
| 162 | sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data | 153 | self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data |
| 163 | sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); | 154 | self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); |
| 155 | } | ||
| 156 | |||
| 157 | /// Access the underlying station management. | ||
| 158 | pub fn station_management(&mut self) -> &mut SM { | ||
| 159 | &mut self.sm | ||
| 164 | } | 160 | } |
| 165 | } | 161 | } |
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 10b3a0517..c8bce0e8a 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] | 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] |
| 6 | mod _version; | 6 | mod _version; |
| 7 | mod generic_phy; | 7 | mod generic_phy; |
| 8 | mod sma; | ||
| 8 | 9 | ||
| 9 | use core::mem::MaybeUninit; | 10 | use core::mem::MaybeUninit; |
| 10 | use core::task::Context; | 11 | use core::task::Context; |
| @@ -15,6 +16,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 15 | 16 | ||
| 16 | pub use self::_version::{InterruptHandler, *}; | 17 | pub use self::_version::{InterruptHandler, *}; |
| 17 | pub use self::generic_phy::*; | 18 | pub use self::generic_phy::*; |
| 19 | pub use self::sma::{Sma, StationManagement}; | ||
| 18 | use crate::rcc::RccPeripheral; | 20 | use crate::rcc::RccPeripheral; |
| 19 | 21 | ||
| 20 | #[allow(unused)] | 22 | #[allow(unused)] |
| @@ -109,7 +111,7 @@ impl<'d, T: Instance, P: Phy> embassy_net_driver::Driver for Ethernet<'d, T, P> | |||
| 109 | } | 111 | } |
| 110 | 112 | ||
| 111 | fn link_state(&mut self, cx: &mut Context) -> LinkState { | 113 | fn link_state(&mut self, cx: &mut Context) -> LinkState { |
| 112 | if self.phy.poll_link(&mut self.station_management, cx) { | 114 | if self.phy.poll_link(cx) { |
| 113 | LinkState::Up | 115 | LinkState::Up |
| 114 | } else { | 116 | } else { |
| 115 | LinkState::Down | 117 | LinkState::Down |
| @@ -157,32 +159,17 @@ impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { | |||
| 157 | } | 159 | } |
| 158 | } | 160 | } |
| 159 | 161 | ||
| 160 | /// Station Management Interface (SMI) on an ethernet PHY | ||
| 161 | pub trait StationManagement { | ||
| 162 | /// Read a register over SMI. | ||
| 163 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; | ||
| 164 | /// Write a register over SMI. | ||
| 165 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Trait for an Ethernet PHY | 162 | /// Trait for an Ethernet PHY |
| 169 | pub trait Phy { | 163 | pub trait Phy { |
| 170 | /// Reset PHY and wait for it to come out of reset. | 164 | /// Reset PHY and wait for it to come out of reset. |
| 171 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S); | 165 | fn phy_reset(&mut self); |
| 172 | /// PHY initialisation. | 166 | /// PHY initialisation. |
| 173 | fn phy_init<S: StationManagement>(&mut self, sm: &mut S); | 167 | fn phy_init(&mut self); |
| 174 | /// Poll link to see if it is up and FD with 100Mbps | 168 | /// Poll link to see if it is up and FD with 100Mbps |
| 175 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool; | 169 | fn poll_link(&mut self, cx: &mut Context) -> bool; |
| 176 | } | 170 | } |
| 177 | 171 | ||
| 178 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 172 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 179 | /// Directly expose the SMI interface used by the Ethernet driver. | ||
| 180 | /// | ||
| 181 | /// This can be used to for example configure special PHY registers for compliance testing. | ||
| 182 | pub fn station_management(&mut self) -> &mut impl StationManagement { | ||
| 183 | &mut self.station_management | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Access the user-supplied `Phy`. | 173 | /// Access the user-supplied `Phy`. |
| 187 | pub fn phy(&self) -> &P { | 174 | pub fn phy(&self) -> &P { |
| 188 | &self.phy | 175 | &self.phy |
| @@ -212,8 +199,8 @@ impl Instance for crate::peripherals::ETH {} | |||
| 212 | pin_trait!(RXClkPin, Instance, @A); | 199 | pin_trait!(RXClkPin, Instance, @A); |
| 213 | pin_trait!(TXClkPin, Instance, @A); | 200 | pin_trait!(TXClkPin, Instance, @A); |
| 214 | pin_trait!(RefClkPin, Instance, @A); | 201 | pin_trait!(RefClkPin, Instance, @A); |
| 215 | pin_trait!(MDIOPin, Instance, @A); | 202 | pin_trait!(MDIOPin, sma::Instance, @A); |
| 216 | pin_trait!(MDCPin, Instance, @A); | 203 | pin_trait!(MDCPin, sma::Instance, @A); |
| 217 | pin_trait!(RXDVPin, Instance, @A); | 204 | pin_trait!(RXDVPin, Instance, @A); |
| 218 | pin_trait!(CRSPin, Instance, @A); | 205 | pin_trait!(CRSPin, Instance, @A); |
| 219 | pin_trait!(RXD0Pin, Instance, @A); | 206 | pin_trait!(RXD0Pin, Instance, @A); |
diff --git a/embassy-stm32/src/eth/sma/mod.rs b/embassy-stm32/src/eth/sma/mod.rs new file mode 100644 index 000000000..6c851911d --- /dev/null +++ b/embassy-stm32/src/eth/sma/mod.rs | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | //! Station Management Agent (also known as MDIO or SMI). | ||
| 2 | |||
| 3 | #![macro_use] | ||
| 4 | |||
| 5 | #[cfg_attr(eth_v2, path = "v2.rs")] | ||
| 6 | #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1.rs")] | ||
| 7 | mod _version; | ||
| 8 | |||
| 9 | use embassy_hal_internal::PeripheralType; | ||
| 10 | use stm32_metapac::common::{RW, Reg}; | ||
| 11 | |||
| 12 | pub use self::_version::*; | ||
| 13 | |||
| 14 | /// Station Management Interface (SMI). | ||
| 15 | pub trait StationManagement { | ||
| 16 | /// Read a register over SMI. | ||
| 17 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; | ||
| 18 | /// Write a register over SMI. | ||
| 19 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); | ||
| 20 | } | ||
| 21 | |||
| 22 | trait SealedInstance { | ||
| 23 | fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>); | ||
| 24 | } | ||
| 25 | |||
| 26 | /// MDIO instance. | ||
| 27 | #[allow(private_bounds)] | ||
| 28 | pub trait Instance: SealedInstance + PeripheralType + Send + 'static {} | ||
| 29 | |||
| 30 | impl SealedInstance for crate::peripherals::ETH_SMA { | ||
| 31 | fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>) { | ||
| 32 | let mac = crate::pac::ETH.ethernet_mac(); | ||
| 33 | |||
| 34 | #[cfg(any(eth_v1a, eth_v1b, eth_v1c))] | ||
| 35 | return (mac.macmiiar(), mac.macmiidr()); | ||
| 36 | |||
| 37 | #[cfg(eth_v2)] | ||
| 38 | return (mac.macmdioar(), mac.macmdiodr()); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | impl Instance for crate::peripherals::ETH_SMA {} | ||
diff --git a/embassy-stm32/src/eth/sma/v1.rs b/embassy-stm32/src/eth/sma/v1.rs new file mode 100644 index 000000000..db64a6c78 --- /dev/null +++ b/embassy-stm32/src/eth/sma/v1.rs | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | use embassy_hal_internal::Peri; | ||
| 2 | pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister}; | ||
| 3 | use stm32_metapac::eth::regs; | ||
| 4 | use stm32_metapac::eth::vals::{Cr, MbProgress, Mw}; | ||
| 5 | |||
| 6 | use super::{Instance, StationManagement}; | ||
| 7 | use crate::eth::{MDCPin, MDIOPin}; | ||
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | ||
| 9 | |||
| 10 | /// Station Management Agent. | ||
| 11 | /// | ||
| 12 | /// This peripheral is used for SMI reads and writes to the connected | ||
| 13 | /// ethernet PHY/device(s). | ||
| 14 | pub struct Sma<'d, T: Instance> { | ||
| 15 | _peri: Peri<'d, T>, | ||
| 16 | clock_range: Cr, | ||
| 17 | pins: [Peri<'d, AnyPin>; 2], | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'d, T: Instance> Sma<'d, T> { | ||
| 21 | /// Create a new instance of this peripheral. | ||
| 22 | pub fn new<#[cfg(afio)] A>( | ||
| 23 | peri: Peri<'d, T>, | ||
| 24 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 25 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 26 | ) -> Self { | ||
| 27 | set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 28 | set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 29 | |||
| 30 | // Enable necessary clocks. | ||
| 31 | critical_section::with(|_| { | ||
| 32 | #[cfg(eth_v1a)] | ||
| 33 | let reg = crate::pac::RCC.ahbenr(); | ||
| 34 | |||
| 35 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 36 | let reg = crate::pac::RCC.ahb1enr(); | ||
| 37 | |||
| 38 | reg.modify(|w| { | ||
| 39 | w.set_ethen(true); | ||
| 40 | }) | ||
| 41 | }); | ||
| 42 | |||
| 43 | let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() }; | ||
| 44 | let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not."); | ||
| 45 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 46 | |||
| 47 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 48 | let clock_range = match hclk_mhz { | ||
| 49 | 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), | ||
| 50 | 25..=34 => Cr::CR_20_35, // Divide by 16 | ||
| 51 | 35..=59 => Cr::CR_35_60, // Divide by 26 | ||
| 52 | 60..=99 => Cr::CR_60_100, // Divide by 42 | ||
| 53 | 100..=149 => Cr::CR_100_150, // Divide by 62 | ||
| 54 | 150..=216 => Cr::CR_150_168, // Divide by 102 | ||
| 55 | _ => { | ||
| 56 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | Self { | ||
| 61 | _peri: peri, | ||
| 62 | clock_range, | ||
| 63 | pins: [mdio.into(), mdc.into()], | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | impl<T: Instance> StationManagement for Sma<'_, T> { | ||
| 69 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 70 | let (macmiiar, macmiidr) = T::regs(); | ||
| 71 | |||
| 72 | macmiiar.modify(|w| { | ||
| 73 | w.set_pa(phy_addr); | ||
| 74 | w.set_mr(reg); | ||
| 75 | w.set_mw(Mw::READ); // read operation | ||
| 76 | w.set_cr(self.clock_range); | ||
| 77 | w.set_mb(MbProgress::BUSY); // indicate that operation is in progress | ||
| 78 | }); | ||
| 79 | while macmiiar.read().mb() == MbProgress::BUSY {} | ||
| 80 | macmiidr.read().md() | ||
| 81 | } | ||
| 82 | |||
| 83 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 84 | let (macmiiar, macmiidr) = T::regs(); | ||
| 85 | |||
| 86 | macmiidr.write(|w| w.set_md(val)); | ||
| 87 | macmiiar.modify(|w| { | ||
| 88 | w.set_pa(phy_addr); | ||
| 89 | w.set_mr(reg); | ||
| 90 | w.set_mw(Mw::WRITE); // write | ||
| 91 | w.set_cr(self.clock_range); | ||
| 92 | w.set_mb(MbProgress::BUSY); | ||
| 93 | }); | ||
| 94 | while macmiiar.read().mb() == MbProgress::BUSY {} | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | impl<T: Instance> Drop for Sma<'_, T> { | ||
| 99 | fn drop(&mut self) { | ||
| 100 | self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/embassy-stm32/src/eth/sma/v2.rs b/embassy-stm32/src/eth/sma/v2.rs new file mode 100644 index 000000000..6bc5230b5 --- /dev/null +++ b/embassy-stm32/src/eth/sma/v2.rs | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | use embassy_hal_internal::Peri; | ||
| 2 | pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister}; | ||
| 3 | use stm32_metapac::eth::regs; | ||
| 4 | |||
| 5 | use super::{Instance, StationManagement}; | ||
| 6 | use crate::eth::{MDCPin, MDIOPin}; | ||
| 7 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | ||
| 8 | |||
| 9 | /// Station Management Agent. | ||
| 10 | /// | ||
| 11 | /// This peripheral is used for SMI reads and writes to the connected | ||
| 12 | /// ethernet PHY/device(s). | ||
| 13 | pub struct Sma<'d, T: Instance> { | ||
| 14 | _peri: Peri<'d, T>, | ||
| 15 | pins: [Peri<'d, AnyPin>; 2], | ||
| 16 | clock_range: u8, | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<'d, T: Instance> Sma<'d, T> { | ||
| 20 | /// Create a new instance of this peripheral. | ||
| 21 | pub fn new(peri: Peri<'d, T>, mdio: Peri<'d, impl MDIOPin<T>>, mdc: Peri<'d, impl MDCPin<T>>) -> Self { | ||
| 22 | set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 23 | set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 24 | |||
| 25 | // Enable necessary clocks. | ||
| 26 | critical_section::with(|_| { | ||
| 27 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 28 | w.set_ethen(true); | ||
| 29 | }) | ||
| 30 | }); | ||
| 31 | |||
| 32 | let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() }; | ||
| 33 | let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not."); | ||
| 34 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 35 | |||
| 36 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 37 | let clock_range = match hclk_mhz { | ||
| 38 | 0..=34 => 2, // Divide by 16 | ||
| 39 | 35..=59 => 3, // Divide by 26 | ||
| 40 | 60..=99 => 0, // Divide by 42 | ||
| 41 | 100..=149 => 1, // Divide by 62 | ||
| 42 | 150..=249 => 4, // Divide by 102 | ||
| 43 | 250..=310 => 5, // Divide by 124 | ||
| 44 | _ => { | ||
| 45 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 46 | } | ||
| 47 | }; | ||
| 48 | |||
| 49 | Self { | ||
| 50 | _peri: peri, | ||
| 51 | clock_range, | ||
| 52 | pins: [mdio.into(), mdc.into()], | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | impl<T: Instance> StationManagement for Sma<'_, T> { | ||
| 58 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 59 | let (macmdioar, macmdiodr) = T::regs(); | ||
| 60 | |||
| 61 | macmdioar.modify(|w| { | ||
| 62 | w.set_pa(phy_addr); | ||
| 63 | w.set_rda(reg); | ||
| 64 | w.set_goc(0b11); // read | ||
| 65 | w.set_cr(self.clock_range); | ||
| 66 | w.set_mb(true); | ||
| 67 | }); | ||
| 68 | |||
| 69 | while macmdioar.read().mb() {} | ||
| 70 | |||
| 71 | macmdiodr.read().md() | ||
| 72 | } | ||
| 73 | |||
| 74 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 75 | let (macmdioar, macmdiodr) = T::regs(); | ||
| 76 | |||
| 77 | macmdiodr.write(|w| w.set_md(val)); | ||
| 78 | macmdioar.modify(|w| { | ||
| 79 | w.set_pa(phy_addr); | ||
| 80 | w.set_rda(reg); | ||
| 81 | w.set_goc(0b01); // write | ||
| 82 | w.set_cr(self.clock_range); | ||
| 83 | w.set_mb(true); | ||
| 84 | }); | ||
| 85 | |||
| 86 | while macmdioar.read().mb() {} | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | impl<T: Instance> Drop for Sma<'_, T> { | ||
| 91 | fn drop(&mut self) { | ||
| 92 | self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); | ||
| 93 | } | ||
| 94 | } | ||
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 5be1c9739..8de26ce9d 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -3,11 +3,10 @@ | |||
| 3 | mod rx_desc; | 3 | mod rx_desc; |
| 4 | mod tx_desc; | 4 | mod tx_desc; |
| 5 | 5 | ||
| 6 | use core::marker::PhantomData; | 6 | use core::sync::atomic::{Ordering, fence}; |
| 7 | use core::sync::atomic::{fence, Ordering}; | ||
| 8 | 7 | ||
| 9 | use embassy_hal_internal::Peri; | 8 | use embassy_hal_internal::Peri; |
| 10 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; | 9 | use stm32_metapac::eth::vals::{Apcs, Dm, DmaomrSr, Fes, Ftf, Ifg, Pbl, Rsf, St, Tsf}; |
| 11 | 10 | ||
| 12 | pub(crate) use self::rx_desc::{RDes, RDesRing}; | 11 | pub(crate) use self::rx_desc::{RDes, RDesRing}; |
| 13 | pub(crate) use self::tx_desc::{TDes, TDesRing}; | 12 | pub(crate) use self::tx_desc::{TDes, TDesRing}; |
| @@ -22,7 +21,6 @@ use crate::pac::AFIO; | |||
| 22 | #[cfg(any(eth_v1b, eth_v1c))] | 21 | #[cfg(any(eth_v1b, eth_v1c))] |
| 23 | use crate::pac::SYSCFG; | 22 | use crate::pac::SYSCFG; |
| 24 | use crate::pac::{ETH, RCC}; | 23 | use crate::pac::{ETH, RCC}; |
| 25 | use crate::rcc::SealedRccPeripheral; | ||
| 26 | 24 | ||
| 27 | /// Interrupt handler. | 25 | /// Interrupt handler. |
| 28 | pub struct InterruptHandler {} | 26 | pub struct InterruptHandler {} |
| @@ -53,14 +51,13 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { | |||
| 53 | 51 | ||
| 54 | pins: Pins<'d>, | 52 | pins: Pins<'d>, |
| 55 | pub(crate) phy: P, | 53 | pub(crate) phy: P, |
| 56 | pub(crate) station_management: EthernetStationManagement<T>, | ||
| 57 | pub(crate) mac_addr: [u8; 6], | 54 | pub(crate) mac_addr: [u8; 6], |
| 58 | } | 55 | } |
| 59 | 56 | ||
| 60 | /// Pins of ethernet driver. | 57 | /// Pins of ethernet driver. |
| 61 | enum Pins<'d> { | 58 | enum Pins<'d> { |
| 62 | Rmii([Peri<'d, AnyPin>; 9]), | 59 | Rmii([Peri<'d, AnyPin>; 7]), |
| 63 | Mii([Peri<'d, AnyPin>; 14]), | 60 | Mii([Peri<'d, AnyPin>; 12]), |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | #[cfg(eth_v1a)] | 63 | #[cfg(eth_v1a)] |
| @@ -97,68 +94,105 @@ macro_rules! config_pins { | |||
| 97 | }; | 94 | }; |
| 98 | } | 95 | } |
| 99 | 96 | ||
| 100 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 97 | impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> { |
| 98 | /// Create a new RMII ethernet driver using 7 pins. | ||
| 99 | /// | ||
| 100 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 101 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 102 | /// | ||
| 103 | /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet | ||
| 104 | /// river with a non-standard PHY. | ||
| 105 | /// | ||
| 101 | /// safety: the returned instance is not leak-safe | 106 | /// safety: the returned instance is not leak-safe |
| 102 | pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>( | 107 | pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>( |
| 103 | queue: &'d mut PacketQueue<TX, RX>, | 108 | queue: &'d mut PacketQueue<TX, RX>, |
| 104 | peri: Peri<'d, T>, | 109 | peri: Peri<'d, T>, |
| 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 110 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 106 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, | 111 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, |
| 107 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 108 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 109 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, | 112 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, |
| 110 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | 113 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, |
| 111 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | 114 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, |
| 112 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | 115 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, |
| 113 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | 116 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, |
| 114 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | 117 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, |
| 115 | phy: P, | ||
| 116 | mac_addr: [u8; 6], | 118 | mac_addr: [u8; 6], |
| 119 | sma: Peri<'d, SMA>, | ||
| 120 | mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>, | ||
| 121 | mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>, | ||
| 117 | ) -> Self { | 122 | ) -> Self { |
| 118 | // Enable the necessary Clocks | 123 | let sma = Sma::new(sma, mdio, mdc); |
| 119 | #[cfg(eth_v1a)] | 124 | let phy = GenericPhy::new_auto(sma); |
| 120 | critical_section::with(|_| { | ||
| 121 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 122 | |||
| 123 | // Select RMII (Reduced Media Independent Interface) | ||
| 124 | // Must be done prior to enabling peripheral clock | ||
| 125 | AFIO.mapr().modify(|w| { | ||
| 126 | w.set_mii_rmii_sel(true); | ||
| 127 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 128 | }); | ||
| 129 | 125 | ||
| 130 | RCC.ahbenr().modify(|w| { | 126 | Self::new_with_phy( |
| 131 | w.set_ethen(true); | 127 | queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy, |
| 132 | w.set_ethtxen(true); | 128 | ) |
| 133 | w.set_ethrxen(true); | 129 | } |
| 134 | }); | ||
| 135 | }); | ||
| 136 | 130 | ||
| 137 | #[cfg(any(eth_v1b, eth_v1c))] | 131 | /// Create a new MII ethernet driver using 14 pins. |
| 138 | critical_section::with(|_| { | 132 | /// |
| 139 | RCC.ahb1enr().modify(|w| { | 133 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the |
| 140 | w.set_ethen(true); | 134 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. |
| 141 | w.set_ethtxen(true); | 135 | /// |
| 142 | w.set_ethrxen(true); | 136 | /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet |
| 143 | }); | 137 | /// river with a non-standard PHY. |
| 138 | pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>( | ||
| 139 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 140 | peri: Peri<'d, T>, | ||
| 141 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 142 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, | ||
| 143 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, | ||
| 144 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, | ||
| 145 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | ||
| 146 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | ||
| 147 | rx_d2: Peri<'d, if_afio!(impl RXD2Pin<T, A>)>, | ||
| 148 | rx_d3: Peri<'d, if_afio!(impl RXD3Pin<T, A>)>, | ||
| 149 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | ||
| 150 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | ||
| 151 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, | ||
| 152 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, | ||
| 153 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | ||
| 154 | mac_addr: [u8; 6], | ||
| 155 | sma: Peri<'d, SMA>, | ||
| 156 | mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>, | ||
| 157 | mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>, | ||
| 158 | ) -> Self { | ||
| 159 | let sma = Sma::new(sma, mdio, mdc); | ||
| 160 | let phy = GenericPhy::new_auto(sma); | ||
| 144 | 161 | ||
| 145 | // RMII (Reduced Media Independent Interface) | 162 | Self::new_mii_with_phy( |
| 146 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); | 163 | queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en, |
| 147 | }); | 164 | mac_addr, phy, |
| 165 | ) | ||
| 166 | } | ||
| 167 | } | ||
| 148 | 168 | ||
| 169 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | ||
| 170 | /// safety: the returned instance is not leak-safe | ||
| 171 | pub fn new_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>( | ||
| 172 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 173 | peri: Peri<'d, T>, | ||
| 174 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 175 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, | ||
| 176 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, | ||
| 177 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | ||
| 178 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | ||
| 179 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, | ||
| 180 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, | ||
| 181 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | ||
| 182 | mac_addr: [u8; 6], | ||
| 183 | phy: P, | ||
| 184 | ) -> Self { | ||
| 149 | #[cfg(eth_v1a)] | 185 | #[cfg(eth_v1a)] |
| 150 | { | 186 | { |
| 151 | config_in_pins!(ref_clk, rx_d0, rx_d1); | 187 | config_in_pins!(ref_clk, rx_d0, rx_d1); |
| 152 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); | 188 | config_af_pins!(tx_d0, tx_d1, tx_en); |
| 153 | } | 189 | } |
| 154 | 190 | ||
| 155 | #[cfg(any(eth_v1b, eth_v1c))] | 191 | #[cfg(any(eth_v1b, eth_v1c))] |
| 156 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 192 | config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 157 | 193 | ||
| 158 | let pins = Pins::Rmii([ | 194 | let pins = Pins::Rmii([ |
| 159 | ref_clk.into(), | 195 | ref_clk.into(), |
| 160 | mdio.into(), | ||
| 161 | mdc.into(), | ||
| 162 | crs.into(), | 196 | crs.into(), |
| 163 | rx_d0.into(), | 197 | rx_d0.into(), |
| 164 | rx_d1.into(), | 198 | rx_d1.into(), |
| @@ -167,7 +201,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 167 | tx_en.into(), | 201 | tx_en.into(), |
| 168 | ]); | 202 | ]); |
| 169 | 203 | ||
| 170 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 204 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, true) |
| 171 | } | 205 | } |
| 172 | 206 | ||
| 173 | fn new_inner<const TX: usize, const RX: usize>( | 207 | fn new_inner<const TX: usize, const RX: usize>( |
| @@ -177,7 +211,39 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 177 | pins: Pins<'d>, | 211 | pins: Pins<'d>, |
| 178 | phy: P, | 212 | phy: P, |
| 179 | mac_addr: [u8; 6], | 213 | mac_addr: [u8; 6], |
| 214 | rmii_mii_sel: bool, | ||
| 180 | ) -> Self { | 215 | ) -> Self { |
| 216 | // Enable the necessary Clocks | ||
| 217 | #[cfg(eth_v1a)] | ||
| 218 | critical_section::with(|_| { | ||
| 219 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 220 | |||
| 221 | // Select (R)MII (Reduced Media Independent Interface) | ||
| 222 | // Must be done prior to enabling peripheral clock | ||
| 223 | AFIO.mapr().modify(|w| { | ||
| 224 | w.set_mii_rmii_sel(rmii_mii_sel); | ||
| 225 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 226 | }); | ||
| 227 | |||
| 228 | RCC.ahbenr().modify(|w| { | ||
| 229 | w.set_ethen(true); | ||
| 230 | w.set_ethtxen(true); | ||
| 231 | w.set_ethrxen(true); | ||
| 232 | }); | ||
| 233 | }); | ||
| 234 | |||
| 235 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 236 | critical_section::with(|_| { | ||
| 237 | RCC.ahb1enr().modify(|w| { | ||
| 238 | w.set_ethen(true); | ||
| 239 | w.set_ethtxen(true); | ||
| 240 | w.set_ethrxen(true); | ||
| 241 | }); | ||
| 242 | |||
| 243 | // (R)MII ((Reduced) Media Independent Interface) | ||
| 244 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(rmii_mii_sel)); | ||
| 245 | }); | ||
| 246 | |||
| 181 | let dma = T::regs().ethernet_dma(); | 247 | let dma = T::regs().ethernet_dma(); |
| 182 | let mac = T::regs().ethernet_mac(); | 248 | let mac = T::regs().ethernet_mac(); |
| 183 | 249 | ||
| @@ -190,7 +256,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 190 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping | 256 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping |
| 191 | w.set_fes(Fes::FES100); // fast ethernet speed | 257 | w.set_fes(Fes::FES100); // fast ethernet speed |
| 192 | w.set_dm(Dm::FULL_DUPLEX); // full duplex | 258 | w.set_dm(Dm::FULL_DUPLEX); // full duplex |
| 193 | // TODO: Carrier sense ? ECRSFD | 259 | // TODO: Carrier sense ? ECRSFD |
| 194 | }); | 260 | }); |
| 195 | 261 | ||
| 196 | // Set the mac to pass all multicast packets | 262 | // Set the mac to pass all multicast packets |
| @@ -226,30 +292,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 226 | 292 | ||
| 227 | // TODO MTU size setting not found for v1 ethernet, check if correct | 293 | // TODO MTU size setting not found for v1 ethernet, check if correct |
| 228 | 294 | ||
| 229 | let hclk = <T as SealedRccPeripheral>::frequency(); | ||
| 230 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 231 | |||
| 232 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 233 | let clock_range = match hclk_mhz { | ||
| 234 | 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), | ||
| 235 | 25..=34 => Cr::CR_20_35, // Divide by 16 | ||
| 236 | 35..=59 => Cr::CR_35_60, // Divide by 26 | ||
| 237 | 60..=99 => Cr::CR_60_100, // Divide by 42 | ||
| 238 | 100..=149 => Cr::CR_100_150, // Divide by 62 | ||
| 239 | 150..=216 => Cr::CR_150_168, // Divide by 102 | ||
| 240 | _ => { | ||
| 241 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 242 | } | ||
| 243 | }; | ||
| 244 | |||
| 245 | let mut this = Self { | 295 | let mut this = Self { |
| 246 | _peri: peri, | 296 | _peri: peri, |
| 247 | pins, | 297 | pins, |
| 248 | phy: phy, | 298 | phy: phy, |
| 249 | station_management: EthernetStationManagement { | ||
| 250 | peri: PhantomData, | ||
| 251 | clock_range: clock_range, | ||
| 252 | }, | ||
| 253 | mac_addr, | 299 | mac_addr, |
| 254 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 300 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 255 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 301 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| @@ -279,8 +325,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 279 | w.set_tie(true); | 325 | w.set_tie(true); |
| 280 | }); | 326 | }); |
| 281 | 327 | ||
| 282 | this.phy.phy_reset(&mut this.station_management); | 328 | this.phy.phy_reset(); |
| 283 | this.phy.phy_init(&mut this.station_management); | 329 | this.phy.phy_init(); |
| 284 | 330 | ||
| 285 | interrupt::ETH.unpend(); | 331 | interrupt::ETH.unpend(); |
| 286 | unsafe { interrupt::ETH.enable() }; | 332 | unsafe { interrupt::ETH.enable() }; |
| @@ -288,15 +334,13 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 288 | this | 334 | this |
| 289 | } | 335 | } |
| 290 | 336 | ||
| 291 | /// Create a new MII ethernet driver using 14 pins. | 337 | /// Create a new MII ethernet driver using 12 pins. |
| 292 | pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>( | 338 | pub fn new_mii_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>( |
| 293 | queue: &'d mut PacketQueue<TX, RX>, | 339 | queue: &'d mut PacketQueue<TX, RX>, |
| 294 | peri: Peri<'d, T>, | 340 | peri: Peri<'d, T>, |
| 295 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 341 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 296 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, | 342 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, |
| 297 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, | 343 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, |
| 298 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, | ||
| 299 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, | ||
| 300 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, | 344 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, |
| 301 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, | 345 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, |
| 302 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, | 346 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, |
| @@ -307,56 +351,23 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 307 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, | 351 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, |
| 308 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, | 352 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, |
| 309 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, | 353 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, |
| 310 | phy: P, | ||
| 311 | mac_addr: [u8; 6], | 354 | mac_addr: [u8; 6], |
| 355 | phy: P, | ||
| 312 | ) -> Self { | 356 | ) -> Self { |
| 313 | // TODO: Handle optional signals like CRS, MII_COL, RX_ER? | ||
| 314 | |||
| 315 | // Enable the necessary Clocks | ||
| 316 | #[cfg(eth_v1a)] | ||
| 317 | critical_section::with(|_| { | ||
| 318 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 319 | |||
| 320 | // Select MII (Media Independent Interface) | ||
| 321 | // Must be done prior to enabling peripheral clock | ||
| 322 | AFIO.mapr().modify(|w| { | ||
| 323 | w.set_mii_rmii_sel(false); | ||
| 324 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 325 | }); | ||
| 326 | |||
| 327 | RCC.ahbenr().modify(|w| { | ||
| 328 | w.set_ethen(true); | ||
| 329 | w.set_ethtxen(true); | ||
| 330 | w.set_ethrxen(true); | ||
| 331 | }); | ||
| 332 | }); | ||
| 333 | |||
| 334 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 335 | critical_section::with(|_| { | ||
| 336 | RCC.ahb1enr().modify(|w| { | ||
| 337 | w.set_ethen(true); | ||
| 338 | w.set_ethtxen(true); | ||
| 339 | w.set_ethrxen(true); | ||
| 340 | }); | ||
| 341 | |||
| 342 | // MII (Media Independent Interface) | ||
| 343 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(false)); | ||
| 344 | }); | ||
| 345 | |||
| 346 | #[cfg(eth_v1a)] | 357 | #[cfg(eth_v1a)] |
| 347 | { | 358 | { |
| 348 | config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); | 359 | config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); |
| 349 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | 360 | config_af_pins!(tx_d0, tx_d1, tx_d2, tx_d3, tx_en); |
| 350 | } | 361 | } |
| 351 | 362 | ||
| 352 | #[cfg(any(eth_v1b, eth_v1c))] | 363 | #[cfg(any(eth_v1b, eth_v1c))] |
| 353 | config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | 364 | config_pins!( |
| 365 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en | ||
| 366 | ); | ||
| 354 | 367 | ||
| 355 | let pins = Pins::Mii([ | 368 | let pins = Pins::Mii([ |
| 356 | rx_clk.into(), | 369 | rx_clk.into(), |
| 357 | tx_clk.into(), | 370 | tx_clk.into(), |
| 358 | mdio.into(), | ||
| 359 | mdc.into(), | ||
| 360 | rxdv.into(), | 371 | rxdv.into(), |
| 361 | rx_d0.into(), | 372 | rx_d0.into(), |
| 362 | rx_d1.into(), | 373 | rx_d1.into(), |
| @@ -369,43 +380,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 369 | tx_en.into(), | 380 | tx_en.into(), |
| 370 | ]); | 381 | ]); |
| 371 | 382 | ||
| 372 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 383 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false) |
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | /// Ethernet station management interface. | ||
| 377 | pub(crate) struct EthernetStationManagement<T: Instance> { | ||
| 378 | peri: PhantomData<T>, | ||
| 379 | clock_range: Cr, | ||
| 380 | } | ||
| 381 | |||
| 382 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 383 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 384 | let mac = T::regs().ethernet_mac(); | ||
| 385 | |||
| 386 | mac.macmiiar().modify(|w| { | ||
| 387 | w.set_pa(phy_addr); | ||
| 388 | w.set_mr(reg); | ||
| 389 | w.set_mw(Mw::READ); // read operation | ||
| 390 | w.set_cr(self.clock_range); | ||
| 391 | w.set_mb(MbProgress::BUSY); // indicate that operation is in progress | ||
| 392 | }); | ||
| 393 | while mac.macmiiar().read().mb() == MbProgress::BUSY {} | ||
| 394 | mac.macmiidr().read().md() | ||
| 395 | } | ||
| 396 | |||
| 397 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 398 | let mac = T::regs().ethernet_mac(); | ||
| 399 | |||
| 400 | mac.macmiidr().write(|w| w.set_md(val)); | ||
| 401 | mac.macmiiar().modify(|w| { | ||
| 402 | w.set_pa(phy_addr); | ||
| 403 | w.set_mr(reg); | ||
| 404 | w.set_mw(Mw::WRITE); // write | ||
| 405 | w.set_cr(self.clock_range); | ||
| 406 | w.set_mb(MbProgress::BUSY); | ||
| 407 | }); | ||
| 408 | while mac.macmiiar().read().mb() == MbProgress::BUSY {} | ||
| 409 | } | 384 | } |
| 410 | } | 385 | } |
| 411 | 386 | ||
diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index 2a46c1895..6ade1f29c 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, fence, Ordering}; | 1 | use core::sync::atomic::{Ordering, compiler_fence, fence}; |
| 2 | 2 | ||
| 3 | use stm32_metapac::eth::vals::{Rpd, Rps}; | 3 | use stm32_metapac::eth::vals::{Rpd, Rps}; |
| 4 | use vcell::VolatileCell; | 4 | use vcell::VolatileCell; |
diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs index 1317d20f4..ba99b66cb 100644 --- a/embassy-stm32/src/eth/v1/tx_desc.rs +++ b/embassy-stm32/src/eth/v1/tx_desc.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, fence, Ordering}; | 1 | use core::sync::atomic::{Ordering, compiler_fence, fence}; |
| 2 | 2 | ||
| 3 | use vcell::VolatileCell; | 3 | use vcell::VolatileCell; |
| 4 | 4 | ||
diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs index 645bfdb14..e335ed8f3 100644 --- a/embassy-stm32/src/eth/v2/descriptors.rs +++ b/embassy-stm32/src/eth/v2/descriptors.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use core::sync::atomic::{fence, Ordering}; | 1 | use core::sync::atomic::{Ordering, fence}; |
| 2 | 2 | ||
| 3 | use vcell::VolatileCell; | 3 | use vcell::VolatileCell; |
| 4 | 4 | ||
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index cf7a9901b..7f92e351c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | mod descriptors; | 1 | mod descriptors; |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::sync::atomic::{Ordering, fence}; |
| 4 | use core::sync::atomic::{fence, Ordering}; | ||
| 5 | 4 | ||
| 6 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 7 | use stm32_metapac::syscfg::vals::EthSelPhy; | 6 | use stm32_metapac::syscfg::vals::EthSelPhy; |
| @@ -12,7 +11,6 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; | |||
| 12 | use crate::interrupt; | 11 | use crate::interrupt; |
| 13 | use crate::interrupt::InterruptExt; | 12 | use crate::interrupt::InterruptExt; |
| 14 | use crate::pac::ETH; | 13 | use crate::pac::ETH; |
| 15 | use crate::rcc::SealedRccPeripheral; | ||
| 16 | 14 | ||
| 17 | /// Interrupt handler. | 15 | /// Interrupt handler. |
| 18 | pub struct InterruptHandler {} | 16 | pub struct InterruptHandler {} |
| @@ -42,14 +40,13 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { | |||
| 42 | pub(crate) rx: RDesRing<'d>, | 40 | pub(crate) rx: RDesRing<'d>, |
| 43 | pins: Pins<'d>, | 41 | pins: Pins<'d>, |
| 44 | pub(crate) phy: P, | 42 | pub(crate) phy: P, |
| 45 | pub(crate) station_management: EthernetStationManagement<T>, | ||
| 46 | pub(crate) mac_addr: [u8; 6], | 43 | pub(crate) mac_addr: [u8; 6], |
| 47 | } | 44 | } |
| 48 | 45 | ||
| 49 | /// Pins of ethernet driver. | 46 | /// Pins of ethernet driver. |
| 50 | enum Pins<'d> { | 47 | enum Pins<'d> { |
| 51 | Rmii([Peri<'d, AnyPin>; 9]), | 48 | Rmii([Peri<'d, AnyPin>; 7]), |
| 52 | Mii([Peri<'d, AnyPin>; 14]), | 49 | Mii([Peri<'d, AnyPin>; 12]), |
| 53 | } | 50 | } |
| 54 | 51 | ||
| 55 | macro_rules! config_pins { | 52 | macro_rules! config_pins { |
| @@ -63,41 +60,96 @@ macro_rules! config_pins { | |||
| 63 | }; | 60 | }; |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 63 | impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> { |
| 67 | /// Create a new RMII ethernet driver using 9 pins. | 64 | /// Create a new RMII ethernet driver using 7 pins. |
| 65 | /// | ||
| 66 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 67 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 68 | /// | ||
| 69 | /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet | ||
| 70 | /// river with a non-standard PHY. | ||
| 68 | pub fn new<const TX: usize, const RX: usize>( | 71 | pub fn new<const TX: usize, const RX: usize>( |
| 69 | queue: &'d mut PacketQueue<TX, RX>, | 72 | queue: &'d mut PacketQueue<TX, RX>, |
| 70 | peri: Peri<'d, T>, | 73 | peri: Peri<'d, T>, |
| 71 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 74 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 72 | ref_clk: Peri<'d, impl RefClkPin<T>>, | 75 | ref_clk: Peri<'d, impl RefClkPin<T>>, |
| 73 | mdio: Peri<'d, impl MDIOPin<T>>, | ||
| 74 | mdc: Peri<'d, impl MDCPin<T>>, | ||
| 75 | crs: Peri<'d, impl CRSPin<T>>, | 76 | crs: Peri<'d, impl CRSPin<T>>, |
| 76 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | 77 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 77 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | 78 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| 78 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | 79 | tx_d0: Peri<'d, impl TXD0Pin<T>>, |
| 79 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | 80 | tx_d1: Peri<'d, impl TXD1Pin<T>>, |
| 80 | tx_en: Peri<'d, impl TXEnPin<T>>, | 81 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 81 | phy: P, | ||
| 82 | mac_addr: [u8; 6], | 82 | mac_addr: [u8; 6], |
| 83 | sma: Peri<'d, SMA>, | ||
| 84 | mdio: Peri<'d, impl MDIOPin<SMA>>, | ||
| 85 | mdc: Peri<'d, impl MDCPin<SMA>>, | ||
| 83 | ) -> Self { | 86 | ) -> Self { |
| 84 | // Enable the necessary clocks | 87 | let sma = Sma::new(sma, mdio, mdc); |
| 85 | critical_section::with(|_| { | 88 | let phy = GenericPhy::new_auto(sma); |
| 86 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 87 | w.set_ethen(true); | ||
| 88 | w.set_ethtxen(true); | ||
| 89 | w.set_ethrxen(true); | ||
| 90 | }); | ||
| 91 | 89 | ||
| 92 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(EthSelPhy::RMII)); | 90 | Self::new_with_phy( |
| 93 | }); | 91 | queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy, |
| 92 | ) | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Create a new MII ethernet driver using 14 pins. | ||
| 96 | /// | ||
| 97 | /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the | ||
| 98 | /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. | ||
| 99 | /// | ||
| 100 | /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet | ||
| 101 | /// river with a non-standard PHY. | ||
| 102 | pub fn new_mii<const TX: usize, const RX: usize>( | ||
| 103 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 104 | peri: Peri<'d, T>, | ||
| 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 106 | rx_clk: Peri<'d, impl RXClkPin<T>>, | ||
| 107 | tx_clk: Peri<'d, impl TXClkPin<T>>, | ||
| 108 | rxdv: Peri<'d, impl RXDVPin<T>>, | ||
| 109 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | ||
| 110 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | ||
| 111 | rx_d2: Peri<'d, impl RXD2Pin<T>>, | ||
| 112 | rx_d3: Peri<'d, impl RXD3Pin<T>>, | ||
| 113 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | ||
| 114 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | ||
| 115 | tx_d2: Peri<'d, impl TXD2Pin<T>>, | ||
| 116 | tx_d3: Peri<'d, impl TXD3Pin<T>>, | ||
| 117 | tx_en: Peri<'d, impl TXEnPin<T>>, | ||
| 118 | mac_addr: [u8; 6], | ||
| 119 | sma: Peri<'d, SMA>, | ||
| 120 | mdio: Peri<'d, impl MDIOPin<SMA>>, | ||
| 121 | mdc: Peri<'d, impl MDCPin<SMA>>, | ||
| 122 | ) -> Self { | ||
| 123 | let sma = Sma::new(sma, mdio, mdc); | ||
| 124 | let phy = GenericPhy::new_auto(sma); | ||
| 125 | |||
| 126 | Self::new_mii_with_phy( | ||
| 127 | queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en, | ||
| 128 | mac_addr, phy, | ||
| 129 | ) | ||
| 130 | } | ||
| 131 | } | ||
| 94 | 132 | ||
| 95 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 133 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 134 | /// Create a new RMII ethernet driver using 7 pins. | ||
| 135 | pub fn new_with_phy<const TX: usize, const RX: usize>( | ||
| 136 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 137 | peri: Peri<'d, T>, | ||
| 138 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 139 | ref_clk: Peri<'d, impl RefClkPin<T>>, | ||
| 140 | crs: Peri<'d, impl CRSPin<T>>, | ||
| 141 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | ||
| 142 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | ||
| 143 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | ||
| 144 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | ||
| 145 | tx_en: Peri<'d, impl TXEnPin<T>>, | ||
| 146 | mac_addr: [u8; 6], | ||
| 147 | phy: P, | ||
| 148 | ) -> Self { | ||
| 149 | config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | ||
| 96 | 150 | ||
| 97 | let pins = Pins::Rmii([ | 151 | let pins = Pins::Rmii([ |
| 98 | ref_clk.into(), | 152 | ref_clk.into(), |
| 99 | mdio.into(), | ||
| 100 | mdc.into(), | ||
| 101 | crs.into(), | 153 | crs.into(), |
| 102 | rx_d0.into(), | 154 | rx_d0.into(), |
| 103 | rx_d1.into(), | 155 | rx_d1.into(), |
| @@ -106,18 +158,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 106 | tx_en.into(), | 158 | tx_en.into(), |
| 107 | ]); | 159 | ]); |
| 108 | 160 | ||
| 109 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 161 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::RMII) |
| 110 | } | 162 | } |
| 111 | 163 | ||
| 112 | /// Create a new MII ethernet driver using 14 pins. | 164 | /// Create a new MII ethernet driver using 12 pins. |
| 113 | pub fn new_mii<const TX: usize, const RX: usize>( | 165 | pub fn new_mii_with_phy<const TX: usize, const RX: usize>( |
| 114 | queue: &'d mut PacketQueue<TX, RX>, | 166 | queue: &'d mut PacketQueue<TX, RX>, |
| 115 | peri: Peri<'d, T>, | 167 | peri: Peri<'d, T>, |
| 116 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 168 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 117 | rx_clk: Peri<'d, impl RXClkPin<T>>, | 169 | rx_clk: Peri<'d, impl RXClkPin<T>>, |
| 118 | tx_clk: Peri<'d, impl TXClkPin<T>>, | 170 | tx_clk: Peri<'d, impl TXClkPin<T>>, |
| 119 | mdio: Peri<'d, impl MDIOPin<T>>, | ||
| 120 | mdc: Peri<'d, impl MDCPin<T>>, | ||
| 121 | rxdv: Peri<'d, impl RXDVPin<T>>, | 171 | rxdv: Peri<'d, impl RXDVPin<T>>, |
| 122 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | 172 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 123 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | 173 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| @@ -128,29 +178,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 128 | tx_d2: Peri<'d, impl TXD2Pin<T>>, | 178 | tx_d2: Peri<'d, impl TXD2Pin<T>>, |
| 129 | tx_d3: Peri<'d, impl TXD3Pin<T>>, | 179 | tx_d3: Peri<'d, impl TXD3Pin<T>>, |
| 130 | tx_en: Peri<'d, impl TXEnPin<T>>, | 180 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 131 | phy: P, | ||
| 132 | mac_addr: [u8; 6], | 181 | mac_addr: [u8; 6], |
| 182 | phy: P, | ||
| 133 | ) -> Self { | 183 | ) -> Self { |
| 134 | // Enable the necessary clocks | 184 | config_pins!( |
| 135 | critical_section::with(|_| { | 185 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en |
| 136 | crate::pac::RCC.ahb1enr().modify(|w| { | 186 | ); |
| 137 | w.set_ethen(true); | ||
| 138 | w.set_ethtxen(true); | ||
| 139 | w.set_ethrxen(true); | ||
| 140 | }); | ||
| 141 | |||
| 142 | crate::pac::SYSCFG | ||
| 143 | .pmcr() | ||
| 144 | .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); | ||
| 145 | }); | ||
| 146 | |||
| 147 | config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | ||
| 148 | 187 | ||
| 149 | let pins = Pins::Mii([ | 188 | let pins = Pins::Mii([ |
| 150 | rx_clk.into(), | 189 | rx_clk.into(), |
| 151 | tx_clk.into(), | 190 | tx_clk.into(), |
| 152 | mdio.into(), | ||
| 153 | mdc.into(), | ||
| 154 | rxdv.into(), | 191 | rxdv.into(), |
| 155 | rx_d0.into(), | 192 | rx_d0.into(), |
| 156 | rx_d1.into(), | 193 | rx_d1.into(), |
| @@ -163,7 +200,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 163 | tx_en.into(), | 200 | tx_en.into(), |
| 164 | ]); | 201 | ]); |
| 165 | 202 | ||
| 166 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 203 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII) |
| 167 | } | 204 | } |
| 168 | 205 | ||
| 169 | fn new_inner<const TX: usize, const RX: usize>( | 206 | fn new_inner<const TX: usize, const RX: usize>( |
| @@ -173,7 +210,19 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 173 | pins: Pins<'d>, | 210 | pins: Pins<'d>, |
| 174 | phy: P, | 211 | phy: P, |
| 175 | mac_addr: [u8; 6], | 212 | mac_addr: [u8; 6], |
| 213 | eth_sel_phy: EthSelPhy, | ||
| 176 | ) -> Self { | 214 | ) -> Self { |
| 215 | // Enable the necessary clocks | ||
| 216 | critical_section::with(|_| { | ||
| 217 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 218 | w.set_ethen(true); | ||
| 219 | w.set_ethtxen(true); | ||
| 220 | w.set_ethrxen(true); | ||
| 221 | }); | ||
| 222 | |||
| 223 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(eth_sel_phy)); | ||
| 224 | }); | ||
| 225 | |||
| 177 | let dma = T::regs().ethernet_dma(); | 226 | let dma = T::regs().ethernet_dma(); |
| 178 | let mac = T::regs().ethernet_mac(); | 227 | let mac = T::regs().ethernet_mac(); |
| 179 | let mtl = T::regs().ethernet_mtl(); | 228 | let mtl = T::regs().ethernet_mtl(); |
| @@ -235,32 +284,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 235 | w.set_rbsz(RX_BUFFER_SIZE as u16); | 284 | w.set_rbsz(RX_BUFFER_SIZE as u16); |
| 236 | }); | 285 | }); |
| 237 | 286 | ||
| 238 | let hclk = <T as SealedRccPeripheral>::frequency(); | ||
| 239 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 240 | |||
| 241 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 242 | let clock_range = match hclk_mhz { | ||
| 243 | 0..=34 => 2, // Divide by 16 | ||
| 244 | 35..=59 => 3, // Divide by 26 | ||
| 245 | 60..=99 => 0, // Divide by 42 | ||
| 246 | 100..=149 => 1, // Divide by 62 | ||
| 247 | 150..=249 => 4, // Divide by 102 | ||
| 248 | 250..=310 => 5, // Divide by 124 | ||
| 249 | _ => { | ||
| 250 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 251 | } | ||
| 252 | }; | ||
| 253 | |||
| 254 | let mut this = Self { | 287 | let mut this = Self { |
| 255 | _peri: peri, | 288 | _peri: peri, |
| 256 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 289 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 257 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 290 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| 258 | pins, | 291 | pins, |
| 259 | phy, | 292 | phy, |
| 260 | station_management: EthernetStationManagement { | ||
| 261 | peri: PhantomData, | ||
| 262 | clock_range: clock_range, | ||
| 263 | }, | ||
| 264 | mac_addr, | 293 | mac_addr, |
| 265 | }; | 294 | }; |
| 266 | 295 | ||
| @@ -286,8 +315,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 286 | w.set_tie(true); | 315 | w.set_tie(true); |
| 287 | }); | 316 | }); |
| 288 | 317 | ||
| 289 | this.phy.phy_reset(&mut this.station_management); | 318 | this.phy.phy_reset(); |
| 290 | this.phy.phy_init(&mut this.station_management); | 319 | this.phy.phy_init(); |
| 291 | 320 | ||
| 292 | interrupt::ETH.unpend(); | 321 | interrupt::ETH.unpend(); |
| 293 | unsafe { interrupt::ETH.enable() }; | 322 | unsafe { interrupt::ETH.enable() }; |
| @@ -296,42 +325,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 296 | } | 325 | } |
| 297 | } | 326 | } |
| 298 | 327 | ||
| 299 | /// Ethernet SMI driver. | ||
| 300 | pub struct EthernetStationManagement<T: Instance> { | ||
| 301 | peri: PhantomData<T>, | ||
| 302 | clock_range: u8, | ||
| 303 | } | ||
| 304 | |||
| 305 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 306 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 307 | let mac = T::regs().ethernet_mac(); | ||
| 308 | |||
| 309 | mac.macmdioar().modify(|w| { | ||
| 310 | w.set_pa(phy_addr); | ||
| 311 | w.set_rda(reg); | ||
| 312 | w.set_goc(0b11); // read | ||
| 313 | w.set_cr(self.clock_range); | ||
| 314 | w.set_mb(true); | ||
| 315 | }); | ||
| 316 | while mac.macmdioar().read().mb() {} | ||
| 317 | mac.macmdiodr().read().md() | ||
| 318 | } | ||
| 319 | |||
| 320 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 321 | let mac = T::regs().ethernet_mac(); | ||
| 322 | |||
| 323 | mac.macmdiodr().write(|w| w.set_md(val)); | ||
| 324 | mac.macmdioar().modify(|w| { | ||
| 325 | w.set_pa(phy_addr); | ||
| 326 | w.set_rda(reg); | ||
| 327 | w.set_goc(0b01); // write | ||
| 328 | w.set_cr(self.clock_range); | ||
| 329 | w.set_mb(true); | ||
| 330 | }); | ||
| 331 | while mac.macmdioar().read().mb() {} | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { | 328 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { |
| 336 | fn drop(&mut self) { | 329 | fn drop(&mut self) { |
| 337 | let dma = T::regs().ethernet_dma(); | 330 | let dma = T::regs().ethernet_dma(); |
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 9fce78f95..7b7896d46 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -5,13 +5,16 @@ use core::marker::PhantomData; | |||
| 5 | use core::pin::Pin; | 5 | use core::pin::Pin; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 8 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use futures_util::FutureExt; | ||
| 10 | 11 | ||
| 11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; | 12 | use crate::gpio::{AnyPin, ExtiPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; |
| 12 | use crate::pac::exti::regs::Lines; | 13 | use crate::interrupt::Interrupt as InterruptEnum; |
| 14 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt as InterruptType}; | ||
| 13 | use crate::pac::EXTI; | 15 | use crate::pac::EXTI; |
| 14 | use crate::{interrupt, pac, peripherals, Peri}; | 16 | use crate::pac::exti::regs::Lines; |
| 17 | use crate::{Peri, pac}; | ||
| 15 | 18 | ||
| 16 | const EXTI_COUNT: usize = 16; | 19 | const EXTI_COUNT: usize = 16; |
| 17 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; | 20 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; |
| @@ -31,11 +34,11 @@ fn cpu_regs() -> pac::exti::Exti { | |||
| 31 | EXTI | 34 | EXTI |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] | 37 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 35 | fn exticr_regs() -> pac::syscfg::Syscfg { | 38 | fn exticr_regs() -> pac::syscfg::Syscfg { |
| 36 | pac::SYSCFG | 39 | pac::SYSCFG |
| 37 | } | 40 | } |
| 38 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 41 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 39 | fn exticr_regs() -> pac::exti::Exti { | 42 | fn exticr_regs() -> pac::exti::Exti { |
| 40 | EXTI | 43 | EXTI |
| 41 | } | 44 | } |
| @@ -45,9 +48,9 @@ fn exticr_regs() -> pac::afio::Afio { | |||
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | unsafe fn on_irq() { | 50 | unsafe fn on_irq() { |
| 48 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 51 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 49 | let bits = EXTI.pr(0).read().0; | 52 | let bits = EXTI.pr(0).read().0; |
| 50 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 53 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 51 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; | 54 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; |
| 52 | 55 | ||
| 53 | // We don't handle or change any EXTI lines above 16. | 56 | // We don't handle or change any EXTI lines above 16. |
| @@ -62,16 +65,16 @@ unsafe fn on_irq() { | |||
| 62 | } | 65 | } |
| 63 | 66 | ||
| 64 | // Clear pending | 67 | // Clear pending |
| 65 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 68 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 66 | EXTI.pr(0).write_value(Lines(bits)); | 69 | EXTI.pr(0).write_value(Lines(bits)); |
| 67 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 70 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 68 | { | 71 | { |
| 69 | EXTI.rpr(0).write_value(Lines(bits)); | 72 | EXTI.rpr(0).write_value(Lines(bits)); |
| 70 | EXTI.fpr(0).write_value(Lines(bits)); | 73 | EXTI.fpr(0).write_value(Lines(bits)); |
| 71 | } | 74 | } |
| 72 | 75 | ||
| 73 | #[cfg(feature = "low-power")] | 76 | #[cfg(feature = "low-power")] |
| 74 | crate::low_power::on_wakeup_irq(); | 77 | crate::low_power::Executor::on_wakeup_irq(); |
| 75 | } | 78 | } |
| 76 | 79 | ||
| 77 | struct BitIter(u32); | 80 | struct BitIter(u32); |
| @@ -105,10 +108,17 @@ impl<'d> Unpin for ExtiInput<'d> {} | |||
| 105 | 108 | ||
| 106 | impl<'d> ExtiInput<'d> { | 109 | impl<'d> ExtiInput<'d> { |
| 107 | /// Create an EXTI input. | 110 | /// Create an EXTI input. |
| 108 | pub fn new<T: GpioPin>(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull) -> Self { | 111 | /// |
| 109 | // Needed if using AnyPin+AnyChannel. | 112 | /// The Binding must bind the Channel's IRQ to [InterruptHandler]. |
| 110 | assert_eq!(pin.pin(), ch.number()); | 113 | pub fn new<T: ExtiPin + GpioPin>( |
| 111 | 114 | pin: Peri<'d, T>, | |
| 115 | _ch: Peri<'d, T::ExtiChannel>, | ||
| 116 | pull: Pull, | ||
| 117 | _irq: impl Binding< | ||
| 118 | <<T as ExtiPin>::ExtiChannel as Channel>::IRQ, | ||
| 119 | InterruptHandler<<<T as ExtiPin>::ExtiChannel as Channel>::IRQ>, | ||
| 120 | >, | ||
| 121 | ) -> Self { | ||
| 112 | Self { | 122 | Self { |
| 113 | pin: Input::new(pin, pull), | 123 | pin: Input::new(pin, pull), |
| 114 | } | 124 | } |
| @@ -133,7 +143,7 @@ impl<'d> ExtiInput<'d> { | |||
| 133 | /// | 143 | /// |
| 134 | /// This returns immediately if the pin is already high. | 144 | /// This returns immediately if the pin is already high. |
| 135 | pub async fn wait_for_high(&mut self) { | 145 | pub async fn wait_for_high(&mut self) { |
| 136 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); | 146 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true); |
| 137 | if self.is_high() { | 147 | if self.is_high() { |
| 138 | return; | 148 | return; |
| 139 | } | 149 | } |
| @@ -144,7 +154,7 @@ impl<'d> ExtiInput<'d> { | |||
| 144 | /// | 154 | /// |
| 145 | /// This returns immediately if the pin is already low. | 155 | /// This returns immediately if the pin is already low. |
| 146 | pub async fn wait_for_low(&mut self) { | 156 | pub async fn wait_for_low(&mut self) { |
| 147 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); | 157 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true); |
| 148 | if self.is_low() { | 158 | if self.is_low() { |
| 149 | return; | 159 | return; |
| 150 | } | 160 | } |
| @@ -155,19 +165,40 @@ impl<'d> ExtiInput<'d> { | |||
| 155 | /// | 165 | /// |
| 156 | /// If the pin is already high, it will wait for it to go low then back high. | 166 | /// If the pin is already high, it will wait for it to go low then back high. |
| 157 | pub async fn wait_for_rising_edge(&mut self) { | 167 | pub async fn wait_for_rising_edge(&mut self) { |
| 158 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await | 168 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true).await |
| 169 | } | ||
| 170 | |||
| 171 | /// Asynchronously wait until the pin sees a rising edge. | ||
| 172 | /// | ||
| 173 | /// If the pin is already high, it will wait for it to go low then back high. | ||
| 174 | pub fn poll_for_rising_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 175 | let _ = | ||
| 176 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, false).poll_unpin(cx); | ||
| 159 | } | 177 | } |
| 160 | 178 | ||
| 161 | /// Asynchronously wait until the pin sees a falling edge. | 179 | /// Asynchronously wait until the pin sees a falling edge. |
| 162 | /// | 180 | /// |
| 163 | /// If the pin is already low, it will wait for it to go high then back low. | 181 | /// If the pin is already low, it will wait for it to go high then back low. |
| 164 | pub async fn wait_for_falling_edge(&mut self) { | 182 | pub async fn wait_for_falling_edge(&mut self) { |
| 165 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await | 183 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true).await |
| 184 | } | ||
| 185 | |||
| 186 | /// Asynchronously wait until the pin sees a falling edge. | ||
| 187 | /// | ||
| 188 | /// If the pin is already low, it will wait for it to go high then back low. | ||
| 189 | pub fn poll_for_falling_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 190 | let _ = | ||
| 191 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, false).poll_unpin(cx); | ||
| 166 | } | 192 | } |
| 167 | 193 | ||
| 168 | /// Asynchronously wait until the pin sees any edge (either rising or falling). | 194 | /// Asynchronously wait until the pin sees any edge (either rising or falling). |
| 169 | pub async fn wait_for_any_edge(&mut self) { | 195 | pub async fn wait_for_any_edge(&mut self) { |
| 170 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await | 196 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, true).await |
| 197 | } | ||
| 198 | |||
| 199 | /// Asynchronously wait until the pin sees any edge (either rising or falling). | ||
| 200 | pub fn poll_for_any_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 201 | let _ = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, false).poll_unpin(cx); | ||
| 171 | } | 202 | } |
| 172 | } | 203 | } |
| 173 | 204 | ||
| @@ -226,12 +257,13 @@ impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> { | |||
| 226 | 257 | ||
| 227 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 258 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 228 | struct ExtiInputFuture<'a> { | 259 | struct ExtiInputFuture<'a> { |
| 229 | pin: u8, | 260 | pin: PinNumber, |
| 261 | drop: bool, | ||
| 230 | phantom: PhantomData<&'a mut AnyPin>, | 262 | phantom: PhantomData<&'a mut AnyPin>, |
| 231 | } | 263 | } |
| 232 | 264 | ||
| 233 | impl<'a> ExtiInputFuture<'a> { | 265 | impl<'a> ExtiInputFuture<'a> { |
| 234 | fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { | 266 | fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool, drop: bool) -> Self { |
| 235 | critical_section::with(|_| { | 267 | critical_section::with(|_| { |
| 236 | let pin = pin as usize; | 268 | let pin = pin as usize; |
| 237 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); | 269 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); |
| @@ -239,9 +271,9 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 239 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); | 271 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); |
| 240 | 272 | ||
| 241 | // clear pending bit | 273 | // clear pending bit |
| 242 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 274 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 243 | EXTI.pr(0).write(|w| w.set_line(pin, true)); | 275 | EXTI.pr(0).write(|w| w.set_line(pin, true)); |
| 244 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 276 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 245 | { | 277 | { |
| 246 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); | 278 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); |
| 247 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); | 279 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); |
| @@ -252,6 +284,7 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 252 | 284 | ||
| 253 | Self { | 285 | Self { |
| 254 | pin, | 286 | pin, |
| 287 | drop, | ||
| 255 | phantom: PhantomData, | 288 | phantom: PhantomData, |
| 256 | } | 289 | } |
| 257 | } | 290 | } |
| @@ -259,10 +292,12 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 259 | 292 | ||
| 260 | impl<'a> Drop for ExtiInputFuture<'a> { | 293 | impl<'a> Drop for ExtiInputFuture<'a> { |
| 261 | fn drop(&mut self) { | 294 | fn drop(&mut self) { |
| 262 | critical_section::with(|_| { | 295 | if self.drop { |
| 263 | let pin = self.pin as _; | 296 | critical_section::with(|_| { |
| 264 | cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); | 297 | let pin = self.pin as _; |
| 265 | }); | 298 | cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); |
| 299 | }); | ||
| 300 | } | ||
| 266 | } | 301 | } |
| 267 | } | 302 | } |
| 268 | 303 | ||
| @@ -302,7 +337,7 @@ macro_rules! foreach_exti_irq { | |||
| 302 | (EXTI15) => { $action!(EXTI15); }; | 337 | (EXTI15) => { $action!(EXTI15); }; |
| 303 | 338 | ||
| 304 | // plus the weird ones | 339 | // plus the weird ones |
| 305 | (EXTI0_1) => { $action!( EXTI0_1 ); }; | 340 | (EXTI0_1) => { $action!(EXTI0_1); }; |
| 306 | (EXTI15_10) => { $action!(EXTI15_10); }; | 341 | (EXTI15_10) => { $action!(EXTI15_10); }; |
| 307 | (EXTI15_4) => { $action!(EXTI15_4); }; | 342 | (EXTI15_4) => { $action!(EXTI15_4); }; |
| 308 | (EXTI1_0) => { $action!(EXTI1_0); }; | 343 | (EXTI1_0) => { $action!(EXTI1_0); }; |
| @@ -315,57 +350,67 @@ macro_rules! foreach_exti_irq { | |||
| 315 | }; | 350 | }; |
| 316 | } | 351 | } |
| 317 | 352 | ||
| 318 | macro_rules! impl_irq { | 353 | ///EXTI interrupt handler. All EXTI interrupt vectors should be bound to this handler. |
| 319 | ($e:ident) => { | 354 | /// |
| 320 | #[allow(non_snake_case)] | 355 | /// It is generic over the [Interrupt](InterruptType) rather |
| 321 | #[cfg(feature = "rt")] | 356 | /// than the [Channel] because it should not be bound multiple |
| 322 | #[interrupt] | 357 | /// times to the same vector on chips which multiplex multiple EXTI interrupts into one vector. |
| 323 | unsafe fn $e() { | 358 | // |
| 324 | on_irq() | 359 | // It technically doesn't need to be generic at all, except to satisfy the generic argument |
| 325 | } | 360 | // of [Handler]. All EXTI interrupts eventually land in the same on_irq() function. |
| 326 | }; | 361 | pub struct InterruptHandler<T: crate::interrupt::typelevel::Interrupt> { |
| 362 | _phantom: PhantomData<T>, | ||
| 327 | } | 363 | } |
| 328 | 364 | ||
| 329 | foreach_exti_irq!(impl_irq); | 365 | impl<T: InterruptType> Handler<T> for InterruptHandler<T> { |
| 366 | unsafe fn on_interrupt() { | ||
| 367 | on_irq() | ||
| 368 | } | ||
| 369 | } | ||
| 330 | 370 | ||
| 331 | trait SealedChannel {} | 371 | trait SealedChannel {} |
| 332 | 372 | ||
| 333 | /// EXTI channel trait. | 373 | /// EXTI channel trait. |
| 334 | #[allow(private_bounds)] | 374 | #[allow(private_bounds)] |
| 335 | pub trait Channel: PeripheralType + SealedChannel + Sized { | 375 | pub trait Channel: PeripheralType + SealedChannel + Sized { |
| 336 | /// Get the EXTI channel number. | 376 | /// EXTI channel number. |
| 337 | fn number(&self) -> u8; | 377 | fn number(&self) -> PinNumber; |
| 378 | /// [Enum-level Interrupt](InterruptEnum), which may be the same for multiple channels. | ||
| 379 | fn irq(&self) -> InterruptEnum; | ||
| 380 | /// [Type-level Interrupt](InterruptType), which may be the same for multiple channels. | ||
| 381 | type IRQ: InterruptType; | ||
| 338 | } | 382 | } |
| 339 | 383 | ||
| 340 | /// Type-erased EXTI channel. | 384 | //Doc isn't hidden in order to surface the explanation to users, even though it's completely inoperable, not just deprecated. |
| 385 | //Entire type along with doc can probably be removed after deprecation has appeared in a release once. | ||
| 386 | /// Deprecated type-erased EXTI channel. | ||
| 341 | /// | 387 | /// |
| 342 | /// This represents ownership over any EXTI channel, known at runtime. | 388 | /// Support for AnyChannel was removed in order to support manually bindable EXTI interrupts via bind_interrupts; [ExtiInput::new()] |
| 389 | /// must know the required IRQ at compile time, and therefore cannot support type-erased channels. | ||
| 390 | #[deprecated = "type-erased EXTI channels are no longer supported, in order to support manually bindable EXTI interrupts (more info: https://github.com/embassy-rs/embassy/pull/4922)"] | ||
| 343 | pub struct AnyChannel { | 391 | pub struct AnyChannel { |
| 344 | number: u8, | 392 | #[allow(unused)] |
| 345 | } | 393 | number: PinNumber, |
| 346 | |||
| 347 | impl_peripheral!(AnyChannel); | ||
| 348 | impl SealedChannel for AnyChannel {} | ||
| 349 | impl Channel for AnyChannel { | ||
| 350 | fn number(&self) -> u8 { | ||
| 351 | self.number | ||
| 352 | } | ||
| 353 | } | 394 | } |
| 354 | 395 | ||
| 355 | macro_rules! impl_exti { | 396 | macro_rules! impl_exti { |
| 356 | ($type:ident, $number:expr) => { | 397 | ($type:ident, $number:expr) => { |
| 357 | impl SealedChannel for peripherals::$type {} | 398 | impl SealedChannel for crate::peripherals::$type {} |
| 358 | impl Channel for peripherals::$type { | 399 | impl Channel for crate::peripherals::$type { |
| 359 | fn number(&self) -> u8 { | 400 | fn number(&self) -> PinNumber { |
| 360 | $number | 401 | $number |
| 361 | } | 402 | } |
| 403 | fn irq(&self) -> InterruptEnum { | ||
| 404 | crate::_generated::peripheral_interrupts::EXTI::$type::IRQ | ||
| 405 | } | ||
| 406 | type IRQ = crate::_generated::peripheral_interrupts::EXTI::$type; | ||
| 362 | } | 407 | } |
| 363 | 408 | ||
| 364 | impl From<peripherals::$type> for AnyChannel { | 409 | //Still here to surface deprecation messages to the user - remove when removing AnyChannel |
| 365 | fn from(val: peripherals::$type) -> Self { | 410 | #[allow(deprecated)] |
| 366 | Self { | 411 | impl From<crate::peripherals::$type> for AnyChannel { |
| 367 | number: val.number() as u8, | 412 | fn from(_val: crate::peripherals::$type) -> Self { |
| 368 | } | 413 | Self { number: $number } |
| 369 | } | 414 | } |
| 370 | } | 415 | } |
| 371 | }; | 416 | }; |
diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 006dcddeb..a131217b7 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs | |||
| @@ -1,17 +1,17 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::drop::OnDrop; | 4 | use embassy_hal_internal::drop::OnDrop; |
| 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 6 | use embassy_sync::mutex::Mutex; | 6 | use embassy_sync::mutex::Mutex; |
| 7 | 7 | ||
| 8 | use super::{ | 8 | use super::{ |
| 9 | blocking_read, ensure_sector_aligned, family, get_flash_regions, get_sector, Async, Error, Flash, FlashLayout, | 9 | Async, Error, FLASH_BASE, FLASH_SIZE, Flash, FlashLayout, WRITE_SIZE, blocking_read, ensure_sector_aligned, family, |
| 10 | FLASH_BASE, FLASH_SIZE, WRITE_SIZE, | 10 | get_flash_regions, get_sector, |
| 11 | }; | 11 | }; |
| 12 | use crate::interrupt::InterruptExt; | 12 | use crate::interrupt::InterruptExt; |
| 13 | use crate::peripherals::FLASH; | 13 | use crate::peripherals::FLASH; |
| 14 | use crate::{interrupt, Peri}; | 14 | use crate::{Peri, interrupt}; |
| 15 | 15 | ||
| 16 | pub(super) static REGION_ACCESS: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(()); | 16 | pub(super) static REGION_ACCESS: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(()); |
| 17 | 17 | ||
diff --git a/embassy-stm32/src/flash/c.rs b/embassy-stm32/src/flash/c.rs new file mode 100644 index 000000000..0ad1002b0 --- /dev/null +++ b/embassy-stm32/src/flash/c.rs | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | use core::ptr::write_volatile; | ||
| 2 | use core::sync::atomic::{Ordering, fence}; | ||
| 3 | |||
| 4 | use cortex_m::interrupt; | ||
| 5 | |||
| 6 | use super::{FlashSector, WRITE_SIZE}; | ||
| 7 | use crate::flash::Error; | ||
| 8 | use crate::pac; | ||
| 9 | |||
| 10 | pub(crate) unsafe fn lock() { | ||
| 11 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | ||
| 12 | } | ||
| 13 | pub(crate) unsafe fn unlock() { | ||
| 14 | // Wait, while the memory interface is busy. | ||
| 15 | wait_busy(); | ||
| 16 | |||
| 17 | // Unlock flash | ||
| 18 | if pac::FLASH.cr().read().lock() { | ||
| 19 | pac::FLASH.keyr().write_value(0x4567_0123); | ||
| 20 | pac::FLASH.keyr().write_value(0xCDEF_89AB); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | pub(crate) unsafe fn enable_blocking_write() { | ||
| 25 | assert_eq!(0, WRITE_SIZE % 4); | ||
| 26 | pac::FLASH.cr().write(|w| w.set_pg(true)); | ||
| 27 | } | ||
| 28 | |||
| 29 | pub(crate) unsafe fn disable_blocking_write() { | ||
| 30 | pac::FLASH.cr().write(|w| w.set_pg(false)); | ||
| 31 | } | ||
| 32 | |||
| 33 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 34 | let mut address = start_address; | ||
| 35 | for val in buf.chunks(4) { | ||
| 36 | write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); | ||
| 37 | address += val.len() as u32; | ||
| 38 | |||
| 39 | // prevents parallelism errors | ||
| 40 | fence(Ordering::SeqCst); | ||
| 41 | } | ||
| 42 | |||
| 43 | wait_ready_blocking() | ||
| 44 | } | ||
| 45 | |||
| 46 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | ||
| 47 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | ||
| 48 | |||
| 49 | #[cfg(feature = "defmt")] | ||
| 50 | defmt::trace!( | ||
| 51 | "STM32C0 Erase: addr=0x{:08x}, idx={}, erase_size={}", | ||
| 52 | sector.start, | ||
| 53 | idx, | ||
| 54 | super::BANK1_REGION.erase_size | ||
| 55 | ); | ||
| 56 | |||
| 57 | wait_busy(); | ||
| 58 | clear_all_err(); | ||
| 59 | |||
| 60 | // Explicitly unlock before erase | ||
| 61 | unlock(); | ||
| 62 | |||
| 63 | interrupt::free(|_| { | ||
| 64 | #[cfg(feature = "defmt")] | ||
| 65 | { | ||
| 66 | let cr_before = pac::FLASH.cr().read(); | ||
| 67 | defmt::trace!("FLASH_CR before: 0x{:08x}", cr_before.0); | ||
| 68 | } | ||
| 69 | |||
| 70 | pac::FLASH.cr().modify(|w| { | ||
| 71 | w.set_per(true); | ||
| 72 | w.set_pnb(idx as u8); | ||
| 73 | w.set_strt(true); | ||
| 74 | }); | ||
| 75 | |||
| 76 | #[cfg(feature = "defmt")] | ||
| 77 | { | ||
| 78 | let cr_after = pac::FLASH.cr().read(); | ||
| 79 | defmt::trace!( | ||
| 80 | "FLASH_CR after: 0x{:08x}, PER={}, PNB={}, STRT={}", | ||
| 81 | cr_after.0, | ||
| 82 | cr_after.per(), | ||
| 83 | cr_after.pnb(), | ||
| 84 | cr_after.strt() | ||
| 85 | ); | ||
| 86 | } | ||
| 87 | }); | ||
| 88 | |||
| 89 | let ret: Result<(), Error> = wait_ready_blocking(); | ||
| 90 | |||
| 91 | // Clear erase bit | ||
| 92 | pac::FLASH.cr().modify(|w| w.set_per(false)); | ||
| 93 | |||
| 94 | // Explicitly lock after erase | ||
| 95 | lock(); | ||
| 96 | |||
| 97 | // Extra wait to ensure operation completes | ||
| 98 | wait_busy(); | ||
| 99 | |||
| 100 | ret | ||
| 101 | } | ||
| 102 | |||
| 103 | pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { | ||
| 104 | while pac::FLASH.sr().read().bsy() {} | ||
| 105 | |||
| 106 | let sr = pac::FLASH.sr().read(); | ||
| 107 | |||
| 108 | if sr.progerr() { | ||
| 109 | return Err(Error::Prog); | ||
| 110 | } | ||
| 111 | |||
| 112 | if sr.wrperr() { | ||
| 113 | return Err(Error::Protected); | ||
| 114 | } | ||
| 115 | |||
| 116 | if sr.pgaerr() { | ||
| 117 | return Err(Error::Unaligned); | ||
| 118 | } | ||
| 119 | |||
| 120 | Ok(()) | ||
| 121 | } | ||
| 122 | |||
| 123 | pub(crate) unsafe fn clear_all_err() { | ||
| 124 | // read and write back the same value. | ||
| 125 | // This clears all "write 1 to clear" bits. | ||
| 126 | pac::FLASH.sr().modify(|_| {}); | ||
| 127 | } | ||
| 128 | |||
| 129 | fn wait_busy() { | ||
| 130 | while pac::FLASH.sr().read().bsy() {} | ||
| 131 | } | ||
diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 10023e637..60d00e766 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs | |||
| @@ -1,14 +1,14 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::drop::OnDrop; | 4 | use embassy_hal_internal::drop::OnDrop; |
| 5 | 5 | ||
| 6 | use super::{ | 6 | use super::{ |
| 7 | family, get_flash_regions, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, | 7 | Async, Blocking, Error, FLASH_SIZE, FlashBank, FlashLayout, FlashRegion, FlashSector, MAX_ERASE_SIZE, READ_SIZE, |
| 8 | MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, | 8 | WRITE_SIZE, family, get_flash_regions, |
| 9 | }; | 9 | }; |
| 10 | use crate::Peri; | ||
| 11 | use crate::_generated::FLASH_BASE; | 10 | use crate::_generated::FLASH_BASE; |
| 11 | use crate::Peri; | ||
| 12 | use crate::peripherals::FLASH; | 12 | use crate::peripherals::FLASH; |
| 13 | 13 | ||
| 14 | /// Internal flash memory driver. | 14 | /// Internal flash memory driver. |
| @@ -102,7 +102,13 @@ pub(super) unsafe fn blocking_write( | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | let mut address = base + offset; | 104 | let mut address = base + offset; |
| 105 | trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); | 105 | trace!( |
| 106 | "Writing {} bytes at 0x{:x} (base=0x{:x}, offset=0x{:x})", | ||
| 107 | bytes.len(), | ||
| 108 | address, | ||
| 109 | base, | ||
| 110 | offset | ||
| 111 | ); | ||
| 106 | 112 | ||
| 107 | for chunk in bytes.chunks(WRITE_SIZE) { | 113 | for chunk in bytes.chunks(WRITE_SIZE) { |
| 108 | write_chunk(address, chunk)?; | 114 | write_chunk(address, chunk)?; |
diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs index cc3529eb9..39c497e3f 100644 --- a/embassy-stm32/src/flash/eeprom.rs +++ b/embassy-stm32/src/flash/eeprom.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use embassy_hal_internal::drop::OnDrop; | 1 | use embassy_hal_internal::drop::OnDrop; |
| 2 | 2 | ||
| 3 | use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE}; | 3 | use super::{Blocking, EEPROM_BASE, EEPROM_SIZE, Error, Flash, family}; |
| 4 | 4 | ||
| 5 | #[cfg(eeprom)] | 5 | #[cfg(eeprom)] |
| 6 | impl<'d> Flash<'d, Blocking> { | 6 | impl<'d> Flash<'d, Blocking> { |
diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 3f9dbe945..5c01fce9c 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
diff --git a/embassy-stm32/src/flash/f1f3.rs b/embassy-stm32/src/flash/f1f3.rs index bf9ad2893..9e469ffbc 100644 --- a/embassy-stm32/src/flash/f1f3.rs +++ b/embassy-stm32/src/flash/f1f3.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
diff --git a/embassy-stm32/src/flash/f2.rs b/embassy-stm32/src/flash/f2.rs index 67e380619..b48ab3b76 100644 --- a/embassy-stm32/src/flash/f2.rs +++ b/embassy-stm32/src/flash/f2.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, AtomicBool, Ordering}; | 2 | use core::sync::atomic::{AtomicBool, Ordering, fence}; |
| 3 | 3 | ||
| 4 | use pac::flash::regs::Sr; | 4 | use pac::flash::regs::Sr; |
| 5 | 5 | ||
| 6 | use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; | 6 | use super::{FlashBank, FlashSector, WRITE_SIZE, get_flash_regions}; |
| 7 | use crate::flash::Error; | 7 | use crate::flash::Error; |
| 8 | use crate::pac; | 8 | use crate::pac; |
| 9 | 9 | ||
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 62e0492b5..9c5051492 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, AtomicBool, Ordering}; | 2 | use core::sync::atomic::{AtomicBool, Ordering, fence}; |
| 3 | 3 | ||
| 4 | use embassy_sync::waitqueue::AtomicWaker; | 4 | use embassy_sync::waitqueue::AtomicWaker; |
| 5 | use pac::flash::regs::Sr; | 5 | use pac::flash::regs::Sr; |
| 6 | 6 | ||
| 7 | use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; | 7 | use super::{FlashBank, FlashSector, WRITE_SIZE, get_flash_regions}; |
| 8 | use crate::_generated::FLASH_SIZE; | 8 | use crate::_generated::FLASH_SIZE; |
| 9 | use crate::flash::Error; | 9 | use crate::flash::Error; |
| 10 | use crate::pac; | 10 | use crate::pac; |
| @@ -246,7 +246,9 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { | |||
| 246 | feature = "stm32f439zi", | 246 | feature = "stm32f439zi", |
| 247 | ))] | 247 | ))] |
| 248 | if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { | 248 | if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { |
| 249 | panic!("Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11"); | 249 | panic!( |
| 250 | "Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11" | ||
| 251 | ); | ||
| 250 | } | 252 | } |
| 251 | 253 | ||
| 252 | #[cfg(any( | 254 | #[cfg(any( |
| @@ -270,14 +272,16 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { | |||
| 270 | feature = "stm32f439zg", | 272 | feature = "stm32f439zg", |
| 271 | ))] | 273 | ))] |
| 272 | if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { | 274 | if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { |
| 273 | panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11"); | 275 | panic!( |
| 276 | "Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11" | ||
| 277 | ); | ||
| 274 | } | 278 | } |
| 275 | } | 279 | } |
| 276 | 280 | ||
| 277 | #[allow(unused)] | 281 | #[allow(unused)] |
| 278 | fn pa12_is_output_pull_low() -> bool { | 282 | fn pa12_is_output_pull_low() -> bool { |
| 279 | use pac::gpio::vals; | ||
| 280 | use pac::GPIOA; | 283 | use pac::GPIOA; |
| 284 | use pac::gpio::vals; | ||
| 281 | const PIN: usize = 12; | 285 | const PIN: usize = 12; |
| 282 | GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT | 286 | GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT |
| 283 | && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULL_DOWN | 287 | && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULL_DOWN |
| @@ -287,7 +291,7 @@ fn pa12_is_output_pull_low() -> bool { | |||
| 287 | #[cfg(test)] | 291 | #[cfg(test)] |
| 288 | mod tests { | 292 | mod tests { |
| 289 | use super::*; | 293 | use super::*; |
| 290 | use crate::flash::{get_sector, FlashBank}; | 294 | use crate::flash::{FlashBank, get_sector}; |
| 291 | 295 | ||
| 292 | #[test] | 296 | #[test] |
| 293 | #[cfg(stm32f429)] | 297 | #[cfg(stm32f429)] |
| @@ -370,9 +374,13 @@ mod tests { | |||
| 370 | #[cfg(all(bank_setup_configurable))] | 374 | #[cfg(all(bank_setup_configurable))] |
| 371 | pub(crate) fn check_bank_setup() { | 375 | pub(crate) fn check_bank_setup() { |
| 372 | if cfg!(feature = "single-bank") && pac::FLASH.optcr().read().db1m() { | 376 | if cfg!(feature = "single-bank") && pac::FLASH.optcr().read().db1m() { |
| 373 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config"); | 377 | panic!( |
| 378 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config" | ||
| 379 | ); | ||
| 374 | } | 380 | } |
| 375 | if cfg!(feature = "dual-bank") && !pac::FLASH.optcr().read().db1m() { | 381 | if cfg!(feature = "dual-bank") && !pac::FLASH.optcr().read().db1m() { |
| 376 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config"); | 382 | panic!( |
| 383 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config" | ||
| 384 | ); | ||
| 377 | } | 385 | } |
| 378 | } | 386 | } |
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 0547c747a..09389c417 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| @@ -99,7 +99,7 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { | |||
| 99 | #[cfg(test)] | 99 | #[cfg(test)] |
| 100 | mod tests { | 100 | mod tests { |
| 101 | use super::*; | 101 | use super::*; |
| 102 | use crate::flash::{get_sector, FlashBank}; | 102 | use crate::flash::{FlashBank, get_sector}; |
| 103 | 103 | ||
| 104 | #[test] | 104 | #[test] |
| 105 | #[cfg(stm32f732)] | 105 | #[cfg(stm32f732)] |
| @@ -218,9 +218,13 @@ mod tests { | |||
| 218 | #[cfg(all(bank_setup_configurable))] | 218 | #[cfg(all(bank_setup_configurable))] |
| 219 | pub(crate) fn check_bank_setup() { | 219 | pub(crate) fn check_bank_setup() { |
| 220 | if cfg!(feature = "single-bank") && !pac::FLASH.optcr().read().n_dbank() { | 220 | if cfg!(feature = "single-bank") && !pac::FLASH.optcr().read().n_dbank() { |
| 221 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config"); | 221 | panic!( |
| 222 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 223 | ); | ||
| 222 | } | 224 | } |
| 223 | if cfg!(feature = "dual-bank") && pac::FLASH.optcr().read().n_dbank() { | 225 | if cfg!(feature = "dual-bank") && pac::FLASH.optcr().read().n_dbank() { |
| 224 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config"); | 226 | panic!( |
| 227 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config" | ||
| 228 | ); | ||
| 225 | } | 229 | } |
| 226 | } | 230 | } |
diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index bc1fd360c..d7ba2f571 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use cortex_m::interrupt; | 4 | use cortex_m::interrupt; |
| 5 | 5 | ||
| @@ -44,7 +44,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 46 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 47 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | ||
| 48 | wait_busy(); | 47 | wait_busy(); |
| 49 | clear_all_err(); | 48 | clear_all_err(); |
| 50 | 49 | ||
| @@ -54,9 +53,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 54 | #[cfg(any(flash_g0x0, flash_g0x1, flash_g4c3))] | 53 | #[cfg(any(flash_g0x0, flash_g0x1, flash_g4c3))] |
| 55 | w.set_bker(sector.bank == crate::flash::FlashBank::Bank2); | 54 | w.set_bker(sector.bank == crate::flash::FlashBank::Bank2); |
| 56 | #[cfg(flash_g0x0)] | 55 | #[cfg(flash_g0x0)] |
| 57 | w.set_pnb(idx as u16); | 56 | w.set_pnb(sector.index_in_bank as u16); |
| 58 | #[cfg(not(flash_g0x0))] | 57 | #[cfg(not(flash_g0x0))] |
| 59 | w.set_pnb(idx as u8); | 58 | w.set_pnb(sector.index_in_bank as u8); |
| 60 | w.set_strt(true); | 59 | w.set_strt(true); |
| 61 | }); | 60 | }); |
| 62 | }); | 61 | }); |
| @@ -105,19 +104,27 @@ fn wait_busy() { | |||
| 105 | #[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] | 104 | #[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] |
| 106 | pub(crate) fn check_bank_setup() { | 105 | pub(crate) fn check_bank_setup() { |
| 107 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { | 106 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { |
| 108 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); | 107 | panic!( |
| 108 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 109 | ); | ||
| 109 | } | 110 | } |
| 110 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { | 111 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { |
| 111 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); | 112 | panic!( |
| 113 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config" | ||
| 114 | ); | ||
| 112 | } | 115 | } |
| 113 | } | 116 | } |
| 114 | 117 | ||
| 115 | #[cfg(all(bank_setup_configurable, flash_g0x1))] | 118 | #[cfg(all(bank_setup_configurable, flash_g0x1))] |
| 116 | pub(crate) fn check_bank_setup() { | 119 | pub(crate) fn check_bank_setup() { |
| 117 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() { | 120 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() { |
| 118 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config"); | 121 | panic!( |
| 122 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 123 | ); | ||
| 119 | } | 124 | } |
| 120 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() { | 125 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() { |
| 121 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config"); | 126 | panic!( |
| 127 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config" | ||
| 128 | ); | ||
| 122 | } | 129 | } |
| 123 | } | 130 | } |
diff --git a/embassy-stm32/src/flash/h5.rs b/embassy-stm32/src/flash/h5.rs index fd9bfcc75..88f247879 100644 --- a/embassy-stm32/src/flash/h5.rs +++ b/embassy-stm32/src/flash/h5.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index f8e210556..91d5da4d6 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /// STM32H50 series flash impl. See RM0492 | 1 | /// STM32H50 series flash impl. See RM0492 |
| 2 | use core::{ | 2 | use core::{ |
| 3 | ptr::write_volatile, | 3 | ptr::write_volatile, |
| 4 | sync::atomic::{fence, Ordering}, | 4 | sync::atomic::{Ordering, fence}, |
| 5 | }; | 5 | }; |
| 6 | 6 | ||
| 7 | use cortex_m::interrupt; | 7 | use cortex_m::interrupt; |
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index f1d84101c..b342f4a83 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs | |||
| @@ -1,10 +1,31 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; | 4 | use embassy_sync::waitqueue::AtomicWaker; |
| 5 | use pac::flash::regs::Sr; | ||
| 6 | |||
| 7 | use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; | ||
| 5 | use crate::flash::Error; | 8 | use crate::flash::Error; |
| 6 | use crate::pac; | 9 | use crate::pac; |
| 7 | 10 | ||
| 11 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 12 | |||
| 13 | pub(crate) unsafe fn on_interrupt() { | ||
| 14 | // Clear IRQ flags | ||
| 15 | pac::FLASH.bank(0).ccr().write(|w| { | ||
| 16 | w.set_clr_eop(true); | ||
| 17 | w.set_clr_operr(true); | ||
| 18 | }); | ||
| 19 | if is_dual_bank() { | ||
| 20 | pac::FLASH.bank(1).ccr().write(|w| { | ||
| 21 | w.set_clr_eop(true); | ||
| 22 | w.set_clr_operr(true); | ||
| 23 | }); | ||
| 24 | } | ||
| 25 | |||
| 26 | WAKER.wake(); | ||
| 27 | } | ||
| 28 | |||
| 8 | const fn is_dual_bank() -> bool { | 29 | const fn is_dual_bank() -> bool { |
| 9 | FLASH_REGIONS.len() >= 2 | 30 | FLASH_REGIONS.len() >= 2 |
| 10 | } | 31 | } |
| @@ -29,12 +50,68 @@ pub(crate) unsafe fn unlock() { | |||
| 29 | } | 50 | } |
| 30 | } | 51 | } |
| 31 | 52 | ||
| 53 | pub(crate) unsafe fn enable_write() { | ||
| 54 | enable_blocking_write(); | ||
| 55 | } | ||
| 56 | |||
| 57 | pub(crate) unsafe fn disable_write() { | ||
| 58 | disable_blocking_write(); | ||
| 59 | } | ||
| 60 | |||
| 32 | pub(crate) unsafe fn enable_blocking_write() { | 61 | pub(crate) unsafe fn enable_blocking_write() { |
| 33 | assert_eq!(0, WRITE_SIZE % 4); | 62 | assert_eq!(0, WRITE_SIZE % 4); |
| 34 | } | 63 | } |
| 35 | 64 | ||
| 36 | pub(crate) unsafe fn disable_blocking_write() {} | 65 | pub(crate) unsafe fn disable_blocking_write() {} |
| 37 | 66 | ||
| 67 | pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 68 | // We cannot have the write setup sequence in begin_write as it depends on the address | ||
| 69 | let bank = if start_address < BANK1_REGION.end() { | ||
| 70 | pac::FLASH.bank(0) | ||
| 71 | } else { | ||
| 72 | pac::FLASH.bank(1) | ||
| 73 | }; | ||
| 74 | bank.cr().write(|w| { | ||
| 75 | w.set_pg(true); | ||
| 76 | #[cfg(flash_h7)] | ||
| 77 | w.set_psize(2); // 32 bits at once | ||
| 78 | w.set_eopie(true); | ||
| 79 | w.set_operrie(true); | ||
| 80 | }); | ||
| 81 | cortex_m::asm::isb(); | ||
| 82 | cortex_m::asm::dsb(); | ||
| 83 | fence(Ordering::SeqCst); | ||
| 84 | |||
| 85 | let mut res = None; | ||
| 86 | let mut address = start_address; | ||
| 87 | for val in buf.chunks(4) { | ||
| 88 | write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); | ||
| 89 | address += val.len() as u32; | ||
| 90 | |||
| 91 | res = Some(wait_ready(bank).await); | ||
| 92 | bank.sr().modify(|w| { | ||
| 93 | if w.eop() { | ||
| 94 | w.set_eop(true); | ||
| 95 | } | ||
| 96 | }); | ||
| 97 | if unwrap!(res).is_err() { | ||
| 98 | break; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | cortex_m::asm::isb(); | ||
| 103 | cortex_m::asm::dsb(); | ||
| 104 | fence(Ordering::SeqCst); | ||
| 105 | |||
| 106 | bank.cr().write(|w| { | ||
| 107 | w.set_pg(false); | ||
| 108 | w.set_eopie(false); | ||
| 109 | w.set_operrie(false); | ||
| 110 | }); | ||
| 111 | |||
| 112 | unwrap!(res) | ||
| 113 | } | ||
| 114 | |||
| 38 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | 115 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { |
| 39 | // We cannot have the write setup sequence in begin_write as it depends on the address | 116 | // We cannot have the write setup sequence in begin_write as it depends on the address |
| 40 | let bank = if start_address < BANK1_REGION.end() { | 117 | let bank = if start_address < BANK1_REGION.end() { |
| @@ -77,6 +154,36 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 77 | unwrap!(res) | 154 | unwrap!(res) |
| 78 | } | 155 | } |
| 79 | 156 | ||
| 157 | pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { | ||
| 158 | let bank = pac::FLASH.bank(sector.bank as usize); | ||
| 159 | bank.cr().modify(|w| { | ||
| 160 | w.set_ser(true); | ||
| 161 | #[cfg(flash_h7)] | ||
| 162 | w.set_snb(sector.index_in_bank); | ||
| 163 | #[cfg(flash_h7ab)] | ||
| 164 | w.set_ssn(sector.index_in_bank); | ||
| 165 | w.set_eopie(true); | ||
| 166 | w.set_operrie(true); | ||
| 167 | }); | ||
| 168 | |||
| 169 | bank.cr().modify(|w| { | ||
| 170 | w.set_start(true); | ||
| 171 | }); | ||
| 172 | |||
| 173 | cortex_m::asm::isb(); | ||
| 174 | cortex_m::asm::dsb(); | ||
| 175 | fence(Ordering::SeqCst); | ||
| 176 | |||
| 177 | let ret: Result<(), Error> = wait_ready(bank).await; | ||
| 178 | bank.cr().modify(|w| { | ||
| 179 | w.set_ser(false); | ||
| 180 | w.set_eopie(false); | ||
| 181 | w.set_operrie(false); | ||
| 182 | }); | ||
| 183 | bank_clear_all_err(bank); | ||
| 184 | ret | ||
| 185 | } | ||
| 186 | |||
| 80 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 187 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 81 | let bank = pac::FLASH.bank(sector.bank as usize); | 188 | let bank = pac::FLASH.bank(sector.bank as usize); |
| 82 | bank.cr().modify(|w| { | 189 | bank.cr().modify(|w| { |
| @@ -112,46 +219,59 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { | |||
| 112 | bank.sr().modify(|_| {}); | 219 | bank.sr().modify(|_| {}); |
| 113 | } | 220 | } |
| 114 | 221 | ||
| 222 | async fn wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { | ||
| 223 | use core::future::poll_fn; | ||
| 224 | use core::task::Poll; | ||
| 225 | |||
| 226 | poll_fn(|cx| { | ||
| 227 | WAKER.register(cx.waker()); | ||
| 228 | |||
| 229 | let sr = bank.sr().read(); | ||
| 230 | if !sr.bsy() && !sr.qw() { | ||
| 231 | Poll::Ready(get_result(sr)) | ||
| 232 | } else { | ||
| 233 | return Poll::Pending; | ||
| 234 | } | ||
| 235 | }) | ||
| 236 | .await | ||
| 237 | } | ||
| 238 | |||
| 115 | unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { | 239 | unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { |
| 116 | loop { | 240 | loop { |
| 117 | let sr = bank.sr().read(); | 241 | let sr = bank.sr().read(); |
| 118 | 242 | ||
| 119 | if !sr.bsy() && !sr.qw() { | 243 | if !sr.bsy() && !sr.qw() { |
| 120 | if sr.wrperr() { | 244 | return get_result(sr); |
| 121 | return Err(Error::Protected); | ||
| 122 | } | ||
| 123 | if sr.pgserr() { | ||
| 124 | error!("pgserr"); | ||
| 125 | return Err(Error::Seq); | ||
| 126 | } | ||
| 127 | if sr.incerr() { | ||
| 128 | // writing to a different address when programming 256 bit word was not finished | ||
| 129 | error!("incerr"); | ||
| 130 | return Err(Error::Seq); | ||
| 131 | } | ||
| 132 | if sr.crcrderr() { | ||
| 133 | error!("crcrderr"); | ||
| 134 | return Err(Error::Seq); | ||
| 135 | } | ||
| 136 | if sr.operr() { | ||
| 137 | return Err(Error::Prog); | ||
| 138 | } | ||
| 139 | if sr.sneccerr1() { | ||
| 140 | // single ECC error | ||
| 141 | return Err(Error::Prog); | ||
| 142 | } | ||
| 143 | if sr.dbeccerr() { | ||
| 144 | // double ECC error | ||
| 145 | return Err(Error::Prog); | ||
| 146 | } | ||
| 147 | if sr.rdperr() { | ||
| 148 | return Err(Error::Protected); | ||
| 149 | } | ||
| 150 | if sr.rdserr() { | ||
| 151 | return Err(Error::Protected); | ||
| 152 | } | ||
| 153 | |||
| 154 | return Ok(()); | ||
| 155 | } | 245 | } |
| 156 | } | 246 | } |
| 157 | } | 247 | } |
| 248 | |||
| 249 | fn get_result(sr: Sr) -> Result<(), Error> { | ||
| 250 | if sr.wrperr() { | ||
| 251 | Err(Error::Protected) | ||
| 252 | } else if sr.pgserr() { | ||
| 253 | error!("pgserr"); | ||
| 254 | Err(Error::Seq) | ||
| 255 | } else if sr.incerr() { | ||
| 256 | // writing to a different address when programming 256 bit word was not finished | ||
| 257 | error!("incerr"); | ||
| 258 | Err(Error::Seq) | ||
| 259 | } else if sr.crcrderr() { | ||
| 260 | error!("crcrderr"); | ||
| 261 | Err(Error::Seq) | ||
| 262 | } else if sr.operr() { | ||
| 263 | Err(Error::Prog) | ||
| 264 | } else if sr.sneccerr1() { | ||
| 265 | // single ECC error | ||
| 266 | Err(Error::Prog) | ||
| 267 | } else if sr.dbeccerr() { | ||
| 268 | // double ECC error | ||
| 269 | Err(Error::Prog) | ||
| 270 | } else if sr.rdperr() { | ||
| 271 | Err(Error::Protected) | ||
| 272 | } else if sr.rdserr() { | ||
| 273 | Err(Error::Protected) | ||
| 274 | } else { | ||
| 275 | Ok(()) | ||
| 276 | } | ||
| 277 | } | ||
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 1b82704ec..b3281f2d5 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashSector, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| @@ -96,14 +96,20 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 96 | #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] | 96 | #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] |
| 97 | { | 97 | { |
| 98 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | 98 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; |
| 99 | #[cfg(any(flash_l4, flash_l5))] | ||
| 100 | let pgn = super::BANK1_REGION.size as u32 / super::BANK1_REGION.erase_size as u32; | ||
| 99 | 101 | ||
| 100 | #[cfg(flash_l4)] | 102 | #[cfg(flash_l4)] |
| 101 | let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; | 103 | let (idx, bank) = if idx > (pgn - 1) { |
| 104 | (idx - pgn, true) | ||
| 105 | } else { | ||
| 106 | (idx, false) | ||
| 107 | }; | ||
| 102 | 108 | ||
| 103 | #[cfg(flash_l5)] | 109 | #[cfg(flash_l5)] |
| 104 | let (idx, bank) = if pac::FLASH.optr().read().dbank() { | 110 | let (idx, bank) = if pac::FLASH.optr().read().dbank() { |
| 105 | if idx > 255 { | 111 | if idx > (pgn - 1) { |
| 106 | (idx - 256, Some(true)) | 112 | (idx - pgn, Some(true)) |
| 107 | } else { | 113 | } else { |
| 108 | (idx, Some(false)) | 114 | (idx, Some(false)) |
| 109 | } | 115 | } |
| @@ -234,19 +240,27 @@ pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { | |||
| 234 | #[cfg(all(bank_setup_configurable, flash_l5))] | 240 | #[cfg(all(bank_setup_configurable, flash_l5))] |
| 235 | pub(crate) fn check_bank_setup() { | 241 | pub(crate) fn check_bank_setup() { |
| 236 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { | 242 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { |
| 237 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); | 243 | panic!( |
| 244 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 245 | ); | ||
| 238 | } | 246 | } |
| 239 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { | 247 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { |
| 240 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); | 248 | panic!( |
| 249 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config" | ||
| 250 | ); | ||
| 241 | } | 251 | } |
| 242 | } | 252 | } |
| 243 | 253 | ||
| 244 | #[cfg(all(bank_setup_configurable, flash_l4))] | 254 | #[cfg(all(bank_setup_configurable, flash_l4))] |
| 245 | pub(crate) fn check_bank_setup() { | 255 | pub(crate) fn check_bank_setup() { |
| 246 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dualbank() { | 256 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dualbank() { |
| 247 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config"); | 257 | panic!( |
| 258 | "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config" | ||
| 259 | ); | ||
| 248 | } | 260 | } |
| 249 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dualbank() { | 261 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dualbank() { |
| 250 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config"); | 262 | panic!( |
| 263 | "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config" | ||
| 264 | ); | ||
| 251 | } | 265 | } |
| 252 | } | 266 | } |
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 3e74d857a..6211a37b7 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs | |||
| @@ -1,14 +1,14 @@ | |||
| 1 | //! Flash memory (FLASH) | 1 | //! Flash memory (FLASH) |
| 2 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; | 2 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; |
| 3 | 3 | ||
| 4 | #[cfg(flash_f4)] | 4 | #[cfg(any(flash_f4, flash_h7, flash_h7ab))] |
| 5 | mod asynch; | 5 | mod asynch; |
| 6 | #[cfg(flash)] | 6 | #[cfg(flash)] |
| 7 | mod common; | 7 | mod common; |
| 8 | #[cfg(eeprom)] | 8 | #[cfg(eeprom)] |
| 9 | mod eeprom; | 9 | mod eeprom; |
| 10 | 10 | ||
| 11 | #[cfg(flash_f4)] | 11 | #[cfg(any(flash_f4, flash_h7, flash_h7ab))] |
| 12 | pub use asynch::InterruptHandler; | 12 | pub use asynch::InterruptHandler; |
| 13 | #[cfg(flash)] | 13 | #[cfg(flash)] |
| 14 | pub use common::*; | 14 | pub use common::*; |
| @@ -99,6 +99,7 @@ compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is | |||
| 99 | #[cfg_attr(flash_f4, path = "f4.rs")] | 99 | #[cfg_attr(flash_f4, path = "f4.rs")] |
| 100 | #[cfg_attr(flash_f7, path = "f7.rs")] | 100 | #[cfg_attr(flash_f7, path = "f7.rs")] |
| 101 | #[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] | 101 | #[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] |
| 102 | #[cfg_attr(flash_c0, path = "c.rs")] | ||
| 102 | #[cfg_attr(flash_h7, path = "h7.rs")] | 103 | #[cfg_attr(flash_h7, path = "h7.rs")] |
| 103 | #[cfg_attr(flash_h7ab, path = "h7.rs")] | 104 | #[cfg_attr(flash_h7ab, path = "h7.rs")] |
| 104 | #[cfg_attr(any(flash_u5, flash_wba), path = "u5.rs")] | 105 | #[cfg_attr(any(flash_u5, flash_wba), path = "u5.rs")] |
| @@ -108,7 +109,7 @@ compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is | |||
| 108 | #[cfg_attr( | 109 | #[cfg_attr( |
| 109 | not(any( | 110 | not(any( |
| 110 | flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, | 111 | flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, |
| 111 | flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, | 112 | flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_c0, flash_h7, flash_h7ab, flash_u5, |
| 112 | flash_wba, flash_h50, flash_u0, flash_h5, | 113 | flash_wba, flash_h50, flash_u0, flash_h5, |
| 113 | )), | 114 | )), |
| 114 | path = "other.rs" | 115 | path = "other.rs" |
diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs index 68d847eca..a64f6c492 100644 --- a/embassy-stm32/src/flash/u0.rs +++ b/embassy-stm32/src/flash/u0.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use cortex_m::interrupt; | 4 | use cortex_m::interrupt; |
| 5 | 5 | ||
diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 6c3d4b422..5f1f562c0 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use super::{FlashBank, FlashSector, WRITE_SIZE}; | 4 | use super::{FlashBank, FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index ff18a8bee..a7c6c90bb 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs | |||
| @@ -4,7 +4,7 @@ use core::marker::PhantomData; | |||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::gpio::{AfType, OutputType, Pull, Speed}; | 6 | use crate::gpio::{AfType, OutputType, Pull, Speed}; |
| 7 | use crate::{rcc, Peri}; | 7 | use crate::{Peri, rcc}; |
| 8 | 8 | ||
| 9 | /// FMC driver | 9 | /// FMC driver |
| 10 | pub struct Fmc<'d, T: Instance> { | 10 | pub struct Fmc<'d, T: Instance> { |
| @@ -236,6 +236,42 @@ impl<'d, T: Instance> Fmc<'d, T> { | |||
| 236 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | 236 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) |
| 237 | ] | 237 | ] |
| 238 | )); | 238 | )); |
| 239 | |||
| 240 | fmc_sdram_constructor!(sdram_a13bits_d16bits_4banks_bank1: ( | ||
| 241 | bank: stm32_fmc::SdramTargetBank::Bank1, | ||
| 242 | addr: [ | ||
| 243 | (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin) | ||
| 244 | ], | ||
| 245 | ba: [(ba0: BA0Pin), (ba1: BA1Pin)], | ||
| 246 | d: [ | ||
| 247 | (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), | ||
| 248 | (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) | ||
| 249 | ], | ||
| 250 | nbl: [ | ||
| 251 | (nbl0: NBL0Pin), (nbl1: NBL1Pin) | ||
| 252 | ], | ||
| 253 | ctrl: [ | ||
| 254 | (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | ||
| 255 | ] | ||
| 256 | )); | ||
| 257 | |||
| 258 | fmc_sdram_constructor!(sdram_a13bits_d16bits_4banks_bank2: ( | ||
| 259 | bank: stm32_fmc::SdramTargetBank::Bank2, | ||
| 260 | addr: [ | ||
| 261 | (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin) | ||
| 262 | ], | ||
| 263 | ba: [(ba0: BA0Pin), (ba1: BA1Pin)], | ||
| 264 | d: [ | ||
| 265 | (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), | ||
| 266 | (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) | ||
| 267 | ], | ||
| 268 | nbl: [ | ||
| 269 | (nbl0: NBL0Pin), (nbl1: NBL1Pin) | ||
| 270 | ], | ||
| 271 | ctrl: [ | ||
| 272 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | ||
| 273 | ] | ||
| 274 | )); | ||
| 239 | } | 275 | } |
| 240 | 276 | ||
| 241 | trait SealedInstance: crate::rcc::RccPeripheral { | 277 | trait SealedInstance: crate::rcc::RccPeripheral { |
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 5a8d23183..e7d4e9ad3 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use core::convert::Infallible; | 4 | use core::convert::Infallible; |
| 5 | 5 | ||
| 6 | use critical_section::CriticalSection; | 6 | use critical_section::CriticalSection; |
| 7 | use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; | 7 | use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; |
| 8 | 8 | ||
| 9 | use crate::pac::gpio::{self, vals}; | 9 | use crate::pac::gpio::{self, vals}; |
| 10 | use crate::peripherals; | 10 | use crate::peripherals; |
| @@ -592,7 +592,7 @@ impl AfType { | |||
| 592 | 592 | ||
| 593 | #[inline(never)] | 593 | #[inline(never)] |
| 594 | #[cfg(gpio_v1)] | 594 | #[cfg(gpio_v1)] |
| 595 | fn set_as_af(pin_port: u8, af_type: AfType) { | 595 | fn set_as_af(pin_port: PinNumber, af_type: AfType) { |
| 596 | let pin = unsafe { AnyPin::steal(pin_port) }; | 596 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 597 | let r = pin.block(); | 597 | let r = pin.block(); |
| 598 | let n = pin._pin() as usize; | 598 | let n = pin._pin() as usize; |
| @@ -649,7 +649,7 @@ impl AfType { | |||
| 649 | 649 | ||
| 650 | #[inline(never)] | 650 | #[inline(never)] |
| 651 | #[cfg(gpio_v2)] | 651 | #[cfg(gpio_v2)] |
| 652 | fn set_as_af(pin_port: u8, af_num: u8, af_type: AfType) { | 652 | fn set_as_af(pin_port: PinNumber, af_num: u8, af_type: AfType) { |
| 653 | let pin = unsafe { AnyPin::steal(pin_port) }; | 653 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 654 | let r = pin.block(); | 654 | let r = pin.block(); |
| 655 | let n = pin._pin() as usize; | 655 | let n = pin._pin() as usize; |
| @@ -663,7 +663,7 @@ fn set_as_af(pin_port: u8, af_num: u8, af_type: AfType) { | |||
| 663 | 663 | ||
| 664 | #[inline(never)] | 664 | #[inline(never)] |
| 665 | #[cfg(gpio_v2)] | 665 | #[cfg(gpio_v2)] |
| 666 | fn set_speed(pin_port: u8, speed: Speed) { | 666 | fn set_speed(pin_port: PinNumber, speed: Speed) { |
| 667 | let pin = unsafe { AnyPin::steal(pin_port) }; | 667 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 668 | let r = pin.block(); | 668 | let r = pin.block(); |
| 669 | let n = pin._pin() as usize; | 669 | let n = pin._pin() as usize; |
| @@ -672,7 +672,7 @@ fn set_speed(pin_port: u8, speed: Speed) { | |||
| 672 | } | 672 | } |
| 673 | 673 | ||
| 674 | #[inline(never)] | 674 | #[inline(never)] |
| 675 | fn set_as_analog(pin_port: u8) { | 675 | fn set_as_analog(pin_port: PinNumber) { |
| 676 | let pin = unsafe { AnyPin::steal(pin_port) }; | 676 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 677 | let r = pin.block(); | 677 | let r = pin.block(); |
| 678 | let n = pin._pin() as usize; | 678 | let n = pin._pin() as usize; |
| @@ -688,7 +688,7 @@ fn set_as_analog(pin_port: u8) { | |||
| 688 | } | 688 | } |
| 689 | 689 | ||
| 690 | #[inline(never)] | 690 | #[inline(never)] |
| 691 | fn get_pull(pin_port: u8) -> Pull { | 691 | fn get_pull(pin_port: PinNumber) -> Pull { |
| 692 | let pin = unsafe { AnyPin::steal(pin_port) }; | 692 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 693 | let r = pin.block(); | 693 | let r = pin.block(); |
| 694 | let n = pin._pin() as usize; | 694 | let n = pin._pin() as usize; |
| @@ -727,15 +727,15 @@ pub struct AfioRemapBool<const V: bool>; | |||
| 727 | pub struct AfioRemapNotApplicable; | 727 | pub struct AfioRemapNotApplicable; |
| 728 | 728 | ||
| 729 | pub(crate) trait SealedPin { | 729 | pub(crate) trait SealedPin { |
| 730 | fn pin_port(&self) -> u8; | 730 | fn pin_port(&self) -> PinNumber; |
| 731 | 731 | ||
| 732 | #[inline] | 732 | #[inline] |
| 733 | fn _pin(&self) -> u8 { | 733 | fn _pin(&self) -> PinNumber { |
| 734 | self.pin_port() % 16 | 734 | self.pin_port() % 16 |
| 735 | } | 735 | } |
| 736 | 736 | ||
| 737 | #[inline] | 737 | #[inline] |
| 738 | fn _port(&self) -> u8 { | 738 | fn _port(&self) -> PinNumber { |
| 739 | self.pin_port() / 16 | 739 | self.pin_port() / 16 |
| 740 | } | 740 | } |
| 741 | 741 | ||
| @@ -798,31 +798,49 @@ pub(crate) trait SealedPin { | |||
| 798 | } | 798 | } |
| 799 | } | 799 | } |
| 800 | 800 | ||
| 801 | /// GPIO pin trait. | 801 | /// GPIO pin number type. |
| 802 | /// | ||
| 803 | /// Some chips have a total number of ports that exceeds 8, a larger integer | ||
| 804 | /// is needed to hold the total pin number `(ports * number)`. | ||
| 805 | #[cfg(not(stm32n6))] | ||
| 806 | pub type PinNumber = u8; | ||
| 807 | |||
| 808 | /// GPIO pin number type. | ||
| 809 | /// | ||
| 810 | /// Some chips have a total number of ports that exceeds 8, a larger integer | ||
| 811 | /// is needed to hold the total pin number `(ports * number)`. | ||
| 812 | #[cfg(stm32n6)] | ||
| 813 | pub type PinNumber = u16; | ||
| 814 | |||
| 815 | /// Pin that can be used to configure an [ExtiInput](crate::exti::ExtiInput). This trait is lost when converting to [AnyPin]. | ||
| 816 | #[cfg(feature = "exti")] | ||
| 802 | #[allow(private_bounds)] | 817 | #[allow(private_bounds)] |
| 803 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | 818 | pub trait ExtiPin: PeripheralType + SealedPin { |
| 804 | /// EXTI channel assigned to this pin. | 819 | /// EXTI channel assigned to this pin. |
| 805 | /// | 820 | /// |
| 806 | /// For example, PC4 uses EXTI4. | 821 | /// For example, PC4 uses EXTI4. |
| 807 | #[cfg(feature = "exti")] | ||
| 808 | type ExtiChannel: crate::exti::Channel; | 822 | type ExtiChannel: crate::exti::Channel; |
| 823 | } | ||
| 809 | 824 | ||
| 825 | /// GPIO pin trait. | ||
| 826 | #[allow(private_bounds)] | ||
| 827 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | ||
| 810 | /// Number of the pin within the port (0..31) | 828 | /// Number of the pin within the port (0..31) |
| 811 | #[inline] | 829 | #[inline] |
| 812 | fn pin(&self) -> u8 { | 830 | fn pin(&self) -> PinNumber { |
| 813 | self._pin() | 831 | self._pin() |
| 814 | } | 832 | } |
| 815 | 833 | ||
| 816 | /// Port of the pin | 834 | /// Port of the pin |
| 817 | #[inline] | 835 | #[inline] |
| 818 | fn port(&self) -> u8 { | 836 | fn port(&self) -> PinNumber { |
| 819 | self._port() | 837 | self._port() |
| 820 | } | 838 | } |
| 821 | } | 839 | } |
| 822 | 840 | ||
| 823 | /// Type-erased GPIO pin | 841 | /// Type-erased GPIO pin. |
| 824 | pub struct AnyPin { | 842 | pub struct AnyPin { |
| 825 | pin_port: u8, | 843 | pin_port: PinNumber, |
| 826 | } | 844 | } |
| 827 | 845 | ||
| 828 | impl AnyPin { | 846 | impl AnyPin { |
| @@ -830,12 +848,12 @@ impl AnyPin { | |||
| 830 | /// | 848 | /// |
| 831 | /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc... | 849 | /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc... |
| 832 | #[inline] | 850 | #[inline] |
| 833 | pub unsafe fn steal(pin_port: u8) -> Peri<'static, Self> { | 851 | pub unsafe fn steal(pin_port: PinNumber) -> Peri<'static, Self> { |
| 834 | Peri::new_unchecked(Self { pin_port }) | 852 | Peri::new_unchecked(Self { pin_port }) |
| 835 | } | 853 | } |
| 836 | 854 | ||
| 837 | #[inline] | 855 | #[inline] |
| 838 | fn _port(&self) -> u8 { | 856 | fn _port(&self) -> PinNumber { |
| 839 | self.pin_port / 16 | 857 | self.pin_port / 16 |
| 840 | } | 858 | } |
| 841 | 859 | ||
| @@ -848,13 +866,10 @@ impl AnyPin { | |||
| 848 | } | 866 | } |
| 849 | 867 | ||
| 850 | impl_peripheral!(AnyPin); | 868 | impl_peripheral!(AnyPin); |
| 851 | impl Pin for AnyPin { | 869 | impl Pin for AnyPin {} |
| 852 | #[cfg(feature = "exti")] | ||
| 853 | type ExtiChannel = crate::exti::AnyChannel; | ||
| 854 | } | ||
| 855 | impl SealedPin for AnyPin { | 870 | impl SealedPin for AnyPin { |
| 856 | #[inline] | 871 | #[inline] |
| 857 | fn pin_port(&self) -> u8 { | 872 | fn pin_port(&self) -> PinNumber { |
| 858 | self.pin_port | 873 | self.pin_port |
| 859 | } | 874 | } |
| 860 | } | 875 | } |
| @@ -864,12 +879,14 @@ impl SealedPin for AnyPin { | |||
| 864 | foreach_pin!( | 879 | foreach_pin!( |
| 865 | ($pin_name:ident, $port_name:ident, $port_num:expr, $pin_num:expr, $exti_ch:ident) => { | 880 | ($pin_name:ident, $port_name:ident, $port_num:expr, $pin_num:expr, $exti_ch:ident) => { |
| 866 | impl Pin for peripherals::$pin_name { | 881 | impl Pin for peripherals::$pin_name { |
| 867 | #[cfg(feature = "exti")] | 882 | } |
| 883 | #[cfg(feature = "exti")] | ||
| 884 | impl ExtiPin for peripherals::$pin_name { | ||
| 868 | type ExtiChannel = peripherals::$exti_ch; | 885 | type ExtiChannel = peripherals::$exti_ch; |
| 869 | } | 886 | } |
| 870 | impl SealedPin for peripherals::$pin_name { | 887 | impl SealedPin for peripherals::$pin_name { |
| 871 | #[inline] | 888 | #[inline] |
| 872 | fn pin_port(&self) -> u8 { | 889 | fn pin_port(&self) -> PinNumber { |
| 873 | $port_num * 16 + $pin_num | 890 | $port_num * 16 + $pin_num |
| 874 | } | 891 | } |
| 875 | } | 892 | } |
diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 90c06c0d8..ba573267c 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs | |||
| @@ -19,7 +19,7 @@ use crate::interrupt::typelevel::Interrupt; | |||
| 19 | use crate::mode::Async; | 19 | use crate::mode::Async; |
| 20 | use crate::mode::{Blocking, Mode}; | 20 | use crate::mode::{Blocking, Mode}; |
| 21 | use crate::peripherals::HASH; | 21 | use crate::peripherals::HASH; |
| 22 | use crate::{interrupt, pac, peripherals, rcc, Peri}; | 22 | use crate::{Peri, interrupt, pac, peripherals, rcc}; |
| 23 | 23 | ||
| 24 | #[cfg(hash_v1)] | 24 | #[cfg(hash_v1)] |
| 25 | const NUM_CONTEXT_REGS: usize = 51; | 25 | const NUM_CONTEXT_REGS: usize = 51; |
| @@ -514,11 +514,7 @@ impl<'d, T: Instance> Hash<'d, T, Async> { | |||
| 514 | T::regs().imr().modify(|reg| reg.set_dcie(true)); | 514 | T::regs().imr().modify(|reg| reg.set_dcie(true)); |
| 515 | // Check for completion. | 515 | // Check for completion. |
| 516 | let bits = T::regs().sr().read(); | 516 | let bits = T::regs().sr().read(); |
| 517 | if bits.dcis() { | 517 | if bits.dcis() { Poll::Ready(()) } else { Poll::Pending } |
| 518 | Poll::Ready(()) | ||
| 519 | } else { | ||
| 520 | Poll::Pending | ||
| 521 | } | ||
| 522 | }) | 518 | }) |
| 523 | .await; | 519 | .await; |
| 524 | 520 | ||
diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 6fece5eb2..6c6807479 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs | |||
| @@ -10,6 +10,7 @@ pub use traits::Instance; | |||
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 11 | use crate::rcc; | 11 | use crate::rcc; |
| 12 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 13 | pub use crate::timer::simple_pwm::PwmPinConfig; | ||
| 13 | 14 | ||
| 14 | /// HRTIM burst controller instance. | 15 | /// HRTIM burst controller instance. |
| 15 | pub struct BurstController<T: Instance> { | 16 | pub struct BurstController<T: Instance> { |
| @@ -73,7 +74,7 @@ pub struct ComplementaryPwmPin<'d, T, C> { | |||
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | macro_rules! advanced_channel_impl { | 76 | macro_rules! advanced_channel_impl { |
| 76 | ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { | 77 | ($new_chx:ident, $new_chx_with_config:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { |
| 77 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { | 78 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { |
| 78 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] | 79 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] |
| 79 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { | 80 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { |
| @@ -86,6 +87,21 @@ macro_rules! advanced_channel_impl { | |||
| 86 | phantom: PhantomData, | 87 | phantom: PhantomData, |
| 87 | } | 88 | } |
| 88 | } | 89 | } |
| 90 | |||
| 91 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with a specific configuration.")] | ||
| 92 | pub fn $new_chx_with_config( | ||
| 93 | pin: Peri<'d, impl $pin_trait<T>>, | ||
| 94 | pin_config: PwmPinConfig, | ||
| 95 | ) -> Self { | ||
| 96 | critical_section::with(|_| { | ||
| 97 | pin.set_low(); | ||
| 98 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); | ||
| 99 | }); | ||
| 100 | PwmPin { | ||
| 101 | _pin: pin.into(), | ||
| 102 | phantom: PhantomData, | ||
| 103 | } | ||
| 104 | } | ||
| 89 | } | 105 | } |
| 90 | 106 | ||
| 91 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { | 107 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { |
| @@ -100,6 +116,21 @@ macro_rules! advanced_channel_impl { | |||
| 100 | phantom: PhantomData, | 116 | phantom: PhantomData, |
| 101 | } | 117 | } |
| 102 | } | 118 | } |
| 119 | |||
| 120 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance with a specific configuration.")] | ||
| 121 | pub fn $new_chx_with_config( | ||
| 122 | pin: Peri<'d, impl $complementary_pin_trait<T>>, | ||
| 123 | pin_config: PwmPinConfig, | ||
| 124 | ) -> Self { | ||
| 125 | critical_section::with(|_| { | ||
| 126 | pin.set_low(); | ||
| 127 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); | ||
| 128 | }); | ||
| 129 | ComplementaryPwmPin { | ||
| 130 | _pin: pin.into(), | ||
| 131 | phantom: PhantomData, | ||
| 132 | } | ||
| 133 | } | ||
| 103 | } | 134 | } |
| 104 | 135 | ||
| 105 | impl<T: Instance> SealedAdvancedChannel<T> for $channel<T> { | 136 | impl<T: Instance> SealedAdvancedChannel<T> for $channel<T> { |
| @@ -111,13 +142,55 @@ macro_rules! advanced_channel_impl { | |||
| 111 | }; | 142 | }; |
| 112 | } | 143 | } |
| 113 | 144 | ||
| 114 | advanced_channel_impl!(new_cha, ChA, 0, ChannelAPin, ChannelAComplementaryPin); | 145 | advanced_channel_impl!( |
| 115 | advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin); | 146 | new_cha, |
| 116 | advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin); | 147 | new_cha_with_config, |
| 117 | advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); | 148 | ChA, |
| 118 | advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); | 149 | 0, |
| 150 | ChannelAPin, | ||
| 151 | ChannelAComplementaryPin | ||
| 152 | ); | ||
| 153 | advanced_channel_impl!( | ||
| 154 | new_chb, | ||
| 155 | new_chb_with_config, | ||
| 156 | ChB, | ||
| 157 | 1, | ||
| 158 | ChannelBPin, | ||
| 159 | ChannelBComplementaryPin | ||
| 160 | ); | ||
| 161 | advanced_channel_impl!( | ||
| 162 | new_chc, | ||
| 163 | new_chc_with_config, | ||
| 164 | ChC, | ||
| 165 | 2, | ||
| 166 | ChannelCPin, | ||
| 167 | ChannelCComplementaryPin | ||
| 168 | ); | ||
| 169 | advanced_channel_impl!( | ||
| 170 | new_chd, | ||
| 171 | new_chd_with_config, | ||
| 172 | ChD, | ||
| 173 | 3, | ||
| 174 | ChannelDPin, | ||
| 175 | ChannelDComplementaryPin | ||
| 176 | ); | ||
| 177 | advanced_channel_impl!( | ||
| 178 | new_che, | ||
| 179 | new_che_with_config, | ||
| 180 | ChE, | ||
| 181 | 4, | ||
| 182 | ChannelEPin, | ||
| 183 | ChannelEComplementaryPin | ||
| 184 | ); | ||
| 119 | #[cfg(hrtim_v2)] | 185 | #[cfg(hrtim_v2)] |
| 120 | advanced_channel_impl!(new_chf, ChF, 5, ChannelFPin, ChannelFComplementaryPin); | 186 | advanced_channel_impl!( |
| 187 | new_chf, | ||
| 188 | new_chf_with_config, | ||
| 189 | ChF, | ||
| 190 | 5, | ||
| 191 | ChannelFPin, | ||
| 192 | ChannelFComplementaryPin | ||
| 193 | ); | ||
| 121 | 194 | ||
| 122 | /// Struct used to divide a high resolution timer into multiple channels | 195 | /// Struct used to divide a high resolution timer into multiple channels |
| 123 | pub struct AdvancedPwm<'d, T: Instance> { | 196 | pub struct AdvancedPwm<'d, T: Instance> { |
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 573a1851d..e62de0454 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs | |||
| @@ -1,14 +1,22 @@ | |||
| 1 | //! Hardware Semaphore (HSEM) | 1 | //! Hardware Semaphore (HSEM) |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 8 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 9 | use critical_section::CriticalSection; | ||
| 3 | use embassy_hal_internal::PeripheralType; | 10 | use embassy_hal_internal::PeripheralType; |
| 11 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 4 | 12 | ||
| 5 | use crate::pac; | ||
| 6 | use crate::rcc::{self, RccPeripheral}; | ||
| 7 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. | 13 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. |
| 8 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, | 14 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, |
| 9 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), | 15 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), |
| 10 | // which is not yet supported by this code. | 16 | // which is not yet supported by this code. |
| 11 | use crate::Peri; | 17 | use crate::Peri; |
| 18 | use crate::rcc::{self, RccPeripheral}; | ||
| 19 | use crate::{interrupt, pac}; | ||
| 12 | 20 | ||
| 13 | /// HSEM error. | 21 | /// HSEM error. |
| 14 | #[derive(Debug)] | 22 | #[derive(Debug)] |
| @@ -41,63 +49,152 @@ pub enum CoreId { | |||
| 41 | Core1 = 0x8, | 49 | Core1 = 0x8, |
| 42 | } | 50 | } |
| 43 | 51 | ||
| 44 | /// Get the current core id | 52 | impl CoreId { |
| 45 | /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. | 53 | /// Get the current core id |
| 46 | #[inline(always)] | 54 | /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. |
| 47 | pub fn get_current_coreid() -> CoreId { | 55 | pub fn current() -> Self { |
| 48 | let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; | 56 | let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; |
| 49 | match (cpuid & 0x000000F0) >> 4 { | 57 | match (cpuid & 0x000000F0) >> 4 { |
| 50 | #[cfg(any(stm32wb, stm32wl))] | 58 | #[cfg(any(stm32wb, stm32wl))] |
| 51 | 0x0 => CoreId::Core1, | 59 | 0x0 => CoreId::Core1, |
| 52 | 60 | ||
| 53 | #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] | 61 | #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] |
| 54 | 0x4 => CoreId::Core0, | 62 | 0x4 => CoreId::Core0, |
| 55 | 63 | ||
| 56 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] | 64 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] |
| 57 | 0x4 => CoreId::Core1, | 65 | 0x4 => CoreId::Core1, |
| 58 | 66 | ||
| 59 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] | 67 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] |
| 60 | 0x7 => CoreId::Core0, | 68 | 0x7 => CoreId::Core0, |
| 61 | _ => panic!("Unknown Cortex-M core"), | 69 | _ => panic!("Unknown Cortex-M core"), |
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Translates the core ID to an index into the interrupt registers. | ||
| 74 | pub fn to_index(&self) -> usize { | ||
| 75 | match &self { | ||
| 76 | CoreId::Core0 => 0, | ||
| 77 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] | ||
| 78 | CoreId::Core1 => 1, | ||
| 79 | } | ||
| 62 | } | 80 | } |
| 63 | } | 81 | } |
| 64 | 82 | ||
| 65 | /// Translates the core ID to an index into the interrupt registers. | 83 | #[cfg(not(all(stm32wb, feature = "low-power")))] |
| 66 | #[inline(always)] | 84 | const PUB_CHANNELS: usize = 6; |
| 67 | fn core_id_to_index(core: CoreId) -> usize { | 85 | |
| 68 | match core { | 86 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 69 | CoreId::Core0 => 0, | 87 | const PUB_CHANNELS: usize = 4; |
| 70 | #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] | 88 | |
| 71 | CoreId::Core1 => 1, | 89 | /// TX interrupt handler. |
| 90 | pub struct HardwareSemaphoreInterruptHandler<T: Instance> { | ||
| 91 | _phantom: PhantomData<T>, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for HardwareSemaphoreInterruptHandler<T> { | ||
| 95 | unsafe fn on_interrupt() { | ||
| 96 | let core_id = CoreId::current(); | ||
| 97 | |||
| 98 | for number in 0..5 { | ||
| 99 | if T::regs().isr(core_id.to_index()).read().isf(number as usize) { | ||
| 100 | T::regs() | ||
| 101 | .icr(core_id.to_index()) | ||
| 102 | .write(|w| w.set_isc(number as usize, true)); | ||
| 103 | |||
| 104 | T::regs() | ||
| 105 | .ier(core_id.to_index()) | ||
| 106 | .modify(|w| w.set_ise(number as usize, false)); | ||
| 107 | |||
| 108 | T::state().waker_for(number).wake(); | ||
| 109 | } | ||
| 110 | } | ||
| 72 | } | 111 | } |
| 73 | } | 112 | } |
| 74 | 113 | ||
| 75 | /// HSEM driver | 114 | /// Hardware semaphore mutex |
| 76 | pub struct HardwareSemaphore<'d, T: Instance> { | 115 | pub struct HardwareSemaphoreMutex<'a, T: Instance> { |
| 77 | _peri: Peri<'d, T>, | 116 | index: u8, |
| 117 | process_id: u8, | ||
| 118 | _lifetime: PhantomData<&'a mut T>, | ||
| 78 | } | 119 | } |
| 79 | 120 | ||
| 80 | impl<'d, T: Instance> HardwareSemaphore<'d, T> { | 121 | impl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> { |
| 81 | /// Creates a new HardwareSemaphore instance. | 122 | fn drop(&mut self) { |
| 82 | pub fn new(peripheral: Peri<'d, T>) -> Self { | 123 | HardwareSemaphoreChannel::<'a, T> { |
| 83 | rcc::enable_and_reset::<T>(); | 124 | index: self.index, |
| 125 | _lifetime: PhantomData, | ||
| 126 | } | ||
| 127 | .unlock(self.process_id); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Hardware semaphore channel | ||
| 132 | pub struct HardwareSemaphoreChannel<'a, T: Instance> { | ||
| 133 | index: u8, | ||
| 134 | _lifetime: PhantomData<&'a mut T>, | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { | ||
| 138 | pub(crate) const fn new(number: u8) -> Self { | ||
| 139 | core::assert!(number > 0 && number <= 6); | ||
| 140 | |||
| 141 | Self { | ||
| 142 | index: number - 1, | ||
| 143 | _lifetime: PhantomData, | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Locks the semaphore. | ||
| 148 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | ||
| 149 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | ||
| 150 | pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> { | ||
| 151 | let core_id = CoreId::current(); | ||
| 152 | |||
| 153 | poll_fn(|cx| { | ||
| 154 | T::state().waker_for(self.index).register(cx.waker()); | ||
| 155 | |||
| 156 | compiler_fence(Ordering::SeqCst); | ||
| 157 | |||
| 158 | T::regs() | ||
| 159 | .ier(core_id.to_index()) | ||
| 160 | .modify(|w| w.set_ise(self.index as usize, true)); | ||
| 84 | 161 | ||
| 85 | HardwareSemaphore { _peri: peripheral } | 162 | match self.try_lock(process_id) { |
| 163 | Some(mutex) => Poll::Ready(mutex), | ||
| 164 | None => Poll::Pending, | ||
| 165 | } | ||
| 166 | }) | ||
| 167 | .await | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Try to lock the semaphor | ||
| 171 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | ||
| 172 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | ||
| 173 | pub fn try_lock(&mut self, process_id: u8) -> Option<HardwareSemaphoreMutex<'a, T>> { | ||
| 174 | if self.two_step_lock(process_id).is_ok() { | ||
| 175 | Some(HardwareSemaphoreMutex { | ||
| 176 | index: self.index, | ||
| 177 | process_id: process_id, | ||
| 178 | _lifetime: PhantomData, | ||
| 179 | }) | ||
| 180 | } else { | ||
| 181 | None | ||
| 182 | } | ||
| 86 | } | 183 | } |
| 87 | 184 | ||
| 88 | /// Locks the semaphore. | 185 | /// Locks the semaphore. |
| 89 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to | 186 | /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to |
| 90 | /// check if the lock has been successful, carried out from the HSEM_Rx register. | 187 | /// check if the lock has been successful, carried out from the HSEM_Rx register. |
| 91 | pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { | 188 | pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> { |
| 92 | T::regs().r(sem_id as usize).write(|w| { | 189 | T::regs().r(self.index as usize).write(|w| { |
| 93 | w.set_procid(process_id); | 190 | w.set_procid(process_id); |
| 94 | w.set_coreid(get_current_coreid() as u8); | 191 | w.set_coreid(CoreId::current() as u8); |
| 95 | w.set_lock(true); | 192 | w.set_lock(true); |
| 96 | }); | 193 | }); |
| 97 | let reg = T::regs().r(sem_id as usize).read(); | 194 | let reg = T::regs().r(self.index as usize).read(); |
| 98 | match ( | 195 | match ( |
| 99 | reg.lock(), | 196 | reg.lock(), |
| 100 | reg.coreid() == get_current_coreid() as u8, | 197 | reg.coreid() == CoreId::current() as u8, |
| 101 | reg.procid() == process_id, | 198 | reg.procid() == process_id, |
| 102 | ) { | 199 | ) { |
| 103 | (true, true, true) => Ok(()), | 200 | (true, true, true) => Ok(()), |
| @@ -108,9 +205,9 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 108 | /// Locks the semaphore. | 205 | /// Locks the semaphore. |
| 109 | /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, | 206 | /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, |
| 110 | /// carried out from the HSEM_RLRx register. | 207 | /// carried out from the HSEM_RLRx register. |
| 111 | pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { | 208 | pub fn one_step_lock(&mut self) -> Result<(), HsemError> { |
| 112 | let reg = T::regs().rlr(sem_id as usize).read(); | 209 | let reg = T::regs().rlr(self.index as usize).read(); |
| 113 | match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { | 210 | match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) { |
| 114 | (false, true, 0) => Ok(()), | 211 | (false, true, 0) => Ok(()), |
| 115 | _ => Err(HsemError::LockFailed), | 212 | _ => Err(HsemError::LockFailed), |
| 116 | } | 213 | } |
| @@ -119,14 +216,60 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 119 | /// Unlocks the semaphore. | 216 | /// Unlocks the semaphore. |
| 120 | /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus | 217 | /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus |
| 121 | /// core ID or by a process not having the semaphore lock right. | 218 | /// core ID or by a process not having the semaphore lock right. |
| 122 | pub fn unlock(&mut self, sem_id: u8, process_id: u8) { | 219 | pub fn unlock(&mut self, process_id: u8) { |
| 123 | T::regs().r(sem_id as usize).write(|w| { | 220 | T::regs().r(self.index as usize).write(|w| { |
| 124 | w.set_procid(process_id); | 221 | w.set_procid(process_id); |
| 125 | w.set_coreid(get_current_coreid() as u8); | 222 | w.set_coreid(CoreId::current() as u8); |
| 126 | w.set_lock(false); | 223 | w.set_lock(false); |
| 127 | }); | 224 | }); |
| 128 | } | 225 | } |
| 129 | 226 | ||
| 227 | /// Return the channel number | ||
| 228 | pub const fn channel(&self) -> u8 { | ||
| 229 | self.index + 1 | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | /// HSEM driver | ||
| 234 | pub struct HardwareSemaphore<T: Instance> { | ||
| 235 | _type: PhantomData<T>, | ||
| 236 | } | ||
| 237 | |||
| 238 | impl<T: Instance> HardwareSemaphore<T> { | ||
| 239 | /// Creates a new HardwareSemaphore instance. | ||
| 240 | pub fn new<'d>( | ||
| 241 | _peripheral: Peri<'d, T>, | ||
| 242 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, HardwareSemaphoreInterruptHandler<T>> + 'd, | ||
| 243 | ) -> Self { | ||
| 244 | rcc::enable_and_reset::<T>(); | ||
| 245 | |||
| 246 | HardwareSemaphore { _type: PhantomData } | ||
| 247 | } | ||
| 248 | |||
| 249 | /// Get a single channel, and keep the global struct | ||
| 250 | pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> { | ||
| 251 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 252 | core::assert!(number != 3 && number != 4); | ||
| 253 | |||
| 254 | HardwareSemaphoreChannel::new(number) | ||
| 255 | } | ||
| 256 | |||
| 257 | /// Split the global struct into channels | ||
| 258 | /// | ||
| 259 | /// If using low-power mode, channels 3 and 4 will not be returned | ||
| 260 | pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; PUB_CHANNELS] { | ||
| 261 | [ | ||
| 262 | HardwareSemaphoreChannel::new(1), | ||
| 263 | HardwareSemaphoreChannel::new(2), | ||
| 264 | #[cfg(not(all(stm32wb, feature = "low-power")))] | ||
| 265 | HardwareSemaphoreChannel::new(3), | ||
| 266 | #[cfg(not(all(stm32wb, feature = "low-power")))] | ||
| 267 | HardwareSemaphoreChannel::new(4), | ||
| 268 | HardwareSemaphoreChannel::new(5), | ||
| 269 | HardwareSemaphoreChannel::new(6), | ||
| 270 | ] | ||
| 271 | } | ||
| 272 | |||
| 130 | /// Unlocks all semaphores. | 273 | /// Unlocks all semaphores. |
| 131 | /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR | 274 | /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR |
| 132 | /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a | 275 | /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a |
| @@ -138,11 +281,6 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 138 | }); | 281 | }); |
| 139 | } | 282 | } |
| 140 | 283 | ||
| 141 | /// Checks if the semaphore is locked. | ||
| 142 | pub fn is_semaphore_locked(&self, sem_id: u8) -> bool { | ||
| 143 | T::regs().r(sem_id as usize).read().lock() | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Sets the clear (unlock) key | 284 | /// Sets the clear (unlock) key |
| 147 | pub fn set_clear_key(&mut self, key: u16) { | 285 | pub fn set_clear_key(&mut self, key: u16) { |
| 148 | T::regs().keyr().modify(|w| w.set_key(key)); | 286 | T::regs().keyr().modify(|w| w.set_key(key)); |
| @@ -152,38 +290,61 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { | |||
| 152 | pub fn get_clear_key(&mut self) -> u16 { | 290 | pub fn get_clear_key(&mut self) -> u16 { |
| 153 | T::regs().keyr().read().key() | 291 | T::regs().keyr().read().key() |
| 154 | } | 292 | } |
| 293 | } | ||
| 155 | 294 | ||
| 156 | /// Sets the interrupt enable bit for the semaphore. | 295 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 157 | pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { | 296 | pub(crate) fn init_hsem(cs: CriticalSection) { |
| 158 | T::regs() | 297 | rcc::enable_and_reset_with_cs::<crate::peripherals::HSEM>(cs); |
| 159 | .ier(core_id_to_index(core_id)) | 298 | |
| 160 | .modify(|w| w.set_ise(sem_x, enable)); | 299 | unsafe { |
| 300 | crate::rcc::REFCOUNT_STOP1 = 0; | ||
| 301 | crate::rcc::REFCOUNT_STOP2 = 0; | ||
| 161 | } | 302 | } |
| 303 | } | ||
| 162 | 304 | ||
| 163 | /// Gets the interrupt flag for the semaphore. | 305 | struct State { |
| 164 | pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool { | 306 | wakers: [AtomicWaker; 6], |
| 165 | T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x) | 307 | } |
| 308 | |||
| 309 | impl State { | ||
| 310 | const fn new() -> Self { | ||
| 311 | Self { | ||
| 312 | wakers: [const { AtomicWaker::new() }; 6], | ||
| 313 | } | ||
| 166 | } | 314 | } |
| 167 | 315 | ||
| 168 | /// Clears the interrupt flag for the semaphore. | 316 | const fn waker_for(&self, index: u8) -> &AtomicWaker { |
| 169 | pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { | 317 | &self.wakers[index as usize] |
| 170 | T::regs() | ||
| 171 | .icr(core_id_to_index(core_id)) | ||
| 172 | .write(|w| w.set_isc(sem_x, false)); | ||
| 173 | } | 318 | } |
| 174 | } | 319 | } |
| 175 | 320 | ||
| 176 | trait SealedInstance { | 321 | trait SealedInstance { |
| 177 | fn regs() -> pac::hsem::Hsem; | 322 | fn regs() -> pac::hsem::Hsem; |
| 323 | fn state() -> &'static State; | ||
| 178 | } | 324 | } |
| 179 | 325 | ||
| 180 | /// HSEM instance trait. | 326 | /// HSEM instance trait. |
| 181 | #[allow(private_bounds)] | 327 | #[allow(private_bounds)] |
| 182 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} | 328 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static { |
| 329 | /// Interrupt for this peripheral. | ||
| 330 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 331 | } | ||
| 183 | 332 | ||
| 184 | impl SealedInstance for crate::peripherals::HSEM { | 333 | impl SealedInstance for crate::peripherals::HSEM { |
| 185 | fn regs() -> crate::pac::hsem::Hsem { | 334 | fn regs() -> crate::pac::hsem::Hsem { |
| 186 | crate::pac::HSEM | 335 | crate::pac::HSEM |
| 187 | } | 336 | } |
| 337 | |||
| 338 | fn state() -> &'static State { | ||
| 339 | static STATE: State = State::new(); | ||
| 340 | &STATE | ||
| 341 | } | ||
| 188 | } | 342 | } |
| 189 | impl Instance for crate::peripherals::HSEM {} | 343 | |
| 344 | foreach_interrupt!( | ||
| 345 | ($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => { | ||
| 346 | impl Instance for crate::peripherals::$inst { | ||
| 347 | type Interrupt = crate::interrupt::typelevel::$irq; | ||
| 348 | } | ||
| 349 | }; | ||
| 350 | ); | ||
diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 59dd7ca16..1d3560678 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs | |||
| @@ -16,7 +16,7 @@ use embassy_embedded_hal::{GetConfig, SetConfig}; | |||
| 16 | use embassy_hal_internal::{Peri, PeripheralType}; | 16 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 17 | pub use enums::*; | 17 | pub use enums::*; |
| 18 | 18 | ||
| 19 | use crate::dma::{word, ChannelAndRequest}; | 19 | use crate::dma::{ChannelAndRequest, word}; |
| 20 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 20 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 21 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 21 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 22 | use crate::pac::hspi::Hspi as Regs; | 22 | use crate::pac::hspi::Hspi as Regs; |
diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs index 4e3b736c7..74fac14b2 100644 --- a/embassy-stm32/src/i2c/config.rs +++ b/embassy-stm32/src/i2c/config.rs | |||
| @@ -4,7 +4,7 @@ use crate::gpio::{AfType, OutputType, Speed}; | |||
| 4 | use crate::time::Hertz; | 4 | use crate::time::Hertz; |
| 5 | 5 | ||
| 6 | #[repr(u8)] | 6 | #[repr(u8)] |
| 7 | #[derive(Copy, Clone)] | 7 | #[derive(Debug, Copy, Clone)] |
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 9 | /// Bits of the I2C OA2 register to mask out. | 9 | /// Bits of the I2C OA2 register to mask out. |
| 10 | pub enum AddrMask { | 10 | pub enum AddrMask { |
| @@ -60,7 +60,7 @@ impl Address { | |||
| 60 | } | 60 | } |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | #[derive(Copy, Clone)] | 63 | #[derive(Debug, Copy, Clone)] |
| 64 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 64 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 65 | /// The second Own Address register. | 65 | /// The second Own Address register. |
| 66 | pub struct OA2 { | 66 | pub struct OA2 { |
| @@ -70,7 +70,7 @@ pub struct OA2 { | |||
| 70 | pub mask: AddrMask, | 70 | pub mask: AddrMask, |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | #[derive(Copy, Clone)] | 73 | #[derive(Debug, Copy, Clone)] |
| 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 75 | /// The Own Address(es) of the I2C peripheral. | 75 | /// The Own Address(es) of the I2C peripheral. |
| 76 | pub enum OwnAddresses { | 76 | pub enum OwnAddresses { |
| @@ -88,7 +88,7 @@ pub enum OwnAddresses { | |||
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | /// Slave Configuration | 90 | /// Slave Configuration |
| 91 | #[derive(Copy, Clone)] | 91 | #[derive(Debug, Copy, Clone)] |
| 92 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 92 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 93 | pub struct SlaveAddrConfig { | 93 | pub struct SlaveAddrConfig { |
| 94 | /// Target Address(es) | 94 | /// Target Address(es) |
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 249bac41c..ee60c3f44 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -154,8 +154,8 @@ impl<'d> I2c<'d, Async, Master> { | |||
| 154 | scl: Peri<'d, if_afio!(impl SclPin<T, A>)>, | 154 | scl: Peri<'d, if_afio!(impl SclPin<T, A>)>, |
| 155 | sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>, | 155 | sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>, |
| 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> | 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> |
| 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> | 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> |
| 158 | + 'd, | 158 | + 'd, |
| 159 | tx_dma: Peri<'d, impl TxDma<T>>, | 159 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 160 | rx_dma: Peri<'d, impl RxDma<T>>, | 160 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 161 | config: Config, | 161 | config: Config, |
| @@ -219,6 +219,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> { | |||
| 219 | sda, | 219 | sda, |
| 220 | }, | 220 | }, |
| 221 | }; | 221 | }; |
| 222 | |||
| 222 | this.enable_and_init(config); | 223 | this.enable_and_init(config); |
| 223 | 224 | ||
| 224 | this | 225 | this |
| @@ -437,15 +438,15 @@ impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { | |||
| 437 | 438 | ||
| 438 | /// Frame type in I2C transaction. | 439 | /// Frame type in I2C transaction. |
| 439 | /// | 440 | /// |
| 440 | /// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST | 441 | /// This tells each method what kind of frame to use, to generate a (repeated) start condition (ST |
| 441 | /// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an | 442 | /// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an |
| 442 | /// ACK or NACK after the last byte received. | 443 | /// ACK or NACK after the last byte received. |
| 443 | /// | 444 | /// |
| 444 | /// For write operations, the following options are identical because they differ only in the (N)ACK | 445 | /// For write operations, the following options are identical because they differ only in the (N)ACK |
| 445 | /// treatment relevant for read operations: | 446 | /// treatment relevant for read operations: |
| 446 | /// | 447 | /// |
| 447 | /// - `FirstFrame` and `FirstAndNextFrame` | 448 | /// - `FirstFrame` and `FirstAndNextFrame` behave identically for writes |
| 448 | /// - `NextFrame` and `LastFrameNoStop` | 449 | /// - `NextFrame` and `LastFrameNoStop` behave identically for writes |
| 449 | /// | 450 | /// |
| 450 | /// Abbreviations used below: | 451 | /// Abbreviations used below: |
| 451 | /// | 452 | /// |
| @@ -474,7 +475,7 @@ enum FrameOptions { | |||
| 474 | 475 | ||
| 475 | #[allow(dead_code)] | 476 | #[allow(dead_code)] |
| 476 | impl FrameOptions { | 477 | impl FrameOptions { |
| 477 | /// Sends start or repeated start condition before transfer. | 478 | /// Returns true if a start or repeated start condition should be generated before this operation. |
| 478 | fn send_start(self) -> bool { | 479 | fn send_start(self) -> bool { |
| 479 | match self { | 480 | match self { |
| 480 | Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, | 481 | Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, |
| @@ -482,7 +483,7 @@ impl FrameOptions { | |||
| 482 | } | 483 | } |
| 483 | } | 484 | } |
| 484 | 485 | ||
| 485 | /// Sends stop condition after transfer. | 486 | /// Returns true if a stop condition should be generated after this operation. |
| 486 | fn send_stop(self) -> bool { | 487 | fn send_stop(self) -> bool { |
| 487 | match self { | 488 | match self { |
| 488 | Self::FirstAndLastFrame | Self::LastFrame => true, | 489 | Self::FirstAndLastFrame | Self::LastFrame => true, |
| @@ -490,7 +491,10 @@ impl FrameOptions { | |||
| 490 | } | 491 | } |
| 491 | } | 492 | } |
| 492 | 493 | ||
| 493 | /// Sends NACK after last byte received, indicating end of read operation. | 494 | /// Returns true if NACK should be sent after the last byte received in a read operation. |
| 495 | /// | ||
| 496 | /// This signals the end of a read sequence and releases the bus for the master's | ||
| 497 | /// next transmission (or stop condition). | ||
| 494 | fn send_nack(self) -> bool { | 498 | fn send_nack(self) -> bool { |
| 495 | match self { | 499 | match self { |
| 496 | Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, | 500 | Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, |
| @@ -499,24 +503,44 @@ impl FrameOptions { | |||
| 499 | } | 503 | } |
| 500 | } | 504 | } |
| 501 | 505 | ||
| 502 | /// Iterates over operations in transaction. | 506 | /// Analyzes I2C transaction operations and assigns appropriate frame to each. |
| 507 | /// | ||
| 508 | /// This function processes a sequence of I2C operations and determines the correct | ||
| 509 | /// frame configuration for each operation to ensure proper I2C protocol compliance. | ||
| 510 | /// It handles the complex logic of: | ||
| 511 | /// | ||
| 512 | /// - Generating start conditions for the first operation of each type (read/write) | ||
| 513 | /// - Generating stop conditions for the final operation in the entire transaction | ||
| 514 | /// - Managing ACK/NACK behavior for read operations, including merging consecutive reads | ||
| 515 | /// - Ensuring proper bus handoff between different operation types | ||
| 516 | /// | ||
| 517 | /// **Transaction Contract Compliance:** | ||
| 518 | /// The frame assignments ensure compliance with the embedded-hal I2C transaction contract, | ||
| 519 | /// where consecutive operations of the same type are logically merged while maintaining | ||
| 520 | /// proper protocol boundaries. | ||
| 503 | /// | 521 | /// |
| 504 | /// Returns necessary frame options for each operation to uphold the [transaction contract] and have | 522 | /// **Error Handling:** |
| 505 | /// the right start/stop/(N)ACK conditions on the wire. | 523 | /// Returns an error if any read operation has an empty buffer, as this would create |
| 524 | /// an invalid I2C transaction that could halt mid-execution. | ||
| 525 | /// | ||
| 526 | /// # Arguments | ||
| 527 | /// * `operations` - Mutable slice of I2C operations from embedded-hal | ||
| 528 | /// | ||
| 529 | /// # Returns | ||
| 530 | /// An iterator over (operation, frame) pairs, or an error if the transaction is invalid | ||
| 506 | /// | 531 | /// |
| 507 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | ||
| 508 | #[allow(dead_code)] | 532 | #[allow(dead_code)] |
| 509 | fn operation_frames<'a, 'b: 'a>( | 533 | fn operation_frames<'a, 'b: 'a>( |
| 510 | operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], | 534 | operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], |
| 511 | ) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { | 535 | ) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { |
| 512 | use embedded_hal_1::i2c::Operation::{Read, Write}; | 536 | use embedded_hal_1::i2c::Operation::{Read, Write}; |
| 513 | 537 | ||
| 514 | // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an | 538 | // Validate that no read operations have empty buffers before starting the transaction. |
| 515 | // error in the middle of the transaction. | 539 | // Empty read operations would risk halting with an error mid-transaction. |
| 516 | // | 540 | // |
| 517 | // In principle, we could allow empty read frames within consecutive read operations, as long as | 541 | // Note: We could theoretically allow empty read operations within consecutive read |
| 518 | // at least one byte remains in the final (merged) read operation, but that makes the logic more | 542 | // sequences as long as the final merged read has at least one byte, but this would |
| 519 | // complicated and error-prone. | 543 | // complicate the logic significantly and create error-prone edge cases. |
| 520 | if operations.iter().any(|op| match op { | 544 | if operations.iter().any(|op| match op { |
| 521 | Read(read) => read.is_empty(), | 545 | Read(read) => read.is_empty(), |
| 522 | Write(_) => false, | 546 | Write(_) => false, |
| @@ -525,46 +549,52 @@ fn operation_frames<'a, 'b: 'a>( | |||
| 525 | } | 549 | } |
| 526 | 550 | ||
| 527 | let mut operations = operations.iter_mut().peekable(); | 551 | let mut operations = operations.iter_mut().peekable(); |
| 528 | 552 | let mut next_first_operation = true; | |
| 529 | let mut next_first_frame = true; | ||
| 530 | 553 | ||
| 531 | Ok(iter::from_fn(move || { | 554 | Ok(iter::from_fn(move || { |
| 532 | let op = operations.next()?; | 555 | let current_op = operations.next()?; |
| 533 | 556 | ||
| 534 | // Is `op` first frame of its type? | 557 | // Determine if this is the first operation of its type (read or write) |
| 535 | let first_frame = next_first_frame; | 558 | let is_first_of_type = next_first_operation; |
| 536 | let next_op = operations.peek(); | 559 | let next_op = operations.peek(); |
| 537 | 560 | ||
| 538 | // Get appropriate frame options as combination of the following properties: | 561 | // Compute the appropriate frame based on three key properties: |
| 539 | // | 562 | // |
| 540 | // - For each first operation of its type, generate a (repeated) start condition. | 563 | // 1. **Start Condition**: Generate (repeated) start for first operation of each type |
| 541 | // - For the last operation overall in the entire transaction, generate a stop condition. | 564 | // 2. **Stop Condition**: Generate stop for the final operation in the entire transaction |
| 542 | // - For read operations, check the next operation: if it is also a read operation, we merge | 565 | // 3. **ACK/NACK for Reads**: For read operations, send ACK if more reads follow in the |
| 543 | // these and send ACK for all bytes in the current operation; send NACK only for the final | 566 | // sequence, or NACK for the final read in a sequence (before write or transaction end) |
| 544 | // read operation's last byte (before write or end of entire transaction) to indicate last | ||
| 545 | // byte read and release the bus for transmission of the bus master's next byte (or stop). | ||
| 546 | // | 567 | // |
| 547 | // We check the third property unconditionally, i.e. even for write opeartions. This is okay | 568 | // The third property is checked for all operations since the resulting frame |
| 548 | // because the resulting frame options are identical for write operations. | 569 | // configurations are identical for write operations regardless of ACK/NACK treatment. |
| 549 | let frame = match (first_frame, next_op) { | 570 | let frame = match (is_first_of_type, next_op) { |
| 571 | // First operation of type, and it's also the final operation overall | ||
| 550 | (true, None) => FrameOptions::FirstAndLastFrame, | 572 | (true, None) => FrameOptions::FirstAndLastFrame, |
| 573 | // First operation of type, next operation is also a read (continue read sequence) | ||
| 551 | (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, | 574 | (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, |
| 575 | // First operation of type, next operation is write (end current sequence) | ||
| 552 | (true, Some(Write(_))) => FrameOptions::FirstFrame, | 576 | (true, Some(Write(_))) => FrameOptions::FirstFrame, |
| 553 | // | 577 | |
| 578 | // Continuation operation, and it's the final operation overall | ||
| 554 | (false, None) => FrameOptions::LastFrame, | 579 | (false, None) => FrameOptions::LastFrame, |
| 580 | // Continuation operation, next operation is also a read (continue read sequence) | ||
| 555 | (false, Some(Read(_))) => FrameOptions::NextFrame, | 581 | (false, Some(Read(_))) => FrameOptions::NextFrame, |
| 582 | // Continuation operation, next operation is write (end current sequence, no stop) | ||
| 556 | (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, | 583 | (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, |
| 557 | }; | 584 | }; |
| 558 | 585 | ||
| 559 | // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at | 586 | // Pre-calculate whether the next operation will be the first of its type. |
| 560 | // the beginning of the loop because we hand out `op` as iterator value and cannot access it | 587 | // This is done here because we consume `current_op` as the iterator value |
| 561 | // anymore in the next iteration. | 588 | // and cannot access it in the next iteration. |
| 562 | next_first_frame = match (&op, next_op) { | 589 | next_first_operation = match (¤t_op, next_op) { |
| 590 | // No next operation | ||
| 563 | (_, None) => false, | 591 | (_, None) => false, |
| 592 | // Operation type changes: next will be first of its type | ||
| 564 | (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, | 593 | (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, |
| 594 | // Operation type continues: next will not be first of its type | ||
| 565 | (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, | 595 | (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, |
| 566 | }; | 596 | }; |
| 567 | 597 | ||
| 568 | Some((op, frame)) | 598 | Some((current_op, frame)) |
| 569 | })) | 599 | })) |
| 570 | } | 600 | } |
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 081eb1191..128a58db7 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -8,7 +8,7 @@ use core::future::poll_fn; | |||
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| 11 | use embassy_futures::select::{select, Either}; | 11 | use embassy_futures::select::{Either, select}; |
| 12 | use embassy_hal_internal::drop::OnDrop; | 12 | use embassy_hal_internal::drop::OnDrop; |
| 13 | use embedded_hal_1::i2c::Operation; | 13 | use embedded_hal_1::i2c::Operation; |
| 14 | use mode::Master; | 14 | use mode::Master; |
| @@ -30,6 +30,7 @@ use crate::pac::i2c; | |||
| 30 | // hit a case like this! | 30 | // hit a case like this! |
| 31 | pub unsafe fn on_interrupt<T: Instance>() { | 31 | pub unsafe fn on_interrupt<T: Instance>() { |
| 32 | let regs = T::info().regs; | 32 | let regs = T::info().regs; |
| 33 | trace!("I2C interrupt triggered"); | ||
| 33 | // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of | 34 | // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of |
| 34 | // other stuff, so we wake the task on every interrupt. | 35 | // other stuff, so we wake the task on every interrupt. |
| 35 | T::state().waker.wake(); | 36 | T::state().waker.wake(); |
| @@ -92,6 +93,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 92 | self.info.regs.cr1().modify(|reg| { | 93 | self.info.regs.cr1().modify(|reg| { |
| 93 | reg.set_pe(true); | 94 | reg.set_pe(true); |
| 94 | }); | 95 | }); |
| 96 | trace!("i2c v1 init complete"); | ||
| 95 | } | 97 | } |
| 96 | 98 | ||
| 97 | fn check_and_clear_error_flags(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { | 99 | fn check_and_clear_error_flags(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { |
| @@ -151,7 +153,13 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 151 | Ok(sr1) | 153 | Ok(sr1) |
| 152 | } | 154 | } |
| 153 | 155 | ||
| 154 | fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout, frame: FrameOptions) -> Result<(), Error> { | 156 | fn write_bytes( |
| 157 | &mut self, | ||
| 158 | address: u8, | ||
| 159 | write_buffer: &[u8], | ||
| 160 | timeout: Timeout, | ||
| 161 | frame: FrameOptions, | ||
| 162 | ) -> Result<(), Error> { | ||
| 155 | if frame.send_start() { | 163 | if frame.send_start() { |
| 156 | // Send a START condition | 164 | // Send a START condition |
| 157 | 165 | ||
| @@ -170,7 +178,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 170 | } | 178 | } |
| 171 | 179 | ||
| 172 | // Set up current address we're trying to talk to | 180 | // Set up current address we're trying to talk to |
| 173 | self.info.regs.dr().write(|reg| reg.set_dr(addr << 1)); | 181 | self.info.regs.dr().write(|reg| reg.set_dr(address << 1)); |
| 174 | 182 | ||
| 175 | // Wait until address was sent | 183 | // Wait until address was sent |
| 176 | // Wait for the address to be acknowledged | 184 | // Wait for the address to be acknowledged |
| @@ -184,7 +192,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 184 | } | 192 | } |
| 185 | 193 | ||
| 186 | // Send bytes | 194 | // Send bytes |
| 187 | for c in bytes { | 195 | for c in write_buffer { |
| 188 | self.send_byte(*c, timeout)?; | 196 | self.send_byte(*c, timeout)?; |
| 189 | } | 197 | } |
| 190 | 198 | ||
| @@ -236,12 +244,12 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 236 | 244 | ||
| 237 | fn blocking_read_timeout( | 245 | fn blocking_read_timeout( |
| 238 | &mut self, | 246 | &mut self, |
| 239 | addr: u8, | 247 | address: u8, |
| 240 | buffer: &mut [u8], | 248 | read_buffer: &mut [u8], |
| 241 | timeout: Timeout, | 249 | timeout: Timeout, |
| 242 | frame: FrameOptions, | 250 | frame: FrameOptions, |
| 243 | ) -> Result<(), Error> { | 251 | ) -> Result<(), Error> { |
| 244 | let Some((last, buffer)) = buffer.split_last_mut() else { | 252 | let Some((last_byte, read_buffer)) = read_buffer.split_last_mut() else { |
| 245 | return Err(Error::Overrun); | 253 | return Err(Error::Overrun); |
| 246 | }; | 254 | }; |
| 247 | 255 | ||
| @@ -263,7 +271,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 263 | } | 271 | } |
| 264 | 272 | ||
| 265 | // Set up current address we're trying to talk to | 273 | // Set up current address we're trying to talk to |
| 266 | self.info.regs.dr().write(|reg| reg.set_dr((addr << 1) + 1)); | 274 | self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1)); |
| 267 | 275 | ||
| 268 | // Wait until address was sent | 276 | // Wait until address was sent |
| 269 | // Wait for the address to be acknowledged | 277 | // Wait for the address to be acknowledged |
| @@ -276,7 +284,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 276 | } | 284 | } |
| 277 | 285 | ||
| 278 | // Receive bytes into buffer | 286 | // Receive bytes into buffer |
| 279 | for c in buffer { | 287 | for c in read_buffer { |
| 280 | *c = self.recv_byte(timeout)?; | 288 | *c = self.recv_byte(timeout)?; |
| 281 | } | 289 | } |
| 282 | 290 | ||
| @@ -291,37 +299,42 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 291 | }); | 299 | }); |
| 292 | 300 | ||
| 293 | // Receive last byte | 301 | // Receive last byte |
| 294 | *last = self.recv_byte(timeout)?; | 302 | *last_byte = self.recv_byte(timeout)?; |
| 295 | 303 | ||
| 296 | // Fallthrough is success | 304 | // Fallthrough is success |
| 297 | Ok(()) | 305 | Ok(()) |
| 298 | } | 306 | } |
| 299 | 307 | ||
| 300 | /// Blocking read. | 308 | /// Blocking read. |
| 301 | pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { | 309 | pub fn blocking_read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { |
| 302 | self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame) | 310 | self.blocking_read_timeout(address, read_buffer, self.timeout(), FrameOptions::FirstAndLastFrame) |
| 303 | } | 311 | } |
| 304 | 312 | ||
| 305 | /// Blocking write. | 313 | /// Blocking write. |
| 306 | pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { | 314 | pub fn blocking_write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { |
| 307 | self.write_bytes(addr, write, self.timeout(), FrameOptions::FirstAndLastFrame)?; | 315 | self.write_bytes(address, write_buffer, self.timeout(), FrameOptions::FirstAndLastFrame)?; |
| 308 | 316 | ||
| 309 | // Fallthrough is success | 317 | // Fallthrough is success |
| 310 | Ok(()) | 318 | Ok(()) |
| 311 | } | 319 | } |
| 312 | 320 | ||
| 313 | /// Blocking write, restart, read. | 321 | /// Blocking write, restart, read. |
| 314 | pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 322 | pub fn blocking_write_read( |
| 323 | &mut self, | ||
| 324 | address: u8, | ||
| 325 | write_buffer: &[u8], | ||
| 326 | read_buffer: &mut [u8], | ||
| 327 | ) -> Result<(), Error> { | ||
| 315 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the | 328 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the |
| 316 | // stop condition below. | 329 | // stop condition below. |
| 317 | if read.is_empty() { | 330 | if read_buffer.is_empty() { |
| 318 | return Err(Error::Overrun); | 331 | return Err(Error::Overrun); |
| 319 | } | 332 | } |
| 320 | 333 | ||
| 321 | let timeout = self.timeout(); | 334 | let timeout = self.timeout(); |
| 322 | 335 | ||
| 323 | self.write_bytes(addr, write, timeout, FrameOptions::FirstFrame)?; | 336 | self.write_bytes(address, write_buffer, timeout, FrameOptions::FirstFrame)?; |
| 324 | self.blocking_read_timeout(addr, read, timeout, FrameOptions::FirstAndLastFrame)?; | 337 | self.blocking_read_timeout(address, read_buffer, timeout, FrameOptions::FirstAndLastFrame)?; |
| 325 | 338 | ||
| 326 | Ok(()) | 339 | Ok(()) |
| 327 | } | 340 | } |
| @@ -331,32 +344,43 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 331 | /// Consecutive operations of same type are merged. See [transaction contract] for details. | 344 | /// Consecutive operations of same type are merged. See [transaction contract] for details. |
| 332 | /// | 345 | /// |
| 333 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 346 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 334 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 347 | pub fn blocking_transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 335 | let timeout = self.timeout(); | 348 | let timeout = self.timeout(); |
| 336 | 349 | ||
| 337 | for (op, frame) in operation_frames(operations)? { | 350 | for (op, frame) in operation_frames(operations)? { |
| 338 | match op { | 351 | match op { |
| 339 | Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?, | 352 | Operation::Read(read_buffer) => self.blocking_read_timeout(address, read_buffer, timeout, frame)?, |
| 340 | Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?, | 353 | Operation::Write(write_buffer) => self.write_bytes(address, write_buffer, timeout, frame)?, |
| 341 | } | 354 | } |
| 342 | } | 355 | } |
| 343 | 356 | ||
| 344 | Ok(()) | 357 | Ok(()) |
| 345 | } | 358 | } |
| 346 | 359 | ||
| 347 | // Async | 360 | /// Can be used by both blocking and async implementations |
| 348 | |||
| 349 | #[inline] // pretty sure this should always be inlined | 361 | #[inline] // pretty sure this should always be inlined |
| 350 | fn enable_interrupts(info: &'static Info) -> () { | 362 | fn enable_interrupts(info: &'static Info) { |
| 351 | info.regs.cr2().modify(|w| { | 363 | // The interrupt handler disables interrupts globally, so we need to re-enable them |
| 352 | w.set_iterren(true); | 364 | // This must be done in a critical section to avoid races |
| 353 | w.set_itevten(true); | 365 | critical_section::with(|_| { |
| 366 | info.regs.cr2().modify(|w| { | ||
| 367 | w.set_iterren(true); | ||
| 368 | w.set_itevten(true); | ||
| 369 | }); | ||
| 354 | }); | 370 | }); |
| 355 | } | 371 | } |
| 372 | |||
| 373 | /// Can be used by both blocking and async implementations | ||
| 374 | fn clear_stop_flag(info: &'static Info) { | ||
| 375 | trace!("I2C slave: clearing STOPF flag (v1 sequence)"); | ||
| 376 | // v1 requires: READ SR1 then WRITE CR1 to clear STOPF | ||
| 377 | let _ = info.regs.sr1().read(); | ||
| 378 | info.regs.cr1().modify(|_| {}); // Dummy write to clear STOPF | ||
| 379 | } | ||
| 356 | } | 380 | } |
| 357 | 381 | ||
| 358 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | 382 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { |
| 359 | async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { | 383 | async fn write_frame(&mut self, address: u8, write_buffer: &[u8], frame: FrameOptions) -> Result<(), Error> { |
| 360 | self.info.regs.cr2().modify(|w| { | 384 | self.info.regs.cr2().modify(|w| { |
| 361 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for | 385 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| 362 | // reception. | 386 | // reception. |
| @@ -439,7 +463,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 439 | // this address from the memory after each TxE event. | 463 | // this address from the memory after each TxE event. |
| 440 | let dst = self.info.regs.dr().as_ptr() as *mut u8; | 464 | let dst = self.info.regs.dr().as_ptr() as *mut u8; |
| 441 | 465 | ||
| 442 | self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) | 466 | self.tx_dma |
| 467 | .as_mut() | ||
| 468 | .unwrap() | ||
| 469 | .write(write_buffer, dst, Default::default()) | ||
| 443 | }; | 470 | }; |
| 444 | 471 | ||
| 445 | // Wait for bytes to be sent, or an error to occur. | 472 | // Wait for bytes to be sent, or an error to occur. |
| @@ -501,28 +528,28 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 501 | } | 528 | } |
| 502 | 529 | ||
| 503 | /// Write. | 530 | /// Write. |
| 504 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 531 | pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { |
| 505 | self.write_frame(address, write, FrameOptions::FirstAndLastFrame) | 532 | self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame) |
| 506 | .await?; | 533 | .await?; |
| 507 | 534 | ||
| 508 | Ok(()) | 535 | Ok(()) |
| 509 | } | 536 | } |
| 510 | 537 | ||
| 511 | /// Read. | 538 | /// Read. |
| 512 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 539 | pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { |
| 513 | self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame) | 540 | self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) |
| 514 | .await?; | 541 | .await?; |
| 515 | 542 | ||
| 516 | Ok(()) | 543 | Ok(()) |
| 517 | } | 544 | } |
| 518 | 545 | ||
| 519 | async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { | 546 | async fn read_frame(&mut self, address: u8, read_buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { |
| 520 | if buffer.is_empty() { | 547 | if read_buffer.is_empty() { |
| 521 | return Err(Error::Overrun); | 548 | return Err(Error::Overrun); |
| 522 | } | 549 | } |
| 523 | 550 | ||
| 524 | // Some branches below depend on whether the buffer contains only a single byte. | 551 | // Some branches below depend on whether the buffer contains only a single byte. |
| 525 | let single_byte = buffer.len() == 1; | 552 | let single_byte = read_buffer.len() == 1; |
| 526 | 553 | ||
| 527 | self.info.regs.cr2().modify(|w| { | 554 | self.info.regs.cr2().modify(|w| { |
| 528 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for | 555 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| @@ -612,7 +639,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 612 | self.info.regs.sr2().read(); | 639 | self.info.regs.sr2().read(); |
| 613 | } else { | 640 | } else { |
| 614 | // Before starting reception of single byte (but without START condition, i.e. in case | 641 | // Before starting reception of single byte (but without START condition, i.e. in case |
| 615 | // of continued frame), program NACK to emit at end of this byte. | 642 | // of merged operations), program NACK to emit at end of this byte. |
| 616 | if frame.send_nack() && single_byte { | 643 | if frame.send_nack() && single_byte { |
| 617 | self.info.regs.cr1().modify(|w| { | 644 | self.info.regs.cr1().modify(|w| { |
| 618 | w.set_ack(false); | 645 | w.set_ack(false); |
| @@ -634,7 +661,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 634 | // from this address from the memory after each RxE event. | 661 | // from this address from the memory after each RxE event. |
| 635 | let src = self.info.regs.dr().as_ptr() as *mut u8; | 662 | let src = self.info.regs.dr().as_ptr() as *mut u8; |
| 636 | 663 | ||
| 637 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | 664 | self.rx_dma.as_mut().unwrap().read(src, read_buffer, Default::default()) |
| 638 | }; | 665 | }; |
| 639 | 666 | ||
| 640 | // Wait for bytes to be received, or an error to occur. | 667 | // Wait for bytes to be received, or an error to occur. |
| @@ -673,15 +700,17 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 673 | } | 700 | } |
| 674 | 701 | ||
| 675 | /// Write, restart, read. | 702 | /// Write, restart, read. |
| 676 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 703 | pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> { |
| 677 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the | 704 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the |
| 678 | // stop condition below. | 705 | // stop condition below. |
| 679 | if read.is_empty() { | 706 | if read_buffer.is_empty() { |
| 680 | return Err(Error::Overrun); | 707 | return Err(Error::Overrun); |
| 681 | } | 708 | } |
| 682 | 709 | ||
| 683 | self.write_frame(address, write, FrameOptions::FirstFrame).await?; | 710 | self.write_frame(address, write_buffer, FrameOptions::FirstFrame) |
| 684 | self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await | 711 | .await?; |
| 712 | self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) | ||
| 713 | .await | ||
| 685 | } | 714 | } |
| 686 | 715 | ||
| 687 | /// Transaction with operations. | 716 | /// Transaction with operations. |
| @@ -689,11 +718,11 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 689 | /// Consecutive operations of same type are merged. See [transaction contract] for details. | 718 | /// Consecutive operations of same type are merged. See [transaction contract] for details. |
| 690 | /// | 719 | /// |
| 691 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 720 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 692 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 721 | pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 693 | for (op, frame) in operation_frames(operations)? { | 722 | for (op, frame) in operation_frames(operations)? { |
| 694 | match op { | 723 | match op { |
| 695 | Operation::Read(read) => self.read_frame(addr, read, frame).await?, | 724 | Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?, |
| 696 | Operation::Write(write) => self.write_frame(addr, write, frame).await?, | 725 | Operation::Write(write_buffer) => self.write_frame(address, write_buffer, frame).await?, |
| 697 | } | 726 | } |
| 698 | } | 727 | } |
| 699 | 728 | ||
| @@ -729,12 +758,956 @@ impl Duty { | |||
| 729 | } | 758 | } |
| 730 | } | 759 | } |
| 731 | 760 | ||
| 761 | /// Result of attempting to send a byte in slave transmitter mode | ||
| 762 | #[derive(Debug, PartialEq)] | ||
| 763 | enum TransmitResult { | ||
| 764 | /// Byte sent and ACKed by master - continue transmission | ||
| 765 | Acknowledged, | ||
| 766 | /// Byte sent but NACKed by master - normal end of read transaction | ||
| 767 | NotAcknowledged, | ||
| 768 | /// STOP condition detected - master terminated transaction | ||
| 769 | Stopped, | ||
| 770 | /// RESTART condition detected - master starting new transaction | ||
| 771 | Restarted, | ||
| 772 | } | ||
| 773 | |||
| 774 | /// Result of attempting to receive a byte in slave receiver mode | ||
| 775 | #[derive(Debug, PartialEq)] | ||
| 776 | enum ReceiveResult { | ||
| 777 | /// Data byte successfully received | ||
| 778 | Data(u8), | ||
| 779 | /// STOP condition detected - end of write transaction | ||
| 780 | Stopped, | ||
| 781 | /// RESTART condition detected - master starting new transaction | ||
| 782 | Restarted, | ||
| 783 | } | ||
| 784 | |||
| 785 | /// Enumeration of slave transaction termination conditions | ||
| 786 | #[derive(Debug, Clone, Copy, PartialEq)] | ||
| 787 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 788 | enum SlaveTermination { | ||
| 789 | /// STOP condition received - normal end of transaction | ||
| 790 | Stop, | ||
| 791 | /// RESTART condition received - master starting new transaction | ||
| 792 | Restart, | ||
| 793 | /// NACK received - normal end of read transaction | ||
| 794 | Nack, | ||
| 795 | } | ||
| 796 | |||
| 797 | impl<'d, M: PeriMode> I2c<'d, M, Master> { | ||
| 798 | /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) | ||
| 799 | pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { | ||
| 800 | let mut slave = I2c { | ||
| 801 | info: self.info, | ||
| 802 | state: self.state, | ||
| 803 | kernel_clock: self.kernel_clock, | ||
| 804 | tx_dma: self.tx_dma.take(), // Use take() to move ownership | ||
| 805 | rx_dma: self.rx_dma.take(), // Use take() to move ownership | ||
| 806 | #[cfg(feature = "time")] | ||
| 807 | timeout: self.timeout, | ||
| 808 | _phantom: PhantomData, | ||
| 809 | _phantom2: PhantomData, | ||
| 810 | _drop_guard: self._drop_guard, // Move the drop guard | ||
| 811 | }; | ||
| 812 | slave.init_slave(slave_addr_config); | ||
| 813 | slave | ||
| 814 | } | ||
| 815 | } | ||
| 816 | |||
| 817 | // Address configuration methods | ||
| 818 | impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | ||
| 819 | /// Initialize slave mode with address configuration | ||
| 820 | pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { | ||
| 821 | trace!("I2C slave: initializing with config={:?}", config); | ||
| 822 | |||
| 823 | // Disable peripheral for configuration | ||
| 824 | self.info.regs.cr1().modify(|reg| reg.set_pe(false)); | ||
| 825 | |||
| 826 | // Configure slave addresses | ||
| 827 | self.apply_address_configuration(config); | ||
| 828 | |||
| 829 | // Enable peripheral with slave settings | ||
| 830 | self.info.regs.cr1().modify(|reg| { | ||
| 831 | reg.set_pe(true); | ||
| 832 | reg.set_ack(true); // Enable acknowledgment for slave mode | ||
| 833 | reg.set_nostretch(false); // Allow clock stretching for processing time | ||
| 834 | }); | ||
| 835 | |||
| 836 | trace!("I2C slave: initialization complete"); | ||
| 837 | } | ||
| 838 | |||
| 839 | /// Apply the complete address configuration for slave mode | ||
| 840 | fn apply_address_configuration(&mut self, config: SlaveAddrConfig) { | ||
| 841 | match config.addr { | ||
| 842 | OwnAddresses::OA1(addr) => { | ||
| 843 | self.configure_primary_address(addr); | ||
| 844 | self.disable_secondary_address(); | ||
| 845 | } | ||
| 846 | OwnAddresses::OA2(oa2) => { | ||
| 847 | self.configure_default_primary_address(); | ||
| 848 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 849 | } | ||
| 850 | OwnAddresses::Both { oa1, oa2 } => { | ||
| 851 | self.configure_primary_address(oa1); | ||
| 852 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 853 | } | ||
| 854 | } | ||
| 855 | |||
| 856 | // Configure general call detection | ||
| 857 | if config.general_call { | ||
| 858 | self.info.regs.cr1().modify(|w| w.set_engc(true)); | ||
| 859 | } | ||
| 860 | } | ||
| 861 | |||
| 862 | /// Configure the primary address (OA1) register | ||
| 863 | fn configure_primary_address(&mut self, addr: Address) { | ||
| 864 | match addr { | ||
| 865 | Address::SevenBit(addr) => { | ||
| 866 | self.info.regs.oar1().write(|reg| { | ||
| 867 | let hw_addr = (addr as u16) << 1; // Address in bits [7:1] | ||
| 868 | reg.set_add(hw_addr); | ||
| 869 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 870 | }); | ||
| 871 | } | ||
| 872 | Address::TenBit(addr) => { | ||
| 873 | self.info.regs.oar1().write(|reg| { | ||
| 874 | reg.set_add(addr); | ||
| 875 | reg.set_addmode(i2c::vals::Addmode::BIT10); | ||
| 876 | }); | ||
| 877 | } | ||
| 878 | } | ||
| 879 | |||
| 880 | // Set required bit 14 as per reference manual | ||
| 881 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 882 | } | ||
| 883 | |||
| 884 | /// Configure the secondary address (OA2) register | ||
| 885 | fn configure_secondary_address(&mut self, addr: u8) { | ||
| 886 | self.info.regs.oar2().write(|reg| { | ||
| 887 | reg.set_add2(addr); | ||
| 888 | reg.set_endual(i2c::vals::Endual::DUAL); | ||
| 889 | }); | ||
| 890 | } | ||
| 891 | |||
| 892 | /// Set a default primary address when using OA2-only mode | ||
| 893 | fn configure_default_primary_address(&mut self) { | ||
| 894 | self.info.regs.oar1().write(|reg| { | ||
| 895 | reg.set_add(0); // Reserved address, safe to use | ||
| 896 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 897 | }); | ||
| 898 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 899 | } | ||
| 900 | |||
| 901 | /// Disable secondary address when not needed | ||
| 902 | fn disable_secondary_address(&mut self) { | ||
| 903 | self.info.regs.oar2().write(|reg| { | ||
| 904 | reg.set_endual(i2c::vals::Endual::SINGLE); | ||
| 905 | }); | ||
| 906 | } | ||
| 907 | } | ||
| 908 | |||
| 909 | impl<'d, M: PeriMode> I2c<'d, M, MultiMaster> { | ||
| 910 | /// Listen for incoming I2C address match and return the command type | ||
| 911 | /// | ||
| 912 | /// This method blocks until the slave address is matched by a master. | ||
| 913 | /// Returns the command type (Read/Write) and the matched address. | ||
| 914 | pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 915 | trace!("I2C slave: starting blocking listen for address match"); | ||
| 916 | let result = self.blocking_listen_with_timeout(self.timeout()); | ||
| 917 | trace!("I2C slave: blocking listen complete, result={:?}", result); | ||
| 918 | result | ||
| 919 | } | ||
| 920 | |||
| 921 | /// Respond to a master read request by transmitting data | ||
| 922 | /// | ||
| 923 | /// Sends the provided data to the master. If the master requests more bytes | ||
| 924 | /// than available, padding bytes (0x00) are sent until the master terminates | ||
| 925 | /// the transaction with NACK. | ||
| 926 | /// | ||
| 927 | /// Returns the total number of bytes transmitted (including padding). | ||
| 928 | pub fn blocking_respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 929 | trace!("I2C slave: starting blocking respond_to_read, data_len={}", data.len()); | ||
| 930 | |||
| 931 | if let Some(zero_length_result) = self.detect_zero_length_read(self.timeout())? { | ||
| 932 | trace!("I2C slave: zero-length read detected"); | ||
| 933 | return Ok(zero_length_result); | ||
| 934 | } | ||
| 935 | |||
| 936 | let result = self.transmit_to_master(data, self.timeout()); | ||
| 937 | trace!("I2C slave: blocking respond_to_read complete, result={:?}", result); | ||
| 938 | result | ||
| 939 | } | ||
| 940 | |||
| 941 | /// Respond to a master write request by receiving data | ||
| 942 | /// | ||
| 943 | /// Receives data from the master into the provided buffer. If the master | ||
| 944 | /// sends more bytes than the buffer can hold, excess bytes are acknowledged | ||
| 945 | /// but discarded. | ||
| 946 | /// | ||
| 947 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 948 | pub fn blocking_respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 949 | trace!( | ||
| 950 | "I2C slave: starting blocking respond_to_write, buffer_len={}", | ||
| 951 | buffer.len() | ||
| 952 | ); | ||
| 953 | let result = self.receive_from_master(buffer, self.timeout()); | ||
| 954 | trace!("I2C slave: blocking respond_to_write complete, result={:?}", result); | ||
| 955 | result | ||
| 956 | } | ||
| 957 | |||
| 958 | // Private implementation methods | ||
| 959 | |||
| 960 | /// Wait for address match and determine transaction type | ||
| 961 | fn blocking_listen_with_timeout(&mut self, timeout: Timeout) -> Result<SlaveCommand, Error> { | ||
| 962 | // Ensure interrupts are disabled for blocking operation | ||
| 963 | self.disable_i2c_interrupts(); | ||
| 964 | |||
| 965 | // Wait for address match (ADDR flag) | ||
| 966 | loop { | ||
| 967 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 968 | |||
| 969 | if sr1.addr() { | ||
| 970 | // Address matched - read SR2 to get direction and clear ADDR flag | ||
| 971 | let sr2 = self.info.regs.sr2().read(); | ||
| 972 | let direction = if sr2.tra() { | ||
| 973 | SlaveCommandKind::Read | ||
| 974 | } else { | ||
| 975 | SlaveCommandKind::Write | ||
| 976 | }; | ||
| 977 | |||
| 978 | // Use the static method instead of the instance method | ||
| 979 | let matched_address = Self::decode_matched_address(sr2, self.info)?; | ||
| 980 | trace!( | ||
| 981 | "I2C slave: address matched, direction={:?}, addr={:?}", | ||
| 982 | direction, matched_address | ||
| 983 | ); | ||
| 984 | |||
| 985 | return Ok(SlaveCommand { | ||
| 986 | kind: direction, | ||
| 987 | address: matched_address, | ||
| 988 | }); | ||
| 989 | } | ||
| 990 | |||
| 991 | timeout.check()?; | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | /// Transmit data to master in response to read request | ||
| 996 | fn transmit_to_master(&mut self, data: &[u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 997 | let mut bytes_transmitted = 0; | ||
| 998 | let mut padding_count = 0; | ||
| 999 | |||
| 1000 | loop { | ||
| 1001 | let byte_to_send = if bytes_transmitted < data.len() { | ||
| 1002 | data[bytes_transmitted] | ||
| 1003 | } else { | ||
| 1004 | padding_count += 1; | ||
| 1005 | 0x00 // Send padding bytes when data is exhausted | ||
| 1006 | }; | ||
| 1007 | |||
| 1008 | match self.transmit_byte(byte_to_send, timeout)? { | ||
| 1009 | TransmitResult::Acknowledged => { | ||
| 1010 | bytes_transmitted += 1; | ||
| 1011 | } | ||
| 1012 | TransmitResult::NotAcknowledged => { | ||
| 1013 | bytes_transmitted += 1; // Count the NACKed byte | ||
| 1014 | break; | ||
| 1015 | } | ||
| 1016 | TransmitResult::Stopped | TransmitResult::Restarted => { | ||
| 1017 | break; | ||
| 1018 | } | ||
| 1019 | } | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | if padding_count > 0 { | ||
| 1023 | trace!( | ||
| 1024 | "I2C slave: sent {} data bytes + {} padding bytes = {} total", | ||
| 1025 | data.len(), | ||
| 1026 | padding_count, | ||
| 1027 | bytes_transmitted | ||
| 1028 | ); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | Ok(bytes_transmitted) | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | /// Receive data from master during write request | ||
| 1035 | fn receive_from_master(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 1036 | let mut bytes_stored = 0; | ||
| 1037 | |||
| 1038 | // Receive bytes that fit in buffer | ||
| 1039 | while bytes_stored < buffer.len() { | ||
| 1040 | match self.receive_byte(timeout)? { | ||
| 1041 | ReceiveResult::Data(byte) => { | ||
| 1042 | buffer[bytes_stored] = byte; | ||
| 1043 | bytes_stored += 1; | ||
| 1044 | } | ||
| 1045 | ReceiveResult::Stopped | ReceiveResult::Restarted => { | ||
| 1046 | return Ok(bytes_stored); | ||
| 1047 | } | ||
| 1048 | } | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | // Handle buffer overflow by discarding excess bytes | ||
| 1052 | if bytes_stored == buffer.len() { | ||
| 1053 | trace!("I2C slave: buffer full, discarding excess bytes"); | ||
| 1054 | self.discard_excess_bytes(timeout)?; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | Ok(bytes_stored) | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | /// Detect zero-length read pattern early | ||
| 1061 | /// | ||
| 1062 | /// Zero-length reads occur when a master sends START+ADDR+R followed immediately | ||
| 1063 | /// by NACK+STOP without wanting any data. This must be detected before attempting | ||
| 1064 | /// to transmit any bytes to avoid SDA line issues. | ||
| 1065 | fn detect_zero_length_read(&mut self, _timeout: Timeout) -> Result<Option<usize>, Error> { | ||
| 1066 | // Quick check for immediate termination signals | ||
| 1067 | let sr1 = self.info.regs.sr1().read(); | ||
| 1068 | |||
| 1069 | // Check for immediate NACK (fastest zero-length pattern) | ||
| 1070 | if sr1.af() { | ||
| 1071 | self.clear_acknowledge_failure(); | ||
| 1072 | return Ok(Some(0)); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | // Check for immediate STOP (alternative zero-length pattern) | ||
| 1076 | if sr1.stopf() { | ||
| 1077 | Self::clear_stop_flag(self.info); | ||
| 1078 | return Ok(Some(0)); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | // Give a brief window for master to send termination signals | ||
| 1082 | // This handles masters that have slight delays between address ACK and NACK | ||
| 1083 | const ZERO_LENGTH_DETECTION_CYCLES: u32 = 20; // ~5-10µs window | ||
| 1084 | |||
| 1085 | for _ in 0..ZERO_LENGTH_DETECTION_CYCLES { | ||
| 1086 | let sr1 = self.info.regs.sr1().read(); | ||
| 1087 | |||
| 1088 | // Immediate NACK indicates zero-length read | ||
| 1089 | if sr1.af() { | ||
| 1090 | self.clear_acknowledge_failure(); | ||
| 1091 | return Ok(Some(0)); | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | // Immediate STOP indicates zero-length read | ||
| 1095 | if sr1.stopf() { | ||
| 1096 | Self::clear_stop_flag(self.info); | ||
| 1097 | return Ok(Some(0)); | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | // If TXE becomes ready, master is waiting for data - not zero-length | ||
| 1101 | if sr1.txe() { | ||
| 1102 | return Ok(None); // Proceed with normal transmission | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | // If RESTART detected, handle as zero-length | ||
| 1106 | if sr1.addr() { | ||
| 1107 | return Ok(Some(0)); | ||
| 1108 | } | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | // No zero-length pattern detected within the window | ||
| 1112 | Ok(None) | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | /// Discard excess bytes when buffer is full | ||
| 1116 | fn discard_excess_bytes(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 1117 | let mut discarded_count = 0; | ||
| 1118 | |||
| 1119 | loop { | ||
| 1120 | match self.receive_byte(timeout)? { | ||
| 1121 | ReceiveResult::Data(_) => { | ||
| 1122 | discarded_count += 1; | ||
| 1123 | continue; | ||
| 1124 | } | ||
| 1125 | ReceiveResult::Stopped | ReceiveResult::Restarted => { | ||
| 1126 | if discarded_count > 0 { | ||
| 1127 | trace!("I2C slave: discarded {} excess bytes", discarded_count); | ||
| 1128 | } | ||
| 1129 | break; | ||
| 1130 | } | ||
| 1131 | } | ||
| 1132 | } | ||
| 1133 | Ok(()) | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | /// Send a single byte and wait for master's response | ||
| 1137 | fn transmit_byte(&mut self, byte: u8, timeout: Timeout) -> Result<TransmitResult, Error> { | ||
| 1138 | // Wait for transmit buffer ready | ||
| 1139 | self.wait_for_transmit_ready(timeout)?; | ||
| 1140 | |||
| 1141 | // Send the byte | ||
| 1142 | self.info.regs.dr().write(|w| w.set_dr(byte)); | ||
| 1143 | |||
| 1144 | // Wait for transmission completion or master response | ||
| 1145 | self.wait_for_transmit_completion(timeout) | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | /// Wait until transmit buffer is ready (TXE flag set) | ||
| 1149 | fn wait_for_transmit_ready(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 1150 | loop { | ||
| 1151 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 1152 | |||
| 1153 | // Check for early termination conditions | ||
| 1154 | if let Some(result) = Self::check_early_termination(sr1) { | ||
| 1155 | return Err(self.handle_early_termination(result)); | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | if sr1.txe() { | ||
| 1159 | return Ok(()); // Ready to transmit | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | timeout.check()?; | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | /// Wait for byte transmission completion or master response | ||
| 1167 | fn wait_for_transmit_completion(&mut self, timeout: Timeout) -> Result<TransmitResult, Error> { | ||
| 1168 | loop { | ||
| 1169 | let sr1 = self.info.regs.sr1().read(); | ||
| 1170 | |||
| 1171 | // Check flags in priority order | ||
| 1172 | if sr1.af() { | ||
| 1173 | self.clear_acknowledge_failure(); | ||
| 1174 | return Ok(TransmitResult::NotAcknowledged); | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | if sr1.btf() { | ||
| 1178 | return Ok(TransmitResult::Acknowledged); | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | if sr1.stopf() { | ||
| 1182 | Self::clear_stop_flag(self.info); | ||
| 1183 | return Ok(TransmitResult::Stopped); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | if sr1.addr() { | ||
| 1187 | return Ok(TransmitResult::Restarted); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | // Check for other error conditions | ||
| 1191 | self.check_for_hardware_errors(sr1)?; | ||
| 1192 | |||
| 1193 | timeout.check()?; | ||
| 1194 | } | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | /// Receive a single byte or detect transaction termination | ||
| 1198 | fn receive_byte(&mut self, timeout: Timeout) -> Result<ReceiveResult, Error> { | ||
| 1199 | loop { | ||
| 1200 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 1201 | |||
| 1202 | // Check for received data first (prioritize data over control signals) | ||
| 1203 | if sr1.rxne() { | ||
| 1204 | let byte = self.info.regs.dr().read().dr(); | ||
| 1205 | return Ok(ReceiveResult::Data(byte)); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | // Check for transaction termination | ||
| 1209 | if sr1.addr() { | ||
| 1210 | return Ok(ReceiveResult::Restarted); | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | if sr1.stopf() { | ||
| 1214 | Self::clear_stop_flag(self.info); | ||
| 1215 | return Ok(ReceiveResult::Stopped); | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | timeout.check()?; | ||
| 1219 | } | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | /// Determine which slave address was matched based on SR2 flags | ||
| 1223 | fn decode_matched_address(sr2: i2c::regs::Sr2, info: &'static Info) -> Result<Address, Error> { | ||
| 1224 | if sr2.gencall() { | ||
| 1225 | Ok(Address::SevenBit(0x00)) // General call address | ||
| 1226 | } else if sr2.dualf() { | ||
| 1227 | // OA2 (secondary address) was matched | ||
| 1228 | let oar2 = info.regs.oar2().read(); | ||
| 1229 | if oar2.endual() != i2c::vals::Endual::DUAL { | ||
| 1230 | return Err(Error::Bus); // Hardware inconsistency | ||
| 1231 | } | ||
| 1232 | Ok(Address::SevenBit(oar2.add2())) | ||
| 1233 | } else { | ||
| 1234 | // OA1 (primary address) was matched | ||
| 1235 | let oar1 = info.regs.oar1().read(); | ||
| 1236 | match oar1.addmode() { | ||
| 1237 | i2c::vals::Addmode::BIT7 => { | ||
| 1238 | let addr = (oar1.add() >> 1) as u8; | ||
| 1239 | Ok(Address::SevenBit(addr)) | ||
| 1240 | } | ||
| 1241 | i2c::vals::Addmode::BIT10 => Ok(Address::TenBit(oar1.add())), | ||
| 1242 | } | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | // Helper methods for hardware interaction | ||
| 1247 | |||
| 1248 | /// Read status register and handle I2C errors (except NACK in slave mode) | ||
| 1249 | fn read_status_and_handle_errors(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { | ||
| 1250 | match Self::check_and_clear_error_flags(info) { | ||
| 1251 | Ok(sr1) => Ok(sr1), | ||
| 1252 | Err(Error::Nack) => { | ||
| 1253 | // In slave mode, NACK is normal protocol behavior, not an error | ||
| 1254 | Ok(info.regs.sr1().read()) | ||
| 1255 | } | ||
| 1256 | Err(other_error) => Err(other_error), | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | /// Check for conditions that cause early termination of operations | ||
| 1261 | fn check_early_termination(sr1: i2c::regs::Sr1) -> Option<TransmitResult> { | ||
| 1262 | if sr1.stopf() { | ||
| 1263 | Some(TransmitResult::Stopped) | ||
| 1264 | } else if sr1.addr() { | ||
| 1265 | Some(TransmitResult::Restarted) | ||
| 1266 | } else if sr1.af() { | ||
| 1267 | Some(TransmitResult::NotAcknowledged) | ||
| 1268 | } else { | ||
| 1269 | None | ||
| 1270 | } | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | /// Convert early termination to appropriate error | ||
| 1274 | fn handle_early_termination(&mut self, result: TransmitResult) -> Error { | ||
| 1275 | match result { | ||
| 1276 | TransmitResult::Stopped => { | ||
| 1277 | Self::clear_stop_flag(self.info); | ||
| 1278 | Error::Bus // Unexpected STOP during setup | ||
| 1279 | } | ||
| 1280 | TransmitResult::Restarted => { | ||
| 1281 | Error::Bus // Unexpected RESTART during setup | ||
| 1282 | } | ||
| 1283 | TransmitResult::NotAcknowledged => { | ||
| 1284 | self.clear_acknowledge_failure(); | ||
| 1285 | Error::Bus // Unexpected NACK during setup | ||
| 1286 | } | ||
| 1287 | TransmitResult::Acknowledged => { | ||
| 1288 | unreachable!() // This should never be passed to this function | ||
| 1289 | } | ||
| 1290 | } | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | /// Check for hardware-level I2C errors during transmission | ||
| 1294 | fn check_for_hardware_errors(&self, sr1: i2c::regs::Sr1) -> Result<(), Error> { | ||
| 1295 | if sr1.timeout() || sr1.ovr() || sr1.arlo() || sr1.berr() { | ||
| 1296 | // Delegate to existing error handling | ||
| 1297 | Self::check_and_clear_error_flags(self.info)?; | ||
| 1298 | } | ||
| 1299 | Ok(()) | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | /// Disable I2C event and error interrupts for blocking operations | ||
| 1303 | fn disable_i2c_interrupts(&mut self) { | ||
| 1304 | self.info.regs.cr2().modify(|w| { | ||
| 1305 | w.set_itevten(false); | ||
| 1306 | w.set_iterren(false); | ||
| 1307 | }); | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | /// Clear the acknowledge failure flag | ||
| 1311 | fn clear_acknowledge_failure(&mut self) { | ||
| 1312 | self.info.regs.sr1().write(|reg| { | ||
| 1313 | reg.0 = !0; | ||
| 1314 | reg.set_af(false); | ||
| 1315 | }); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | /// Configure DMA settings for slave operations (shared between read/write) | ||
| 1319 | fn setup_slave_dma_base(&mut self) { | ||
| 1320 | self.info.regs.cr2().modify(|w| { | ||
| 1321 | w.set_itbufen(false); // Always disable buffer interrupts when using DMA | ||
| 1322 | w.set_dmaen(true); // Enable DMA requests | ||
| 1323 | w.set_last(false); // LAST bit not used in slave mode for v1 hardware | ||
| 1324 | }); | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | /// Disable DMA and interrupts in a critical section | ||
| 1328 | fn disable_dma_and_interrupts(info: &'static Info) { | ||
| 1329 | critical_section::with(|_| { | ||
| 1330 | info.regs.cr2().modify(|w| { | ||
| 1331 | w.set_dmaen(false); | ||
| 1332 | w.set_iterren(false); | ||
| 1333 | w.set_itevten(false); | ||
| 1334 | }); | ||
| 1335 | }); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | /// Check for early termination conditions during slave operations | ||
| 1339 | /// Returns Some(result) if termination detected, None to continue | ||
| 1340 | fn check_slave_termination_conditions(sr1: i2c::regs::Sr1) -> Option<SlaveTermination> { | ||
| 1341 | if sr1.stopf() { | ||
| 1342 | Some(SlaveTermination::Stop) | ||
| 1343 | } else if sr1.addr() { | ||
| 1344 | Some(SlaveTermination::Restart) | ||
| 1345 | } else if sr1.af() { | ||
| 1346 | Some(SlaveTermination::Nack) | ||
| 1347 | } else { | ||
| 1348 | None | ||
| 1349 | } | ||
| 1350 | } | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | impl<'d> I2c<'d, Async, MultiMaster> { | ||
| 1354 | /// Async listen for incoming I2C messages using interrupts | ||
| 1355 | /// | ||
| 1356 | /// Waits for a master to address this slave and returns the command type | ||
| 1357 | /// (Read/Write) and the matched address. This method will suspend until | ||
| 1358 | /// an address match occurs. | ||
| 1359 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 1360 | trace!("I2C slave: starting async listen for address match"); | ||
| 1361 | let state = self.state; | ||
| 1362 | let info = self.info; | ||
| 1363 | |||
| 1364 | Self::enable_interrupts(info); | ||
| 1365 | |||
| 1366 | let on_drop = OnDrop::new(|| { | ||
| 1367 | Self::disable_dma_and_interrupts(info); | ||
| 1368 | }); | ||
| 1369 | |||
| 1370 | let result = poll_fn(|cx| { | ||
| 1371 | state.waker.register(cx.waker()); | ||
| 1372 | |||
| 1373 | match Self::check_and_clear_error_flags(info) { | ||
| 1374 | Err(e) => { | ||
| 1375 | error!("I2C slave: error during listen: {:?}", e); | ||
| 1376 | Poll::Ready(Err(e)) | ||
| 1377 | } | ||
| 1378 | Ok(sr1) => { | ||
| 1379 | if sr1.addr() { | ||
| 1380 | let sr2 = info.regs.sr2().read(); | ||
| 1381 | let direction = if sr2.tra() { | ||
| 1382 | SlaveCommandKind::Read | ||
| 1383 | } else { | ||
| 1384 | SlaveCommandKind::Write | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | let matched_address = match Self::decode_matched_address(sr2, info) { | ||
| 1388 | Ok(addr) => { | ||
| 1389 | trace!("I2C slave: address matched, direction={:?}, addr={:?}", direction, addr); | ||
| 1390 | addr | ||
| 1391 | } | ||
| 1392 | Err(e) => { | ||
| 1393 | error!("I2C slave: failed to decode matched address: {:?}", e); | ||
| 1394 | return Poll::Ready(Err(e)); | ||
| 1395 | } | ||
| 1396 | }; | ||
| 1397 | |||
| 1398 | Poll::Ready(Ok(SlaveCommand { | ||
| 1399 | kind: direction, | ||
| 1400 | address: matched_address, | ||
| 1401 | })) | ||
| 1402 | } else { | ||
| 1403 | Self::enable_interrupts(info); | ||
| 1404 | Poll::Pending | ||
| 1405 | } | ||
| 1406 | } | ||
| 1407 | } | ||
| 1408 | }) | ||
| 1409 | .await; | ||
| 1410 | |||
| 1411 | drop(on_drop); | ||
| 1412 | trace!("I2C slave: listen complete, result={:?}", result); | ||
| 1413 | result | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | /// Async respond to write command using RX DMA | ||
| 1417 | /// | ||
| 1418 | /// Receives data from the master into the provided buffer using DMA. | ||
| 1419 | /// If the master sends more bytes than the buffer can hold, excess bytes | ||
| 1420 | /// are acknowledged but discarded to prevent interrupt flooding. | ||
| 1421 | /// | ||
| 1422 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 1423 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 1424 | trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); | ||
| 1425 | |||
| 1426 | if buffer.is_empty() { | ||
| 1427 | warn!("I2C slave: respond_to_write called with empty buffer"); | ||
| 1428 | return Err(Error::Overrun); | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | let state = self.state; | ||
| 1432 | let info = self.info; | ||
| 1433 | |||
| 1434 | self.setup_slave_dma_base(); | ||
| 1435 | |||
| 1436 | let on_drop = OnDrop::new(|| { | ||
| 1437 | Self::disable_dma_and_interrupts(info); | ||
| 1438 | }); | ||
| 1439 | |||
| 1440 | info.regs.sr2().read(); | ||
| 1441 | |||
| 1442 | let result = self.execute_slave_receive_transfer(buffer, state, info).await; | ||
| 1443 | |||
| 1444 | drop(on_drop); | ||
| 1445 | trace!("I2C slave: respond_to_write complete, result={:?}", result); | ||
| 1446 | result | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | /// Async respond to read command using TX DMA | ||
| 1450 | /// | ||
| 1451 | /// Transmits data to the master using DMA. If the master requests more bytes | ||
| 1452 | /// than available in the data buffer, padding bytes (0x00) are sent until | ||
| 1453 | /// the master terminates the transaction with NACK, STOP, or RESTART. | ||
| 1454 | /// | ||
| 1455 | /// Returns the total number of bytes transmitted (data + padding). | ||
| 1456 | pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 1457 | trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); | ||
| 1458 | |||
| 1459 | if data.is_empty() { | ||
| 1460 | warn!("I2C slave: respond_to_read called with empty data"); | ||
| 1461 | return Err(Error::Overrun); | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | let state = self.state; | ||
| 1465 | let info = self.info; | ||
| 1466 | |||
| 1467 | self.setup_slave_dma_base(); | ||
| 1468 | |||
| 1469 | let on_drop = OnDrop::new(|| { | ||
| 1470 | Self::disable_dma_and_interrupts(info); | ||
| 1471 | }); | ||
| 1472 | |||
| 1473 | info.regs.sr2().read(); | ||
| 1474 | |||
| 1475 | let result = self.execute_slave_transmit_transfer(data, state, info).await; | ||
| 1476 | |||
| 1477 | drop(on_drop); | ||
| 1478 | trace!("I2C slave: respond_to_read complete, result={:?}", result); | ||
| 1479 | result | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | // === Private Transfer Execution Methods === | ||
| 1483 | |||
| 1484 | /// Execute complete slave receive transfer with excess byte handling | ||
| 1485 | async fn execute_slave_receive_transfer( | ||
| 1486 | &mut self, | ||
| 1487 | buffer: &mut [u8], | ||
| 1488 | state: &'static State, | ||
| 1489 | info: &'static Info, | ||
| 1490 | ) -> Result<usize, Error> { | ||
| 1491 | let dma_transfer = unsafe { | ||
| 1492 | let src = info.regs.dr().as_ptr() as *mut u8; | ||
| 1493 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1494 | }; | ||
| 1495 | |||
| 1496 | let i2c_monitor = | ||
| 1497 | Self::create_termination_monitor(state, info, &[SlaveTermination::Stop, SlaveTermination::Restart]); | ||
| 1498 | |||
| 1499 | match select(dma_transfer, i2c_monitor).await { | ||
| 1500 | Either::Second(Err(e)) => { | ||
| 1501 | error!("I2C slave: error during receive transfer: {:?}", e); | ||
| 1502 | Self::disable_dma_and_interrupts(info); | ||
| 1503 | Err(e) | ||
| 1504 | } | ||
| 1505 | Either::First(_) => { | ||
| 1506 | trace!("I2C slave: DMA receive completed, handling excess bytes"); | ||
| 1507 | Self::disable_dma_and_interrupts(info); | ||
| 1508 | self.handle_excess_bytes(state, info).await?; | ||
| 1509 | Ok(buffer.len()) | ||
| 1510 | } | ||
| 1511 | Either::Second(Ok(termination)) => { | ||
| 1512 | trace!("I2C slave: receive terminated by I2C event: {:?}", termination); | ||
| 1513 | Self::disable_dma_and_interrupts(info); | ||
| 1514 | Ok(buffer.len()) | ||
| 1515 | } | ||
| 1516 | } | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | /// Execute complete slave transmit transfer with padding byte handling | ||
| 1520 | async fn execute_slave_transmit_transfer( | ||
| 1521 | &mut self, | ||
| 1522 | data: &[u8], | ||
| 1523 | state: &'static State, | ||
| 1524 | info: &'static Info, | ||
| 1525 | ) -> Result<usize, Error> { | ||
| 1526 | let dma_transfer = unsafe { | ||
| 1527 | let dst = info.regs.dr().as_ptr() as *mut u8; | ||
| 1528 | self.tx_dma.as_mut().unwrap().write(data, dst, Default::default()) | ||
| 1529 | }; | ||
| 1530 | |||
| 1531 | let i2c_monitor = Self::create_termination_monitor( | ||
| 1532 | state, | ||
| 1533 | info, | ||
| 1534 | &[ | ||
| 1535 | SlaveTermination::Stop, | ||
| 1536 | SlaveTermination::Restart, | ||
| 1537 | SlaveTermination::Nack, | ||
| 1538 | ], | ||
| 1539 | ); | ||
| 1540 | |||
| 1541 | match select(dma_transfer, i2c_monitor).await { | ||
| 1542 | Either::Second(Err(e)) => { | ||
| 1543 | error!("I2C slave: error during transmit transfer: {:?}", e); | ||
| 1544 | Self::disable_dma_and_interrupts(info); | ||
| 1545 | Err(e) | ||
| 1546 | } | ||
| 1547 | Either::First(_) => { | ||
| 1548 | trace!("I2C slave: DMA transmit completed, handling padding bytes"); | ||
| 1549 | Self::disable_dma_and_interrupts(info); | ||
| 1550 | let padding_count = self.handle_padding_bytes(state, info).await?; | ||
| 1551 | let total_bytes = data.len() + padding_count; | ||
| 1552 | trace!( | ||
| 1553 | "I2C slave: sent {} data bytes + {} padding bytes = {} total", | ||
| 1554 | data.len(), | ||
| 1555 | padding_count, | ||
| 1556 | total_bytes | ||
| 1557 | ); | ||
| 1558 | Ok(total_bytes) | ||
| 1559 | } | ||
| 1560 | Either::Second(Ok(termination)) => { | ||
| 1561 | trace!("I2C slave: transmit terminated by I2C event: {:?}", termination); | ||
| 1562 | Self::disable_dma_and_interrupts(info); | ||
| 1563 | Ok(data.len()) | ||
| 1564 | } | ||
| 1565 | } | ||
| 1566 | } | ||
| 1567 | |||
| 1568 | /// Create a future that monitors for specific slave termination conditions | ||
| 1569 | fn create_termination_monitor( | ||
| 1570 | state: &'static State, | ||
| 1571 | info: &'static Info, | ||
| 1572 | allowed_terminations: &'static [SlaveTermination], | ||
| 1573 | ) -> impl Future<Output = Result<SlaveTermination, Error>> { | ||
| 1574 | poll_fn(move |cx| { | ||
| 1575 | state.waker.register(cx.waker()); | ||
| 1576 | |||
| 1577 | match Self::check_and_clear_error_flags(info) { | ||
| 1578 | Err(Error::Nack) if allowed_terminations.contains(&SlaveTermination::Nack) => { | ||
| 1579 | Poll::Ready(Ok(SlaveTermination::Nack)) | ||
| 1580 | } | ||
| 1581 | Err(e) => Poll::Ready(Err(e)), | ||
| 1582 | Ok(sr1) => { | ||
| 1583 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1584 | if allowed_terminations.contains(&termination) { | ||
| 1585 | // Handle the specific termination condition | ||
| 1586 | match termination { | ||
| 1587 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1588 | SlaveTermination::Nack => { | ||
| 1589 | info.regs.sr1().write(|reg| { | ||
| 1590 | reg.0 = !0; | ||
| 1591 | reg.set_af(false); | ||
| 1592 | }); | ||
| 1593 | } | ||
| 1594 | SlaveTermination::Restart => { | ||
| 1595 | // ADDR flag will be handled by next listen() call | ||
| 1596 | } | ||
| 1597 | } | ||
| 1598 | Poll::Ready(Ok(termination)) | ||
| 1599 | } else { | ||
| 1600 | // Unexpected termination condition | ||
| 1601 | Poll::Ready(Err(Error::Bus)) | ||
| 1602 | } | ||
| 1603 | } else { | ||
| 1604 | Self::enable_interrupts(info); | ||
| 1605 | Poll::Pending | ||
| 1606 | } | ||
| 1607 | } | ||
| 1608 | } | ||
| 1609 | }) | ||
| 1610 | } | ||
| 1611 | |||
| 1612 | /// Handle excess bytes after DMA buffer is full | ||
| 1613 | /// | ||
| 1614 | /// Reads and discards bytes until transaction termination to prevent interrupt flooding | ||
| 1615 | async fn handle_excess_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<(), Error> { | ||
| 1616 | let mut discarded_count = 0; | ||
| 1617 | |||
| 1618 | poll_fn(|cx| { | ||
| 1619 | state.waker.register(cx.waker()); | ||
| 1620 | |||
| 1621 | match Self::check_and_clear_error_flags(info) { | ||
| 1622 | Err(e) => { | ||
| 1623 | error!("I2C slave: error while discarding excess bytes: {:?}", e); | ||
| 1624 | Poll::Ready(Err(e)) | ||
| 1625 | } | ||
| 1626 | Ok(sr1) => { | ||
| 1627 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1628 | match termination { | ||
| 1629 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1630 | SlaveTermination::Restart => {} | ||
| 1631 | SlaveTermination::Nack => unreachable!("NACK not expected during receive"), | ||
| 1632 | } | ||
| 1633 | if discarded_count > 0 { | ||
| 1634 | trace!("I2C slave: discarded {} excess bytes", discarded_count); | ||
| 1635 | } | ||
| 1636 | return Poll::Ready(Ok(())); | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | if sr1.rxne() { | ||
| 1640 | let _discarded_byte = info.regs.dr().read().dr(); | ||
| 1641 | discarded_count += 1; | ||
| 1642 | Self::enable_interrupts(info); | ||
| 1643 | return Poll::Pending; | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | Self::enable_interrupts(info); | ||
| 1647 | Poll::Pending | ||
| 1648 | } | ||
| 1649 | } | ||
| 1650 | }) | ||
| 1651 | .await | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | /// Handle padding bytes after DMA data is exhausted | ||
| 1655 | /// | ||
| 1656 | /// Sends 0x00 bytes until transaction termination to prevent interrupt flooding | ||
| 1657 | async fn handle_padding_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<usize, Error> { | ||
| 1658 | let mut padding_count = 0; | ||
| 1659 | |||
| 1660 | poll_fn(|cx| { | ||
| 1661 | state.waker.register(cx.waker()); | ||
| 1662 | |||
| 1663 | match Self::check_and_clear_error_flags(info) { | ||
| 1664 | Err(Error::Nack) => Poll::Ready(Ok(padding_count)), | ||
| 1665 | Err(e) => { | ||
| 1666 | error!("I2C slave: error while sending padding bytes: {:?}", e); | ||
| 1667 | Poll::Ready(Err(e)) | ||
| 1668 | } | ||
| 1669 | Ok(sr1) => { | ||
| 1670 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1671 | match termination { | ||
| 1672 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1673 | SlaveTermination::Restart => {} | ||
| 1674 | SlaveTermination::Nack => { | ||
| 1675 | info.regs.sr1().write(|reg| { | ||
| 1676 | reg.0 = !0; | ||
| 1677 | reg.set_af(false); | ||
| 1678 | }); | ||
| 1679 | } | ||
| 1680 | } | ||
| 1681 | return Poll::Ready(Ok(padding_count)); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | if sr1.txe() { | ||
| 1685 | info.regs.dr().write(|w| w.set_dr(0x00)); | ||
| 1686 | padding_count += 1; | ||
| 1687 | Self::enable_interrupts(info); | ||
| 1688 | return Poll::Pending; | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | Self::enable_interrupts(info); | ||
| 1692 | Poll::Pending | ||
| 1693 | } | ||
| 1694 | } | ||
| 1695 | }) | ||
| 1696 | .await | ||
| 1697 | } | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | /// Timing configuration for I2C v1 hardware | ||
| 1701 | /// | ||
| 1702 | /// This struct encapsulates the complex timing calculations required for STM32 I2C v1 | ||
| 1703 | /// peripherals, which use three separate registers (CR2.FREQ, CCR, TRISE) instead of | ||
| 1704 | /// the unified TIMINGR register found in v2 hardware. | ||
| 732 | struct Timings { | 1705 | struct Timings { |
| 733 | freq: u8, | 1706 | freq: u8, // APB frequency in MHz for CR2.FREQ register |
| 734 | mode: Mode, | 1707 | mode: Mode, // Standard or Fast mode selection |
| 735 | trise: u8, | 1708 | trise: u8, // Rise time compensation value |
| 736 | ccr: u16, | 1709 | ccr: u16, // Clock control register value |
| 737 | duty: Duty, | 1710 | duty: Duty, // Fast mode duty cycle selection |
| 738 | } | 1711 | } |
| 739 | 1712 | ||
| 740 | impl Timings { | 1713 | impl Timings { |
| @@ -762,11 +1735,7 @@ impl Timings { | |||
| 762 | mode = Mode::Standard; | 1735 | mode = Mode::Standard; |
| 763 | ccr = { | 1736 | ccr = { |
| 764 | let ccr = clock / (frequency * 2); | 1737 | let ccr = clock / (frequency * 2); |
| 765 | if ccr < 4 { | 1738 | if ccr < 4 { 4 } else { ccr } |
| 766 | 4 | ||
| 767 | } else { | ||
| 768 | ccr | ||
| 769 | } | ||
| 770 | }; | 1739 | }; |
| 771 | } else { | 1740 | } else { |
| 772 | const DUTYCYCLE: u8 = 0; | 1741 | const DUTYCYCLE: u8 = 0; |
| @@ -775,14 +1744,10 @@ impl Timings { | |||
| 775 | duty = Duty::Duty2_1; | 1744 | duty = Duty::Duty2_1; |
| 776 | ccr = clock / (frequency * 3); | 1745 | ccr = clock / (frequency * 3); |
| 777 | ccr = if ccr < 1 { 1 } else { ccr }; | 1746 | ccr = if ccr < 1 { 1 } else { ccr }; |
| 778 | |||
| 779 | // Set clock to fast mode with appropriate parameters for selected speed (2:1 duty cycle) | ||
| 780 | } else { | 1747 | } else { |
| 781 | duty = Duty::Duty16_9; | 1748 | duty = Duty::Duty16_9; |
| 782 | ccr = clock / (frequency * 25); | 1749 | ccr = clock / (frequency * 25); |
| 783 | ccr = if ccr < 1 { 1 } else { ccr }; | 1750 | ccr = if ccr < 1 { 1 } else { ccr }; |
| 784 | |||
| 785 | // Set clock to fast mode with appropriate parameters for selected speed (16:9 duty cycle) | ||
| 786 | } | 1751 | } |
| 787 | } | 1752 | } |
| 788 | 1753 | ||
| @@ -792,11 +1757,6 @@ impl Timings { | |||
| 792 | ccr: ccr as u16, | 1757 | ccr: ccr as u16, |
| 793 | duty, | 1758 | duty, |
| 794 | mode, | 1759 | mode, |
| 795 | //prescale: presc_reg, | ||
| 796 | //scll, | ||
| 797 | //sclh, | ||
| 798 | //sdadel, | ||
| 799 | //scldel, | ||
| 800 | } | 1760 | } |
| 801 | } | 1761 | } |
| 802 | } | 1762 | } |
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 0bfc795ac..61e550ad4 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -2,7 +2,7 @@ use core::cmp; | |||
| 2 | use core::future::poll_fn; | 2 | use core::future::poll_fn; |
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use config::{Address, OwnAddresses, OA2}; | 5 | use config::{Address, OA2, OwnAddresses}; |
| 6 | use embassy_embedded_hal::SetConfig; | 6 | use embassy_embedded_hal::SetConfig; |
| 7 | use embassy_hal_internal::drop::OnDrop; | 7 | use embassy_hal_internal::drop::OnDrop; |
| 8 | use embedded_hal_1::i2c::Operation; | 8 | use embedded_hal_1::i2c::Operation; |
| @@ -70,6 +70,11 @@ fn debug_print_interrupts(isr: stm32_metapac::i2c::regs::Isr) { | |||
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | pub(crate) unsafe fn on_interrupt<T: Instance>() { | 72 | pub(crate) unsafe fn on_interrupt<T: Instance>() { |
| 73 | // restore the clocks to their last configured state as | ||
| 74 | // much is lost in STOP modes | ||
| 75 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 76 | crate::low_power::Executor::on_wakeup_irq(); | ||
| 77 | |||
| 73 | let regs = T::info().regs; | 78 | let regs = T::info().regs; |
| 74 | let isr = regs.isr().read(); | 79 | let isr = regs.isr().read(); |
| 75 | 80 | ||
| @@ -93,6 +98,27 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() { | |||
| 93 | } | 98 | } |
| 94 | 99 | ||
| 95 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | 100 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { |
| 101 | #[inline] | ||
| 102 | fn to_reload(reload: bool) -> i2c::vals::Reload { | ||
| 103 | if reload { | ||
| 104 | i2c::vals::Reload::NOT_COMPLETED | ||
| 105 | } else { | ||
| 106 | i2c::vals::Reload::COMPLETED | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Calculate total bytes in a group of operations | ||
| 111 | #[inline] | ||
| 112 | fn total_operation_bytes(operations: &[Operation<'_>]) -> usize { | ||
| 113 | operations | ||
| 114 | .iter() | ||
| 115 | .map(|op| match op { | ||
| 116 | Operation::Write(buf) => buf.len(), | ||
| 117 | Operation::Read(buf) => buf.len(), | ||
| 118 | }) | ||
| 119 | .sum() | ||
| 120 | } | ||
| 121 | |||
| 96 | pub(crate) fn init(&mut self, config: Config) { | 122 | pub(crate) fn init(&mut self, config: Config) { |
| 97 | self.info.regs.cr1().modify(|reg| { | 123 | self.info.regs.cr1().modify(|reg| { |
| 98 | reg.set_pe(false); | 124 | reg.set_pe(false); |
| @@ -142,12 +168,6 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 142 | // `buffer`. The START bit can be set even if the bus | 168 | // `buffer`. The START bit can be set even if the bus |
| 143 | // is BUSY or I2C is in slave mode. | 169 | // is BUSY or I2C is in slave mode. |
| 144 | 170 | ||
| 145 | let reload = if reload { | ||
| 146 | i2c::vals::Reload::NOT_COMPLETED | ||
| 147 | } else { | ||
| 148 | i2c::vals::Reload::COMPLETED | ||
| 149 | }; | ||
| 150 | |||
| 151 | info.regs.cr2().modify(|w| { | 171 | info.regs.cr2().modify(|w| { |
| 152 | w.set_sadd(address.addr() << 1); | 172 | w.set_sadd(address.addr() << 1); |
| 153 | w.set_add10(address.add_mode()); | 173 | w.set_add10(address.add_mode()); |
| @@ -155,7 +175,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 155 | w.set_nbytes(length as u8); | 175 | w.set_nbytes(length as u8); |
| 156 | w.set_start(true); | 176 | w.set_start(true); |
| 157 | w.set_autoend(stop.autoend()); | 177 | w.set_autoend(stop.autoend()); |
| 158 | w.set_reload(reload); | 178 | w.set_reload(Self::to_reload(reload)); |
| 159 | }); | 179 | }); |
| 160 | 180 | ||
| 161 | Ok(()) | 181 | Ok(()) |
| @@ -167,28 +187,25 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 167 | length: usize, | 187 | length: usize, |
| 168 | stop: Stop, | 188 | stop: Stop, |
| 169 | reload: bool, | 189 | reload: bool, |
| 190 | restart: bool, | ||
| 170 | timeout: Timeout, | 191 | timeout: Timeout, |
| 171 | ) -> Result<(), Error> { | 192 | ) -> Result<(), Error> { |
| 172 | assert!(length < 256); | 193 | assert!(length < 256); |
| 173 | 194 | ||
| 174 | // Wait for any previous address sequence to end | 195 | if !restart { |
| 175 | // automatically. This could be up to 50% of a bus | 196 | // Wait for any previous address sequence to end |
| 176 | // cycle (ie. up to 0.5/freq) | 197 | // automatically. This could be up to 50% of a bus |
| 177 | while info.regs.cr2().read().start() { | 198 | // cycle (ie. up to 0.5/freq) |
| 178 | timeout.check()?; | 199 | while info.regs.cr2().read().start() { |
| 179 | } | 200 | timeout.check()?; |
| 201 | } | ||
| 180 | 202 | ||
| 181 | // Wait for the bus to be free | 203 | // Wait for the bus to be free |
| 182 | while info.regs.isr().read().busy() { | 204 | while info.regs.isr().read().busy() { |
| 183 | timeout.check()?; | 205 | timeout.check()?; |
| 206 | } | ||
| 184 | } | 207 | } |
| 185 | 208 | ||
| 186 | let reload = if reload { | ||
| 187 | i2c::vals::Reload::NOT_COMPLETED | ||
| 188 | } else { | ||
| 189 | i2c::vals::Reload::COMPLETED | ||
| 190 | }; | ||
| 191 | |||
| 192 | // Set START and prepare to send `bytes`. The | 209 | // Set START and prepare to send `bytes`. The |
| 193 | // START bit can be set even if the bus is BUSY or | 210 | // START bit can be set even if the bus is BUSY or |
| 194 | // I2C is in slave mode. | 211 | // I2C is in slave mode. |
| @@ -199,28 +216,36 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 199 | w.set_nbytes(length as u8); | 216 | w.set_nbytes(length as u8); |
| 200 | w.set_start(true); | 217 | w.set_start(true); |
| 201 | w.set_autoend(stop.autoend()); | 218 | w.set_autoend(stop.autoend()); |
| 202 | w.set_reload(reload); | 219 | w.set_reload(Self::to_reload(reload)); |
| 203 | }); | 220 | }); |
| 204 | 221 | ||
| 205 | Ok(()) | 222 | Ok(()) |
| 206 | } | 223 | } |
| 207 | 224 | ||
| 208 | fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> { | 225 | fn reload( |
| 226 | info: &'static Info, | ||
| 227 | length: usize, | ||
| 228 | will_reload: bool, | ||
| 229 | stop: Stop, | ||
| 230 | timeout: Timeout, | ||
| 231 | ) -> Result<(), Error> { | ||
| 209 | assert!(length < 256 && length > 0); | 232 | assert!(length < 256 && length > 0); |
| 210 | 233 | ||
| 211 | while !info.regs.isr().read().tcr() { | 234 | // Wait for either TCR (Transfer Complete Reload) or TC (Transfer Complete) |
| 235 | // TCR occurs when RELOAD=1, TC occurs when RELOAD=0 | ||
| 236 | // Both indicate the peripheral is ready for the next transfer | ||
| 237 | loop { | ||
| 238 | let isr = info.regs.isr().read(); | ||
| 239 | if isr.tcr() || isr.tc() { | ||
| 240 | break; | ||
| 241 | } | ||
| 212 | timeout.check()?; | 242 | timeout.check()?; |
| 213 | } | 243 | } |
| 214 | 244 | ||
| 215 | let will_reload = if will_reload { | ||
| 216 | i2c::vals::Reload::NOT_COMPLETED | ||
| 217 | } else { | ||
| 218 | i2c::vals::Reload::COMPLETED | ||
| 219 | }; | ||
| 220 | |||
| 221 | info.regs.cr2().modify(|w| { | 245 | info.regs.cr2().modify(|w| { |
| 222 | w.set_nbytes(length as u8); | 246 | w.set_nbytes(length as u8); |
| 223 | w.set_reload(will_reload); | 247 | w.set_reload(Self::to_reload(will_reload)); |
| 248 | w.set_autoend(stop.autoend()); | ||
| 224 | }); | 249 | }); |
| 225 | 250 | ||
| 226 | Ok(()) | 251 | Ok(()) |
| @@ -364,7 +389,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 364 | loop { | 389 | loop { |
| 365 | let isr = self.info.regs.isr().read(); | 390 | let isr = self.info.regs.isr().read(); |
| 366 | self.error_occurred(&isr, timeout)?; | 391 | self.error_occurred(&isr, timeout)?; |
| 367 | if isr.tc() { | 392 | // Wait for either TC or TCR - both indicate transfer completion |
| 393 | // TCR occurs when RELOAD=1, TC occurs when RELOAD=0 | ||
| 394 | if isr.tc() || isr.tcr() { | ||
| 368 | return Ok(()); | 395 | return Ok(()); |
| 369 | } | 396 | } |
| 370 | timeout.check()?; | 397 | timeout.check()?; |
| @@ -391,14 +418,20 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 391 | address, | 418 | address, |
| 392 | read.len().min(255), | 419 | read.len().min(255), |
| 393 | Stop::Automatic, | 420 | Stop::Automatic, |
| 394 | last_chunk_idx != 0, | 421 | last_chunk_idx != 0, // reload |
| 395 | restart, | 422 | restart, |
| 396 | timeout, | 423 | timeout, |
| 397 | )?; | 424 | )?; |
| 398 | 425 | ||
| 399 | for (number, chunk) in read.chunks_mut(255).enumerate() { | 426 | for (number, chunk) in read.chunks_mut(255).enumerate() { |
| 400 | if number != 0 { | 427 | if number != 0 { |
| 401 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 428 | Self::reload( |
| 429 | self.info, | ||
| 430 | chunk.len(), | ||
| 431 | number != last_chunk_idx, | ||
| 432 | Stop::Automatic, | ||
| 433 | timeout, | ||
| 434 | )?; | ||
| 402 | } | 435 | } |
| 403 | 436 | ||
| 404 | for byte in chunk { | 437 | for byte in chunk { |
| @@ -436,6 +469,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 436 | write.len().min(255), | 469 | write.len().min(255), |
| 437 | Stop::Software, | 470 | Stop::Software, |
| 438 | last_chunk_idx != 0, | 471 | last_chunk_idx != 0, |
| 472 | false, // restart | ||
| 439 | timeout, | 473 | timeout, |
| 440 | ) { | 474 | ) { |
| 441 | if send_stop { | 475 | if send_stop { |
| @@ -446,7 +480,13 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 446 | 480 | ||
| 447 | for (number, chunk) in write.chunks(255).enumerate() { | 481 | for (number, chunk) in write.chunks(255).enumerate() { |
| 448 | if number != 0 { | 482 | if number != 0 { |
| 449 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 483 | Self::reload( |
| 484 | self.info, | ||
| 485 | chunk.len(), | ||
| 486 | number != last_chunk_idx, | ||
| 487 | Stop::Software, | ||
| 488 | timeout, | ||
| 489 | )?; | ||
| 450 | } | 490 | } |
| 451 | 491 | ||
| 452 | for byte in chunk { | 492 | for byte in chunk { |
| @@ -502,9 +542,215 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 502 | /// | 542 | /// |
| 503 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 543 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 504 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 544 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 505 | let _ = addr; | 545 | if operations.is_empty() { |
| 506 | let _ = operations; | 546 | return Err(Error::ZeroLengthTransfer); |
| 507 | todo!() | 547 | } |
| 548 | |||
| 549 | let address = addr.into(); | ||
| 550 | let timeout = self.timeout(); | ||
| 551 | |||
| 552 | // Group consecutive operations of the same type | ||
| 553 | let mut op_idx = 0; | ||
| 554 | let mut is_first_group = true; | ||
| 555 | |||
| 556 | while op_idx < operations.len() { | ||
| 557 | // Determine the type of current group and find all consecutive operations of same type | ||
| 558 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 559 | let group_start = op_idx; | ||
| 560 | |||
| 561 | // Find end of this group (consecutive operations of same type) | ||
| 562 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 563 | op_idx += 1; | ||
| 564 | } | ||
| 565 | let group_end = op_idx; | ||
| 566 | let is_last_group = op_idx >= operations.len(); | ||
| 567 | |||
| 568 | // Execute this group of operations | ||
| 569 | if is_read { | ||
| 570 | self.execute_read_group( | ||
| 571 | address, | ||
| 572 | &mut operations[group_start..group_end], | ||
| 573 | is_first_group, | ||
| 574 | is_last_group, | ||
| 575 | timeout, | ||
| 576 | )?; | ||
| 577 | } else { | ||
| 578 | self.execute_write_group( | ||
| 579 | address, | ||
| 580 | &operations[group_start..group_end], | ||
| 581 | is_first_group, | ||
| 582 | is_last_group, | ||
| 583 | timeout, | ||
| 584 | )?; | ||
| 585 | } | ||
| 586 | |||
| 587 | is_first_group = false; | ||
| 588 | } | ||
| 589 | |||
| 590 | Ok(()) | ||
| 591 | } | ||
| 592 | |||
| 593 | fn execute_write_group( | ||
| 594 | &mut self, | ||
| 595 | address: Address, | ||
| 596 | operations: &[Operation<'_>], | ||
| 597 | is_first_group: bool, | ||
| 598 | is_last_group: bool, | ||
| 599 | timeout: Timeout, | ||
| 600 | ) -> Result<(), Error> { | ||
| 601 | // Calculate total bytes across all operations in this group | ||
| 602 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 603 | |||
| 604 | if total_bytes == 0 { | ||
| 605 | // Handle empty write group - just send address | ||
| 606 | if is_first_group { | ||
| 607 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 608 | } | ||
| 609 | if is_last_group { | ||
| 610 | self.master_stop(); | ||
| 611 | } | ||
| 612 | return Ok(()); | ||
| 613 | } | ||
| 614 | |||
| 615 | let mut total_remaining = total_bytes; | ||
| 616 | let mut first_chunk = true; | ||
| 617 | |||
| 618 | for operation in operations { | ||
| 619 | if let Operation::Write(buffer) = operation { | ||
| 620 | for chunk in buffer.chunks(255) { | ||
| 621 | let chunk_len = chunk.len(); | ||
| 622 | total_remaining -= chunk_len; | ||
| 623 | let is_last_chunk = total_remaining == 0; | ||
| 624 | let will_reload = !is_last_chunk; | ||
| 625 | |||
| 626 | if first_chunk { | ||
| 627 | // First chunk: initiate transfer | ||
| 628 | // If not first group, use RESTART instead of START | ||
| 629 | Self::master_write( | ||
| 630 | self.info, | ||
| 631 | address, | ||
| 632 | chunk_len, | ||
| 633 | Stop::Software, | ||
| 634 | will_reload, | ||
| 635 | !is_first_group, | ||
| 636 | timeout, | ||
| 637 | )?; | ||
| 638 | first_chunk = false; | ||
| 639 | } else { | ||
| 640 | // Subsequent chunks: use reload | ||
| 641 | // Always use Software stop for writes | ||
| 642 | Self::reload(self.info, chunk_len, will_reload, Stop::Software, timeout)?; | ||
| 643 | } | ||
| 644 | |||
| 645 | // Send data bytes | ||
| 646 | for byte in chunk { | ||
| 647 | self.wait_txis(timeout)?; | ||
| 648 | self.info.regs.txdr().write(|w| w.set_txdata(*byte)); | ||
| 649 | } | ||
| 650 | } | ||
| 651 | } | ||
| 652 | } | ||
| 653 | |||
| 654 | // Wait for transfer to complete | ||
| 655 | if is_last_group { | ||
| 656 | self.wait_tc(timeout)?; | ||
| 657 | self.master_stop(); | ||
| 658 | self.wait_stop(timeout)?; | ||
| 659 | } else { | ||
| 660 | // Wait for TC before next group (enables RESTART) | ||
| 661 | self.wait_tc(timeout)?; | ||
| 662 | } | ||
| 663 | |||
| 664 | Ok(()) | ||
| 665 | } | ||
| 666 | |||
| 667 | fn execute_read_group( | ||
| 668 | &mut self, | ||
| 669 | address: Address, | ||
| 670 | operations: &mut [Operation<'_>], | ||
| 671 | is_first_group: bool, | ||
| 672 | is_last_group: bool, | ||
| 673 | timeout: Timeout, | ||
| 674 | ) -> Result<(), Error> { | ||
| 675 | // Calculate total bytes across all operations in this group | ||
| 676 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 677 | |||
| 678 | if total_bytes == 0 { | ||
| 679 | // Handle empty read group | ||
| 680 | if is_first_group { | ||
| 681 | Self::master_read( | ||
| 682 | self.info, | ||
| 683 | address, | ||
| 684 | 0, | ||
| 685 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 686 | false, // reload | ||
| 687 | !is_first_group, | ||
| 688 | timeout, | ||
| 689 | )?; | ||
| 690 | } | ||
| 691 | if is_last_group { | ||
| 692 | self.wait_stop(timeout)?; | ||
| 693 | } | ||
| 694 | return Ok(()); | ||
| 695 | } | ||
| 696 | |||
| 697 | let mut total_remaining = total_bytes; | ||
| 698 | let mut first_chunk = true; | ||
| 699 | |||
| 700 | for operation in operations { | ||
| 701 | if let Operation::Read(buffer) = operation { | ||
| 702 | for chunk in buffer.chunks_mut(255) { | ||
| 703 | let chunk_len = chunk.len(); | ||
| 704 | total_remaining -= chunk_len; | ||
| 705 | let is_last_chunk = total_remaining == 0; | ||
| 706 | let will_reload = !is_last_chunk; | ||
| 707 | |||
| 708 | if first_chunk { | ||
| 709 | // First chunk: initiate transfer | ||
| 710 | let stop = if is_last_group && is_last_chunk { | ||
| 711 | Stop::Automatic | ||
| 712 | } else { | ||
| 713 | Stop::Software | ||
| 714 | }; | ||
| 715 | |||
| 716 | Self::master_read( | ||
| 717 | self.info, | ||
| 718 | address, | ||
| 719 | chunk_len, | ||
| 720 | stop, | ||
| 721 | will_reload, | ||
| 722 | !is_first_group, // restart if not first group | ||
| 723 | timeout, | ||
| 724 | )?; | ||
| 725 | first_chunk = false; | ||
| 726 | } else { | ||
| 727 | // Subsequent chunks: use reload | ||
| 728 | let stop = if is_last_group && is_last_chunk { | ||
| 729 | Stop::Automatic | ||
| 730 | } else { | ||
| 731 | Stop::Software | ||
| 732 | }; | ||
| 733 | Self::reload(self.info, chunk_len, will_reload, stop, timeout)?; | ||
| 734 | } | ||
| 735 | |||
| 736 | // Receive data bytes | ||
| 737 | for byte in chunk { | ||
| 738 | self.wait_rxne(timeout)?; | ||
| 739 | *byte = self.info.regs.rxdr().read().rxdata(); | ||
| 740 | } | ||
| 741 | } | ||
| 742 | } | ||
| 743 | } | ||
| 744 | |||
| 745 | // Wait for transfer to complete | ||
| 746 | if is_last_group { | ||
| 747 | self.wait_stop(timeout)?; | ||
| 748 | } | ||
| 749 | // For non-last read groups, don't wait for TC - the peripheral may hold SCL low | ||
| 750 | // in Software AUTOEND mode until the next START is issued. Just proceed directly | ||
| 751 | // to the next group which will issue RESTART and release the clock. | ||
| 752 | |||
| 753 | Ok(()) | ||
| 508 | } | 754 | } |
| 509 | 755 | ||
| 510 | /// Blocking write multiple buffers. | 756 | /// Blocking write multiple buffers. |
| @@ -526,6 +772,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 526 | first_length.min(255), | 772 | first_length.min(255), |
| 527 | Stop::Software, | 773 | Stop::Software, |
| 528 | (first_length > 255) || (last_slice_index != 0), | 774 | (first_length > 255) || (last_slice_index != 0), |
| 775 | false, // restart | ||
| 529 | timeout, | 776 | timeout, |
| 530 | ) { | 777 | ) { |
| 531 | self.master_stop(); | 778 | self.master_stop(); |
| @@ -547,6 +794,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 547 | self.info, | 794 | self.info, |
| 548 | slice_len.min(255), | 795 | slice_len.min(255), |
| 549 | (idx != last_slice_index) || (slice_len > 255), | 796 | (idx != last_slice_index) || (slice_len > 255), |
| 797 | Stop::Software, | ||
| 550 | timeout, | 798 | timeout, |
| 551 | ) { | 799 | ) { |
| 552 | if err != Error::Nack { | 800 | if err != Error::Nack { |
| @@ -562,6 +810,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 562 | self.info, | 810 | self.info, |
| 563 | chunk.len(), | 811 | chunk.len(), |
| 564 | (number != last_chunk_idx) || (idx != last_slice_index), | 812 | (number != last_chunk_idx) || (idx != last_slice_index), |
| 813 | Stop::Software, | ||
| 565 | timeout, | 814 | timeout, |
| 566 | ) { | 815 | ) { |
| 567 | if err != Error::Nack { | 816 | if err != Error::Nack { |
| @@ -605,6 +854,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 605 | first_slice: bool, | 854 | first_slice: bool, |
| 606 | last_slice: bool, | 855 | last_slice: bool, |
| 607 | send_stop: bool, | 856 | send_stop: bool, |
| 857 | restart: bool, | ||
| 608 | timeout: Timeout, | 858 | timeout: Timeout, |
| 609 | ) -> Result<(), Error> { | 859 | ) -> Result<(), Error> { |
| 610 | let total_len = write.len(); | 860 | let total_len = write.len(); |
| @@ -671,10 +921,17 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 671 | total_len.min(255), | 921 | total_len.min(255), |
| 672 | Stop::Software, | 922 | Stop::Software, |
| 673 | (total_len > 255) || !last_slice, | 923 | (total_len > 255) || !last_slice, |
| 924 | restart, | ||
| 674 | timeout, | 925 | timeout, |
| 675 | )?; | 926 | )?; |
| 676 | } else { | 927 | } else { |
| 677 | Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; | 928 | Self::reload( |
| 929 | self.info, | ||
| 930 | total_len.min(255), | ||
| 931 | (total_len > 255) || !last_slice, | ||
| 932 | Stop::Software, | ||
| 933 | timeout, | ||
| 934 | )?; | ||
| 678 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 935 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 679 | } | 936 | } |
| 680 | } else if !(isr.tcr() || isr.tc()) { | 937 | } else if !(isr.tcr() || isr.tc()) { |
| @@ -683,9 +940,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 683 | } else if remaining_len == 0 { | 940 | } else if remaining_len == 0 { |
| 684 | return Poll::Ready(Ok(())); | 941 | return Poll::Ready(Ok(())); |
| 685 | } else { | 942 | } else { |
| 686 | let last_piece = (remaining_len <= 255) && last_slice; | 943 | if let Err(e) = Self::reload( |
| 687 | 944 | self.info, | |
| 688 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { | 945 | remaining_len.min(255), |
| 946 | (remaining_len > 255) || !last_slice, | ||
| 947 | Stop::Software, | ||
| 948 | timeout, | ||
| 949 | ) { | ||
| 689 | return Poll::Ready(Err(e)); | 950 | return Poll::Ready(Err(e)); |
| 690 | } | 951 | } |
| 691 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 952 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| @@ -697,10 +958,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 697 | .await?; | 958 | .await?; |
| 698 | 959 | ||
| 699 | dma_transfer.await; | 960 | dma_transfer.await; |
| 700 | if last_slice { | 961 | |
| 701 | // This should be done already | 962 | // Always wait for TC after DMA completes - needed for consecutive buffers |
| 702 | self.wait_tc(timeout)?; | 963 | self.wait_tc(timeout)?; |
| 703 | } | ||
| 704 | 964 | ||
| 705 | if last_slice & send_stop { | 965 | if last_slice & send_stop { |
| 706 | self.master_stop(); | 966 | self.master_stop(); |
| @@ -775,7 +1035,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 775 | address, | 1035 | address, |
| 776 | total_len.min(255), | 1036 | total_len.min(255), |
| 777 | Stop::Automatic, | 1037 | Stop::Automatic, |
| 778 | total_len > 255, | 1038 | total_len > 255, // reload |
| 779 | restart, | 1039 | restart, |
| 780 | timeout, | 1040 | timeout, |
| 781 | )?; | 1041 | )?; |
| @@ -783,12 +1043,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 783 | return Poll::Ready(Ok(())); | 1043 | return Poll::Ready(Ok(())); |
| 784 | } | 1044 | } |
| 785 | } else if isr.tcr() { | 1045 | } else if isr.tcr() { |
| 786 | // poll_fn was woken without an interrupt present | 1046 | // Transfer Complete Reload - need to set up next chunk |
| 787 | return Poll::Pending; | ||
| 788 | } else { | ||
| 789 | let last_piece = remaining_len <= 255; | 1047 | let last_piece = remaining_len <= 255; |
| 790 | 1048 | ||
| 791 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { | 1049 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Automatic, timeout) { |
| 792 | return Poll::Ready(Err(e)); | 1050 | return Poll::Ready(Err(e)); |
| 793 | } | 1051 | } |
| 794 | // Return here if we are on last chunk, | 1052 | // Return here if we are on last chunk, |
| @@ -797,6 +1055,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 797 | return Poll::Ready(Ok(())); | 1055 | return Poll::Ready(Ok(())); |
| 798 | } | 1056 | } |
| 799 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 1057 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 1058 | } else { | ||
| 1059 | // poll_fn was woken without TCR interrupt | ||
| 1060 | return Poll::Pending; | ||
| 800 | } | 1061 | } |
| 801 | 1062 | ||
| 802 | remaining_len = remaining_len.saturating_sub(255); | 1063 | remaining_len = remaining_len.saturating_sub(255); |
| @@ -814,12 +1075,14 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 814 | 1075 | ||
| 815 | /// Write. | 1076 | /// Write. |
| 816 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 1077 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 1078 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 1079 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 817 | let timeout = self.timeout(); | 1080 | let timeout = self.timeout(); |
| 818 | if write.is_empty() { | 1081 | if write.is_empty() { |
| 819 | self.write_internal(address.into(), write, true, timeout) | 1082 | self.write_internal(address.into(), write, true, timeout) |
| 820 | } else { | 1083 | } else { |
| 821 | timeout | 1084 | timeout |
| 822 | .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) | 1085 | .with(self.write_dma_internal(address.into(), write, true, true, true, false, timeout)) |
| 823 | .await | 1086 | .await |
| 824 | } | 1087 | } |
| 825 | } | 1088 | } |
| @@ -828,21 +1091,31 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 828 | /// | 1091 | /// |
| 829 | /// The buffers are concatenated in a single write transaction. | 1092 | /// The buffers are concatenated in a single write transaction. |
| 830 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { | 1093 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { |
| 1094 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 1095 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 831 | let timeout = self.timeout(); | 1096 | let timeout = self.timeout(); |
| 832 | 1097 | ||
| 833 | if write.is_empty() { | 1098 | if write.is_empty() { |
| 834 | return Err(Error::ZeroLengthTransfer); | 1099 | return Err(Error::ZeroLengthTransfer); |
| 835 | } | 1100 | } |
| 836 | let mut iter = write.iter(); | ||
| 837 | 1101 | ||
| 1102 | let mut iter = write.iter(); | ||
| 838 | let mut first = true; | 1103 | let mut first = true; |
| 839 | let mut current = iter.next(); | 1104 | let mut current = iter.next(); |
| 1105 | |||
| 840 | while let Some(c) = current { | 1106 | while let Some(c) = current { |
| 841 | let next = iter.next(); | 1107 | let next = iter.next(); |
| 842 | let is_last = next.is_none(); | 1108 | let is_last = next.is_none(); |
| 843 | 1109 | ||
| 844 | let fut = self.write_dma_internal(address, c, first, is_last, is_last, timeout); | 1110 | let fut = self.write_dma_internal( |
| 1111 | address, c, first, // first_slice | ||
| 1112 | is_last, // last_slice | ||
| 1113 | is_last, // send_stop (only on last buffer) | ||
| 1114 | false, // restart (false for all - they're one continuous write) | ||
| 1115 | timeout, | ||
| 1116 | ); | ||
| 845 | timeout.with(fut).await?; | 1117 | timeout.with(fut).await?; |
| 1118 | |||
| 846 | first = false; | 1119 | first = false; |
| 847 | current = next; | 1120 | current = next; |
| 848 | } | 1121 | } |
| @@ -851,6 +1124,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 851 | 1124 | ||
| 852 | /// Read. | 1125 | /// Read. |
| 853 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 1126 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| 1127 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 1128 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 854 | let timeout = self.timeout(); | 1129 | let timeout = self.timeout(); |
| 855 | 1130 | ||
| 856 | if buffer.is_empty() { | 1131 | if buffer.is_empty() { |
| @@ -863,12 +1138,14 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 863 | 1138 | ||
| 864 | /// Write, restart, read. | 1139 | /// Write, restart, read. |
| 865 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 1140 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 1141 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 1142 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 866 | let timeout = self.timeout(); | 1143 | let timeout = self.timeout(); |
| 867 | 1144 | ||
| 868 | if write.is_empty() { | 1145 | if write.is_empty() { |
| 869 | self.write_internal(address.into(), write, false, timeout)?; | 1146 | self.write_internal(address.into(), write, false, timeout)?; |
| 870 | } else { | 1147 | } else { |
| 871 | let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); | 1148 | let fut = self.write_dma_internal(address.into(), write, true, true, false, false, timeout); |
| 872 | timeout.with(fut).await?; | 1149 | timeout.with(fut).await?; |
| 873 | } | 1150 | } |
| 874 | 1151 | ||
| @@ -888,9 +1165,301 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 888 | /// | 1165 | /// |
| 889 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 1166 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 890 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 1167 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 891 | let _ = addr; | 1168 | #[cfg(all(feature = "low-power", stm32wlex))] |
| 892 | let _ = operations; | 1169 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); |
| 893 | todo!() | 1170 | |
| 1171 | if operations.is_empty() { | ||
| 1172 | return Err(Error::ZeroLengthTransfer); | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | let address = addr.into(); | ||
| 1176 | let timeout = self.timeout(); | ||
| 1177 | |||
| 1178 | // Group consecutive operations of the same type | ||
| 1179 | let mut op_idx = 0; | ||
| 1180 | let mut is_first_group = true; | ||
| 1181 | |||
| 1182 | while op_idx < operations.len() { | ||
| 1183 | // Determine the type of current group and find all consecutive operations of same type | ||
| 1184 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 1185 | let group_start = op_idx; | ||
| 1186 | |||
| 1187 | // Find end of this group (consecutive operations of same type) | ||
| 1188 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 1189 | op_idx += 1; | ||
| 1190 | } | ||
| 1191 | let group_end = op_idx; | ||
| 1192 | let is_last_group = op_idx >= operations.len(); | ||
| 1193 | |||
| 1194 | // Execute this group of operations | ||
| 1195 | if is_read { | ||
| 1196 | self.execute_read_group_async( | ||
| 1197 | address, | ||
| 1198 | &mut operations[group_start..group_end], | ||
| 1199 | is_first_group, | ||
| 1200 | is_last_group, | ||
| 1201 | timeout, | ||
| 1202 | ) | ||
| 1203 | .await?; | ||
| 1204 | } else { | ||
| 1205 | self.execute_write_group_async( | ||
| 1206 | address, | ||
| 1207 | &operations[group_start..group_end], | ||
| 1208 | is_first_group, | ||
| 1209 | is_last_group, | ||
| 1210 | timeout, | ||
| 1211 | ) | ||
| 1212 | .await?; | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | is_first_group = false; | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | Ok(()) | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | async fn execute_write_group_async( | ||
| 1222 | &mut self, | ||
| 1223 | address: Address, | ||
| 1224 | operations: &[Operation<'_>], | ||
| 1225 | is_first_group: bool, | ||
| 1226 | is_last_group: bool, | ||
| 1227 | timeout: Timeout, | ||
| 1228 | ) -> Result<(), Error> { | ||
| 1229 | // Calculate total bytes across all operations in this group | ||
| 1230 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1231 | |||
| 1232 | if total_bytes == 0 { | ||
| 1233 | // Handle empty write group using blocking call | ||
| 1234 | if is_first_group { | ||
| 1235 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 1236 | } | ||
| 1237 | if is_last_group { | ||
| 1238 | self.master_stop(); | ||
| 1239 | } | ||
| 1240 | return Ok(()); | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | // Collect all write buffers | ||
| 1244 | let mut write_buffers: heapless::Vec<&[u8], 16> = heapless::Vec::new(); | ||
| 1245 | for operation in operations { | ||
| 1246 | if let Operation::Write(buffer) = operation { | ||
| 1247 | if !buffer.is_empty() { | ||
| 1248 | let _ = write_buffers.push(buffer); | ||
| 1249 | } | ||
| 1250 | } | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | if write_buffers.is_empty() { | ||
| 1254 | return Ok(()); | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | // Send each buffer using DMA | ||
| 1258 | let num_buffers = write_buffers.len(); | ||
| 1259 | for (idx, buffer) in write_buffers.iter().enumerate() { | ||
| 1260 | let is_first_buffer = idx == 0; | ||
| 1261 | let is_last_buffer = idx == num_buffers - 1; | ||
| 1262 | |||
| 1263 | let fut = self.write_dma_internal( | ||
| 1264 | address, | ||
| 1265 | buffer, | ||
| 1266 | is_first_buffer, // first_slice | ||
| 1267 | is_last_buffer, // last_slice | ||
| 1268 | is_last_buffer && is_last_group, // send_stop | ||
| 1269 | is_first_buffer && !is_first_group, // restart (only for first buffer if not first group) | ||
| 1270 | timeout, | ||
| 1271 | ); | ||
| 1272 | timeout.with(fut).await?; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | Ok(()) | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | async fn execute_read_group_async( | ||
| 1279 | &mut self, | ||
| 1280 | address: Address, | ||
| 1281 | operations: &mut [Operation<'_>], | ||
| 1282 | is_first_group: bool, | ||
| 1283 | is_last_group: bool, | ||
| 1284 | timeout: Timeout, | ||
| 1285 | ) -> Result<(), Error> { | ||
| 1286 | // Calculate total bytes across all operations in this group | ||
| 1287 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1288 | |||
| 1289 | if total_bytes == 0 { | ||
| 1290 | // Handle empty read group using blocking call | ||
| 1291 | if is_first_group { | ||
| 1292 | Self::master_read( | ||
| 1293 | self.info, | ||
| 1294 | address, | ||
| 1295 | 0, | ||
| 1296 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 1297 | false, // reload | ||
| 1298 | !is_first_group, | ||
| 1299 | timeout, | ||
| 1300 | )?; | ||
| 1301 | } | ||
| 1302 | if is_last_group { | ||
| 1303 | self.wait_stop(timeout)?; | ||
| 1304 | } | ||
| 1305 | return Ok(()); | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | // Use DMA for read operations - need to handle multiple buffers | ||
| 1309 | let restart = !is_first_group; | ||
| 1310 | let mut total_remaining = total_bytes; | ||
| 1311 | let mut is_first_in_group = true; | ||
| 1312 | |||
| 1313 | for operation in operations { | ||
| 1314 | if let Operation::Read(buffer) = operation { | ||
| 1315 | if buffer.is_empty() { | ||
| 1316 | // Skip empty buffers | ||
| 1317 | continue; | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | let buf_len = buffer.len(); | ||
| 1321 | total_remaining -= buf_len; | ||
| 1322 | let is_last_in_group = total_remaining == 0; | ||
| 1323 | |||
| 1324 | // Perform DMA read | ||
| 1325 | if is_first_in_group { | ||
| 1326 | // First buffer: use read_dma_internal which handles restart properly | ||
| 1327 | // Only use Automatic stop if this is the last buffer in the last group | ||
| 1328 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1329 | Stop::Automatic | ||
| 1330 | } else { | ||
| 1331 | Stop::Software | ||
| 1332 | }; | ||
| 1333 | |||
| 1334 | // We need a custom DMA read that respects our stop mode | ||
| 1335 | self.read_dma_group_internal(address, buffer, restart, stop_mode, timeout) | ||
| 1336 | .await?; | ||
| 1337 | is_first_in_group = false; | ||
| 1338 | } else { | ||
| 1339 | // Subsequent buffers: need to reload and continue | ||
| 1340 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1341 | Stop::Automatic | ||
| 1342 | } else { | ||
| 1343 | Stop::Software | ||
| 1344 | }; | ||
| 1345 | |||
| 1346 | self.read_dma_group_internal( | ||
| 1347 | address, buffer, false, // no restart for subsequent buffers in same group | ||
| 1348 | stop_mode, timeout, | ||
| 1349 | ) | ||
| 1350 | .await?; | ||
| 1351 | } | ||
| 1352 | } | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | // Wait for transfer to complete | ||
| 1356 | if is_last_group { | ||
| 1357 | self.wait_stop(timeout)?; | ||
| 1358 | } | ||
| 1359 | |||
| 1360 | Ok(()) | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | /// Internal DMA read helper for transaction groups | ||
| 1364 | async fn read_dma_group_internal( | ||
| 1365 | &mut self, | ||
| 1366 | address: Address, | ||
| 1367 | buffer: &mut [u8], | ||
| 1368 | restart: bool, | ||
| 1369 | stop_mode: Stop, | ||
| 1370 | timeout: Timeout, | ||
| 1371 | ) -> Result<(), Error> { | ||
| 1372 | let total_len = buffer.len(); | ||
| 1373 | |||
| 1374 | let dma_transfer = unsafe { | ||
| 1375 | let regs = self.info.regs; | ||
| 1376 | regs.cr1().modify(|w| { | ||
| 1377 | w.set_rxdmaen(true); | ||
| 1378 | w.set_tcie(true); | ||
| 1379 | w.set_nackie(true); | ||
| 1380 | w.set_errie(true); | ||
| 1381 | }); | ||
| 1382 | let src = regs.rxdr().as_ptr() as *mut u8; | ||
| 1383 | |||
| 1384 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | let mut remaining_len = total_len; | ||
| 1388 | |||
| 1389 | let on_drop = OnDrop::new(|| { | ||
| 1390 | let regs = self.info.regs; | ||
| 1391 | regs.cr1().modify(|w| { | ||
| 1392 | w.set_rxdmaen(false); | ||
| 1393 | w.set_tcie(false); | ||
| 1394 | w.set_nackie(false); | ||
| 1395 | w.set_errie(false); | ||
| 1396 | }); | ||
| 1397 | regs.icr().write(|w| { | ||
| 1398 | w.set_nackcf(true); | ||
| 1399 | w.set_berrcf(true); | ||
| 1400 | w.set_arlocf(true); | ||
| 1401 | w.set_ovrcf(true); | ||
| 1402 | }); | ||
| 1403 | }); | ||
| 1404 | |||
| 1405 | poll_fn(|cx| { | ||
| 1406 | self.state.waker.register(cx.waker()); | ||
| 1407 | |||
| 1408 | let isr = self.info.regs.isr().read(); | ||
| 1409 | |||
| 1410 | if isr.nackf() { | ||
| 1411 | return Poll::Ready(Err(Error::Nack)); | ||
| 1412 | } | ||
| 1413 | if isr.arlo() { | ||
| 1414 | return Poll::Ready(Err(Error::Arbitration)); | ||
| 1415 | } | ||
| 1416 | if isr.berr() { | ||
| 1417 | return Poll::Ready(Err(Error::Bus)); | ||
| 1418 | } | ||
| 1419 | if isr.ovr() { | ||
| 1420 | return Poll::Ready(Err(Error::Overrun)); | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | if remaining_len == total_len { | ||
| 1424 | Self::master_read( | ||
| 1425 | self.info, | ||
| 1426 | address, | ||
| 1427 | total_len.min(255), | ||
| 1428 | stop_mode, | ||
| 1429 | total_len > 255, // reload | ||
| 1430 | restart, | ||
| 1431 | timeout, | ||
| 1432 | )?; | ||
| 1433 | if total_len <= 255 { | ||
| 1434 | return Poll::Ready(Ok(())); | ||
| 1435 | } | ||
| 1436 | } else if isr.tcr() { | ||
| 1437 | // Transfer Complete Reload - need to set up next chunk | ||
| 1438 | let last_piece = remaining_len <= 255; | ||
| 1439 | |||
| 1440 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, stop_mode, timeout) { | ||
| 1441 | return Poll::Ready(Err(e)); | ||
| 1442 | } | ||
| 1443 | // Return here if we are on last chunk, | ||
| 1444 | // end of transfer will be awaited with the DMA below | ||
| 1445 | if last_piece { | ||
| 1446 | return Poll::Ready(Ok(())); | ||
| 1447 | } | ||
| 1448 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | ||
| 1449 | } else { | ||
| 1450 | // poll_fn was woken without TCR interrupt | ||
| 1451 | return Poll::Pending; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1455 | Poll::Pending | ||
| 1456 | }) | ||
| 1457 | .await?; | ||
| 1458 | |||
| 1459 | dma_transfer.await; | ||
| 1460 | drop(on_drop); | ||
| 1461 | |||
| 1462 | Ok(()) | ||
| 894 | } | 1463 | } |
| 895 | } | 1464 | } |
| 896 | 1465 | ||
| @@ -1028,7 +1597,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1028 | if number == 0 { | 1597 | if number == 0 { |
| 1029 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1598 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1030 | } else { | 1599 | } else { |
| 1031 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1600 | Self::reload( |
| 1601 | self.info, | ||
| 1602 | chunk.len(), | ||
| 1603 | number != last_chunk_idx, | ||
| 1604 | Stop::Software, | ||
| 1605 | timeout, | ||
| 1606 | )?; | ||
| 1032 | } | 1607 | } |
| 1033 | 1608 | ||
| 1034 | let mut index = 0; | 1609 | let mut index = 0; |
| @@ -1077,7 +1652,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1077 | if number == 0 { | 1652 | if number == 0 { |
| 1078 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1653 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1079 | } else { | 1654 | } else { |
| 1080 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1655 | Self::reload( |
| 1656 | self.info, | ||
| 1657 | chunk.len(), | ||
| 1658 | number != last_chunk_idx, | ||
| 1659 | Stop::Software, | ||
| 1660 | timeout, | ||
| 1661 | )?; | ||
| 1081 | } | 1662 | } |
| 1082 | 1663 | ||
| 1083 | let mut index = 0; | 1664 | let mut index = 0; |
| @@ -1181,7 +1762,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1181 | 1762 | ||
| 1182 | let regs = self.info.regs; | 1763 | let regs = self.info.regs; |
| 1183 | 1764 | ||
| 1184 | let dma_transfer = unsafe { | 1765 | let mut dma_transfer = unsafe { |
| 1185 | regs.cr1().modify(|w| { | 1766 | regs.cr1().modify(|w| { |
| 1186 | w.set_rxdmaen(true); | 1767 | w.set_rxdmaen(true); |
| 1187 | w.set_stopie(true); | 1768 | w.set_stopie(true); |
| @@ -1213,13 +1794,20 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1213 | Poll::Pending | 1794 | Poll::Pending |
| 1214 | } else if isr.tcr() { | 1795 | } else if isr.tcr() { |
| 1215 | let is_last_slice = remaining_len <= 255; | 1796 | let is_last_slice = remaining_len <= 255; |
| 1216 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1797 | if let Err(e) = Self::reload( |
| 1798 | self.info, | ||
| 1799 | remaining_len.min(255), | ||
| 1800 | !is_last_slice, | ||
| 1801 | Stop::Software, | ||
| 1802 | timeout, | ||
| 1803 | ) { | ||
| 1217 | return Poll::Ready(Err(e)); | 1804 | return Poll::Ready(Err(e)); |
| 1218 | } | 1805 | } |
| 1219 | remaining_len = remaining_len.saturating_sub(255); | 1806 | remaining_len = remaining_len.saturating_sub(255); |
| 1220 | regs.cr1().modify(|w| w.set_tcie(true)); | 1807 | regs.cr1().modify(|w| w.set_tcie(true)); |
| 1221 | Poll::Pending | 1808 | Poll::Pending |
| 1222 | } else if isr.stopf() { | 1809 | } else if isr.stopf() { |
| 1810 | remaining_len = remaining_len.saturating_add(dma_transfer.get_remaining_transfers() as usize); | ||
| 1223 | regs.icr().write(|reg| reg.set_stopcf(true)); | 1811 | regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1224 | let poll = Poll::Ready(Ok(total_len - remaining_len)); | 1812 | let poll = Poll::Ready(Ok(total_len - remaining_len)); |
| 1225 | poll | 1813 | poll |
| @@ -1229,6 +1817,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1229 | }) | 1817 | }) |
| 1230 | .await?; | 1818 | .await?; |
| 1231 | 1819 | ||
| 1820 | dma_transfer.request_pause(); | ||
| 1232 | dma_transfer.await; | 1821 | dma_transfer.await; |
| 1233 | 1822 | ||
| 1234 | drop(on_drop); | 1823 | drop(on_drop); |
| @@ -1258,7 +1847,8 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1258 | w.set_txdmaen(false); | 1847 | w.set_txdmaen(false); |
| 1259 | w.set_stopie(false); | 1848 | w.set_stopie(false); |
| 1260 | w.set_tcie(false); | 1849 | w.set_tcie(false); |
| 1261 | }) | 1850 | }); |
| 1851 | regs.isr().write(|w| w.set_txe(true)); | ||
| 1262 | }); | 1852 | }); |
| 1263 | 1853 | ||
| 1264 | let state = self.state; | 1854 | let state = self.state; |
| @@ -1274,13 +1864,24 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1274 | Poll::Pending | 1864 | Poll::Pending |
| 1275 | } else if isr.tcr() { | 1865 | } else if isr.tcr() { |
| 1276 | let is_last_slice = remaining_len <= 255; | 1866 | let is_last_slice = remaining_len <= 255; |
| 1277 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1867 | if let Err(e) = Self::reload( |
| 1868 | self.info, | ||
| 1869 | remaining_len.min(255), | ||
| 1870 | !is_last_slice, | ||
| 1871 | Stop::Software, | ||
| 1872 | timeout, | ||
| 1873 | ) { | ||
| 1278 | return Poll::Ready(Err(e)); | 1874 | return Poll::Ready(Err(e)); |
| 1279 | } | 1875 | } |
| 1280 | remaining_len = remaining_len.saturating_sub(255); | 1876 | remaining_len = remaining_len.saturating_sub(255); |
| 1281 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 1877 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 1282 | Poll::Pending | 1878 | Poll::Pending |
| 1283 | } else if isr.stopf() { | 1879 | } else if isr.stopf() { |
| 1880 | let mut leftover_bytes = dma_transfer.get_remaining_transfers(); | ||
| 1881 | if !self.info.regs.isr().read().txe() { | ||
| 1882 | leftover_bytes = leftover_bytes.saturating_add(1); | ||
| 1883 | } | ||
| 1884 | remaining_len = remaining_len.saturating_add(leftover_bytes as usize); | ||
| 1284 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); | 1885 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1285 | if remaining_len > 0 { | 1886 | if remaining_len > 0 { |
| 1286 | dma_transfer.request_pause(); | 1887 | dma_transfer.request_pause(); |
| @@ -1294,6 +1895,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1294 | }) | 1895 | }) |
| 1295 | .await?; | 1896 | .await?; |
| 1296 | 1897 | ||
| 1898 | dma_transfer.request_pause(); | ||
| 1297 | dma_transfer.await; | 1899 | dma_transfer.await; |
| 1298 | 1900 | ||
| 1299 | drop(on_drop); | 1901 | drop(on_drop); |
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index b6d3daf54..df077a3ae 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs | |||
| @@ -3,12 +3,13 @@ | |||
| 3 | use embassy_futures::join::join; | 3 | use embassy_futures::join::join; |
| 4 | use stm32_metapac::spi::vals; | 4 | use stm32_metapac::spi::vals; |
| 5 | 5 | ||
| 6 | use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer}; | 6 | use crate::Peri; |
| 7 | use crate::dma::{ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer, ringbuffer}; | ||
| 7 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 8 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 8 | use crate::mode::Async; | 9 | use crate::mode::Async; |
| 10 | use crate::spi::mode::Master; | ||
| 9 | use crate::spi::{Config as SpiConfig, RegsExt as _, *}; | 11 | use crate::spi::{Config as SpiConfig, RegsExt as _, *}; |
| 10 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 11 | use crate::Peri; | ||
| 12 | 13 | ||
| 13 | /// I2S mode | 14 | /// I2S mode |
| 14 | #[derive(Copy, Clone)] | 15 | #[derive(Copy, Clone)] |
| @@ -225,7 +226,7 @@ impl<'s, 'd, W: Word> Reader<'s, 'd, W> { | |||
| 225 | pub struct I2S<'d, W: Word> { | 226 | pub struct I2S<'d, W: Word> { |
| 226 | #[allow(dead_code)] | 227 | #[allow(dead_code)] |
| 227 | mode: Mode, | 228 | mode: Mode, |
| 228 | spi: Spi<'d, Async>, | 229 | spi: Spi<'d, Async, Master>, |
| 229 | txsd: Option<Peri<'d, AnyPin>>, | 230 | txsd: Option<Peri<'d, AnyPin>>, |
| 230 | rxsd: Option<Peri<'d, AnyPin>>, | 231 | rxsd: Option<Peri<'d, AnyPin>>, |
| 231 | ws: Option<Peri<'d, AnyPin>>, | 232 | ws: Option<Peri<'d, AnyPin>>, |
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 670d8332c..10c4a820b 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | //! Inter-Process Communication Controller (IPCC) | 1 | //! Inter-Process Communication Controller (IPCC) |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::marker::PhantomData; |
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | 7 | ||
| 8 | use embassy_hal_internal::Peri; | ||
| 7 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 8 | 10 | ||
| 9 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| @@ -17,25 +19,16 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for Receive | |||
| 17 | unsafe fn on_interrupt() { | 19 | unsafe fn on_interrupt() { |
| 18 | let regs = IPCC::regs(); | 20 | let regs = IPCC::regs(); |
| 19 | 21 | ||
| 20 | let channels = [ | ||
| 21 | IpccChannel::Channel1, | ||
| 22 | IpccChannel::Channel2, | ||
| 23 | IpccChannel::Channel3, | ||
| 24 | IpccChannel::Channel4, | ||
| 25 | IpccChannel::Channel5, | ||
| 26 | IpccChannel::Channel6, | ||
| 27 | ]; | ||
| 28 | |||
| 29 | // Status register gives channel occupied status. For rx, use cpu1. | 22 | // Status register gives channel occupied status. For rx, use cpu1. |
| 30 | let sr = regs.cpu(1).sr().read(); | 23 | let sr = regs.cpu(1).sr().read(); |
| 31 | regs.cpu(0).mr().modify(|w| { | 24 | regs.cpu(0).mr().modify(|w| { |
| 32 | for channel in channels { | 25 | for index in 0..5 { |
| 33 | if sr.chf(channel as usize) { | 26 | if sr.chf(index as usize) { |
| 34 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 27 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 35 | w.set_chom(channel as usize, true); | 28 | w.set_chom(index as usize, true); |
| 36 | 29 | ||
| 37 | // There shouldn't be a race because the channel is masked only if the interrupt has fired | 30 | // There shouldn't be a race because the channel is masked only if the interrupt has fired |
| 38 | IPCC::state().rx_waker_for(channel).wake(); | 31 | IPCC::state().rx_waker_for(index).wake(); |
| 39 | } | 32 | } |
| 40 | } | 33 | } |
| 41 | }) | 34 | }) |
| @@ -49,25 +42,16 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for Transmi | |||
| 49 | unsafe fn on_interrupt() { | 42 | unsafe fn on_interrupt() { |
| 50 | let regs = IPCC::regs(); | 43 | let regs = IPCC::regs(); |
| 51 | 44 | ||
| 52 | let channels = [ | ||
| 53 | IpccChannel::Channel1, | ||
| 54 | IpccChannel::Channel2, | ||
| 55 | IpccChannel::Channel3, | ||
| 56 | IpccChannel::Channel4, | ||
| 57 | IpccChannel::Channel5, | ||
| 58 | IpccChannel::Channel6, | ||
| 59 | ]; | ||
| 60 | |||
| 61 | // Status register gives channel occupied status. For tx, use cpu0. | 45 | // Status register gives channel occupied status. For tx, use cpu0. |
| 62 | let sr = regs.cpu(0).sr().read(); | 46 | let sr = regs.cpu(0).sr().read(); |
| 63 | regs.cpu(0).mr().modify(|w| { | 47 | regs.cpu(0).mr().modify(|w| { |
| 64 | for channel in channels { | 48 | for index in 0..5 { |
| 65 | if !sr.chf(channel as usize) { | 49 | if !sr.chf(index as usize) { |
| 66 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 50 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 67 | w.set_chfm(channel as usize, true); | 51 | w.set_chfm(index as usize, true); |
| 68 | 52 | ||
| 69 | // There shouldn't be a race because the channel is masked only if the interrupt has fired | 53 | // There shouldn't be a race because the channel is masked only if the interrupt has fired |
| 70 | IPCC::state().tx_waker_for(channel).wake(); | 54 | IPCC::state().tx_waker_for(index).wake(); |
| 71 | } | 55 | } |
| 72 | } | 56 | } |
| 73 | }); | 57 | }); |
| @@ -82,76 +66,55 @@ pub struct Config { | |||
| 82 | // reserved for future use | 66 | // reserved for future use |
| 83 | } | 67 | } |
| 84 | 68 | ||
| 85 | /// Channel. | 69 | /// IPCC TX Channel |
| 86 | #[allow(missing_docs)] | 70 | pub struct IpccTxChannel<'a> { |
| 87 | #[derive(Debug, Clone, Copy)] | 71 | index: u8, |
| 88 | #[repr(C)] | 72 | _lifetime: PhantomData<&'a mut usize>, |
| 89 | pub enum IpccChannel { | ||
| 90 | Channel1 = 0, | ||
| 91 | Channel2 = 1, | ||
| 92 | Channel3 = 2, | ||
| 93 | Channel4 = 3, | ||
| 94 | Channel5 = 4, | ||
| 95 | Channel6 = 5, | ||
| 96 | } | 73 | } |
| 97 | 74 | ||
| 98 | /// IPCC driver. | 75 | impl<'a> IpccTxChannel<'a> { |
| 99 | pub struct Ipcc; | 76 | pub(crate) const fn new(index: u8) -> Self { |
| 100 | 77 | core::assert!(index < 6); | |
| 101 | impl Ipcc { | ||
| 102 | /// Enable IPCC. | ||
| 103 | pub fn enable(_config: Config) { | ||
| 104 | rcc::enable_and_reset::<IPCC>(); | ||
| 105 | IPCC::set_cpu2(true); | ||
| 106 | |||
| 107 | let regs = IPCC::regs(); | ||
| 108 | |||
| 109 | regs.cpu(0).cr().modify(|w| { | ||
| 110 | w.set_rxoie(true); | ||
| 111 | w.set_txfie(true); | ||
| 112 | }); | ||
| 113 | |||
| 114 | // enable interrupts | ||
| 115 | crate::interrupt::typelevel::IPCC_C1_RX::unpend(); | ||
| 116 | crate::interrupt::typelevel::IPCC_C1_TX::unpend(); | ||
| 117 | 78 | ||
| 118 | unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; | 79 | Self { |
| 119 | unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; | 80 | index: index, |
| 81 | _lifetime: PhantomData, | ||
| 82 | } | ||
| 120 | } | 83 | } |
| 121 | 84 | ||
| 122 | /// Send data to an IPCC channel. The closure is called to write the data when appropriate. | 85 | /// Send data to an IPCC channel. The closure is called to write the data when appropriate. |
| 123 | pub async fn send(channel: IpccChannel, f: impl FnOnce()) { | 86 | pub async fn send(&mut self, f: impl FnOnce()) { |
| 124 | let regs = IPCC::regs(); | 87 | let regs = IPCC::regs(); |
| 125 | 88 | ||
| 126 | Self::flush(channel).await; | 89 | self.flush().await; |
| 127 | 90 | ||
| 128 | f(); | 91 | f(); |
| 129 | 92 | ||
| 130 | compiler_fence(Ordering::SeqCst); | 93 | compiler_fence(Ordering::SeqCst); |
| 131 | 94 | ||
| 132 | trace!("ipcc: ch {}: send data", channel as u8); | 95 | trace!("ipcc: ch {}: send data", self.index as u8); |
| 133 | regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)); | 96 | regs.cpu(0).scr().write(|w| w.set_chs(self.index as usize, true)); |
| 134 | } | 97 | } |
| 135 | 98 | ||
| 136 | /// Wait for the tx channel to become clear | 99 | /// Wait for the tx channel to become clear |
| 137 | pub async fn flush(channel: IpccChannel) { | 100 | pub async fn flush(&mut self) { |
| 138 | let regs = IPCC::regs(); | 101 | let regs = IPCC::regs(); |
| 139 | 102 | ||
| 140 | // This is a race, but is nice for debugging | 103 | // This is a race, but is nice for debugging |
| 141 | if regs.cpu(0).sr().read().chf(channel as usize) { | 104 | if regs.cpu(0).sr().read().chf(self.index as usize) { |
| 142 | trace!("ipcc: ch {}: wait for tx free", channel as u8); | 105 | trace!("ipcc: ch {}: wait for tx free", self.index as u8); |
| 143 | } | 106 | } |
| 144 | 107 | ||
| 145 | poll_fn(|cx| { | 108 | poll_fn(|cx| { |
| 146 | IPCC::state().tx_waker_for(channel).register(cx.waker()); | 109 | IPCC::state().tx_waker_for(self.index).register(cx.waker()); |
| 147 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt | 110 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt |
| 148 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)); | 111 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, false)); |
| 149 | 112 | ||
| 150 | compiler_fence(Ordering::SeqCst); | 113 | compiler_fence(Ordering::SeqCst); |
| 151 | 114 | ||
| 152 | if !regs.cpu(0).sr().read().chf(channel as usize) { | 115 | if !regs.cpu(0).sr().read().chf(self.index as usize) { |
| 153 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 116 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 154 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); | 117 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, true)); |
| 155 | 118 | ||
| 156 | Poll::Ready(()) | 119 | Poll::Ready(()) |
| 157 | } else { | 120 | } else { |
| @@ -160,27 +123,44 @@ impl Ipcc { | |||
| 160 | }) | 123 | }) |
| 161 | .await; | 124 | .await; |
| 162 | } | 125 | } |
| 126 | } | ||
| 127 | |||
| 128 | /// IPCC RX Channel | ||
| 129 | pub struct IpccRxChannel<'a> { | ||
| 130 | index: u8, | ||
| 131 | _lifetime: PhantomData<&'a mut usize>, | ||
| 132 | } | ||
| 133 | |||
| 134 | impl<'a> IpccRxChannel<'a> { | ||
| 135 | pub(crate) const fn new(index: u8) -> Self { | ||
| 136 | core::assert!(index < 6); | ||
| 137 | |||
| 138 | Self { | ||
| 139 | index: index, | ||
| 140 | _lifetime: PhantomData, | ||
| 141 | } | ||
| 142 | } | ||
| 163 | 143 | ||
| 164 | /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. | 144 | /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. |
| 165 | pub async fn receive<R>(channel: IpccChannel, mut f: impl FnMut() -> Option<R>) -> R { | 145 | pub async fn receive<R>(&mut self, mut f: impl FnMut() -> Option<R>) -> R { |
| 166 | let regs = IPCC::regs(); | 146 | let regs = IPCC::regs(); |
| 167 | 147 | ||
| 168 | loop { | 148 | loop { |
| 169 | // This is a race, but is nice for debugging | 149 | // This is a race, but is nice for debugging |
| 170 | if !regs.cpu(1).sr().read().chf(channel as usize) { | 150 | if !regs.cpu(1).sr().read().chf(self.index as usize) { |
| 171 | trace!("ipcc: ch {}: wait for rx occupied", channel as u8); | 151 | trace!("ipcc: ch {}: wait for rx occupied", self.index as u8); |
| 172 | } | 152 | } |
| 173 | 153 | ||
| 174 | poll_fn(|cx| { | 154 | poll_fn(|cx| { |
| 175 | IPCC::state().rx_waker_for(channel).register(cx.waker()); | 155 | IPCC::state().rx_waker_for(self.index).register(cx.waker()); |
| 176 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt | 156 | // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt |
| 177 | regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)); | 157 | regs.cpu(0).mr().modify(|w| w.set_chom(self.index as usize, false)); |
| 178 | 158 | ||
| 179 | compiler_fence(Ordering::SeqCst); | 159 | compiler_fence(Ordering::SeqCst); |
| 180 | 160 | ||
| 181 | if regs.cpu(1).sr().read().chf(channel as usize) { | 161 | if regs.cpu(1).sr().read().chf(self.index as usize) { |
| 182 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt | 162 | // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt |
| 183 | regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); | 163 | regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, true)); |
| 184 | 164 | ||
| 185 | Poll::Ready(()) | 165 | Poll::Ready(()) |
| 186 | } else { | 166 | } else { |
| @@ -189,21 +169,111 @@ impl Ipcc { | |||
| 189 | }) | 169 | }) |
| 190 | .await; | 170 | .await; |
| 191 | 171 | ||
| 192 | trace!("ipcc: ch {}: read data", channel as u8); | 172 | trace!("ipcc: ch {}: read data", self.index as u8); |
| 193 | 173 | ||
| 194 | match f() { | 174 | match f() { |
| 195 | Some(ret) => return ret, | 175 | Some(ret) => return ret, |
| 196 | None => {} | 176 | None => {} |
| 197 | } | 177 | } |
| 198 | 178 | ||
| 199 | trace!("ipcc: ch {}: clear rx", channel as u8); | 179 | trace!("ipcc: ch {}: clear rx", self.index as u8); |
| 200 | compiler_fence(Ordering::SeqCst); | 180 | compiler_fence(Ordering::SeqCst); |
| 201 | // If the channel is clear and the read function returns none, fetch more data | 181 | // If the channel is clear and the read function returns none, fetch more data |
| 202 | regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)); | 182 | regs.cpu(0).scr().write(|w| w.set_chc(self.index as usize, true)); |
| 203 | } | 183 | } |
| 204 | } | 184 | } |
| 205 | } | 185 | } |
| 206 | 186 | ||
| 187 | /// IPCC Channel | ||
| 188 | pub struct IpccChannel<'a> { | ||
| 189 | index: u8, | ||
| 190 | _lifetime: PhantomData<&'a mut usize>, | ||
| 191 | } | ||
| 192 | |||
| 193 | impl<'a> IpccChannel<'a> { | ||
| 194 | pub(crate) const fn new(number: u8) -> Self { | ||
| 195 | core::assert!(number > 0 && number <= 6); | ||
| 196 | |||
| 197 | Self { | ||
| 198 | index: number - 1, | ||
| 199 | _lifetime: PhantomData, | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Split into a tx and rx channel | ||
| 204 | pub const fn split(self) -> (IpccTxChannel<'a>, IpccRxChannel<'a>) { | ||
| 205 | (IpccTxChannel::new(self.index), IpccRxChannel::new(self.index)) | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// IPCC driver. | ||
| 210 | pub struct Ipcc { | ||
| 211 | _private: (), | ||
| 212 | } | ||
| 213 | |||
| 214 | impl Ipcc { | ||
| 215 | /// Creates a new HardwareSemaphore instance. | ||
| 216 | pub fn new<'d>( | ||
| 217 | _peripheral: Peri<'d, crate::peripherals::IPCC>, | ||
| 218 | _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> | ||
| 219 | + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler> | ||
| 220 | + 'd, | ||
| 221 | _config: Config, | ||
| 222 | ) -> Self { | ||
| 223 | rcc::enable_and_reset::<IPCC>(); | ||
| 224 | IPCC::set_cpu2(true); | ||
| 225 | |||
| 226 | let regs = IPCC::regs(); | ||
| 227 | |||
| 228 | regs.cpu(0).cr().modify(|w| { | ||
| 229 | w.set_rxoie(true); | ||
| 230 | w.set_txfie(true); | ||
| 231 | }); | ||
| 232 | |||
| 233 | // enable interrupts | ||
| 234 | crate::interrupt::typelevel::IPCC_C1_RX::unpend(); | ||
| 235 | crate::interrupt::typelevel::IPCC_C1_TX::unpend(); | ||
| 236 | |||
| 237 | unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; | ||
| 238 | unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; | ||
| 239 | |||
| 240 | Self { _private: () } | ||
| 241 | } | ||
| 242 | |||
| 243 | /// Split into a tx and rx channel | ||
| 244 | pub const fn split<'a>(self) -> [(IpccTxChannel<'a>, IpccRxChannel<'a>); 6] { | ||
| 245 | [ | ||
| 246 | IpccChannel::new(1).split(), | ||
| 247 | IpccChannel::new(2).split(), | ||
| 248 | IpccChannel::new(3).split(), | ||
| 249 | IpccChannel::new(4).split(), | ||
| 250 | IpccChannel::new(5).split(), | ||
| 251 | IpccChannel::new(6).split(), | ||
| 252 | ] | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Receive from a channel number | ||
| 256 | pub async unsafe fn receive<R>(number: u8, f: impl FnMut() -> Option<R>) -> R { | ||
| 257 | core::assert!(number > 0 && number <= 6); | ||
| 258 | |||
| 259 | IpccRxChannel::new(number - 1).receive(f).await | ||
| 260 | } | ||
| 261 | |||
| 262 | /// Send to a channel number | ||
| 263 | pub async unsafe fn send(number: u8, f: impl FnOnce()) { | ||
| 264 | core::assert!(number > 0 && number <= 6); | ||
| 265 | |||
| 266 | IpccTxChannel::new(number - 1).send(f).await | ||
| 267 | } | ||
| 268 | |||
| 269 | /// Send to a channel number | ||
| 270 | pub async unsafe fn flush(number: u8) { | ||
| 271 | core::assert!(number > 0 && number <= 6); | ||
| 272 | |||
| 273 | IpccTxChannel::new(number - 1).flush().await | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 207 | impl SealedInstance for crate::peripherals::IPCC { | 277 | impl SealedInstance for crate::peripherals::IPCC { |
| 208 | fn regs() -> crate::pac::ipcc::Ipcc { | 278 | fn regs() -> crate::pac::ipcc::Ipcc { |
| 209 | crate::pac::IPCC | 279 | crate::pac::IPCC |
| @@ -232,26 +302,12 @@ impl State { | |||
| 232 | } | 302 | } |
| 233 | } | 303 | } |
| 234 | 304 | ||
| 235 | const fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { | 305 | const fn rx_waker_for(&self, index: u8) -> &AtomicWaker { |
| 236 | match channel { | 306 | &self.rx_wakers[index as usize] |
| 237 | IpccChannel::Channel1 => &self.rx_wakers[0], | ||
| 238 | IpccChannel::Channel2 => &self.rx_wakers[1], | ||
| 239 | IpccChannel::Channel3 => &self.rx_wakers[2], | ||
| 240 | IpccChannel::Channel4 => &self.rx_wakers[3], | ||
| 241 | IpccChannel::Channel5 => &self.rx_wakers[4], | ||
| 242 | IpccChannel::Channel6 => &self.rx_wakers[5], | ||
| 243 | } | ||
| 244 | } | 307 | } |
| 245 | 308 | ||
| 246 | const fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { | 309 | const fn tx_waker_for(&self, index: u8) -> &AtomicWaker { |
| 247 | match channel { | 310 | &self.tx_wakers[index as usize] |
| 248 | IpccChannel::Channel1 => &self.tx_wakers[0], | ||
| 249 | IpccChannel::Channel2 => &self.tx_wakers[1], | ||
| 250 | IpccChannel::Channel3 => &self.tx_wakers[2], | ||
| 251 | IpccChannel::Channel4 => &self.tx_wakers[3], | ||
| 252 | IpccChannel::Channel5 => &self.tx_wakers[4], | ||
| 253 | IpccChannel::Channel6 => &self.tx_wakers[5], | ||
| 254 | } | ||
| 255 | } | 311 | } |
| 256 | } | 312 | } |
| 257 | 313 | ||
diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs new file mode 100644 index 000000000..ea29f1398 --- /dev/null +++ b/embassy-stm32/src/lcd.rs | |||
| @@ -0,0 +1,510 @@ | |||
| 1 | //! LCD | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | |||
| 6 | use crate::gpio::{AfType, AnyPin, SealedPin}; | ||
| 7 | use crate::peripherals; | ||
| 8 | use crate::rcc::{self, RccPeripheral}; | ||
| 9 | use crate::time::Hertz; | ||
| 10 | |||
| 11 | #[cfg(any(stm32u0, stm32l073, stm32l083))] | ||
| 12 | const NUM_SEGMENTS: u8 = 52; | ||
| 13 | #[cfg(any(stm32wb, stm32l4x6, stm32l15x, stm32l162, stm32l4x3, stm32l4x6))] | ||
| 14 | const NUM_SEGMENTS: u8 = 44; | ||
| 15 | #[cfg(any(stm32l053, stm32l063, stm32l100))] | ||
| 16 | const NUM_SEGMENTS: u8 = 32; | ||
| 17 | |||
| 18 | /// LCD configuration struct | ||
| 19 | #[non_exhaustive] | ||
| 20 | #[derive(Debug, Clone, Copy)] | ||
| 21 | pub struct Config { | ||
| 22 | #[cfg(lcd_v2)] | ||
| 23 | /// Enable the voltage output buffer for higher driving capability. | ||
| 24 | /// | ||
| 25 | /// The LCD driving capability is improved as buffers prevent the LCD capacitive loads from loading the resistor | ||
| 26 | /// bridge unacceptably and interfering with its voltage generation. | ||
| 27 | pub use_voltage_output_buffer: bool, | ||
| 28 | /// Enable SEG pin remapping. SEG[31:28] multiplexed with SEG[43:40] | ||
| 29 | pub use_segment_muxing: bool, | ||
| 30 | /// Bias selector | ||
| 31 | pub bias: Bias, | ||
| 32 | /// Duty selector | ||
| 33 | pub duty: Duty, | ||
| 34 | /// Internal or external voltage source | ||
| 35 | pub voltage_source: VoltageSource, | ||
| 36 | /// The frequency used to update the LCD with. | ||
| 37 | /// Should be between ~30 and ~100. Lower is better for power consumption, but has lower visual fidelity. | ||
| 38 | pub target_fps: Hertz, | ||
| 39 | /// LCD driver selector | ||
| 40 | pub drive: Drive, | ||
| 41 | } | ||
| 42 | |||
| 43 | impl Default for Config { | ||
| 44 | fn default() -> Self { | ||
| 45 | Self { | ||
| 46 | #[cfg(lcd_v2)] | ||
| 47 | use_voltage_output_buffer: false, | ||
| 48 | use_segment_muxing: false, | ||
| 49 | bias: Default::default(), | ||
| 50 | duty: Default::default(), | ||
| 51 | voltage_source: Default::default(), | ||
| 52 | target_fps: Hertz(60), | ||
| 53 | drive: Drive::Medium, | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// The number of voltage levels used when driving an LCD. | ||
| 59 | /// Your LCD datasheet should tell you what to use. | ||
| 60 | #[repr(u8)] | ||
| 61 | #[derive(Debug, Default, Clone, Copy)] | ||
| 62 | pub enum Bias { | ||
| 63 | /// 1/4 bias | ||
| 64 | #[default] | ||
| 65 | Quarter = 0b00, | ||
| 66 | /// 1/2 bias | ||
| 67 | Half = 0b01, | ||
| 68 | /// 1/3 bias | ||
| 69 | Third = 0b10, | ||
| 70 | } | ||
| 71 | |||
| 72 | /// The duty used by the LCD driver. | ||
| 73 | /// | ||
| 74 | /// This is essentially how many COM pins you're using. | ||
| 75 | #[repr(u8)] | ||
| 76 | #[derive(Debug, Default, Clone, Copy)] | ||
| 77 | pub enum Duty { | ||
| 78 | #[default] | ||
| 79 | /// Use a single COM pin | ||
| 80 | Static = 0b000, | ||
| 81 | /// Use two COM pins | ||
| 82 | Half = 0b001, | ||
| 83 | /// Use three COM pins | ||
| 84 | Third = 0b010, | ||
| 85 | /// Use four COM pins | ||
| 86 | Quarter = 0b011, | ||
| 87 | /// Use eight COM pins. | ||
| 88 | /// | ||
| 89 | /// In this mode, `COM[7:4]` outputs are available on `SEG[51:48]`. | ||
| 90 | /// This allows reducing the number of available segments. | ||
| 91 | Eigth = 0b100, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl Duty { | ||
| 95 | fn num_com_pins(&self) -> u8 { | ||
| 96 | match self { | ||
| 97 | Duty::Static => 1, | ||
| 98 | Duty::Half => 2, | ||
| 99 | Duty::Third => 3, | ||
| 100 | Duty::Quarter => 4, | ||
| 101 | Duty::Eigth => 8, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Whether to use the internal or external voltage source to drive the LCD | ||
| 107 | #[repr(u8)] | ||
| 108 | #[derive(Debug, Default, Clone, Copy)] | ||
| 109 | pub enum VoltageSource { | ||
| 110 | #[default] | ||
| 111 | /// Voltage stepup converter | ||
| 112 | Internal, | ||
| 113 | /// VLCD pin | ||
| 114 | External, | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Defines the pulse duration in terms of ck_ps pulses. | ||
| 118 | /// | ||
| 119 | /// A short pulse leads to lower power consumption, but displays with high internal resistance | ||
| 120 | /// may need a longer pulse to achieve satisfactory contrast. | ||
| 121 | /// Note that the pulse is never longer than one half prescaled LCD clock period. | ||
| 122 | /// | ||
| 123 | /// Displays with high internal resistance may need a longer drive time to achieve satisfactory contrast. | ||
| 124 | /// `PermanentHighDrive` is useful in this case if some additional power consumption can be tolerated. | ||
| 125 | /// | ||
| 126 | /// Basically, for power usage, you want this as low as possible while still being able to use the LCD | ||
| 127 | /// with a good enough contrast. | ||
| 128 | #[repr(u8)] | ||
| 129 | #[derive(Debug, Clone, Copy)] | ||
| 130 | pub enum Drive { | ||
| 131 | /// Zero clock pulse on duration | ||
| 132 | Lowest = 0x00, | ||
| 133 | /// One clock pulse on duration | ||
| 134 | VeryLow = 0x01, | ||
| 135 | /// Two clock pulse on duration | ||
| 136 | Low = 0x02, | ||
| 137 | /// Three clock pulse on duration | ||
| 138 | Medium = 0x03, | ||
| 139 | /// Four clock pulse on duration | ||
| 140 | MediumHigh = 0x04, | ||
| 141 | /// Five clock pulse on duration | ||
| 142 | High = 0x05, | ||
| 143 | /// Six clock pulse on duration | ||
| 144 | VeryHigh = 0x06, | ||
| 145 | /// Seven clock pulse on duration | ||
| 146 | Highest = 0x07, | ||
| 147 | /// Enables the highdrive bit of the hardware | ||
| 148 | PermanentHighDrive = 0x09, | ||
| 149 | } | ||
| 150 | |||
| 151 | /// LCD driver. | ||
| 152 | pub struct Lcd<'d, T: Instance> { | ||
| 153 | _peri: PhantomData<&'d mut T>, | ||
| 154 | duty: Duty, | ||
| 155 | ck_div: u32, | ||
| 156 | } | ||
| 157 | |||
| 158 | impl<'d, T: Instance> Lcd<'d, T> { | ||
| 159 | /// Initialize the lcd driver. | ||
| 160 | /// | ||
| 161 | /// The `pins` parameter must contain *all* segment and com pins that are connected to the LCD. | ||
| 162 | /// This is not further checked by this driver. Pins not routed to the LCD can be used for other purposes. | ||
| 163 | pub fn new<const N: usize>( | ||
| 164 | _peripheral: Peri<'d, T>, | ||
| 165 | config: Config, | ||
| 166 | vlcd_pin: Peri<'_, impl VlcdPin<T>>, | ||
| 167 | pins: [LcdPin<'d, T>; N], | ||
| 168 | ) -> Self { | ||
| 169 | rcc::enable_and_reset::<T>(); | ||
| 170 | |||
| 171 | vlcd_pin.set_as_af( | ||
| 172 | vlcd_pin.af_num(), | ||
| 173 | AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), | ||
| 174 | ); | ||
| 175 | |||
| 176 | assert_eq!( | ||
| 177 | pins.iter().filter(|pin| !pin.is_seg).count(), | ||
| 178 | config.duty.num_com_pins() as usize, | ||
| 179 | "The number of provided COM pins is not the same as the duty configures" | ||
| 180 | ); | ||
| 181 | |||
| 182 | // Set the pins | ||
| 183 | for pin in pins { | ||
| 184 | pin.pin.set_as_af( | ||
| 185 | pin.af_num, | ||
| 186 | AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), | ||
| 187 | ); | ||
| 188 | } | ||
| 189 | |||
| 190 | // Initialize the display ram to 0 | ||
| 191 | for i in 0..8 { | ||
| 192 | T::regs().ram_com(i).low().write_value(0); | ||
| 193 | T::regs().ram_com(i).high().write_value(0); | ||
| 194 | } | ||
| 195 | |||
| 196 | // Calculate the clock dividers | ||
| 197 | let Some(lcd_clk) = (unsafe { rcc::get_freqs().rtc.to_hertz() }) else { | ||
| 198 | panic!("The LCD driver needs the RTC/LCD clock to be running"); | ||
| 199 | }; | ||
| 200 | let duty_divider = match config.duty { | ||
| 201 | Duty::Static => 1, | ||
| 202 | Duty::Half => 2, | ||
| 203 | Duty::Third => 3, | ||
| 204 | Duty::Quarter => 4, | ||
| 205 | Duty::Eigth => 8, | ||
| 206 | }; | ||
| 207 | let target_clock = config.target_fps.0 * duty_divider; | ||
| 208 | let target_division = lcd_clk.0 / target_clock; | ||
| 209 | |||
| 210 | let mut ps = 0; | ||
| 211 | let mut div = 0; | ||
| 212 | let mut best_fps_match = u32::MAX; | ||
| 213 | |||
| 214 | for trial_div in 0..0xF { | ||
| 215 | let trial_ps = (target_division / (trial_div + 16)) | ||
| 216 | .next_power_of_two() | ||
| 217 | .trailing_zeros(); | ||
| 218 | let fps = lcd_clk.0 / ((1 << trial_ps) * (trial_div + 16)) / duty_divider; | ||
| 219 | |||
| 220 | if fps < config.target_fps.0 { | ||
| 221 | continue; | ||
| 222 | } | ||
| 223 | |||
| 224 | if fps < best_fps_match { | ||
| 225 | ps = trial_ps; | ||
| 226 | div = trial_div; | ||
| 227 | best_fps_match = fps; | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | let ck_div = lcd_clk.0 / ((1 << ps) * (div + 16)); | ||
| 232 | |||
| 233 | trace!( | ||
| 234 | "lcd_clk: {}, fps: {}, ps: {}, div: {}, ck_div: {}", | ||
| 235 | lcd_clk, best_fps_match, ps, div, ck_div | ||
| 236 | ); | ||
| 237 | |||
| 238 | if best_fps_match == u32::MAX || ps > 0xF { | ||
| 239 | panic!("Lcd clock error"); | ||
| 240 | } | ||
| 241 | |||
| 242 | // Set the frame control | ||
| 243 | T::regs().fcr().modify(|w| { | ||
| 244 | w.set_ps(ps as u8); | ||
| 245 | w.set_div(div as u8); | ||
| 246 | w.set_cc(0b100); // Init in the middle-ish | ||
| 247 | w.set_dead(0b000); | ||
| 248 | w.set_pon(config.drive as u8 & 0x07); | ||
| 249 | w.set_hd((config.drive as u8 & !0x07) != 0); | ||
| 250 | }); | ||
| 251 | |||
| 252 | // Wait for the frame control to synchronize | ||
| 253 | while !T::regs().sr().read().fcrsf() {} | ||
| 254 | |||
| 255 | // Set the control register values | ||
| 256 | T::regs().cr().modify(|w| { | ||
| 257 | #[cfg(lcd_v2)] | ||
| 258 | w.set_bufen(config.use_voltage_output_buffer); | ||
| 259 | w.set_mux_seg(config.use_segment_muxing); | ||
| 260 | w.set_bias(config.bias as u8); | ||
| 261 | w.set_duty(config.duty as u8); | ||
| 262 | w.set_vsel(matches!(config.voltage_source, VoltageSource::External)); | ||
| 263 | }); | ||
| 264 | |||
| 265 | // Enable the lcd | ||
| 266 | T::regs().cr().modify(|w| w.set_lcden(true)); | ||
| 267 | |||
| 268 | // Wait for the lcd to be enabled | ||
| 269 | while !T::regs().sr().read().ens() {} | ||
| 270 | |||
| 271 | // Wait for the stepup converter to be ready | ||
| 272 | while !T::regs().sr().read().rdy() {} | ||
| 273 | |||
| 274 | Self { | ||
| 275 | _peri: PhantomData, | ||
| 276 | duty: config.duty, | ||
| 277 | ck_div, | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | /// Change the contrast by changing the voltage being used. | ||
| 282 | /// | ||
| 283 | /// This is from low at 0 to high at 7. | ||
| 284 | pub fn set_contrast_control(&mut self, value: u8) { | ||
| 285 | assert!((0..=7).contains(&value)); | ||
| 286 | T::regs().fcr().modify(|w| w.set_cc(value)); | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Change the contrast by introducing a deadtime to the signals | ||
| 290 | /// where the voltages are held at 0V. | ||
| 291 | /// | ||
| 292 | /// This is from no dead time at 0 to high dead time at 7. | ||
| 293 | pub fn set_dead_time(&mut self, value: u8) { | ||
| 294 | assert!((0..=7).contains(&value)); | ||
| 295 | T::regs() | ||
| 296 | .fcr() | ||
| 297 | .modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value)); | ||
| 298 | } | ||
| 299 | |||
| 300 | /// Write data into the display RAM. This overwrites the data already in it for the specified com index. | ||
| 301 | /// | ||
| 302 | /// The `com_index` value determines which part of the RAM is written to. | ||
| 303 | /// The `segments` value is a bitmap where each bit represents whether a pixel is turned on or off. | ||
| 304 | /// | ||
| 305 | /// This function waits last update request to be finished, but does not submit the buffer to the LCD with a new request. | ||
| 306 | /// Submission has to be done manually using [Self::submit_frame]. | ||
| 307 | pub fn write_com_segments(&mut self, com_index: u8, segments: u64) { | ||
| 308 | while T::regs().sr().read().udr() {} | ||
| 309 | |||
| 310 | assert!( | ||
| 311 | com_index < self.duty.num_com_pins(), | ||
| 312 | "Com index cannot be higher than number of configured com pins (through the Duty setting in the config)" | ||
| 313 | ); | ||
| 314 | |||
| 315 | assert!( | ||
| 316 | segments.leading_zeros() >= 64 - self.num_segments() as u32, | ||
| 317 | "Invalid segment pixel set", | ||
| 318 | ); | ||
| 319 | |||
| 320 | T::regs() | ||
| 321 | .ram_com(com_index as usize) | ||
| 322 | .low() | ||
| 323 | .write_value((segments & 0xFFFF_FFFF) as u32); | ||
| 324 | T::regs() | ||
| 325 | .ram_com(com_index as usize) | ||
| 326 | .high() | ||
| 327 | .write_value(((segments >> 32) & 0xFFFF_FFFF) as u32); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Read the data from the display RAM. | ||
| 331 | /// | ||
| 332 | /// The `com_index` value determines which part of the RAM is read from. | ||
| 333 | /// | ||
| 334 | /// This function waits for the last update request to be finished. | ||
| 335 | pub fn read_com_segments(&self, com_index: u8) -> u64 { | ||
| 336 | while T::regs().sr().read().udr() {} | ||
| 337 | |||
| 338 | assert!( | ||
| 339 | com_index < self.duty.num_com_pins(), | ||
| 340 | "Com index cannot be higher than number of configured com pins (through the Duty setting in the config)" | ||
| 341 | ); | ||
| 342 | |||
| 343 | let low = T::regs().ram_com(com_index as usize).low().read(); | ||
| 344 | let high = T::regs().ram_com(com_index as usize).high().read(); | ||
| 345 | |||
| 346 | ((high as u64) << 32) | low as u64 | ||
| 347 | } | ||
| 348 | |||
| 349 | /// Submit the current RAM data to the LCD. | ||
| 350 | /// | ||
| 351 | /// This function waits until the RAM is writable, but does not wait for the frame to be drawn. | ||
| 352 | pub fn submit_frame(&mut self) { | ||
| 353 | while T::regs().sr().read().udr() {} | ||
| 354 | // Clear the update done flag | ||
| 355 | T::regs().sr().write(|w| w.set_udd(true)); | ||
| 356 | // Set the update request flag | ||
| 357 | T::regs().sr().write(|w| w.set_udr(true)); | ||
| 358 | } | ||
| 359 | |||
| 360 | /// Get the number of segments that are supported on this LCD | ||
| 361 | pub fn num_segments(&self) -> u8 { | ||
| 362 | match self.duty { | ||
| 363 | Duty::Eigth => NUM_SEGMENTS - 4, // With 8 coms, 4 of the segment pins turn into com pins | ||
| 364 | _ => NUM_SEGMENTS, | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | /// Get the pixel mask for the current LCD setup. | ||
| 369 | /// This is a mask of all bits that are allowed to be set in the [Self::write_com_segments] function. | ||
| 370 | pub fn segment_pixel_mask(&self) -> u64 { | ||
| 371 | (1 << self.num_segments()) - 1 | ||
| 372 | } | ||
| 373 | |||
| 374 | /// Get the number of COM pins that were configured through the Drive config | ||
| 375 | pub fn num_com_pins(&self) -> u8 { | ||
| 376 | self.duty.num_com_pins() | ||
| 377 | } | ||
| 378 | |||
| 379 | /// Set the blink behavior on some pixels. | ||
| 380 | /// | ||
| 381 | /// The blink frequency is an approximation. It's divided from the clock selected by the FPS. | ||
| 382 | /// Play with the FPS value if you want the blink frequency to be more accurate. | ||
| 383 | /// | ||
| 384 | /// If a blink frequency cannot be attained, this function will panic. | ||
| 385 | pub fn set_blink(&mut self, selector: BlinkSelector, freq: BlinkFreq) { | ||
| 386 | // Freq * 100 to be able to do integer math | ||
| 387 | let scaled_blink_freq = match freq { | ||
| 388 | BlinkFreq::Hz0_25 => 25, | ||
| 389 | BlinkFreq::Hz0_5 => 50, | ||
| 390 | BlinkFreq::Hz1 => 100, | ||
| 391 | BlinkFreq::Hz2 => 200, | ||
| 392 | BlinkFreq::Hz4 => 400, | ||
| 393 | }; | ||
| 394 | |||
| 395 | let desired_divider = self.ck_div * 100 / scaled_blink_freq; | ||
| 396 | let target_divider = desired_divider.next_power_of_two(); | ||
| 397 | let power_divisions = target_divider.trailing_zeros(); | ||
| 398 | |||
| 399 | trace!( | ||
| 400 | "Setting LCD blink frequency -> desired_divider: {}, target_divider: {}", | ||
| 401 | desired_divider, target_divider | ||
| 402 | ); | ||
| 403 | |||
| 404 | assert!( | ||
| 405 | (8..=1024).contains(&target_divider), | ||
| 406 | "LCD blink frequency cannot be attained" | ||
| 407 | ); | ||
| 408 | |||
| 409 | T::regs().fcr().modify(|reg| { | ||
| 410 | reg.set_blinkf((power_divisions - 3) as u8); | ||
| 411 | reg.set_blink(selector as u8); | ||
| 412 | }) | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 416 | impl<'d, T: Instance> Drop for Lcd<'d, T> { | ||
| 417 | fn drop(&mut self) { | ||
| 418 | // Disable the lcd | ||
| 419 | T::regs().cr().modify(|w| w.set_lcden(false)); | ||
| 420 | rcc::disable::<T>(); | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | /// Blink frequency | ||
| 425 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 426 | pub enum BlinkFreq { | ||
| 427 | /// 0.25 hz | ||
| 428 | Hz0_25, | ||
| 429 | /// 0.5 hz | ||
| 430 | Hz0_5, | ||
| 431 | /// 1 hz | ||
| 432 | Hz1, | ||
| 433 | /// 2 hz | ||
| 434 | Hz2, | ||
| 435 | /// 4 hz | ||
| 436 | Hz4, | ||
| 437 | } | ||
| 438 | |||
| 439 | /// Blink pixel selector | ||
| 440 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 441 | #[repr(u8)] | ||
| 442 | pub enum BlinkSelector { | ||
| 443 | /// No pixels blink | ||
| 444 | None = 0b00, | ||
| 445 | /// The SEG0, COM0 pixel blinks if the pixel is set | ||
| 446 | Seg0Com0 = 0b01, | ||
| 447 | /// The SEG0 pixel of all COMs blinks if the pixel is set | ||
| 448 | Seg0ComAll = 0b10, | ||
| 449 | /// All pixels blink if the pixel is set | ||
| 450 | All = 0b11, | ||
| 451 | } | ||
| 452 | |||
| 453 | /// A type-erased pin that can be configured as an LCD pin. | ||
| 454 | /// This is used for passing pins to the new function in the array. | ||
| 455 | pub struct LcdPin<'d, T: Instance> { | ||
| 456 | pin: Peri<'d, AnyPin>, | ||
| 457 | af_num: u8, | ||
| 458 | is_seg: bool, | ||
| 459 | _phantom: PhantomData<T>, | ||
| 460 | } | ||
| 461 | |||
| 462 | impl<'d, T: Instance> LcdPin<'d, T> { | ||
| 463 | /// Construct an LCD pin from any pin that supports it | ||
| 464 | pub fn new_seg(pin: Peri<'d, impl SegPin<T>>) -> Self { | ||
| 465 | let af = pin.af_num(); | ||
| 466 | |||
| 467 | Self { | ||
| 468 | pin: pin.into(), | ||
| 469 | af_num: af, | ||
| 470 | is_seg: true, | ||
| 471 | _phantom: PhantomData, | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | /// Construct an LCD pin from any pin that supports it | ||
| 476 | pub fn new_com(pin: Peri<'d, impl ComPin<T>>) -> Self { | ||
| 477 | let af = pin.af_num(); | ||
| 478 | |||
| 479 | Self { | ||
| 480 | pin: pin.into(), | ||
| 481 | af_num: af, | ||
| 482 | is_seg: false, | ||
| 483 | _phantom: PhantomData, | ||
| 484 | } | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | trait SealedInstance: crate::rcc::SealedRccPeripheral + PeripheralType { | ||
| 489 | fn regs() -> crate::pac::lcd::Lcd; | ||
| 490 | } | ||
| 491 | |||
| 492 | /// DSI instance trait. | ||
| 493 | #[allow(private_bounds)] | ||
| 494 | pub trait Instance: SealedInstance + RccPeripheral + 'static {} | ||
| 495 | |||
| 496 | pin_trait!(SegPin, Instance); | ||
| 497 | pin_trait!(ComPin, Instance); | ||
| 498 | pin_trait!(VlcdPin, Instance); | ||
| 499 | |||
| 500 | foreach_peripheral!( | ||
| 501 | (lcd, $inst:ident) => { | ||
| 502 | impl crate::lcd::SealedInstance for peripherals::$inst { | ||
| 503 | fn regs() -> crate::pac::lcd::Lcd { | ||
| 504 | crate::pac::$inst | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | impl crate::lcd::Instance for peripherals::$inst {} | ||
| 509 | }; | ||
| 510 | ); | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 7e0f7884e..2f783bf64 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #![cfg_attr(not(test), no_std)] | 1 | #![cfg_attr(not(test), no_std)] |
| 2 | #![allow(async_fn_in_trait)] | 2 | #![allow(async_fn_in_trait)] |
| 3 | #![allow(unsafe_op_in_unsafe_fn)] | ||
| 3 | #![cfg_attr( | 4 | #![cfg_attr( |
| 4 | docsrs, | 5 | docsrs, |
| 5 | doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-stm32'>browse the `embassy-stm32` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only (stm32h7, stm32h7rs55 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\n\n" | 6 | doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-stm32'>browse the `embassy-stm32` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only (stm32h7, stm32h7rs55 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\n\n" |
| @@ -53,6 +54,8 @@ pub mod timer; | |||
| 53 | 54 | ||
| 54 | #[cfg(adc)] | 55 | #[cfg(adc)] |
| 55 | pub mod adc; | 56 | pub mod adc; |
| 57 | #[cfg(backup_sram)] | ||
| 58 | pub mod backup_sram; | ||
| 56 | #[cfg(can)] | 59 | #[cfg(can)] |
| 57 | pub mod can; | 60 | pub mod can; |
| 58 | // FIXME: Cordic driver cause stm32u5a5zj crash | 61 | // FIXME: Cordic driver cause stm32u5a5zj crash |
| @@ -74,6 +77,7 @@ pub mod dts; | |||
| 74 | pub mod eth; | 77 | pub mod eth; |
| 75 | #[cfg(feature = "exti")] | 78 | #[cfg(feature = "exti")] |
| 76 | pub mod exti; | 79 | pub mod exti; |
| 80 | #[cfg(flash)] | ||
| 77 | pub mod flash; | 81 | pub mod flash; |
| 78 | #[cfg(fmc)] | 82 | #[cfg(fmc)] |
| 79 | pub mod fmc; | 83 | pub mod fmc; |
| @@ -91,6 +95,8 @@ pub mod i2c; | |||
| 91 | pub mod i2s; | 95 | pub mod i2s; |
| 92 | #[cfg(stm32wb)] | 96 | #[cfg(stm32wb)] |
| 93 | pub mod ipcc; | 97 | pub mod ipcc; |
| 98 | #[cfg(lcd)] | ||
| 99 | pub mod lcd; | ||
| 94 | #[cfg(feature = "low-power")] | 100 | #[cfg(feature = "low-power")] |
| 95 | pub mod low_power; | 101 | pub mod low_power; |
| 96 | #[cfg(lptim)] | 102 | #[cfg(lptim)] |
| @@ -147,7 +153,7 @@ pub use crate::_generated::interrupt; | |||
| 147 | /// Macro to bind interrupts to handlers. | 153 | /// Macro to bind interrupts to handlers. |
| 148 | /// | 154 | /// |
| 149 | /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) | 155 | /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) |
| 150 | /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to | 156 | /// and implements the right [`Binding`](crate::interrupt::typelevel::Binding)s for it. You can pass this struct to drivers to |
| 151 | /// prove at compile-time that the right interrupts have been bound. | 157 | /// prove at compile-time that the right interrupts have been bound. |
| 152 | /// | 158 | /// |
| 153 | /// Example of how to bind one interrupt: | 159 | /// Example of how to bind one interrupt: |
| @@ -174,7 +180,10 @@ pub use crate::_generated::interrupt; | |||
| 174 | /// } | 180 | /// } |
| 175 | /// ); | 181 | /// ); |
| 176 | /// ``` | 182 | /// ``` |
| 177 | 183 | /// | |
| 184 | /// Some chips collate multiple interrupt signals into a single interrupt vector. In the above example, I2C2_3 is a | ||
| 185 | /// single vector which is activated by events and errors on both peripherals I2C2 and I2C3. Check your chip's list | ||
| 186 | /// of interrupt vectors if you get an unexpected compile error trying to bind the standard name. | ||
| 178 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. | 187 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. |
| 179 | #[macro_export] | 188 | #[macro_export] |
| 180 | macro_rules! bind_interrupts { | 189 | macro_rules! bind_interrupts { |
| @@ -194,7 +203,7 @@ macro_rules! bind_interrupts { | |||
| 194 | 203 | ||
| 195 | $( | 204 | $( |
| 196 | #[allow(non_snake_case)] | 205 | #[allow(non_snake_case)] |
| 197 | #[no_mangle] | 206 | #[unsafe(no_mangle)] |
| 198 | $(#[cfg($cond_irq)])? | 207 | $(#[cfg($cond_irq)])? |
| 199 | $(#[doc = $doc])* | 208 | $(#[doc = $doc])* |
| 200 | unsafe extern "C" fn $irq() { | 209 | unsafe extern "C" fn $irq() { |
| @@ -222,7 +231,7 @@ macro_rules! bind_interrupts { | |||
| 222 | } | 231 | } |
| 223 | 232 | ||
| 224 | // Reexports | 233 | // Reexports |
| 225 | pub use _generated::{peripherals, Peripherals}; | 234 | pub use _generated::{Peripherals, peripherals}; |
| 226 | pub use embassy_hal_internal::{Peri, PeripheralType}; | 235 | pub use embassy_hal_internal::{Peri, PeripheralType}; |
| 227 | #[cfg(feature = "unstable-pac")] | 236 | #[cfg(feature = "unstable-pac")] |
| 228 | pub use stm32_metapac as pac; | 237 | pub use stm32_metapac as pac; |
| @@ -240,6 +249,14 @@ pub struct Config { | |||
| 240 | /// RCC config. | 249 | /// RCC config. |
| 241 | pub rcc: rcc::Config, | 250 | pub rcc: rcc::Config, |
| 242 | 251 | ||
| 252 | #[cfg(feature = "low-power")] | ||
| 253 | /// RTC config | ||
| 254 | pub rtc: rtc::RtcConfig, | ||
| 255 | |||
| 256 | #[cfg(feature = "low-power")] | ||
| 257 | /// Minimum time to stop | ||
| 258 | pub min_stop_pause: embassy_time::Duration, | ||
| 259 | |||
| 243 | /// Enable debug during sleep and stop. | 260 | /// Enable debug during sleep and stop. |
| 244 | /// | 261 | /// |
| 245 | /// May increase power consumption. Defaults to true. | 262 | /// May increase power consumption. Defaults to true. |
| @@ -293,6 +310,10 @@ impl Default for Config { | |||
| 293 | fn default() -> Self { | 310 | fn default() -> Self { |
| 294 | Self { | 311 | Self { |
| 295 | rcc: Default::default(), | 312 | rcc: Default::default(), |
| 313 | #[cfg(feature = "low-power")] | ||
| 314 | rtc: Default::default(), | ||
| 315 | #[cfg(feature = "low-power")] | ||
| 316 | min_stop_pause: embassy_time::Duration::from_millis(250), | ||
| 296 | #[cfg(dbgmcu)] | 317 | #[cfg(dbgmcu)] |
| 297 | enable_debug_during_sleep: true, | 318 | enable_debug_during_sleep: true, |
| 298 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] | 319 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] |
| @@ -495,6 +516,16 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 495 | critical_section::with(|cs| { | 516 | critical_section::with(|cs| { |
| 496 | let p = Peripherals::take_with_cs(cs); | 517 | let p = Peripherals::take_with_cs(cs); |
| 497 | 518 | ||
| 519 | #[cfg(dbgmcu_n6)] | ||
| 520 | { | ||
| 521 | crate::pac::RCC.miscensr().write(|w| w.set_dbgens(true)); | ||
| 522 | crate::pac::RCC.miscenr().read(); // volatile read | ||
| 523 | crate::pac::DBGMCU | ||
| 524 | .cr() | ||
| 525 | .modify(|w| w.set_dbgclken(stm32_metapac::dbgmcu::vals::Dbgclken::B_0X1)); | ||
| 526 | crate::pac::DBGMCU.cr().read(); | ||
| 527 | } | ||
| 528 | |||
| 498 | #[cfg(dbgmcu)] | 529 | #[cfg(dbgmcu)] |
| 499 | crate::pac::DBGMCU.cr().modify(|cr| { | 530 | crate::pac::DBGMCU.cr().modify(|cr| { |
| 500 | #[cfg(dbgmcu_h5)] | 531 | #[cfg(dbgmcu_h5)] |
| @@ -509,7 +540,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 509 | } | 540 | } |
| 510 | #[cfg(any( | 541 | #[cfg(any( |
| 511 | dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, | 542 | dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, |
| 512 | dbgmcu_l4, dbgmcu_wb, dbgmcu_wl | 543 | dbgmcu_l4, dbgmcu_wb, dbgmcu_wl, dbgmcu_n6 |
| 513 | ))] | 544 | ))] |
| 514 | { | 545 | { |
| 515 | cr.set_dbg_sleep(config.enable_debug_during_sleep); | 546 | cr.set_dbg_sleep(config.enable_debug_during_sleep); |
| @@ -530,7 +561,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 530 | rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs); | 561 | rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs); |
| 531 | #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))] | 562 | #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))] |
| 532 | rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs); | 563 | rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs); |
| 533 | #[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs)))] | 564 | #[cfg(all(flash, not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs))))] |
| 534 | rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs); | 565 | rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs); |
| 535 | 566 | ||
| 536 | // Enable the VDDIO2 power supply on chips that have it. | 567 | // Enable the VDDIO2 power supply on chips that have it. |
| @@ -590,7 +621,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 590 | #[cfg(ucpd)] | 621 | #[cfg(ucpd)] |
| 591 | ucpd::init( | 622 | ucpd::init( |
| 592 | cs, | 623 | cs, |
| 593 | #[cfg(peri_ucpd1)] | 624 | #[cfg(all(peri_ucpd1, not(stm32n6)))] |
| 594 | config.enable_ucpd1_dead_battery, | 625 | config.enable_ucpd1_dead_battery, |
| 595 | #[cfg(peri_ucpd2)] | 626 | #[cfg(peri_ucpd2)] |
| 596 | config.enable_ucpd2_dead_battery, | 627 | config.enable_ucpd2_dead_battery, |
| @@ -622,8 +653,28 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 622 | exti::init(cs); | 653 | exti::init(cs); |
| 623 | 654 | ||
| 624 | rcc::init_rcc(cs, config.rcc); | 655 | rcc::init_rcc(cs, config.rcc); |
| 656 | |||
| 657 | #[cfg(feature = "low-power")] | ||
| 658 | rtc::init_rtc(cs, config.rtc, config.min_stop_pause); | ||
| 659 | |||
| 660 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 661 | hsem::init_hsem(cs); | ||
| 625 | } | 662 | } |
| 626 | 663 | ||
| 627 | p | 664 | p |
| 628 | }) | 665 | }) |
| 629 | } | 666 | } |
| 667 | |||
| 668 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 669 | #[allow(unused)] | ||
| 670 | pub(crate) fn block_for_us(us: u64) { | ||
| 671 | cfg_if::cfg_if! { | ||
| 672 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called | ||
| 673 | // as in sometimes 15 us (1 tick) would take > 20 seconds. | ||
| 674 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { | ||
| 675 | embassy_time::block_for(embassy_time::Duration::from_micros(us)); | ||
| 676 | } else { | ||
| 677 | cortex_m::asm::delay(unsafe { rcc::get_freqs().sys.to_hertz().unwrap().0 as u64 * us / 1_000_000 } as u32); | ||
| 678 | } | ||
| 679 | } | ||
| 680 | } | ||
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 342f73bc8..bd8290da0 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | //! | 14 | //! |
| 15 | //! Since entering and leaving low-power modes typically incurs a significant latency, the | 15 | //! Since entering and leaving low-power modes typically incurs a significant latency, the |
| 16 | //! low-power executor will only attempt to enter when the next timer event is at least | 16 | //! low-power executor will only attempt to enter when the next timer event is at least |
| 17 | //! [`time_driver::MIN_STOP_PAUSE`] in the future. | 17 | //! [`time_driver::min_stop_pause`] in the future. |
| 18 | //! | 18 | //! |
| 19 | //! Currently there is no macro analogous to `embassy_executor::main` for this executor; | 19 | //! Currently there is no macro analogous to `embassy_executor::main` for this executor; |
| 20 | //! consequently one must define their entrypoint manually. Moreover, you must relinquish control | 20 | //! consequently one must define their entrypoint manually. Moreover, you must relinquish control |
| @@ -22,53 +22,73 @@ | |||
| 22 | //! | 22 | //! |
| 23 | //! ```rust,no_run | 23 | //! ```rust,no_run |
| 24 | //! use embassy_executor::Spawner; | 24 | //! use embassy_executor::Spawner; |
| 25 | //! use embassy_stm32::low_power::Executor; | 25 | //! use embassy_stm32::low_power; |
| 26 | //! use embassy_stm32::rtc::{Rtc, RtcConfig}; | 26 | //! use embassy_stm32::rtc::{Rtc, RtcConfig}; |
| 27 | //! use static_cell::StaticCell; | 27 | //! use embassy_time::Duration; |
| 28 | //! | 28 | //! |
| 29 | //! #[cortex_m_rt::entry] | 29 | //! #[embassy_executor::main(executor = "low_power::Executor")] |
| 30 | //! fn main() -> ! { | ||
| 31 | //! Executor::take().run(|spawner| { | ||
| 32 | //! spawner.spawn(unwrap!(async_main(spawner))); | ||
| 33 | //! }); | ||
| 34 | //! } | ||
| 35 | //! | ||
| 36 | //! #[embassy_executor::task] | ||
| 37 | //! async fn async_main(spawner: Spawner) { | 30 | //! async fn async_main(spawner: Spawner) { |
| 38 | //! // initialize the platform... | 31 | //! // initialize the platform... |
| 39 | //! let mut config = embassy_stm32::Config::default(); | 32 | //! let mut config = embassy_stm32::Config::default(); |
| 33 | //! // the default value, but can be adjusted | ||
| 34 | //! config.min_stop_pause = Duration::from_millis(250); | ||
| 40 | //! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working | 35 | //! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working |
| 41 | //! config.enable_debug_during_sleep = false; | 36 | //! config.enable_debug_during_sleep = false; |
| 42 | //! let p = embassy_stm32::init(config); | 37 | //! let p = embassy_stm32::init(config); |
| 43 | //! | 38 | //! |
| 44 | //! // give the RTC to the executor... | ||
| 45 | //! let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); | ||
| 46 | //! static RTC: StaticCell<Rtc> = StaticCell::new(); | ||
| 47 | //! let rtc = RTC.init(rtc); | ||
| 48 | //! embassy_stm32::low_power::stop_with_rtc(rtc); | ||
| 49 | //! | ||
| 50 | //! // your application here... | 39 | //! // your application here... |
| 51 | //! } | 40 | //! } |
| 52 | //! ``` | 41 | //! ``` |
| 53 | 42 | ||
| 54 | // TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.` | ||
| 55 | #![allow(static_mut_refs)] | ||
| 56 | |||
| 57 | use core::arch::asm; | 43 | use core::arch::asm; |
| 58 | use core::marker::PhantomData; | 44 | use core::marker::PhantomData; |
| 59 | use core::sync::atomic::{compiler_fence, Ordering}; | 45 | use core::mem; |
| 46 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 60 | 47 | ||
| 61 | use cortex_m::peripheral::SCB; | 48 | use cortex_m::peripheral::SCB; |
| 49 | use critical_section::CriticalSection; | ||
| 62 | use embassy_executor::*; | 50 | use embassy_executor::*; |
| 63 | 51 | ||
| 64 | use crate::interrupt; | 52 | use crate::interrupt; |
| 65 | use crate::time_driver::{get_driver, RtcDriver}; | 53 | pub use crate::rcc::StopMode; |
| 54 | use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount}; | ||
| 55 | use crate::time_driver::get_driver; | ||
| 66 | 56 | ||
| 67 | const THREAD_PENDER: usize = usize::MAX; | 57 | const THREAD_PENDER: usize = usize::MAX; |
| 68 | 58 | ||
| 69 | use crate::rtc::Rtc; | 59 | static mut EXECUTOR_TAKEN: bool = false; |
| 60 | |||
| 61 | /// Prevent the device from going into the stop mode if held | ||
| 62 | pub struct DeviceBusy(StopMode); | ||
| 63 | |||
| 64 | impl DeviceBusy { | ||
| 65 | /// Create a new DeviceBusy with stop1. | ||
| 66 | pub fn new_stop1() -> Self { | ||
| 67 | Self::new(StopMode::Stop1) | ||
| 68 | } | ||
| 69 | |||
| 70 | /// Create a new DeviceBusy with stop2. | ||
| 71 | pub fn new_stop2() -> Self { | ||
| 72 | Self::new(StopMode::Stop2) | ||
| 73 | } | ||
| 70 | 74 | ||
| 71 | static mut EXECUTOR: Option<Executor> = None; | 75 | /// Create a new DeviceBusy. |
| 76 | pub fn new(stop_mode: StopMode) -> Self { | ||
| 77 | critical_section::with(|cs| { | ||
| 78 | increment_stop_refcount(cs, stop_mode); | ||
| 79 | }); | ||
| 80 | |||
| 81 | Self(stop_mode) | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | impl Drop for DeviceBusy { | ||
| 86 | fn drop(&mut self) { | ||
| 87 | critical_section::with(|cs| { | ||
| 88 | decrement_stop_refcount(cs, self.0); | ||
| 89 | }); | ||
| 90 | } | ||
| 91 | } | ||
| 72 | 92 | ||
| 73 | #[cfg(not(stm32u0))] | 93 | #[cfg(not(stm32u0))] |
| 74 | foreach_interrupt! { | 94 | foreach_interrupt! { |
| @@ -76,7 +96,7 @@ foreach_interrupt! { | |||
| 76 | #[interrupt] | 96 | #[interrupt] |
| 77 | #[allow(non_snake_case)] | 97 | #[allow(non_snake_case)] |
| 78 | unsafe fn $irq() { | 98 | unsafe fn $irq() { |
| 79 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 99 | Executor::on_wakeup_irq(); |
| 80 | } | 100 | } |
| 81 | }; | 101 | }; |
| 82 | } | 102 | } |
| @@ -87,55 +107,35 @@ foreach_interrupt! { | |||
| 87 | #[interrupt] | 107 | #[interrupt] |
| 88 | #[allow(non_snake_case)] | 108 | #[allow(non_snake_case)] |
| 89 | unsafe fn $irq() { | 109 | unsafe fn $irq() { |
| 90 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 110 | Executor::on_wakeup_irq(); |
| 91 | } | 111 | } |
| 92 | }; | 112 | }; |
| 93 | } | 113 | } |
| 94 | 114 | ||
| 95 | #[allow(dead_code)] | ||
| 96 | pub(crate) unsafe fn on_wakeup_irq() { | ||
| 97 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Configure STOP mode with RTC. | ||
| 101 | pub fn stop_with_rtc(rtc: &'static Rtc) { | ||
| 102 | unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Get whether the core is ready to enter the given stop mode. | 115 | /// Get whether the core is ready to enter the given stop mode. |
| 106 | /// | 116 | /// |
| 107 | /// This will return false if some peripheral driver is in use that | 117 | /// This will return false if some peripheral driver is in use that |
| 108 | /// prevents entering the given stop mode. | 118 | /// prevents entering the given stop mode. |
| 109 | pub fn stop_ready(stop_mode: StopMode) -> bool { | 119 | pub fn stop_ready(stop_mode: StopMode) -> bool { |
| 110 | match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() { | 120 | critical_section::with(|cs| match Executor::stop_mode(cs) { |
| 111 | Some(StopMode::Stop2) => true, | 121 | Some(StopMode::Standby | StopMode::Stop2) => true, |
| 112 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, | 122 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, |
| 113 | None => false, | 123 | None => false, |
| 114 | } | 124 | }) |
| 115 | } | ||
| 116 | |||
| 117 | /// Available Stop modes. | ||
| 118 | #[non_exhaustive] | ||
| 119 | #[derive(PartialEq)] | ||
| 120 | pub enum StopMode { | ||
| 121 | /// STOP 1 | ||
| 122 | Stop1, | ||
| 123 | /// STOP 2 | ||
| 124 | Stop2, | ||
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] | 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] |
| 128 | use stm32_metapac::pwr::vals::Lpms; | 128 | use crate::pac::pwr::vals::Lpms; |
| 129 | 129 | ||
| 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] | 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] |
| 131 | impl Into<Lpms> for StopMode { | 131 | impl Into<Lpms> for StopMode { |
| 132 | fn into(self) -> Lpms { | 132 | fn into(self) -> Lpms { |
| 133 | match self { | 133 | match self { |
| 134 | StopMode::Stop1 => Lpms::STOP1, | 134 | StopMode::Stop1 => Lpms::STOP1, |
| 135 | #[cfg(not(stm32wba))] | 135 | #[cfg(not(any(stm32wb, stm32wba)))] |
| 136 | StopMode::Stop2 => Lpms::STOP2, | 136 | StopMode::Standby | StopMode::Stop2 => Lpms::STOP2, |
| 137 | #[cfg(stm32wba)] | 137 | #[cfg(any(stm32wb, stm32wba))] |
| 138 | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? | 138 | StopMode::Standby | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? |
| 139 | } | 139 | } |
| 140 | } | 140 | } |
| 141 | } | 141 | } |
| @@ -153,55 +153,139 @@ impl Into<Lpms> for StopMode { | |||
| 153 | pub struct Executor { | 153 | pub struct Executor { |
| 154 | inner: raw::Executor, | 154 | inner: raw::Executor, |
| 155 | not_send: PhantomData<*mut ()>, | 155 | not_send: PhantomData<*mut ()>, |
| 156 | scb: SCB, | ||
| 157 | time_driver: &'static RtcDriver, | ||
| 158 | } | 156 | } |
| 159 | 157 | ||
| 160 | impl Executor { | 158 | impl Executor { |
| 161 | /// Create a new Executor. | 159 | /// Create a new Executor. |
| 162 | pub fn take() -> &'static mut Self { | 160 | pub fn new() -> Self { |
| 163 | critical_section::with(|_| unsafe { | 161 | unsafe { |
| 164 | assert!(EXECUTOR.is_none()); | 162 | if EXECUTOR_TAKEN { |
| 165 | 163 | panic!("Low power executor can only be taken once."); | |
| 166 | EXECUTOR = Some(Self { | 164 | } else { |
| 167 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), | 165 | EXECUTOR_TAKEN = true; |
| 168 | not_send: PhantomData, | 166 | } |
| 169 | scb: cortex_m::Peripherals::steal().SCB, | 167 | } |
| 170 | time_driver: get_driver(), | ||
| 171 | }); | ||
| 172 | |||
| 173 | let executor = EXECUTOR.as_mut().unwrap(); | ||
| 174 | 168 | ||
| 175 | executor | 169 | Self { |
| 176 | }) | 170 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), |
| 171 | not_send: PhantomData, | ||
| 172 | } | ||
| 177 | } | 173 | } |
| 178 | 174 | ||
| 179 | unsafe fn on_wakeup_irq(&mut self) { | 175 | pub(crate) unsafe fn on_wakeup_irq() { |
| 180 | self.time_driver.resume_time(); | 176 | critical_section::with(|cs| { |
| 181 | trace!("low power: resume"); | 177 | #[cfg(stm32wlex)] |
| 178 | { | ||
| 179 | use crate::pac::rcc::vals::Sw; | ||
| 180 | use crate::pac::{PWR, RCC}; | ||
| 181 | use crate::rcc::init as init_rcc; | ||
| 182 | |||
| 183 | let extscr = PWR.extscr().read(); | ||
| 184 | if extscr.c1stop2f() || extscr.c1stopf() { | ||
| 185 | // when we wake from any stop mode we need to re-initialize the rcc | ||
| 186 | while RCC.cfgr().read().sws() != Sw::MSI {} | ||
| 187 | |||
| 188 | init_rcc(RCC_CONFIG.unwrap()); | ||
| 189 | |||
| 190 | if extscr.c1stop2f() { | ||
| 191 | // when we wake from STOP2, we need to re-initialize the time driver | ||
| 192 | get_driver().init_timer(cs); | ||
| 193 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) | ||
| 194 | // and given that we just woke from STOP2, we can reset them | ||
| 195 | REFCOUNT_STOP2 = 0; | ||
| 196 | REFCOUNT_STOP1 = 0; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | get_driver().resume_time(cs); | ||
| 201 | trace!("low power: resume"); | ||
| 202 | }); | ||
| 182 | } | 203 | } |
| 183 | 204 | ||
| 184 | pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { | 205 | const fn get_scb() -> SCB { |
| 185 | self.time_driver.set_rtc(rtc); | 206 | unsafe { mem::transmute(()) } |
| 186 | |||
| 187 | rtc.enable_wakeup_line(); | ||
| 188 | |||
| 189 | trace!("low power: stop with rtc configured"); | ||
| 190 | } | 207 | } |
| 191 | 208 | ||
| 192 | fn stop_mode(&self) -> Option<StopMode> { | 209 | fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { |
| 193 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } && unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 210 | // We cannot enter standby because we will lose program state. |
| 211 | if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } { | ||
| 212 | trace!("low power: stop 2"); | ||
| 194 | Some(StopMode::Stop2) | 213 | Some(StopMode::Stop2) |
| 195 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 214 | } else if unsafe { REFCOUNT_STOP1 == 0 } { |
| 215 | trace!("low power: stop 1"); | ||
| 196 | Some(StopMode::Stop1) | 216 | Some(StopMode::Stop1) |
| 197 | } else { | 217 | } else { |
| 218 | trace!("low power: not ready to stop (refcount_stop1: {})", unsafe { | ||
| 219 | REFCOUNT_STOP1 | ||
| 220 | }); | ||
| 198 | None | 221 | None |
| 199 | } | 222 | } |
| 200 | } | 223 | } |
| 201 | 224 | ||
| 225 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 226 | fn configure_stop_stm32wb(&self, _cs: CriticalSection) -> Result<(), ()> { | ||
| 227 | use core::task::Poll; | ||
| 228 | |||
| 229 | use embassy_futures::poll_once; | ||
| 230 | |||
| 231 | use crate::hsem::HardwareSemaphoreChannel; | ||
| 232 | use crate::pac::rcc::vals::{Smps, Sw}; | ||
| 233 | use crate::pac::{PWR, RCC}; | ||
| 234 | |||
| 235 | trace!("low power: trying to get sem3"); | ||
| 236 | |||
| 237 | let sem3_mutex = match poll_once(HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(3).lock(0)) { | ||
| 238 | Poll::Pending => None, | ||
| 239 | Poll::Ready(mutex) => Some(mutex), | ||
| 240 | } | ||
| 241 | .ok_or(())?; | ||
| 242 | |||
| 243 | trace!("low power: got sem3"); | ||
| 244 | |||
| 245 | let sem4_mutex = HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(4).try_lock(0); | ||
| 246 | if let Some(sem4_mutex) = sem4_mutex { | ||
| 247 | trace!("low power: got sem4"); | ||
| 248 | |||
| 249 | if PWR.extscr().read().c2ds() { | ||
| 250 | drop(sem4_mutex); | ||
| 251 | } else { | ||
| 252 | return Ok(()); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | // Sem4 not granted | ||
| 257 | // Set HSION | ||
| 258 | RCC.cr().modify(|w| { | ||
| 259 | w.set_hsion(true); | ||
| 260 | }); | ||
| 261 | |||
| 262 | // Wait for HSIRDY | ||
| 263 | while !RCC.cr().read().hsirdy() {} | ||
| 264 | |||
| 265 | // Set SW to HSI | ||
| 266 | RCC.cfgr().modify(|w| { | ||
| 267 | w.set_sw(Sw::HSI); | ||
| 268 | }); | ||
| 269 | |||
| 270 | // Wait for SWS to report HSI | ||
| 271 | while !RCC.cfgr().read().sws().eq(&Sw::HSI) {} | ||
| 272 | |||
| 273 | // Set SMPSSEL to HSI | ||
| 274 | RCC.smpscr().modify(|w| { | ||
| 275 | w.set_smpssel(Smps::HSI); | ||
| 276 | }); | ||
| 277 | |||
| 278 | drop(sem3_mutex); | ||
| 279 | |||
| 280 | Ok(()) | ||
| 281 | } | ||
| 282 | |||
| 202 | #[allow(unused_variables)] | 283 | #[allow(unused_variables)] |
| 203 | fn configure_stop(&mut self, stop_mode: StopMode) { | 284 | fn configure_stop(&self, _cs: CriticalSection, stop_mode: StopMode) -> Result<(), ()> { |
| 204 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba))] | 285 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 286 | self.configure_stop_stm32wb(_cs)?; | ||
| 287 | |||
| 288 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb, stm32wba, stm32wlex))] | ||
| 205 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); | 289 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); |
| 206 | #[cfg(stm32h5)] | 290 | #[cfg(stm32h5)] |
| 207 | crate::pac::PWR.pmcr().modify(|v| { | 291 | crate::pac::PWR.pmcr().modify(|v| { |
| @@ -209,34 +293,32 @@ impl Executor { | |||
| 209 | v.set_lpms(vals::Lpms::STOP); | 293 | v.set_lpms(vals::Lpms::STOP); |
| 210 | v.set_svos(vals::Svos::SCALE3); | 294 | v.set_svos(vals::Svos::SCALE3); |
| 211 | }); | 295 | }); |
| 296 | |||
| 297 | Ok(()) | ||
| 212 | } | 298 | } |
| 213 | 299 | ||
| 214 | fn configure_pwr(&mut self) { | 300 | fn configure_pwr(&self) { |
| 215 | self.scb.clear_sleepdeep(); | 301 | Self::get_scb().clear_sleepdeep(); |
| 302 | // Clear any previous stop flags | ||
| 303 | #[cfg(stm32wlex)] | ||
| 304 | crate::pac::PWR.extscr().modify(|w| { | ||
| 305 | w.set_c1cssf(true); | ||
| 306 | }); | ||
| 216 | 307 | ||
| 217 | compiler_fence(Ordering::SeqCst); | 308 | compiler_fence(Ordering::SeqCst); |
| 218 | 309 | ||
| 219 | let stop_mode = self.stop_mode(); | 310 | critical_section::with(|cs| { |
| 311 | let _ = unsafe { RCC_CONFIG }?; | ||
| 312 | let stop_mode = Self::stop_mode(cs)?; | ||
| 313 | get_driver().pause_time(cs).ok()?; | ||
| 314 | self.configure_stop(cs, stop_mode).ok()?; | ||
| 220 | 315 | ||
| 221 | if stop_mode.is_none() { | 316 | Some(()) |
| 222 | trace!("low power: not ready to stop"); | 317 | }) |
| 223 | return; | 318 | .map(|_| { |
| 224 | } | 319 | #[cfg(not(feature = "low-power-debug-with-sleep"))] |
| 225 | 320 | Self::get_scb().set_sleepdeep(); | |
| 226 | if self.time_driver.pause_time().is_err() { | 321 | }); |
| 227 | trace!("low power: failed to pause time"); | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | |||
| 231 | let stop_mode = stop_mode.unwrap(); | ||
| 232 | match stop_mode { | ||
| 233 | StopMode::Stop1 => trace!("low power: stop 1"), | ||
| 234 | StopMode::Stop2 => trace!("low power: stop 2"), | ||
| 235 | } | ||
| 236 | self.configure_stop(stop_mode); | ||
| 237 | |||
| 238 | #[cfg(not(feature = "low-power-debug-with-sleep"))] | ||
| 239 | self.scb.set_sleepdeep(); | ||
| 240 | } | 322 | } |
| 241 | 323 | ||
| 242 | /// Run the executor. | 324 | /// Run the executor. |
| @@ -258,14 +340,26 @@ impl Executor { | |||
| 258 | /// | 340 | /// |
| 259 | /// This function never returns. | 341 | /// This function never returns. |
| 260 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 342 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 261 | let executor = unsafe { EXECUTOR.as_mut().unwrap() }; | 343 | init(self.inner.spawner()); |
| 262 | init(executor.inner.spawner()); | ||
| 263 | 344 | ||
| 264 | loop { | 345 | loop { |
| 265 | unsafe { | 346 | unsafe { |
| 266 | executor.inner.poll(); | 347 | self.inner.poll(); |
| 267 | self.configure_pwr(); | 348 | self.configure_pwr(); |
| 268 | asm!("wfe"); | 349 | asm!("wfe"); |
| 350 | #[cfg(stm32wlex)] | ||
| 351 | { | ||
| 352 | let es = crate::pac::PWR.extscr().read(); | ||
| 353 | match (es.c1stopf(), es.c1stop2f()) { | ||
| 354 | (true, false) => debug!("low power: wake from STOP1"), | ||
| 355 | (false, true) => debug!("low power: wake from STOP2"), | ||
| 356 | (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"), | ||
| 357 | (false, false) => trace!("low power: stop mode not entered"), | ||
| 358 | }; | ||
| 359 | crate::pac::PWR.extscr().modify(|w| { | ||
| 360 | w.set_c1cssf(false); | ||
| 361 | }); | ||
| 362 | } | ||
| 269 | }; | 363 | }; |
| 270 | } | 364 | } |
| 271 | } | 365 | } |
diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs index 96af9f4d9..a69db3caf 100644 --- a/embassy-stm32/src/lptim/pwm.rs +++ b/embassy-stm32/src/lptim/pwm.rs | |||
| @@ -4,12 +4,12 @@ use core::marker::PhantomData; | |||
| 4 | 4 | ||
| 5 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 6 | 6 | ||
| 7 | use super::timer::Timer; | ||
| 8 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] | 7 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] |
| 9 | use super::OutputPin; | 8 | use super::OutputPin; |
| 10 | #[cfg(any(lptim_v2a, lptim_v2b))] | 9 | use super::timer::Timer; |
| 11 | use super::{channel::Channel, timer::ChannelDirection, Channel1Pin, Channel2Pin}; | ||
| 12 | use super::{BasicInstance, Instance}; | 10 | use super::{BasicInstance, Instance}; |
| 11 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 12 | use super::{Channel1Pin, Channel2Pin, channel::Channel, timer::ChannelDirection}; | ||
| 13 | #[cfg(gpio_v2)] | 13 | #[cfg(gpio_v2)] |
| 14 | use crate::gpio::Pull; | 14 | use crate::gpio::Pull; |
| 15 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 15 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs index 0f6ef569c..de2db9872 100644 --- a/embassy-stm32/src/ltdc.rs +++ b/embassy-stm32/src/ltdc.rs | |||
| @@ -14,7 +14,7 @@ use stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr}; | |||
| 14 | use crate::gpio::{AfType, OutputType, Speed}; | 14 | use crate::gpio::{AfType, OutputType, Speed}; |
| 15 | use crate::interrupt::typelevel::Interrupt; | 15 | use crate::interrupt::typelevel::Interrupt; |
| 16 | use crate::interrupt::{self}; | 16 | use crate::interrupt::{self}; |
| 17 | use crate::{peripherals, rcc, Peri}; | 17 | use crate::{Peri, peripherals, rcc}; |
| 18 | 18 | ||
| 19 | static LTDC_WAKER: AtomicWaker = AtomicWaker::new(); | 19 | static LTDC_WAKER: AtomicWaker = AtomicWaker::new(); |
| 20 | 20 | ||
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index e36719ef3..4a55f5bd3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -3,19 +3,12 @@ | |||
| 3 | 3 | ||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::Peri; | ||
| 7 | #[cfg(opamp_v5)] | ||
| 8 | use crate::block_for_us; | ||
| 6 | use crate::pac::opamp::vals::*; | 9 | use crate::pac::opamp::vals::*; |
| 7 | #[cfg(not(any(stm32g4, stm32f3)))] | 10 | #[cfg(not(any(stm32g4, stm32f3)))] |
| 8 | use crate::rcc::RccInfo; | 11 | use crate::rcc::RccInfo; |
| 9 | use crate::Peri; | ||
| 10 | |||
| 11 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 12 | #[cfg(opamp_v5)] | ||
| 13 | fn blocking_delay_ms(ms: u32) { | ||
| 14 | #[cfg(feature = "time")] | ||
| 15 | embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); | ||
| 16 | #[cfg(not(feature = "time"))] | ||
| 17 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms); | ||
| 18 | } | ||
| 19 | 12 | ||
| 20 | /// Gain | 13 | /// Gain |
| 21 | #[allow(missing_docs)] | 14 | #[allow(missing_docs)] |
| @@ -439,7 +432,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 439 | 432 | ||
| 440 | // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize | 433 | // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize |
| 441 | // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7 | 434 | // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7 |
| 442 | blocking_delay_ms(2); | 435 | block_for_us(2_000); |
| 443 | 436 | ||
| 444 | if !T::regs().csr().read().calout() { | 437 | if !T::regs().csr().read().calout() { |
| 445 | if mid == 0 { | 438 | if mid == 0 { |
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index dbcf07469..2d5dbd95a 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs | |||
| @@ -12,14 +12,14 @@ use embassy_hal_internal::PeripheralType; | |||
| 12 | pub use enums::*; | 12 | pub use enums::*; |
| 13 | use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; | 13 | use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; |
| 14 | 14 | ||
| 15 | use crate::dma::{word, ChannelAndRequest}; | 15 | use crate::dma::{ChannelAndRequest, word}; |
| 16 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 16 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 17 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 17 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 18 | use crate::pac::octospi::{vals, Octospi as Regs}; | 18 | use crate::pac::octospi::{Octospi as Regs, vals}; |
| 19 | #[cfg(octospim_v1)] | 19 | #[cfg(octospim_v1)] |
| 20 | use crate::pac::octospim::Octospim; | 20 | use crate::pac::octospim::Octospim; |
| 21 | use crate::rcc::{self, RccPeripheral}; | 21 | use crate::rcc::{self, RccPeripheral}; |
| 22 | use crate::{peripherals, Peri}; | 22 | use crate::{Peri, peripherals}; |
| 23 | 23 | ||
| 24 | /// OPSI driver config. | 24 | /// OPSI driver config. |
| 25 | #[derive(Clone, Copy)] | 25 | #[derive(Clone, Copy)] |
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index b03cd9009..bb4f4f1d0 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs | |||
| @@ -14,7 +14,7 @@ use crate::gpio::{AfType, AnyPin, OutputType, Pull, Speed}; | |||
| 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 15 | use crate::pac::quadspi::Quadspi as Regs; | 15 | use crate::pac::quadspi::Quadspi as Regs; |
| 16 | use crate::rcc::{self, RccPeripheral}; | 16 | use crate::rcc::{self, RccPeripheral}; |
| 17 | use crate::{peripherals, Peri}; | 17 | use crate::{Peri, peripherals}; |
| 18 | 18 | ||
| 19 | /// QSPI transfer configuration. | 19 | /// QSPI transfer configuration. |
| 20 | #[derive(Clone, Copy)] | 20 | #[derive(Clone, Copy)] |
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 63fc195dd..219be208f 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, Ordering}; | 1 | #[cfg(not(stm32n6))] |
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 2 | 3 | ||
| 3 | use crate::pac::common::{Reg, RW}; | 4 | #[cfg(not(stm32n6))] |
| 5 | use crate::pac::common::{RW, Reg}; | ||
| 6 | #[cfg(backup_sram)] | ||
| 7 | use crate::pac::pwr::vals::Retention; | ||
| 4 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; | 8 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; |
| 5 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 6 | 10 | ||
| @@ -52,7 +56,7 @@ impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv { | |||
| 52 | } | 56 | } |
| 53 | } | 57 | } |
| 54 | 58 | ||
| 55 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] | 59 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0, stm32n6)))] |
| 56 | type Bdcr = crate::pac::rcc::regs::Bdcr; | 60 | type Bdcr = crate::pac::rcc::regs::Bdcr; |
| 57 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] | 61 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 58 | type Bdcr = crate::pac::rcc::regs::Csr; | 62 | type Bdcr = crate::pac::rcc::regs::Csr; |
| @@ -62,19 +66,22 @@ type Bdcr = crate::pac::rcc::regs::Csr1; | |||
| 62 | #[cfg(any(stm32c0))] | 66 | #[cfg(any(stm32c0))] |
| 63 | fn unlock() {} | 67 | fn unlock() {} |
| 64 | 68 | ||
| 65 | #[cfg(not(any(stm32c0)))] | 69 | #[cfg(not(any(stm32c0, stm32n6)))] |
| 66 | fn unlock() { | 70 | fn unlock() { |
| 67 | #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))] | 71 | #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))] |
| 68 | let cr = crate::pac::PWR.cr(); | 72 | let cr = crate::pac::PWR.cr(); |
| 69 | #[cfg(not(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1, stm32u5, stm32h5, stm32wba)))] | 73 | #[cfg(not(any( |
| 74 | stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1, stm32u5, stm32h5, stm32wba, stm32n6 | ||
| 75 | )))] | ||
| 70 | let cr = crate::pac::PWR.cr1(); | 76 | let cr = crate::pac::PWR.cr1(); |
| 71 | #[cfg(any(stm32u5, stm32h5, stm32wba))] | 77 | #[cfg(any(stm32u5, stm32h5, stm32wba, stm32n6))] |
| 72 | let cr = crate::pac::PWR.dbpcr(); | 78 | let cr = crate::pac::PWR.dbpcr(); |
| 73 | 79 | ||
| 74 | cr.modify(|w| w.set_dbp(true)); | 80 | cr.modify(|w| w.set_dbp(true)); |
| 75 | while !cr.read().dbp() {} | 81 | while !cr.read().dbp() {} |
| 76 | } | 82 | } |
| 77 | 83 | ||
| 84 | #[cfg(not(stm32n6))] | ||
| 78 | fn bdcr() -> Reg<Bdcr, RW> { | 85 | fn bdcr() -> Reg<Bdcr, RW> { |
| 79 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] | 86 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 80 | return crate::pac::RCC.csr(); | 87 | return crate::pac::RCC.csr(); |
| @@ -89,6 +96,8 @@ pub struct LsConfig { | |||
| 89 | pub rtc: RtcClockSource, | 96 | pub rtc: RtcClockSource, |
| 90 | pub lsi: bool, | 97 | pub lsi: bool, |
| 91 | pub lse: Option<LseConfig>, | 98 | pub lse: Option<LseConfig>, |
| 99 | #[cfg(backup_sram)] | ||
| 100 | pub enable_backup_sram: bool, | ||
| 92 | } | 101 | } |
| 93 | 102 | ||
| 94 | impl LsConfig { | 103 | impl LsConfig { |
| @@ -113,6 +122,8 @@ impl LsConfig { | |||
| 113 | peripherals_clocked: false, | 122 | peripherals_clocked: false, |
| 114 | }), | 123 | }), |
| 115 | lsi: false, | 124 | lsi: false, |
| 125 | #[cfg(backup_sram)] | ||
| 126 | enable_backup_sram: false, | ||
| 116 | } | 127 | } |
| 117 | } | 128 | } |
| 118 | 129 | ||
| @@ -121,6 +132,8 @@ impl LsConfig { | |||
| 121 | rtc: RtcClockSource::LSI, | 132 | rtc: RtcClockSource::LSI, |
| 122 | lsi: true, | 133 | lsi: true, |
| 123 | lse: None, | 134 | lse: None, |
| 135 | #[cfg(backup_sram)] | ||
| 136 | enable_backup_sram: false, | ||
| 124 | } | 137 | } |
| 125 | } | 138 | } |
| 126 | 139 | ||
| @@ -129,6 +142,8 @@ impl LsConfig { | |||
| 129 | rtc: RtcClockSource::DISABLE, | 142 | rtc: RtcClockSource::DISABLE, |
| 130 | lsi: false, | 143 | lsi: false, |
| 131 | lse: None, | 144 | lse: None, |
| 145 | #[cfg(backup_sram)] | ||
| 146 | enable_backup_sram: false, | ||
| 132 | } | 147 | } |
| 133 | } | 148 | } |
| 134 | } | 149 | } |
| @@ -140,6 +155,7 @@ impl Default for LsConfig { | |||
| 140 | } | 155 | } |
| 141 | 156 | ||
| 142 | impl LsConfig { | 157 | impl LsConfig { |
| 158 | #[cfg(not(stm32n6))] | ||
| 143 | pub(crate) fn init(&self) -> Option<Hertz> { | 159 | pub(crate) fn init(&self) -> Option<Hertz> { |
| 144 | let rtc_clk = match self.rtc { | 160 | let rtc_clk = match self.rtc { |
| 145 | RtcClockSource::LSI => { | 161 | RtcClockSource::LSI => { |
| @@ -175,14 +191,19 @@ impl LsConfig { | |||
| 175 | if self.lsi { | 191 | if self.lsi { |
| 176 | #[cfg(any(stm32u5, stm32h5, stm32wba))] | 192 | #[cfg(any(stm32u5, stm32h5, stm32wba))] |
| 177 | let csr = crate::pac::RCC.bdcr(); | 193 | let csr = crate::pac::RCC.bdcr(); |
| 178 | #[cfg(not(any(stm32u5, stm32h5, stm32wba, stm32c0)))] | 194 | #[cfg(stm32n6)] |
| 195 | let csr = crate::pac::RCC.sr(); | ||
| 196 | #[cfg(not(any(stm32u5, stm32h5, stm32wba, stm32c0, stm32n6)))] | ||
| 179 | let csr = crate::pac::RCC.csr(); | 197 | let csr = crate::pac::RCC.csr(); |
| 180 | #[cfg(any(stm32c0))] | 198 | #[cfg(stm32c0)] |
| 181 | let csr = crate::pac::RCC.csr2(); | 199 | let csr = crate::pac::RCC.csr2(); |
| 182 | 200 | ||
| 183 | #[cfg(not(any(rcc_wb, rcc_wba)))] | 201 | #[cfg(not(any(rcc_wb, rcc_wba, rcc_n6)))] |
| 184 | csr.modify(|w| w.set_lsion(true)); | 202 | csr.modify(|w| w.set_lsion(true)); |
| 185 | 203 | ||
| 204 | #[cfg(rcc_n6)] | ||
| 205 | crate::pac::RCC.cr().modify(|w| w.set_lsion(true)); | ||
| 206 | |||
| 186 | #[cfg(any(rcc_wb, rcc_wba))] | 207 | #[cfg(any(rcc_wb, rcc_wba))] |
| 187 | csr.modify(|w| w.set_lsi1on(true)); | 208 | csr.modify(|w| w.set_lsi1on(true)); |
| 188 | 209 | ||
| @@ -193,28 +214,77 @@ impl LsConfig { | |||
| 193 | while !csr.read().lsi1rdy() {} | 214 | while !csr.read().lsi1rdy() {} |
| 194 | } | 215 | } |
| 195 | 216 | ||
| 217 | // Enable backup regulator for peristent battery backed sram | ||
| 218 | #[cfg(backup_sram)] | ||
| 219 | { | ||
| 220 | unsafe { super::BKSRAM_RETAINED = crate::pac::PWR.bdcr().read().bren() == Retention::PRESERVED }; | ||
| 221 | |||
| 222 | crate::pac::PWR.bdcr().modify(|w| { | ||
| 223 | w.set_bren(match self.enable_backup_sram { | ||
| 224 | true => Retention::PRESERVED, | ||
| 225 | false => Retention::LOST, | ||
| 226 | }); | ||
| 227 | }); | ||
| 228 | |||
| 229 | // Wait for backup regulator voltage to stabilize | ||
| 230 | while self.enable_backup_sram && !crate::pac::PWR.bdsr().read().brrdy() {} | ||
| 231 | } | ||
| 232 | |||
| 196 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. | 233 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. |
| 197 | // once set, changing it requires a backup domain reset. | 234 | // once set, changing it requires a backup domain reset. |
| 198 | // first check if the configuration matches what we want. | 235 | // first check if the configuration matches what we want. |
| 236 | // N6 has all the fields spread across multiple registers under RCC. | ||
| 199 | 237 | ||
| 200 | // check if it's already enabled and in the source we want. | 238 | // check if it's already enabled and in the source we want. |
| 239 | #[cfg(not(rcc_n6))] | ||
| 201 | let reg = bdcr().read(); | 240 | let reg = bdcr().read(); |
| 241 | #[cfg(rcc_n6)] | ||
| 242 | let reg = crate::pac::RCC.cr().read(); | ||
| 243 | #[cfg(rcc_n6)] | ||
| 244 | let apb4lenr = crate::pac::RCC.apb4lenr().read(); | ||
| 245 | #[cfg(rcc_n6)] | ||
| 246 | let ccipr7 = crate::pac::RCC.ccipr7().read(); | ||
| 247 | #[cfg(rcc_n6)] | ||
| 248 | let lsecfgr = crate::pac::RCC.lsecfgr().read(); | ||
| 249 | |||
| 202 | let mut ok = true; | 250 | let mut ok = true; |
| 203 | ok &= reg.rtcsel() == self.rtc; | 251 | #[cfg(not(rcc_n6))] |
| 204 | #[cfg(not(rcc_wba))] | 252 | { |
| 253 | ok &= reg.rtcsel() == self.rtc; | ||
| 254 | } | ||
| 255 | #[cfg(rcc_n6)] | ||
| 256 | { | ||
| 257 | ok &= ccipr7.rtcsel() == self.rtc; | ||
| 258 | } | ||
| 259 | #[cfg(not(any(rcc_wba, rcc_n6)))] | ||
| 205 | { | 260 | { |
| 206 | ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE); | 261 | ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE); |
| 207 | } | 262 | } |
| 263 | #[cfg(rcc_n6)] | ||
| 264 | { | ||
| 265 | ok &= apb4lenr.rtcen() == (self.rtc != RtcClockSource::DISABLE); | ||
| 266 | } | ||
| 208 | ok &= reg.lseon() == lse_en; | 267 | ok &= reg.lseon() == lse_en; |
| 209 | ok &= reg.lsebyp() == lse_byp; | 268 | #[cfg(not(rcc_n6))] |
| 269 | { | ||
| 270 | ok &= reg.lsebyp() == lse_byp; | ||
| 271 | } | ||
| 272 | #[cfg(rcc_n6)] | ||
| 273 | { | ||
| 274 | ok &= lsecfgr.lsebyp() == lse_byp; | ||
| 275 | } | ||
| 210 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] | 276 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] |
| 211 | if let Some(lse_sysen) = lse_sysen { | 277 | if let Some(lse_sysen) = lse_sysen { |
| 212 | ok &= reg.lsesysen() == lse_sysen; | 278 | ok &= reg.lsesysen() == lse_sysen; |
| 213 | } | 279 | } |
| 214 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] | 280 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1, rcc_n6)))] |
| 215 | if let Some(lse_drv) = lse_drv { | 281 | if let Some(lse_drv) = lse_drv { |
| 216 | ok &= reg.lsedrv() == lse_drv.into(); | 282 | ok &= reg.lsedrv() == lse_drv.into(); |
| 217 | } | 283 | } |
| 284 | #[cfg(rcc_n6)] | ||
| 285 | if let Some(lse_drv) = lse_drv { | ||
| 286 | ok &= lsecfgr.lsedrv() == lse_drv.into(); | ||
| 287 | } | ||
| 218 | 288 | ||
| 219 | // if configuration is OK, we're done. | 289 | // if configuration is OK, we're done. |
| 220 | if ok { | 290 | if ok { |
| @@ -223,7 +293,7 @@ impl LsConfig { | |||
| 223 | } | 293 | } |
| 224 | 294 | ||
| 225 | // If not OK, reset backup domain and configure it. | 295 | // If not OK, reset backup domain and configure it. |
| 226 | #[cfg(not(any(rcc_l0, rcc_l0_v2, rcc_l1, stm32h5, stm32h7rs, stm32c0)))] | 296 | #[cfg(not(any(rcc_l0, rcc_l0_v2, rcc_l1, stm32h5, stm32h7rs, stm32c0, stm32n6)))] |
| 227 | { | 297 | { |
| 228 | bdcr().modify(|w| w.set_bdrst(true)); | 298 | bdcr().modify(|w| w.set_bdrst(true)); |
| 229 | bdcr().modify(|w| w.set_bdrst(false)); | 299 | bdcr().modify(|w| w.set_bdrst(false)); |
| @@ -236,7 +306,7 @@ impl LsConfig { | |||
| 236 | // STM32H503CB/EB/KB/RB device errata - 2.2.8 SRAM2 unduly erased upon a backup domain reset | 306 | // STM32H503CB/EB/KB/RB device errata - 2.2.8 SRAM2 unduly erased upon a backup domain reset |
| 237 | // STM32H562xx/563xx/573xx device errata - 2.2.14 SRAM2 is erased when the backup domain is reset | 307 | // STM32H562xx/563xx/573xx device errata - 2.2.14 SRAM2 is erased when the backup domain is reset |
| 238 | //#[cfg(any(stm32h5, stm32h7rs))] | 308 | //#[cfg(any(stm32h5, stm32h7rs))] |
| 239 | #[cfg(any(stm32h7rs))] | 309 | #[cfg(any(stm32h7rs, stm32n6))] |
| 240 | { | 310 | { |
| 241 | bdcr().modify(|w| w.set_vswrst(true)); | 311 | bdcr().modify(|w| w.set_vswrst(true)); |
| 242 | bdcr().modify(|w| w.set_vswrst(false)); | 312 | bdcr().modify(|w| w.set_vswrst(false)); |
| @@ -248,16 +318,31 @@ impl LsConfig { | |||
| 248 | } | 318 | } |
| 249 | 319 | ||
| 250 | if lse_en { | 320 | if lse_en { |
| 251 | bdcr().modify(|w| { | 321 | #[cfg(not(rcc_n6))] |
| 252 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] | 322 | { |
| 253 | if let Some(lse_drv) = lse_drv { | 323 | bdcr().modify(|w| { |
| 254 | w.set_lsedrv(lse_drv.into()); | 324 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] |
| 255 | } | 325 | if let Some(lse_drv) = lse_drv { |
| 256 | w.set_lsebyp(lse_byp); | 326 | w.set_lsedrv(lse_drv.into()); |
| 257 | w.set_lseon(true); | 327 | } |
| 258 | }); | 328 | w.set_lsebyp(lse_byp); |
| 329 | w.set_lseon(true); | ||
| 330 | }); | ||
| 331 | |||
| 332 | while !bdcr().read().lserdy() {} | ||
| 333 | } | ||
| 334 | #[cfg(rcc_n6)] | ||
| 335 | { | ||
| 336 | crate::pac::RCC.lsecfgr().modify(|w| { | ||
| 337 | if let Some(lse_drv) = lse_drv { | ||
| 338 | w.set_lsedrv(lse_drv.into()); | ||
| 339 | } | ||
| 340 | w.set_lsebyp(lse_byp); | ||
| 341 | }); | ||
| 342 | crate::pac::RCC.cr().modify(|w| w.set_lseon(true)); | ||
| 259 | 343 | ||
| 260 | while !bdcr().read().lserdy() {} | 344 | while !crate::pac::RCC.sr().read().lserdy() {} |
| 345 | } | ||
| 261 | 346 | ||
| 262 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] | 347 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] |
| 263 | if let Some(lse_sysen) = lse_sysen { | 348 | if let Some(lse_sysen) = lse_sysen { |
| @@ -272,6 +357,7 @@ impl LsConfig { | |||
| 272 | } | 357 | } |
| 273 | 358 | ||
| 274 | if self.rtc != RtcClockSource::DISABLE { | 359 | if self.rtc != RtcClockSource::DISABLE { |
| 360 | #[cfg(not(rcc_n6))] | ||
| 275 | bdcr().modify(|w| { | 361 | bdcr().modify(|w| { |
| 276 | #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] | 362 | #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] |
| 277 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | 363 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); |
| @@ -280,6 +366,12 @@ impl LsConfig { | |||
| 280 | w.set_rtcen(true); | 366 | w.set_rtcen(true); |
| 281 | w.set_rtcsel(self.rtc); | 367 | w.set_rtcsel(self.rtc); |
| 282 | }); | 368 | }); |
| 369 | |||
| 370 | #[cfg(rcc_n6)] | ||
| 371 | { | ||
| 372 | crate::pac::RCC.ccipr7().modify(|w| w.set_rtcsel(self.rtc)); | ||
| 373 | crate::pac::RCC.apb4lenr().modify(|w| w.set_rtcen(true)) | ||
| 374 | } | ||
| 283 | } | 375 | } |
| 284 | 376 | ||
| 285 | trace!("BDCR configured: {:08x}", bdcr().read().0); | 377 | trace!("BDCR configured: {:08x}", bdcr().read().0); |
diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 8f2e8db5f..d941054cd 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | use stm32_metapac::flash::vals::Latency; | 1 | use stm32_metapac::flash::vals::Latency; |
| 2 | 2 | ||
| 3 | #[cfg(any(stm32f4, stm32f7))] | ||
| 4 | use crate::pac::PWR; | ||
| 3 | #[cfg(any(stm32f413, stm32f423, stm32f412))] | 5 | #[cfg(any(stm32f413, stm32f423, stm32f412))] |
| 4 | pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource; | 6 | pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource; |
| 5 | pub use crate::pac::rcc::vals::{ | 7 | pub use crate::pac::rcc::vals::{ |
| 6 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, | 8 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, |
| 7 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 9 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 8 | }; | 10 | }; |
| 9 | #[cfg(any(stm32f4, stm32f7))] | ||
| 10 | use crate::pac::PWR; | ||
| 11 | use crate::pac::{FLASH, RCC}; | 11 | use crate::pac::{FLASH, RCC}; |
| 12 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 13 | 13 | ||
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 331bab7a0..485edd390 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -597,7 +597,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 597 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 597 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 598 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 598 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 599 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 599 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 600 | _ => panic!("cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 600 | _ => panic!( |
| 601 | "cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 602 | clk_val | ||
| 603 | ), | ||
| 601 | }, | 604 | }, |
| 602 | None => Usbrefcksel::MHZ24, | 605 | None => Usbrefcksel::MHZ24, |
| 603 | }; | 606 | }; |
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 81b89046e..2e1cbd702 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -499,9 +499,9 @@ pub use pll::*; | |||
| 499 | 499 | ||
| 500 | #[cfg(any(stm32l0, stm32l1))] | 500 | #[cfg(any(stm32l0, stm32l1))] |
| 501 | mod pll { | 501 | mod pll { |
| 502 | use super::{pll_enable, PllInstance}; | 502 | use super::{PllInstance, pll_enable}; |
| 503 | pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; | ||
| 504 | use crate::pac::RCC; | 503 | use crate::pac::RCC; |
| 504 | pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; | ||
| 505 | use crate::time::Hertz; | 505 | use crate::time::Hertz; |
| 506 | 506 | ||
| 507 | #[derive(Clone, Copy)] | 507 | #[derive(Clone, Copy)] |
| @@ -563,11 +563,11 @@ mod pll { | |||
| 563 | 563 | ||
| 564 | #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] | 564 | #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] |
| 565 | mod pll { | 565 | mod pll { |
| 566 | use super::{pll_enable, PllInstance}; | 566 | use super::{PllInstance, pll_enable}; |
| 567 | use crate::pac::RCC; | ||
| 567 | pub use crate::pac::rcc::vals::{ | 568 | pub use crate::pac::rcc::vals::{ |
| 568 | Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, | 569 | Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, |
| 569 | }; | 570 | }; |
| 570 | use crate::pac::RCC; | ||
| 571 | use crate::time::Hertz; | 571 | use crate::time::Hertz; |
| 572 | 572 | ||
| 573 | #[derive(Clone, Copy)] | 573 | #[derive(Clone, Copy)] |
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index fa4b45a20..0624fdf26 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -3,6 +3,7 @@ use core::marker::PhantomData; | |||
| 3 | use embassy_hal_internal::PeripheralType; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | 4 | ||
| 5 | use crate::gpio::{AfType, OutputType, Speed}; | 5 | use crate::gpio::{AfType, OutputType, Speed}; |
| 6 | use crate::pac::RCC; | ||
| 6 | #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] | 7 | #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] |
| 7 | pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; | 8 | pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; |
| 8 | #[cfg(not(any( | 9 | #[cfg(not(any( |
| @@ -15,7 +16,8 @@ pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; | |||
| 15 | rcc_h7ab, | 16 | rcc_h7ab, |
| 16 | rcc_h7rm0433, | 17 | rcc_h7rm0433, |
| 17 | rcc_h7, | 18 | rcc_h7, |
| 18 | rcc_h7rs | 19 | rcc_h7rs, |
| 20 | rcc_n6 | ||
| 19 | )))] | 21 | )))] |
| 20 | pub use crate::pac::rcc::vals::Mcosel as McoSource; | 22 | pub use crate::pac::rcc::vals::Mcosel as McoSource; |
| 21 | #[cfg(any( | 23 | #[cfg(any( |
| @@ -28,11 +30,11 @@ pub use crate::pac::rcc::vals::Mcosel as McoSource; | |||
| 28 | rcc_h7ab, | 30 | rcc_h7ab, |
| 29 | rcc_h7rm0433, | 31 | rcc_h7rm0433, |
| 30 | rcc_h7, | 32 | rcc_h7, |
| 31 | rcc_h7rs | 33 | rcc_h7rs, |
| 34 | rcc_n6 | ||
| 32 | ))] | 35 | ))] |
| 33 | pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; | 36 | pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; |
| 34 | use crate::pac::RCC; | 37 | use crate::{Peri, peripherals}; |
| 35 | use crate::{peripherals, Peri}; | ||
| 36 | 38 | ||
| 37 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] | 39 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] |
| 38 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | 40 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] |
| @@ -59,10 +61,12 @@ macro_rules! impl_peri { | |||
| 59 | type Source = $source; | 61 | type Source = $source; |
| 60 | 62 | ||
| 61 | unsafe fn _apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { | 63 | unsafe fn _apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { |
| 62 | #[cfg(not(any(stm32u5, stm32wba)))] | 64 | #[cfg(not(any(stm32u5, stm32wba, stm32n6)))] |
| 63 | let r = RCC.cfgr(); | 65 | let r = RCC.cfgr(); |
| 64 | #[cfg(any(stm32u5, stm32wba))] | 66 | #[cfg(any(stm32u5, stm32wba))] |
| 65 | let r = RCC.cfgr1(); | 67 | let r = RCC.cfgr1(); |
| 68 | #[cfg(any(stm32n6))] | ||
| 69 | let r = RCC.ccipr5(); | ||
| 66 | 70 | ||
| 67 | r.modify(|w| { | 71 | r.modify(|w| { |
| 68 | w.$set_source(source); | 72 | w.$set_source(source); |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c41f81816..85434fa83 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -28,12 +28,13 @@ pub use hsi48::*; | |||
| 28 | #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] | 28 | #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] |
| 29 | #[cfg_attr(stm32u5, path = "u5.rs")] | 29 | #[cfg_attr(stm32u5, path = "u5.rs")] |
| 30 | #[cfg_attr(stm32wba, path = "wba.rs")] | 30 | #[cfg_attr(stm32wba, path = "wba.rs")] |
| 31 | #[cfg_attr(stm32n6, path = "n6.rs")] | ||
| 31 | mod _version; | 32 | mod _version; |
| 32 | 33 | ||
| 33 | pub use _version::*; | 34 | pub use _version::*; |
| 34 | use stm32_metapac::RCC; | 35 | use stm32_metapac::RCC; |
| 35 | 36 | ||
| 36 | pub use crate::_generated::{mux, Clocks}; | 37 | pub use crate::_generated::{Clocks, mux}; |
| 37 | use crate::time::Hertz; | 38 | use crate::time::Hertz; |
| 38 | 39 | ||
| 39 | #[cfg(feature = "low-power")] | 40 | #[cfg(feature = "low-power")] |
| @@ -48,6 +49,12 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; | |||
| 48 | /// May be read without a critical section | 49 | /// May be read without a critical section |
| 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 50 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 50 | 51 | ||
| 52 | #[cfg(feature = "low-power")] | ||
| 53 | pub(crate) static mut RCC_CONFIG: Option<Config> = None; | ||
| 54 | |||
| 55 | #[cfg(backup_sram)] | ||
| 56 | pub(crate) static mut BKSRAM_RETAINED: bool = false; | ||
| 57 | |||
| 51 | #[cfg(not(feature = "_dual-core"))] | 58 | #[cfg(not(feature = "_dual-core"))] |
| 52 | /// Frozen clock frequencies | 59 | /// Frozen clock frequencies |
| 53 | /// | 60 | /// |
| @@ -104,6 +111,32 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo | |||
| 104 | unsafe { get_freqs() } | 111 | unsafe { get_freqs() } |
| 105 | } | 112 | } |
| 106 | 113 | ||
| 114 | #[cfg(feature = "low-power")] | ||
| 115 | pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | ||
| 116 | match stop_mode { | ||
| 117 | StopMode::Standby => {} | ||
| 118 | StopMode::Stop2 => unsafe { | ||
| 119 | REFCOUNT_STOP2 += 1; | ||
| 120 | }, | ||
| 121 | StopMode::Stop1 => unsafe { | ||
| 122 | REFCOUNT_STOP1 += 1; | ||
| 123 | }, | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | #[cfg(feature = "low-power")] | ||
| 128 | pub(crate) fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | ||
| 129 | match stop_mode { | ||
| 130 | StopMode::Standby => {} | ||
| 131 | StopMode::Stop2 => unsafe { | ||
| 132 | REFCOUNT_STOP2 -= 1; | ||
| 133 | }, | ||
| 134 | StopMode::Stop1 => unsafe { | ||
| 135 | REFCOUNT_STOP1 -= 1; | ||
| 136 | }, | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 107 | pub(crate) trait SealedRccPeripheral { | 140 | pub(crate) trait SealedRccPeripheral { |
| 108 | fn frequency() -> Hertz; | 141 | fn frequency() -> Hertz; |
| 109 | #[allow(dead_code)] | 142 | #[allow(dead_code)] |
| @@ -134,12 +167,19 @@ pub(crate) struct RccInfo { | |||
| 134 | stop_mode: StopMode, | 167 | stop_mode: StopMode, |
| 135 | } | 168 | } |
| 136 | 169 | ||
| 170 | /// Specifies a limit for the stop mode of the peripheral or the stop mode to be entered. | ||
| 171 | /// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode. | ||
| 137 | #[cfg(feature = "low-power")] | 172 | #[cfg(feature = "low-power")] |
| 138 | #[allow(dead_code)] | 173 | #[allow(dead_code)] |
| 139 | pub(crate) enum StopMode { | 174 | #[derive(Debug, Clone, Copy, PartialEq, Default)] |
| 140 | Standby, | 175 | pub enum StopMode { |
| 141 | Stop2, | 176 | #[default] |
| 177 | /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1 | ||
| 142 | Stop1, | 178 | Stop1, |
| 179 | /// Peripheral prevents chip from entering Stop2 or executor will enter Stop2 | ||
| 180 | Stop2, | ||
| 181 | /// Peripheral does not prevent chip from entering Stop | ||
| 182 | Standby, | ||
| 143 | } | 183 | } |
| 144 | 184 | ||
| 145 | impl RccInfo { | 185 | impl RccInfo { |
| @@ -195,15 +235,7 @@ impl RccInfo { | |||
| 195 | } | 235 | } |
| 196 | 236 | ||
| 197 | #[cfg(feature = "low-power")] | 237 | #[cfg(feature = "low-power")] |
| 198 | match self.stop_mode { | 238 | increment_stop_refcount(_cs, self.stop_mode); |
| 199 | StopMode::Standby => {} | ||
| 200 | StopMode::Stop2 => unsafe { | ||
| 201 | REFCOUNT_STOP2 += 1; | ||
| 202 | }, | ||
| 203 | StopMode::Stop1 => unsafe { | ||
| 204 | REFCOUNT_STOP1 += 1; | ||
| 205 | }, | ||
| 206 | } | ||
| 207 | 239 | ||
| 208 | // set the xxxRST bit | 240 | // set the xxxRST bit |
| 209 | let reset_ptr = self.reset_ptr(); | 241 | let reset_ptr = self.reset_ptr(); |
| @@ -261,15 +293,7 @@ impl RccInfo { | |||
| 261 | } | 293 | } |
| 262 | 294 | ||
| 263 | #[cfg(feature = "low-power")] | 295 | #[cfg(feature = "low-power")] |
| 264 | match self.stop_mode { | 296 | decrement_stop_refcount(_cs, self.stop_mode); |
| 265 | StopMode::Standby => {} | ||
| 266 | StopMode::Stop2 => unsafe { | ||
| 267 | REFCOUNT_STOP2 -= 1; | ||
| 268 | }, | ||
| 269 | StopMode::Stop1 => unsafe { | ||
| 270 | REFCOUNT_STOP1 -= 1; | ||
| 271 | }, | ||
| 272 | } | ||
| 273 | 297 | ||
| 274 | // clear the xxxEN bit | 298 | // clear the xxxEN bit |
| 275 | let enable_ptr = self.enable_ptr(); | 299 | let enable_ptr = self.enable_ptr(); |
| @@ -390,7 +414,7 @@ pub fn disable<T: RccPeripheral>() { | |||
| 390 | /// | 414 | /// |
| 391 | /// This should only be called after `init`. | 415 | /// This should only be called after `init`. |
| 392 | #[cfg(not(feature = "_dual-core"))] | 416 | #[cfg(not(feature = "_dual-core"))] |
| 393 | pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { | 417 | pub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) { |
| 394 | critical_section::with(|cs| init_rcc(cs, config)) | 418 | critical_section::with(|cs| init_rcc(cs, config)) |
| 395 | } | 419 | } |
| 396 | 420 | ||
| @@ -404,8 +428,39 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { | |||
| 404 | 428 | ||
| 405 | #[cfg(feature = "low-power")] | 429 | #[cfg(feature = "low-power")] |
| 406 | { | 430 | { |
| 431 | RCC_CONFIG = Some(config); | ||
| 407 | REFCOUNT_STOP2 = 0; | 432 | REFCOUNT_STOP2 = 0; |
| 408 | REFCOUNT_STOP1 = 0; | 433 | REFCOUNT_STOP1 = 0; |
| 409 | } | 434 | } |
| 410 | } | 435 | } |
| 411 | } | 436 | } |
| 437 | |||
| 438 | /// Calculate intermediate prescaler number used to calculate peripheral prescalers | ||
| 439 | /// | ||
| 440 | /// This function is intended to calculate a number indicating a minimum division | ||
| 441 | /// necessary to result in a frequency lower than the provided `freq_max`. | ||
| 442 | /// | ||
| 443 | /// The returned value indicates the `val + 1` divider is necessary to result in | ||
| 444 | /// the output frequency that is below the maximum provided. | ||
| 445 | /// | ||
| 446 | /// For example: | ||
| 447 | /// 0 = divider of 1 => no division necessary as the input frequency is below max | ||
| 448 | /// 1 = divider of 2 => division by 2 necessary | ||
| 449 | /// ... | ||
| 450 | /// | ||
| 451 | /// The provided max frequency is inclusive. So if `freq_in == freq_max` the result | ||
| 452 | /// will be 0, indicating that no division is necessary. To accomplish that we subtract | ||
| 453 | /// 1 from the input frequency so that the integer rounding plays in our favor. | ||
| 454 | /// | ||
| 455 | /// For example: | ||
| 456 | /// Let the input frequency be 110 and the max frequency be 55. | ||
| 457 | /// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3 | ||
| 458 | /// which in reality will be rounded up to 4 as usually a 3 division is not available. | ||
| 459 | /// In either case the resulting frequency will be either 36 or 27 which is lower than | ||
| 460 | /// what we would want. The result should be 1. | ||
| 461 | /// If we do the following instead `109/55 = 1` indicating that we need a divide by 2 | ||
| 462 | /// which will result in the correct 55. | ||
| 463 | #[allow(unused)] | ||
| 464 | pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 { | ||
| 465 | freq_in.saturating_sub(1) / freq_max | ||
| 466 | } | ||
diff --git a/embassy-stm32/src/rcc/n6.rs b/embassy-stm32/src/rcc/n6.rs new file mode 100644 index 000000000..866851bbd --- /dev/null +++ b/embassy-stm32/src/rcc/n6.rs | |||
| @@ -0,0 +1,1046 @@ | |||
| 1 | use stm32_metapac::rcc::vals::{ | ||
| 2 | Cpusw, Cpusws, Hseext, Hsitrim, Icint, Icsel, Msifreqsel, Plldivm, Pllmodssdis, Pllpdiv, Pllsel, Syssw, Syssws, | ||
| 3 | Timpre, | ||
| 4 | }; | ||
| 5 | pub use stm32_metapac::rcc::vals::{ | ||
| 6 | Hpre as AhbPrescaler, Hsidiv as HsiPrescaler, Hsitrim as HsiCalibration, Ppre as ApbPrescaler, | ||
| 7 | }; | ||
| 8 | |||
| 9 | use crate::pac::{PWR, RCC, SYSCFG}; | ||
| 10 | use crate::time::Hertz; | ||
| 11 | |||
| 12 | pub const HSI_FREQ: Hertz = Hertz(64_000_000); | ||
| 13 | pub const LSE_FREQ: Hertz = Hertz(32_768); | ||
| 14 | |||
| 15 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 16 | pub enum HseMode { | ||
| 17 | /// crystal/ceramic oscillator | ||
| 18 | Oscillator, | ||
| 19 | /// oscillator bypassed with external clock (analog) | ||
| 20 | Bypass, | ||
| 21 | /// oscillator bypassed with external digital clock | ||
| 22 | BypassDigital, | ||
| 23 | } | ||
| 24 | |||
| 25 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 26 | pub struct Hse { | ||
| 27 | /// HSE frequency. | ||
| 28 | pub freq: Hertz, | ||
| 29 | /// HSE oscillator mode. | ||
| 30 | pub mode: HseMode, | ||
| 31 | } | ||
| 32 | |||
| 33 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 34 | pub struct Hsi { | ||
| 35 | pub pre: HsiPrescaler, | ||
| 36 | pub trim: Hsitrim, | ||
| 37 | } | ||
| 38 | |||
| 39 | #[derive(Clone, Copy, PartialEq)] | ||
| 40 | pub enum SupplyConfig { | ||
| 41 | Smps, | ||
| 42 | External, | ||
| 43 | } | ||
| 44 | |||
| 45 | #[derive(Clone, Copy, PartialEq)] | ||
| 46 | pub enum CpuClk { | ||
| 47 | Hse, | ||
| 48 | Ic1 { source: Icsel, divider: Icint }, | ||
| 49 | Msi, | ||
| 50 | Hsi, | ||
| 51 | } | ||
| 52 | |||
| 53 | impl CpuClk { | ||
| 54 | const fn to_bits(self) -> u8 { | ||
| 55 | match self { | ||
| 56 | Self::Hsi => 0x0, | ||
| 57 | Self::Msi => 0x1, | ||
| 58 | Self::Hse => 0x2, | ||
| 59 | Self::Ic1 { .. } => 0x3, | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | #[derive(Clone, Copy, PartialEq)] | ||
| 65 | pub struct IcConfig { | ||
| 66 | source: Icsel, | ||
| 67 | divider: Icint, | ||
| 68 | } | ||
| 69 | |||
| 70 | #[derive(Clone, Copy, PartialEq)] | ||
| 71 | pub enum SysClk { | ||
| 72 | Hse, | ||
| 73 | Ic2 { | ||
| 74 | ic2: IcConfig, | ||
| 75 | ic6: IcConfig, | ||
| 76 | ic11: IcConfig, | ||
| 77 | }, | ||
| 78 | Msi, | ||
| 79 | Hsi, | ||
| 80 | } | ||
| 81 | |||
| 82 | impl SysClk { | ||
| 83 | const fn to_bits(self) -> u8 { | ||
| 84 | match self { | ||
| 85 | Self::Hsi => 0x0, | ||
| 86 | Self::Msi => 0x1, | ||
| 87 | Self::Hse => 0x2, | ||
| 88 | Self::Ic2 { .. } => 0x3, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | #[derive(Clone, Copy, PartialEq)] | ||
| 94 | pub struct Msi { | ||
| 95 | pub freq: Msifreqsel, | ||
| 96 | pub trim: u8, | ||
| 97 | } | ||
| 98 | |||
| 99 | #[derive(Clone, Copy, PartialEq)] | ||
| 100 | pub enum Pll { | ||
| 101 | Oscillator { | ||
| 102 | source: Pllsel, | ||
| 103 | divm: Plldivm, | ||
| 104 | fractional: u32, | ||
| 105 | divn: u16, | ||
| 106 | divp1: Pllpdiv, | ||
| 107 | divp2: Pllpdiv, | ||
| 108 | }, | ||
| 109 | Bypass { | ||
| 110 | source: Pllsel, | ||
| 111 | }, | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Configuration of the core clocks | ||
| 115 | #[non_exhaustive] | ||
| 116 | #[derive(Clone, Copy)] | ||
| 117 | pub struct Config { | ||
| 118 | pub hsi: Option<Hsi>, | ||
| 119 | pub hse: Option<Hse>, | ||
| 120 | pub msi: Option<Msi>, | ||
| 121 | pub lsi: bool, | ||
| 122 | pub lse: bool, | ||
| 123 | |||
| 124 | pub sys: SysClk, | ||
| 125 | pub cpu: CpuClk, | ||
| 126 | |||
| 127 | pub pll1: Option<Pll>, | ||
| 128 | pub pll2: Option<Pll>, | ||
| 129 | pub pll3: Option<Pll>, | ||
| 130 | pub pll4: Option<Pll>, | ||
| 131 | |||
| 132 | pub ahb: AhbPrescaler, | ||
| 133 | pub apb1: ApbPrescaler, | ||
| 134 | pub apb2: ApbPrescaler, | ||
| 135 | pub apb4: ApbPrescaler, | ||
| 136 | pub apb5: ApbPrescaler, | ||
| 137 | |||
| 138 | pub supply_config: SupplyConfig, | ||
| 139 | } | ||
| 140 | |||
| 141 | impl Config { | ||
| 142 | pub const fn new() -> Self { | ||
| 143 | Self { | ||
| 144 | hsi: Some(Hsi { | ||
| 145 | pre: HsiPrescaler::DIV1, | ||
| 146 | trim: HsiCalibration::from_bits(32), | ||
| 147 | }), | ||
| 148 | hse: None, | ||
| 149 | msi: None, | ||
| 150 | lsi: true, | ||
| 151 | lse: false, | ||
| 152 | sys: SysClk::Hsi, | ||
| 153 | cpu: CpuClk::Hsi, | ||
| 154 | pll1: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 155 | pll2: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 156 | pll3: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 157 | pll4: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 158 | |||
| 159 | ahb: AhbPrescaler::DIV2, | ||
| 160 | apb1: ApbPrescaler::DIV1, | ||
| 161 | apb2: ApbPrescaler::DIV1, | ||
| 162 | apb4: ApbPrescaler::DIV1, | ||
| 163 | apb5: ApbPrescaler::DIV1, | ||
| 164 | |||
| 165 | supply_config: SupplyConfig::Smps, | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | #[allow(dead_code)] | ||
| 171 | struct ClocksOutput { | ||
| 172 | cpuclk: Hertz, | ||
| 173 | sysclk: Hertz, | ||
| 174 | pclk_tim: Hertz, | ||
| 175 | ahb: Hertz, | ||
| 176 | apb1: Hertz, | ||
| 177 | apb2: Hertz, | ||
| 178 | apb4: Hertz, | ||
| 179 | apb5: Hertz, | ||
| 180 | } | ||
| 181 | |||
| 182 | struct ClocksInput { | ||
| 183 | hsi: Option<Hertz>, | ||
| 184 | msi: Option<Hertz>, | ||
| 185 | hse: Option<Hertz>, | ||
| 186 | } | ||
| 187 | |||
| 188 | fn init_clocks(config: Config, input: &ClocksInput) -> ClocksOutput { | ||
| 189 | // handle increasing dividers | ||
| 190 | debug!("configuring increasing pclk dividers"); | ||
| 191 | RCC.cfgr2().modify(|w| { | ||
| 192 | if config.apb1 > w.ppre1() { | ||
| 193 | debug!(" - APB1"); | ||
| 194 | w.set_ppre1(config.apb1); | ||
| 195 | } | ||
| 196 | if config.apb2 > w.ppre2() { | ||
| 197 | debug!(" - APB2"); | ||
| 198 | w.set_ppre2(config.apb2); | ||
| 199 | } | ||
| 200 | if config.apb4 > w.ppre4() { | ||
| 201 | debug!(" - APB4"); | ||
| 202 | w.set_ppre4(config.apb4); | ||
| 203 | } | ||
| 204 | if config.apb5 > w.ppre5() { | ||
| 205 | debug!(" - APB5"); | ||
| 206 | w.set_ppre5(config.apb5); | ||
| 207 | } | ||
| 208 | if config.ahb > w.hpre() { | ||
| 209 | debug!(" - AHB"); | ||
| 210 | w.set_hpre(config.ahb); | ||
| 211 | } | ||
| 212 | }); | ||
| 213 | // cpuclk | ||
| 214 | debug!("configuring cpuclk"); | ||
| 215 | match config.cpu { | ||
| 216 | CpuClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"), | ||
| 217 | CpuClk::Ic1 { source, divider } => { | ||
| 218 | if !pll_sources_ready(RCC.iccfgr(0).read().icsel().to_bits(), source.to_bits()) { | ||
| 219 | panic!("ICx clock switch requires both origin and destination clock source to be active") | ||
| 220 | } | ||
| 221 | |||
| 222 | RCC.iccfgr(0).write(|w| { | ||
| 223 | w.set_icsel(source); | ||
| 224 | w.set_icint(divider); | ||
| 225 | }); | ||
| 226 | RCC.divensr().modify(|w| w.set_ic1ens(true)); | ||
| 227 | } | ||
| 228 | CpuClk::Msi if !RCC.sr().read().msirdy() => panic!("MSI is not ready to be selected as CPU clock source"), | ||
| 229 | CpuClk::Hsi if !RCC.sr().read().hsirdy() => panic!("HSI is not ready to be selected as CPU clock source"), | ||
| 230 | _ => {} | ||
| 231 | } | ||
| 232 | // set source | ||
| 233 | let cpusw = Cpusw::from_bits(config.cpu.to_bits()); | ||
| 234 | RCC.cfgr().modify(|w| w.set_cpusw(cpusw)); | ||
| 235 | // wait for changes to take effect | ||
| 236 | while RCC.cfgr().read().cpusws() != Cpusws::from_bits(config.cpu.to_bits()) {} | ||
| 237 | |||
| 238 | // sysclk | ||
| 239 | debug!("configuring sysclk"); | ||
| 240 | match config.sys { | ||
| 241 | SysClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"), | ||
| 242 | SysClk::Ic2 { ic2, ic6, ic11 } => { | ||
| 243 | if !pll_sources_ready(RCC.iccfgr(1).read().icsel().to_bits(), ic2.source.to_bits()) { | ||
| 244 | panic!("IC2 clock switch requires both origin and destination clock source to be active") | ||
| 245 | } | ||
| 246 | if !pll_sources_ready(RCC.iccfgr(5).read().icsel().to_bits(), ic6.source.to_bits()) { | ||
| 247 | panic!("IC6 clock switch requires both origin and destination clock source to be active") | ||
| 248 | } | ||
| 249 | if !pll_sources_ready(RCC.iccfgr(10).read().icsel().to_bits(), ic11.source.to_bits()) { | ||
| 250 | panic!("IC11 clock switch requires both origin and destination clock source to be active") | ||
| 251 | } | ||
| 252 | |||
| 253 | RCC.iccfgr(1).write(|w| { | ||
| 254 | w.set_icsel(ic2.source); | ||
| 255 | w.set_icint(ic2.divider); | ||
| 256 | }); | ||
| 257 | RCC.iccfgr(5).write(|w| { | ||
| 258 | w.set_icsel(ic6.source); | ||
| 259 | w.set_icint(ic6.divider); | ||
| 260 | }); | ||
| 261 | RCC.iccfgr(10).write(|w| { | ||
| 262 | w.set_icsel(ic11.source); | ||
| 263 | w.set_icint(ic11.divider); | ||
| 264 | }); | ||
| 265 | RCC.divensr().modify(|w| { | ||
| 266 | w.set_ic2ens(true); | ||
| 267 | w.set_ic6ens(true); | ||
| 268 | w.set_ic11ens(true); | ||
| 269 | }); | ||
| 270 | } | ||
| 271 | SysClk::Msi if !RCC.sr().read().msirdy() => panic!("MSI is not ready to be selected as CPU clock source"), | ||
| 272 | SysClk::Hsi if !RCC.sr().read().hsirdy() => panic!("HSI is not ready to be selected as CPU clock source"), | ||
| 273 | _ => {} | ||
| 274 | } | ||
| 275 | // switch the system bus clock | ||
| 276 | let syssw = Syssw::from_bits(config.sys.to_bits()); | ||
| 277 | RCC.cfgr().modify(|w| w.set_syssw(syssw)); | ||
| 278 | // wait for changes to be applied | ||
| 279 | while RCC.cfgr().read().syssws() != Syssws::from_bits(config.sys.to_bits()) {} | ||
| 280 | |||
| 281 | // decreasing dividers | ||
| 282 | debug!("configuring decreasing pclk dividers"); | ||
| 283 | RCC.cfgr2().modify(|w| { | ||
| 284 | if config.ahb < w.hpre() { | ||
| 285 | debug!(" - AHB"); | ||
| 286 | w.set_hpre(config.ahb); | ||
| 287 | } | ||
| 288 | if config.apb1 < w.ppre1() { | ||
| 289 | debug!(" - APB1"); | ||
| 290 | w.set_ppre1(config.apb1); | ||
| 291 | } | ||
| 292 | if config.apb2 < w.ppre2() { | ||
| 293 | debug!(" - APB2"); | ||
| 294 | w.set_ppre2(config.apb2); | ||
| 295 | } | ||
| 296 | if config.apb4 < w.ppre4() { | ||
| 297 | debug!(" - APB4"); | ||
| 298 | w.set_ppre4(config.apb4); | ||
| 299 | } | ||
| 300 | if config.apb5 < w.ppre5() { | ||
| 301 | debug!(" - APB5"); | ||
| 302 | w.set_ppre5(config.apb5); | ||
| 303 | } | ||
| 304 | }); | ||
| 305 | |||
| 306 | let cpuclk = match config.cpu { | ||
| 307 | CpuClk::Hsi => unwrap!(input.hsi), | ||
| 308 | CpuClk::Msi => unwrap!(input.msi), | ||
| 309 | CpuClk::Hse => unwrap!(input.hse), | ||
| 310 | CpuClk::Ic1 { .. } => todo!(), | ||
| 311 | }; | ||
| 312 | |||
| 313 | let sysclk = match config.sys { | ||
| 314 | SysClk::Hsi => unwrap!(input.hsi), | ||
| 315 | SysClk::Msi => unwrap!(input.msi), | ||
| 316 | SysClk::Hse => unwrap!(input.hse), | ||
| 317 | SysClk::Ic2 { .. } => todo!(), | ||
| 318 | }; | ||
| 319 | |||
| 320 | let timpre: u32 = match RCC.cfgr2().read().timpre() { | ||
| 321 | Timpre::DIV1 => 1, | ||
| 322 | Timpre::DIV2 => 2, | ||
| 323 | Timpre::DIV4 => 4, | ||
| 324 | Timpre::_RESERVED_3 => 8, | ||
| 325 | }; | ||
| 326 | |||
| 327 | let hpre = periph_prescaler_to_value(config.ahb.to_bits()); | ||
| 328 | let ppre1 = periph_prescaler_to_value(config.apb1.to_bits()); | ||
| 329 | let ppre2 = periph_prescaler_to_value(config.apb2.to_bits()); | ||
| 330 | let ppre4 = periph_prescaler_to_value(config.apb4.to_bits()); | ||
| 331 | let ppre5 = periph_prescaler_to_value(config.apb5.to_bits()); | ||
| 332 | |||
| 333 | // enable all peripherals in sleep mode | ||
| 334 | enable_low_power_peripherals(); | ||
| 335 | |||
| 336 | // enable interrupts | ||
| 337 | unsafe { | ||
| 338 | core::arch::asm!("cpsie i"); | ||
| 339 | } | ||
| 340 | |||
| 341 | ClocksOutput { | ||
| 342 | sysclk, | ||
| 343 | cpuclk, | ||
| 344 | pclk_tim: sysclk / timpre, | ||
| 345 | ahb: Hertz(sysclk.0 / hpre as u32), | ||
| 346 | apb1: sysclk / hpre / ppre1, | ||
| 347 | apb2: sysclk / hpre / ppre2, | ||
| 348 | apb4: sysclk / hpre / ppre4, | ||
| 349 | apb5: sysclk / hpre / ppre5, | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | fn enable_low_power_peripherals() { | ||
| 354 | // AHB1-5 | ||
| 355 | RCC.ahb1lpenr().modify(|w| { | ||
| 356 | w.set_adc12lpen(true); | ||
| 357 | w.set_gpdma1lpen(true); | ||
| 358 | }); | ||
| 359 | RCC.ahb2lpenr().modify(|w| { | ||
| 360 | w.set_adf1lpen(true); | ||
| 361 | w.set_mdf1lpen(true); | ||
| 362 | w.set_ramcfglpen(true); | ||
| 363 | }); | ||
| 364 | RCC.ahb3lpenr().modify(|w| { | ||
| 365 | w.set_risaflpen(true); | ||
| 366 | w.set_iaclpen(true); | ||
| 367 | w.set_rifsclpen(true); | ||
| 368 | w.set_pkalpen(true); | ||
| 369 | w.set_saeslpen(true); | ||
| 370 | w.set_cryplpen(true); | ||
| 371 | w.set_hashlpen(true); | ||
| 372 | w.set_rnglpen(true); | ||
| 373 | }); | ||
| 374 | RCC.ahb4lpenr().modify(|w| { | ||
| 375 | w.set_crclpen(true); | ||
| 376 | w.set_pwrlpen(true); | ||
| 377 | w.set_gpioqlpen(true); | ||
| 378 | w.set_gpioplpen(true); | ||
| 379 | w.set_gpioolpen(true); | ||
| 380 | w.set_gpionlpen(true); | ||
| 381 | w.set_gpiohlpen(true); | ||
| 382 | w.set_gpioglpen(true); | ||
| 383 | w.set_gpioflpen(true); | ||
| 384 | w.set_gpioelpen(true); | ||
| 385 | w.set_gpiodlpen(true); | ||
| 386 | w.set_gpioclpen(true); | ||
| 387 | w.set_gpioblpen(true); | ||
| 388 | w.set_gpioalpen(true); | ||
| 389 | }); | ||
| 390 | RCC.ahb5lpenr().modify(|w| { | ||
| 391 | w.set_npulpen(true); | ||
| 392 | w.set_npucachelpen(true); | ||
| 393 | w.set_otg2lpen(true); | ||
| 394 | w.set_otgphy2lpen(true); | ||
| 395 | w.set_otgphy1lpen(true); | ||
| 396 | w.set_otg1lpen(true); | ||
| 397 | w.set_eth1lpen(true); | ||
| 398 | w.set_eth1rxlpen(true); | ||
| 399 | w.set_eth1txlpen(true); | ||
| 400 | w.set_eth1maclpen(true); | ||
| 401 | w.set_gpulpen(true); | ||
| 402 | w.set_gfxmmulpen(true); | ||
| 403 | w.set_mce4lpen(true); | ||
| 404 | w.set_xspi3lpen(true); | ||
| 405 | w.set_mce3lpen(true); | ||
| 406 | w.set_mce2lpen(true); | ||
| 407 | w.set_mce1lpen(true); | ||
| 408 | w.set_xspimlpen(true); | ||
| 409 | w.set_xspi2lpen(true); | ||
| 410 | w.set_sdmmc1lpen(true); | ||
| 411 | w.set_sdmmc2lpen(true); | ||
| 412 | w.set_pssilpen(true); | ||
| 413 | w.set_xspi1lpen(true); | ||
| 414 | w.set_fmclpen(true); | ||
| 415 | w.set_jpeglpen(true); | ||
| 416 | w.set_dma2dlpen(true); | ||
| 417 | w.set_hpdma1lpen(true); | ||
| 418 | }); | ||
| 419 | |||
| 420 | // APB1-5 | ||
| 421 | RCC.apb1llpenr().modify(|w| { | ||
| 422 | w.set_uart8lpen(true); | ||
| 423 | w.set_uart7lpen(true); | ||
| 424 | w.set_i3c2lpen(true); | ||
| 425 | w.set_i3c1lpen(true); | ||
| 426 | w.set_i2c3lpen(true); | ||
| 427 | w.set_i2c2lpen(true); | ||
| 428 | w.set_i2c1lpen(true); | ||
| 429 | w.set_uart5lpen(true); | ||
| 430 | w.set_uart4lpen(true); | ||
| 431 | w.set_usart3lpen(true); | ||
| 432 | w.set_usart2lpen(true); | ||
| 433 | w.set_spdifrx1lpen(true); | ||
| 434 | w.set_spi3lpen(true); | ||
| 435 | w.set_spi2lpen(true); | ||
| 436 | w.set_tim11lpen(true); | ||
| 437 | w.set_tim10lpen(true); | ||
| 438 | w.set_wwdglpen(true); | ||
| 439 | w.set_lptim1lpen(true); | ||
| 440 | w.set_tim14lpen(true); | ||
| 441 | w.set_tim13lpen(true); | ||
| 442 | w.set_tim12lpen(true); | ||
| 443 | w.set_tim7lpen(true); | ||
| 444 | w.set_tim6lpen(true); | ||
| 445 | w.set_tim5lpen(true); | ||
| 446 | w.set_tim4lpen(true); | ||
| 447 | w.set_tim3lpen(true); | ||
| 448 | w.set_tim2lpen(true); | ||
| 449 | }); | ||
| 450 | RCC.apb1hlpenr().modify(|w| { | ||
| 451 | w.set_ucpd1lpen(true); | ||
| 452 | w.set_fdcanlpen(true); | ||
| 453 | w.set_mdioslpen(true); | ||
| 454 | }); | ||
| 455 | RCC.apb2lpenr().modify(|w| { | ||
| 456 | w.set_sai2lpen(true); | ||
| 457 | w.set_sai1lpen(true); | ||
| 458 | w.set_spi5lpen(true); | ||
| 459 | w.set_tim9lpen(true); | ||
| 460 | w.set_tim17lpen(true); | ||
| 461 | w.set_tim16lpen(true); | ||
| 462 | w.set_tim15lpen(true); | ||
| 463 | w.set_tim18lpen(true); | ||
| 464 | w.set_spi4lpen(true); | ||
| 465 | w.set_spi1lpen(true); | ||
| 466 | w.set_usart10lpen(true); | ||
| 467 | w.set_uart9lpen(true); | ||
| 468 | w.set_usart6lpen(true); | ||
| 469 | w.set_usart1lpen(true); | ||
| 470 | w.set_tim8lpen(true); | ||
| 471 | w.set_tim1lpen(true); | ||
| 472 | }); | ||
| 473 | RCC.apb3lpenr().modify(|w| { | ||
| 474 | w.set_dftlpen(true); | ||
| 475 | }); | ||
| 476 | RCC.apb4llpenr().modify(|w| { | ||
| 477 | w.set_rtcapblpen(true); | ||
| 478 | w.set_rtclpen(true); | ||
| 479 | w.set_vrefbuflpen(true); | ||
| 480 | w.set_lptim5lpen(true); | ||
| 481 | w.set_lptim4lpen(true); | ||
| 482 | w.set_lptim3lpen(true); | ||
| 483 | w.set_lptim2lpen(true); | ||
| 484 | w.set_i2c4lpen(true); | ||
| 485 | w.set_spi6lpen(true); | ||
| 486 | w.set_lpuart1lpen(true); | ||
| 487 | w.set_hdplpen(true); | ||
| 488 | }); | ||
| 489 | RCC.apb4hlpenr().modify(|w| { | ||
| 490 | w.set_dtslpen(true); | ||
| 491 | w.set_bseclpen(true); | ||
| 492 | w.set_syscfglpen(true); | ||
| 493 | }); | ||
| 494 | RCC.apb5lpenr().modify(|w| { | ||
| 495 | w.set_csilpen(true); | ||
| 496 | w.set_venclpen(true); | ||
| 497 | w.set_gfxtimlpen(true); | ||
| 498 | w.set_dcmilpen(true); | ||
| 499 | w.set_ltdclpen(true); | ||
| 500 | }); | ||
| 501 | |||
| 502 | RCC.buslpenr().modify(|w| { | ||
| 503 | w.set_aclknclpen(true); | ||
| 504 | w.set_aclknlpen(true); | ||
| 505 | }); | ||
| 506 | |||
| 507 | RCC.memlpenr().modify(|w| { | ||
| 508 | w.set_bootromlpen(true); | ||
| 509 | w.set_vencramlpen(true); | ||
| 510 | w.set_npucacheramlpen(true); | ||
| 511 | w.set_flexramlpen(true); | ||
| 512 | w.set_axisram2lpen(true); | ||
| 513 | w.set_axisram1lpen(true); | ||
| 514 | w.set_bkpsramlpen(true); | ||
| 515 | w.set_ahbsram2lpen(true); | ||
| 516 | w.set_ahbsram1lpen(true); | ||
| 517 | w.set_axisram6lpen(true); | ||
| 518 | w.set_axisram5lpen(true); | ||
| 519 | w.set_axisram4lpen(true); | ||
| 520 | w.set_axisram3lpen(true); | ||
| 521 | }); | ||
| 522 | |||
| 523 | RCC.misclpenr().modify(|w| { | ||
| 524 | w.set_perlpen(true); | ||
| 525 | w.set_xspiphycomplpen(true); | ||
| 526 | w.set_dbglpen(true); | ||
| 527 | }); | ||
| 528 | } | ||
| 529 | |||
| 530 | const fn periph_prescaler_to_value(bits: u8) -> u8 { | ||
| 531 | match bits { | ||
| 532 | 0 => 1, | ||
| 533 | 1 => 2, | ||
| 534 | 2 => 4, | ||
| 535 | 3 => 8, | ||
| 536 | 4 => 16, | ||
| 537 | 5 => 32, | ||
| 538 | 6 => 64, | ||
| 539 | 7.. => 128, | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | fn pll_source_ready(source: u8) -> bool { | ||
| 544 | match source { | ||
| 545 | 0x0 if !RCC.sr().read().pllrdy(0) && !RCC.pllcfgr1(0).read().pllbyp() => false, | ||
| 546 | 0x1 if !RCC.sr().read().pllrdy(1) && !RCC.pllcfgr1(1).read().pllbyp() => false, | ||
| 547 | 0x2 if !RCC.sr().read().pllrdy(2) && !RCC.pllcfgr1(2).read().pllbyp() => false, | ||
| 548 | 0x3 if !RCC.sr().read().pllrdy(3) && !RCC.pllcfgr1(3).read().pllbyp() => false, | ||
| 549 | _ => true, | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | fn pll_sources_ready(source1: u8, source2: u8) -> bool { | ||
| 554 | pll_source_ready(source1) && pll_source_ready(source2) | ||
| 555 | } | ||
| 556 | |||
| 557 | impl Default for Config { | ||
| 558 | fn default() -> Self { | ||
| 559 | Self::new() | ||
| 560 | } | ||
| 561 | } | ||
| 562 | |||
| 563 | fn power_supply_config(supply_config: SupplyConfig) { | ||
| 564 | // power supply config | ||
| 565 | PWR.cr1().modify(|w| { | ||
| 566 | w.set_sden(match supply_config { | ||
| 567 | SupplyConfig::External => false, | ||
| 568 | SupplyConfig::Smps => true, | ||
| 569 | }); | ||
| 570 | }); | ||
| 571 | |||
| 572 | // Validate supply configuration | ||
| 573 | while !PWR.voscr().read().actvosrdy() {} | ||
| 574 | } | ||
| 575 | |||
| 576 | struct PllInput { | ||
| 577 | hsi: Option<Hertz>, | ||
| 578 | msi: Option<Hertz>, | ||
| 579 | hse: Option<Hertz>, | ||
| 580 | i2s_ckin: Option<Hertz>, | ||
| 581 | } | ||
| 582 | |||
| 583 | #[derive(Clone, Copy, Default)] | ||
| 584 | #[allow(dead_code)] | ||
| 585 | struct PllOutput { | ||
| 586 | divm: Option<Hertz>, | ||
| 587 | divn: Option<Hertz>, | ||
| 588 | divp1: Option<Hertz>, | ||
| 589 | divp2: Option<Hertz>, | ||
| 590 | output: Option<Hertz>, | ||
| 591 | } | ||
| 592 | |||
| 593 | fn init_pll(pll_config: Option<Pll>, pll_index: usize, input: &PllInput) -> PllOutput { | ||
| 594 | let cfgr1 = RCC.pllcfgr1(pll_index); | ||
| 595 | let cfgr2 = RCC.pllcfgr2(pll_index); | ||
| 596 | let cfgr3 = RCC.pllcfgr3(pll_index); | ||
| 597 | |||
| 598 | match pll_config { | ||
| 599 | Some(Pll::Oscillator { | ||
| 600 | source, | ||
| 601 | divm, | ||
| 602 | fractional, | ||
| 603 | divn, | ||
| 604 | divp1, | ||
| 605 | divp2, | ||
| 606 | }) => { | ||
| 607 | // ensure pll is disabled | ||
| 608 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 609 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 610 | |||
| 611 | // ensure PLLxMODSSDIS=1 to work in fractional mode | ||
| 612 | cfgr3.modify(|w| w.set_pllmodssdis(Pllmodssdis::FRACTIONAL_DIVIDE)); | ||
| 613 | // clear bypass mode | ||
| 614 | cfgr1.modify(|w| w.set_pllbyp(false)); | ||
| 615 | // configure the pll clock source, mul and div factors | ||
| 616 | cfgr1.modify(|w| { | ||
| 617 | w.set_pllsel(source); | ||
| 618 | w.set_plldivm(divm); | ||
| 619 | w.set_plldivn(divn); | ||
| 620 | }); | ||
| 621 | |||
| 622 | let in_clk = match source { | ||
| 623 | Pllsel::HSI => unwrap!(input.hsi), | ||
| 624 | Pllsel::MSI => unwrap!(input.msi), | ||
| 625 | Pllsel::HSE => unwrap!(input.hse), | ||
| 626 | Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin), | ||
| 627 | _ => panic!("reserved PLL source not allowed"), | ||
| 628 | }; | ||
| 629 | |||
| 630 | let m = divm.to_bits() as u32; | ||
| 631 | let n = divn as u32; | ||
| 632 | |||
| 633 | cfgr3.modify(|w| { | ||
| 634 | w.set_pllpdiv1(divp1); | ||
| 635 | w.set_pllpdiv2(divp2); | ||
| 636 | }); | ||
| 637 | |||
| 638 | let p1 = divp1.to_bits() as u32; | ||
| 639 | let p2 = divp2.to_bits() as u32; | ||
| 640 | |||
| 641 | // configure pll divnfrac | ||
| 642 | cfgr2.modify(|w| w.set_plldivnfrac(fractional)); | ||
| 643 | // clear pllxmoddsen | ||
| 644 | cfgr3.modify(|w| w.set_pllmoddsen(false)); | ||
| 645 | // fractional mode | ||
| 646 | if fractional != 0 { | ||
| 647 | cfgr3.modify(|w| { | ||
| 648 | w.set_pllmoddsen(true); | ||
| 649 | w.set_plldacen(true); | ||
| 650 | }) | ||
| 651 | } | ||
| 652 | // enable pll post divider output | ||
| 653 | cfgr3.modify(|w| { | ||
| 654 | w.set_pllmodssrst(true); | ||
| 655 | w.set_pllpdiven(true); | ||
| 656 | }); | ||
| 657 | // enable the pll | ||
| 658 | RCC.csr().write(|w| w.pllons(pll_index)); | ||
| 659 | // wait until ready | ||
| 660 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 661 | |||
| 662 | PllOutput { | ||
| 663 | divm: Some(Hertz(m)), | ||
| 664 | divn: Some(Hertz(n)), | ||
| 665 | divp1: Some(Hertz(p1)), | ||
| 666 | divp2: Some(Hertz(p2)), | ||
| 667 | output: Some(Hertz(in_clk.0 / m / n / p1 / p2)), | ||
| 668 | } | ||
| 669 | } | ||
| 670 | Some(Pll::Bypass { source }) => { | ||
| 671 | // check if source is ready | ||
| 672 | if !pll_source_ready(source.to_bits()) { | ||
| 673 | panic!("PLL source is not ready") | ||
| 674 | } | ||
| 675 | |||
| 676 | // ensure pll is disabled | ||
| 677 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 678 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 679 | |||
| 680 | cfgr1.modify(|w| { | ||
| 681 | w.set_pllbyp(true); | ||
| 682 | w.set_pllsel(source); | ||
| 683 | }); | ||
| 684 | |||
| 685 | let in_clk = match source { | ||
| 686 | Pllsel::HSI => unwrap!(input.hsi), | ||
| 687 | Pllsel::MSI => unwrap!(input.msi), | ||
| 688 | Pllsel::HSE => unwrap!(input.hse), | ||
| 689 | Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin), | ||
| 690 | _ => panic!("reserved PLL source not allowed"), | ||
| 691 | }; | ||
| 692 | |||
| 693 | PllOutput { | ||
| 694 | output: Some(in_clk), | ||
| 695 | ..Default::default() | ||
| 696 | } | ||
| 697 | } | ||
| 698 | None => { | ||
| 699 | cfgr3.modify(|w| w.set_pllpdiven(false)); | ||
| 700 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 701 | // wait till disabled | ||
| 702 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 703 | |||
| 704 | // clear bypass mode | ||
| 705 | cfgr1.modify(|w| w.set_pllbyp(false)); | ||
| 706 | |||
| 707 | PllOutput::default() | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | #[allow(dead_code)] | ||
| 713 | struct OscOutput { | ||
| 714 | hsi: Option<Hertz>, | ||
| 715 | hse: Option<Hertz>, | ||
| 716 | msi: Option<Hertz>, | ||
| 717 | lsi: Option<Hertz>, | ||
| 718 | lse: Option<Hertz>, | ||
| 719 | pll1: Option<Hertz>, | ||
| 720 | pll2: Option<Hertz>, | ||
| 721 | pll3: Option<Hertz>, | ||
| 722 | pll4: Option<Hertz>, | ||
| 723 | ic1sel: Icsel, | ||
| 724 | ic2sel: Icsel, | ||
| 725 | ic6sel: Icsel, | ||
| 726 | ic11sel: Icsel, | ||
| 727 | } | ||
| 728 | |||
| 729 | fn init_osc(config: Config) -> OscOutput { | ||
| 730 | let (cpu_src, sys_src) = { | ||
| 731 | let reg = RCC.cfgr().read(); | ||
| 732 | (reg.cpusws(), reg.syssws()) | ||
| 733 | }; | ||
| 734 | let pll1_src = RCC.pllcfgr1(0).read().pllsel(); | ||
| 735 | let pll2_src = RCC.pllcfgr1(1).read().pllsel(); | ||
| 736 | let pll3_src = RCC.pllcfgr1(2).read().pllsel(); | ||
| 737 | let pll4_src = RCC.pllcfgr1(3).read().pllsel(); | ||
| 738 | let rcc_sr = RCC.sr().read(); | ||
| 739 | |||
| 740 | debug!("configuring HSE"); | ||
| 741 | |||
| 742 | // hse configuration | ||
| 743 | let hse = if let Some(hse) = config.hse { | ||
| 744 | match hse.mode { | ||
| 745 | HseMode::Oscillator => { | ||
| 746 | debug!("HSE in oscillator mode"); | ||
| 747 | } | ||
| 748 | HseMode::Bypass => { | ||
| 749 | debug!("HSE in bypass mode"); | ||
| 750 | RCC.hsecfgr().modify(|w| { | ||
| 751 | w.set_hsebyp(true); | ||
| 752 | w.set_hseext(Hseext::ANALOG); | ||
| 753 | }); | ||
| 754 | } | ||
| 755 | HseMode::BypassDigital => { | ||
| 756 | debug!("HSE in bypass digital mode"); | ||
| 757 | RCC.hsecfgr().modify(|w| { | ||
| 758 | w.set_hsebyp(true); | ||
| 759 | w.set_hseext(Hseext::DIGITAL); | ||
| 760 | }); | ||
| 761 | } | ||
| 762 | } | ||
| 763 | RCC.csr().write(|w| w.set_hseons(true)); | ||
| 764 | |||
| 765 | // wait until the hse is ready | ||
| 766 | while !RCC.sr().read().hserdy() {} | ||
| 767 | |||
| 768 | Some(hse.freq) | ||
| 769 | } else if cpu_src == Cpusws::HSE | ||
| 770 | || sys_src == Syssws::HSE | ||
| 771 | || (pll1_src == Pllsel::HSE && rcc_sr.pllrdy(0)) | ||
| 772 | || (pll2_src == Pllsel::HSE && rcc_sr.pllrdy(1)) | ||
| 773 | || (pll3_src == Pllsel::HSE && rcc_sr.pllrdy(2)) | ||
| 774 | || (pll4_src == Pllsel::HSE && rcc_sr.pllrdy(3)) | ||
| 775 | { | ||
| 776 | panic!( | ||
| 777 | "When the HSE is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 778 | ); | ||
| 779 | } else { | ||
| 780 | debug!("HSE off"); | ||
| 781 | |||
| 782 | RCC.ccr().write(|w| w.set_hseonc(true)); | ||
| 783 | RCC.hsecfgr().modify(|w| { | ||
| 784 | w.set_hseext(Hseext::ANALOG); | ||
| 785 | w.set_hsebyp(false); | ||
| 786 | }); | ||
| 787 | |||
| 788 | // wait until the hse is disabled | ||
| 789 | while RCC.sr().read().hserdy() {} | ||
| 790 | |||
| 791 | None | ||
| 792 | }; | ||
| 793 | |||
| 794 | // hsi configuration | ||
| 795 | debug!("configuring HSI"); | ||
| 796 | let hsi = if let Some(hsi) = config.hsi { | ||
| 797 | RCC.csr().write(|w| w.set_hsions(true)); | ||
| 798 | while !RCC.sr().read().hsirdy() {} | ||
| 799 | |||
| 800 | // set divider and calibration | ||
| 801 | RCC.hsicfgr().modify(|w| { | ||
| 802 | w.set_hsidiv(hsi.pre); | ||
| 803 | w.set_hsitrim(hsi.trim); | ||
| 804 | }); | ||
| 805 | |||
| 806 | Some(HSI_FREQ / hsi.pre) | ||
| 807 | } else if cpu_src == Cpusws::HSI | ||
| 808 | || sys_src == Syssws::HSI | ||
| 809 | || (pll1_src == Pllsel::HSI && rcc_sr.pllrdy(0)) | ||
| 810 | || (pll2_src == Pllsel::HSI && rcc_sr.pllrdy(1)) | ||
| 811 | || (pll3_src == Pllsel::HSI && rcc_sr.pllrdy(2)) | ||
| 812 | || (pll4_src == Pllsel::HSI && rcc_sr.pllrdy(3)) | ||
| 813 | { | ||
| 814 | panic!( | ||
| 815 | "When the HSI is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 816 | ); | ||
| 817 | } else { | ||
| 818 | debug!("HSI off"); | ||
| 819 | |||
| 820 | RCC.ccr().write(|w| w.set_hsionc(true)); | ||
| 821 | while RCC.sr().read().hsirdy() {} | ||
| 822 | |||
| 823 | None | ||
| 824 | }; | ||
| 825 | |||
| 826 | // msi configuration | ||
| 827 | debug!("configuring MSI"); | ||
| 828 | let msi = if let Some(msi) = config.msi { | ||
| 829 | RCC.msicfgr().modify(|w| w.set_msifreqsel(msi.freq)); | ||
| 830 | RCC.csr().write(|w| w.set_msions(true)); | ||
| 831 | while !RCC.sr().read().msirdy() {} | ||
| 832 | RCC.msicfgr().modify(|w| w.set_msitrim(msi.trim)); | ||
| 833 | |||
| 834 | Some(match msi.freq { | ||
| 835 | Msifreqsel::_4MHZ => Hertz::mhz(4), | ||
| 836 | Msifreqsel::_16MHZ => Hertz::mhz(16), | ||
| 837 | }) | ||
| 838 | } else if cpu_src == Cpusws::MSI | ||
| 839 | || sys_src == Syssws::MSI | ||
| 840 | || (pll1_src == Pllsel::MSI && rcc_sr.pllrdy(0)) | ||
| 841 | || (pll2_src == Pllsel::MSI && rcc_sr.pllrdy(1)) | ||
| 842 | || (pll3_src == Pllsel::MSI && rcc_sr.pllrdy(2)) | ||
| 843 | || (pll4_src == Pllsel::MSI && rcc_sr.pllrdy(3)) | ||
| 844 | { | ||
| 845 | panic!( | ||
| 846 | "When the MSI is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 847 | ); | ||
| 848 | } else { | ||
| 849 | RCC.ccr().write(|w| w.set_msionc(true)); | ||
| 850 | while RCC.sr().read().msirdy() {} | ||
| 851 | |||
| 852 | None | ||
| 853 | }; | ||
| 854 | |||
| 855 | // lsi configuration | ||
| 856 | debug!("configuring LSI"); | ||
| 857 | let lsi = if config.lsi { | ||
| 858 | RCC.csr().write(|w| w.set_lsions(true)); | ||
| 859 | while !RCC.sr().read().lsirdy() {} | ||
| 860 | Some(super::LSI_FREQ) | ||
| 861 | } else { | ||
| 862 | RCC.ccr().write(|w| w.set_lsionc(true)); | ||
| 863 | while RCC.sr().read().lsirdy() {} | ||
| 864 | None | ||
| 865 | }; | ||
| 866 | |||
| 867 | // lse configuration | ||
| 868 | debug!("configuring LSE"); | ||
| 869 | let lse = if config.lse { | ||
| 870 | RCC.csr().write(|w| w.set_lseons(true)); | ||
| 871 | while !RCC.sr().read().lserdy() {} | ||
| 872 | Some(LSE_FREQ) | ||
| 873 | } else { | ||
| 874 | RCC.ccr().write(|w| w.set_lseonc(true)); | ||
| 875 | while RCC.sr().read().lserdy() {} | ||
| 876 | None | ||
| 877 | }; | ||
| 878 | |||
| 879 | let pll_input = PllInput { | ||
| 880 | hse, | ||
| 881 | msi, | ||
| 882 | hsi, | ||
| 883 | i2s_ckin: None, | ||
| 884 | }; | ||
| 885 | |||
| 886 | // pll1,2,3,4 config | ||
| 887 | let pll_configs = [config.pll1, config.pll2, config.pll3, config.pll4]; | ||
| 888 | let mut pll_outputs: [PllOutput; 4] = [PllOutput::default(); 4]; | ||
| 889 | |||
| 890 | let ic1_src = RCC.iccfgr(0).read().icsel(); | ||
| 891 | let ic2_src = RCC.iccfgr(1).read().icsel(); | ||
| 892 | let ic6_src = RCC.iccfgr(5).read().icsel(); | ||
| 893 | let ic11_src = RCC.iccfgr(10).read().icsel(); | ||
| 894 | |||
| 895 | for (n, (&pll, out)) in pll_configs.iter().zip(pll_outputs.iter_mut()).enumerate() { | ||
| 896 | debug!("configuring PLL{}", n + 1); | ||
| 897 | let pll_ready = RCC.sr().read().pllrdy(n); | ||
| 898 | |||
| 899 | if is_new_pll_config(pll, 0) { | ||
| 900 | let this_pll = Icsel::from_bits(n as u8); | ||
| 901 | |||
| 902 | if cpu_src == Cpusws::IC1 && ic1_src == this_pll { | ||
| 903 | panic!("PLL should not be disabled / reconfigured if used for IC1 (cpuclksrc)") | ||
| 904 | } | ||
| 905 | |||
| 906 | if sys_src == Syssws::IC2 && (ic2_src == this_pll || ic6_src == this_pll || ic11_src == this_pll) { | ||
| 907 | panic!("PLL should not be disabled / reconfigured if used for IC2, IC6 or IC11 (sysclksrc)") | ||
| 908 | } | ||
| 909 | |||
| 910 | *out = init_pll(pll, 0, &pll_input); | ||
| 911 | } else if pll.is_some() && !pll_ready { | ||
| 912 | RCC.csr().write(|w| w.pllons(n)); | ||
| 913 | while !RCC.sr().read().pllrdy(n) {} | ||
| 914 | } | ||
| 915 | } | ||
| 916 | |||
| 917 | OscOutput { | ||
| 918 | hsi, | ||
| 919 | hse, | ||
| 920 | msi, | ||
| 921 | lsi, | ||
| 922 | lse, | ||
| 923 | pll1: pll_outputs[0].output, | ||
| 924 | pll2: pll_outputs[1].output, | ||
| 925 | pll3: pll_outputs[2].output, | ||
| 926 | pll4: pll_outputs[3].output, | ||
| 927 | ic1sel: ic1_src, | ||
| 928 | ic2sel: ic2_src, | ||
| 929 | ic6sel: ic6_src, | ||
| 930 | ic11sel: ic11_src, | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | fn is_new_pll_config(pll: Option<Pll>, pll_index: usize) -> bool { | ||
| 935 | let cfgr1 = RCC.pllcfgr1(pll_index).read(); | ||
| 936 | let cfgr2 = RCC.pllcfgr2(pll_index).read(); | ||
| 937 | let cfgr3 = RCC.pllcfgr3(pll_index).read(); | ||
| 938 | |||
| 939 | let ready = RCC.sr().read().pllrdy(pll_index); | ||
| 940 | let bypass = cfgr1.pllbyp(); | ||
| 941 | |||
| 942 | match (pll, ready, bypass) { | ||
| 943 | (None, true, _) => return true, | ||
| 944 | (Some(_), false, _) => return true, | ||
| 945 | (Some(conf), true, bypass) => match (conf, bypass) { | ||
| 946 | (Pll::Bypass { .. }, false) => return true, | ||
| 947 | (Pll::Oscillator { .. }, true) => return true, | ||
| 948 | _ => {} | ||
| 949 | }, | ||
| 950 | _ => {} | ||
| 951 | } | ||
| 952 | |||
| 953 | match pll { | ||
| 954 | Some(Pll::Bypass { source }) => cfgr1.pllsel() != source, | ||
| 955 | Some(Pll::Oscillator { | ||
| 956 | source, | ||
| 957 | divm: m, | ||
| 958 | fractional, | ||
| 959 | divn: n, | ||
| 960 | divp1: p1, | ||
| 961 | divp2: p2, | ||
| 962 | }) => { | ||
| 963 | cfgr1.pllsel() != source | ||
| 964 | || cfgr1.plldivm() != m | ||
| 965 | || cfgr1.plldivn() != n | ||
| 966 | || cfgr2.plldivnfrac() != fractional | ||
| 967 | || cfgr3.pllpdiv1() != p1 | ||
| 968 | || cfgr3.pllpdiv2() != p2 | ||
| 969 | } | ||
| 970 | None => false, | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | pub(crate) unsafe fn init(config: Config) { | ||
| 975 | debug!("enabling SYSCFG"); | ||
| 976 | // system configuration setup | ||
| 977 | RCC.apb4hensr().write(|w| w.set_syscfgens(true)); | ||
| 978 | // delay after RCC peripheral clock enabling | ||
| 979 | RCC.apb4hensr().read(); | ||
| 980 | |||
| 981 | debug!("setting VTOR"); | ||
| 982 | |||
| 983 | let vtor = unsafe { | ||
| 984 | let p = cortex_m::Peripherals::steal(); | ||
| 985 | p.SCB.vtor.read() | ||
| 986 | }; | ||
| 987 | |||
| 988 | // set default vector table location after reset or standby | ||
| 989 | SYSCFG.initsvtorcr().write(|w| w.set_svtor_addr(vtor)); | ||
| 990 | // read back the value to ensure it is written before deactivating SYSCFG | ||
| 991 | SYSCFG.initsvtorcr().read(); | ||
| 992 | |||
| 993 | debug!("deactivating SYSCFG"); | ||
| 994 | |||
| 995 | // deactivate SYSCFG | ||
| 996 | RCC.apb4hensr().write(|w| w.set_syscfgens(false)); | ||
| 997 | |||
| 998 | debug!("enabling FPU"); | ||
| 999 | |||
| 1000 | // enable fpu | ||
| 1001 | unsafe { | ||
| 1002 | let p = cortex_m::Peripherals::steal(); | ||
| 1003 | p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22)); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | debug!("setting power supply config"); | ||
| 1007 | |||
| 1008 | power_supply_config(config.supply_config); | ||
| 1009 | |||
| 1010 | let osc = init_osc(config); | ||
| 1011 | let clock_inputs = ClocksInput { | ||
| 1012 | hsi: osc.hsi, | ||
| 1013 | msi: osc.msi, | ||
| 1014 | hse: osc.hse, | ||
| 1015 | }; | ||
| 1016 | let clocks = init_clocks(config, &clock_inputs); | ||
| 1017 | |||
| 1018 | // TODO: sysb, sysc, sysd must have the same clock source | ||
| 1019 | |||
| 1020 | set_clocks!( | ||
| 1021 | sys: Some(clocks.sysclk), | ||
| 1022 | hsi: osc.hsi, | ||
| 1023 | hsi_div: None, | ||
| 1024 | hse: osc.hse, | ||
| 1025 | msi: osc.msi, | ||
| 1026 | hclk1: Some(clocks.ahb), | ||
| 1027 | hclk2: Some(clocks.ahb), | ||
| 1028 | hclk3: Some(clocks.ahb), | ||
| 1029 | hclk4: Some(clocks.ahb), | ||
| 1030 | hclk5: Some(clocks.ahb), | ||
| 1031 | pclk1: Some(clocks.apb1), | ||
| 1032 | pclk2: Some(clocks.apb2), | ||
| 1033 | pclk1_tim: Some(clocks.pclk_tim), | ||
| 1034 | pclk2_tim: Some(clocks.pclk_tim), | ||
| 1035 | pclk4: Some(clocks.apb4), | ||
| 1036 | pclk5: Some(clocks.apb5), | ||
| 1037 | per: None, | ||
| 1038 | rtc: None, | ||
| 1039 | i2s_ckin: None, | ||
| 1040 | ic8: None, | ||
| 1041 | ic9: None, | ||
| 1042 | ic14: None, | ||
| 1043 | ic17: None, | ||
| 1044 | ic20: None, | ||
| 1045 | ); | ||
| 1046 | } | ||
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 06895a99a..7b0dcb63f 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs | |||
| @@ -6,9 +6,9 @@ pub use crate::pac::rcc::vals::{ | |||
| 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 7 | }; | 7 | }; |
| 8 | use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; | 8 | use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; |
| 9 | #[cfg(all(peri_usb_otg_hs))] | ||
| 10 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 11 | use crate::pac::{FLASH, PWR, RCC}; | 9 | use crate::pac::{FLASH, PWR, RCC}; |
| 10 | #[cfg(all(peri_usb_otg_hs))] | ||
| 11 | pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel}; | ||
| 12 | use crate::rcc::LSI_FREQ; | 12 | use crate::rcc::LSI_FREQ; |
| 13 | use crate::time::Hertz; | 13 | use crate::time::Hertz; |
| 14 | 14 | ||
| @@ -442,7 +442,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 442 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 442 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 443 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 443 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 444 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 444 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 445 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 445 | _ => panic!( |
| 446 | "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 447 | clk_val | ||
| 448 | ), | ||
| 446 | }, | 449 | }, |
| 447 | None => Usbrefcksel::MHZ24, | 450 | None => Usbrefcksel::MHZ24, |
| 448 | }; | 451 | }; |
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index 481437939..2528996d5 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs | |||
| @@ -7,9 +7,9 @@ pub use crate::pac::rcc::vals::{ | |||
| 7 | Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, | 7 | Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, |
| 8 | Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sai1sel, Sw as Sysclk, | 8 | Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sai1sel, Sw as Sysclk, |
| 9 | }; | 9 | }; |
| 10 | #[cfg(all(peri_usb_otg_hs))] | ||
| 11 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 12 | use crate::pac::{FLASH, RCC}; | 10 | use crate::pac::{FLASH, RCC}; |
| 11 | #[cfg(all(peri_usb_otg_hs))] | ||
| 12 | pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel}; | ||
| 13 | use crate::rcc::LSI_FREQ; | 13 | use crate::rcc::LSI_FREQ; |
| 14 | use crate::time::Hertz; | 14 | use crate::time::Hertz; |
| 15 | 15 | ||
| @@ -245,7 +245,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 245 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 245 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 246 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 246 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 247 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 247 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 248 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 248 | _ => panic!( |
| 249 | "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 250 | clk_val | ||
| 251 | ), | ||
| 249 | }, | 252 | }, |
| 250 | None => Usbrefcksel::MHZ24, | 253 | None => Usbrefcksel::MHZ24, |
| 251 | }; | 254 | }; |
diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 63654639e..dada9bda1 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs | |||
| @@ -9,7 +9,7 @@ use embassy_hal_internal::PeripheralType; | |||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | 10 | ||
| 11 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| 12 | use crate::{interrupt, pac, peripherals, rcc, Peri}; | 12 | use crate::{Peri, interrupt, pac, peripherals, rcc}; |
| 13 | 13 | ||
| 14 | static RNG_WAKER: AtomicWaker = AtomicWaker::new(); | 14 | static RNG_WAKER: AtomicWaker = AtomicWaker::new(); |
| 15 | 15 | ||
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index a81ac6746..f049d6b12 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | #[cfg(feature = "time")] | 1 | #[cfg(feature = "time")] |
| 2 | use embassy_time::{Duration, TICK_HZ}; | 2 | use embassy_time::{Duration, TICK_HZ}; |
| 3 | 3 | ||
| 4 | use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; | 4 | use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; |
| 5 | use crate::interrupt::typelevel::Interrupt; | 5 | use crate::interrupt::typelevel::Interrupt; |
| 6 | use crate::pac::rtc::vals::Wucksel; | ||
| 6 | use crate::peripherals::RTC; | 7 | use crate::peripherals::RTC; |
| 7 | use crate::rtc::SealedInstance; | 8 | use crate::rtc::{RtcTimeProvider, SealedInstance}; |
| 8 | 9 | ||
| 9 | /// Represents an instant in time that can be substracted to compute a duration | 10 | /// Represents an instant in time that can be substracted to compute a duration |
| 10 | pub(super) struct RtcInstant { | 11 | pub(super) struct RtcInstant { |
| @@ -58,66 +59,22 @@ impl core::ops::Sub for RtcInstant { | |||
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | #[repr(u8)] | 62 | fn wucksel_compute_min(val: u32) -> (Wucksel, u32) { |
| 62 | #[derive(Clone, Copy, Debug)] | 63 | *[ |
| 63 | pub(crate) enum WakeupPrescaler { | 64 | (Wucksel::DIV2, 2), |
| 64 | Div2 = 2, | 65 | (Wucksel::DIV4, 4), |
| 65 | Div4 = 4, | 66 | (Wucksel::DIV8, 8), |
| 66 | Div8 = 8, | 67 | (Wucksel::DIV16, 16), |
| 67 | Div16 = 16, | 68 | ] |
| 68 | } | 69 | .iter() |
| 69 | 70 | .find(|(_, psc)| *psc as u32 > val) | |
| 70 | #[cfg(any( | 71 | .unwrap_or(&(Wucksel::DIV16, 16)) |
| 71 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba | ||
| 72 | ))] | ||
| 73 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | ||
| 74 | fn from(val: WakeupPrescaler) -> Self { | ||
| 75 | use crate::pac::rtc::vals::Wucksel; | ||
| 76 | |||
| 77 | match val { | ||
| 78 | WakeupPrescaler::Div2 => Wucksel::DIV2, | ||
| 79 | WakeupPrescaler::Div4 => Wucksel::DIV4, | ||
| 80 | WakeupPrescaler::Div8 => Wucksel::DIV8, | ||
| 81 | WakeupPrescaler::Div16 => Wucksel::DIV16, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | #[cfg(any( | ||
| 87 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba | ||
| 88 | ))] | ||
| 89 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | ||
| 90 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | ||
| 91 | use crate::pac::rtc::vals::Wucksel; | ||
| 92 | |||
| 93 | match val { | ||
| 94 | Wucksel::DIV2 => WakeupPrescaler::Div2, | ||
| 95 | Wucksel::DIV4 => WakeupPrescaler::Div4, | ||
| 96 | Wucksel::DIV8 => WakeupPrescaler::Div8, | ||
| 97 | Wucksel::DIV16 => WakeupPrescaler::Div16, | ||
| 98 | _ => unreachable!(), | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | impl WakeupPrescaler { | ||
| 104 | pub fn compute_min(val: u32) -> Self { | ||
| 105 | *[ | ||
| 106 | WakeupPrescaler::Div2, | ||
| 107 | WakeupPrescaler::Div4, | ||
| 108 | WakeupPrescaler::Div8, | ||
| 109 | WakeupPrescaler::Div16, | ||
| 110 | ] | ||
| 111 | .iter() | ||
| 112 | .find(|psc| **psc as u32 > val) | ||
| 113 | .unwrap_or(&WakeupPrescaler::Div16) | ||
| 114 | } | ||
| 115 | } | 72 | } |
| 116 | 73 | ||
| 117 | impl Rtc { | 74 | impl Rtc { |
| 118 | /// Return the current instant. | 75 | /// Return the current instant. |
| 119 | fn instant(&self) -> Result<RtcInstant, RtcError> { | 76 | fn instant(&self) -> Result<RtcInstant, RtcError> { |
| 120 | self.time_provider().read(|_, tr, ss| { | 77 | RtcTimeProvider::new().read(|_, tr, ss| { |
| 121 | let second = bcd2_to_byte((tr.st(), tr.su())); | 78 | let second = bcd2_to_byte((tr.st(), tr.su())); |
| 122 | 79 | ||
| 123 | RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) | 80 | RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) |
| @@ -127,7 +84,7 @@ impl Rtc { | |||
| 127 | /// start the wakeup alarm and with a duration that is as close to but less than | 84 | /// start the wakeup alarm and with a duration that is as close to but less than |
| 128 | /// the requested duration, and record the instant the wakeup alarm was started | 85 | /// the requested duration, and record the instant the wakeup alarm was started |
| 129 | pub(crate) fn start_wakeup_alarm( | 86 | pub(crate) fn start_wakeup_alarm( |
| 130 | &self, | 87 | &mut self, |
| 131 | requested_duration: embassy_time::Duration, | 88 | requested_duration: embassy_time::Duration, |
| 132 | cs: critical_section::CriticalSection, | 89 | cs: critical_section::CriticalSection, |
| 133 | ) { | 90 | ) { |
| @@ -138,7 +95,7 @@ impl Rtc { | |||
| 138 | let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); | 95 | let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); |
| 139 | let rtc_hz = Self::frequency().0 as u64; | 96 | let rtc_hz = Self::frequency().0 as u64; |
| 140 | let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; | 97 | let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; |
| 141 | let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); | 98 | let (wucksel, prescaler) = wucksel_compute_min((rtc_ticks / u16::MAX as u64) as u32); |
| 142 | 99 | ||
| 143 | // adjust the rtc ticks to the prescaler and subtract one rtc tick | 100 | // adjust the rtc ticks to the prescaler and subtract one rtc tick |
| 144 | let rtc_ticks = rtc_ticks / prescaler as u64; | 101 | let rtc_ticks = rtc_ticks / prescaler as u64; |
| @@ -159,7 +116,7 @@ impl Rtc { | |||
| 159 | while !regs.icsr().read().wutwf() {} | 116 | while !regs.icsr().read().wutwf() {} |
| 160 | } | 117 | } |
| 161 | 118 | ||
| 162 | regs.cr().modify(|w| w.set_wucksel(prescaler.into())); | 119 | regs.cr().modify(|w| w.set_wucksel(wucksel)); |
| 163 | regs.wutr().write(|w| w.set_wut(rtc_ticks)); | 120 | regs.wutr().write(|w| w.set_wut(rtc_ticks)); |
| 164 | regs.cr().modify(|w| w.set_wute(true)); | 121 | regs.cr().modify(|w| w.set_wute(true)); |
| 165 | regs.cr().modify(|w| w.set_wutie(true)); | 122 | regs.cr().modify(|w| w.set_wutie(true)); |
| @@ -179,7 +136,10 @@ impl Rtc { | |||
| 179 | 136 | ||
| 180 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` | 137 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` |
| 181 | /// was called, otherwise none | 138 | /// was called, otherwise none |
| 182 | pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { | 139 | pub(crate) fn stop_wakeup_alarm( |
| 140 | &mut self, | ||
| 141 | cs: critical_section::CriticalSection, | ||
| 142 | ) -> Option<embassy_time::Duration> { | ||
| 183 | let instant = self.instant().unwrap(); | 143 | let instant = self.instant().unwrap(); |
| 184 | if RTC::regs().cr().read().wute() { | 144 | if RTC::regs().cr().read().wute() { |
| 185 | trace!("rtc: stop wakeup alarm at {}", instant); | 145 | trace!("rtc: stop wakeup alarm at {}", instant); |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 92dec0960..e88bd7ab2 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -5,15 +5,19 @@ mod datetime; | |||
| 5 | mod low_power; | 5 | mod low_power; |
| 6 | 6 | ||
| 7 | #[cfg(feature = "low-power")] | 7 | #[cfg(feature = "low-power")] |
| 8 | use core::cell::Cell; | 8 | use core::cell::{Cell, RefCell, RefMut}; |
| 9 | #[cfg(feature = "low-power")] | ||
| 10 | use core::ops; | ||
| 9 | 11 | ||
| 10 | #[cfg(feature = "low-power")] | 12 | #[cfg(feature = "low-power")] |
| 11 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 13 | use critical_section::CriticalSection; |
| 12 | #[cfg(feature = "low-power")] | 14 | #[cfg(feature = "low-power")] |
| 13 | use embassy_sync::blocking_mutex::Mutex; | 15 | use embassy_sync::blocking_mutex::Mutex; |
| 16 | #[cfg(feature = "low-power")] | ||
| 17 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 14 | 18 | ||
| 15 | use self::datetime::{day_of_week_from_u8, day_of_week_to_u8}; | ||
| 16 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; | 19 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; |
| 20 | use self::datetime::{day_of_week_from_u8, day_of_week_to_u8}; | ||
| 17 | use crate::pac::rtc::regs::{Dr, Tr}; | 21 | use crate::pac::rtc::regs::{Dr, Tr}; |
| 18 | use crate::time::Hertz; | 22 | use crate::time::Hertz; |
| 19 | 23 | ||
| @@ -25,8 +29,8 @@ mod _version; | |||
| 25 | #[allow(unused_imports)] | 29 | #[allow(unused_imports)] |
| 26 | pub use _version::*; | 30 | pub use _version::*; |
| 27 | 31 | ||
| 28 | use crate::peripherals::RTC; | ||
| 29 | use crate::Peri; | 32 | use crate::Peri; |
| 33 | use crate::peripherals::RTC; | ||
| 30 | 34 | ||
| 31 | /// Errors that can occur on methods on [RtcClock] | 35 | /// Errors that can occur on methods on [RtcClock] |
| 32 | #[non_exhaustive] | 36 | #[non_exhaustive] |
| @@ -44,11 +48,17 @@ pub enum RtcError { | |||
| 44 | } | 48 | } |
| 45 | 49 | ||
| 46 | /// Provides immutable access to the current time of the RTC. | 50 | /// Provides immutable access to the current time of the RTC. |
| 51 | #[derive(Clone)] | ||
| 47 | pub struct RtcTimeProvider { | 52 | pub struct RtcTimeProvider { |
| 48 | _private: (), | 53 | _private: (), |
| 49 | } | 54 | } |
| 50 | 55 | ||
| 51 | impl RtcTimeProvider { | 56 | impl RtcTimeProvider { |
| 57 | /// Create a new RTC time provider instance. | ||
| 58 | pub(self) const fn new() -> Self { | ||
| 59 | Self { _private: () } | ||
| 60 | } | ||
| 61 | |||
| 52 | /// Return the current datetime. | 62 | /// Return the current datetime. |
| 53 | /// | 63 | /// |
| 54 | /// # Errors | 64 | /// # Errors |
| @@ -106,6 +116,50 @@ impl RtcTimeProvider { | |||
| 106 | } | 116 | } |
| 107 | } | 117 | } |
| 108 | 118 | ||
| 119 | #[cfg(feature = "low-power")] | ||
| 120 | /// Contains an RTC driver. | ||
| 121 | pub struct RtcContainer { | ||
| 122 | pub(self) mutex: &'static Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>, | ||
| 123 | } | ||
| 124 | |||
| 125 | #[cfg(feature = "low-power")] | ||
| 126 | impl RtcContainer { | ||
| 127 | pub(self) const fn new() -> Self { | ||
| 128 | Self { | ||
| 129 | mutex: &crate::time_driver::get_driver().rtc, | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Acquire an RTC borrow. | ||
| 134 | pub fn borrow_mut<'a>(&self, cs: CriticalSection<'a>) -> RtcBorrow<'a> { | ||
| 135 | RtcBorrow { | ||
| 136 | ref_mut: self.mutex.borrow(cs).borrow_mut(), | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | #[cfg(feature = "low-power")] | ||
| 142 | /// Contains an RTC borrow. | ||
| 143 | pub struct RtcBorrow<'a> { | ||
| 144 | pub(self) ref_mut: RefMut<'a, Option<Rtc>>, | ||
| 145 | } | ||
| 146 | |||
| 147 | #[cfg(feature = "low-power")] | ||
| 148 | impl<'a> ops::Deref for RtcBorrow<'a> { | ||
| 149 | type Target = Rtc; | ||
| 150 | |||
| 151 | fn deref(&self) -> &Self::Target { | ||
| 152 | self.ref_mut.as_ref().unwrap() | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | #[cfg(feature = "low-power")] | ||
| 157 | impl<'a> ops::DerefMut for RtcBorrow<'a> { | ||
| 158 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 159 | self.ref_mut.as_mut().unwrap() | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 109 | /// RTC driver. | 163 | /// RTC driver. |
| 110 | pub struct Rtc { | 164 | pub struct Rtc { |
| 111 | #[cfg(feature = "low-power")] | 165 | #[cfg(feature = "low-power")] |
| @@ -121,13 +175,21 @@ pub struct RtcConfig { | |||
| 121 | /// | 175 | /// |
| 122 | /// A high counter frequency may impact stop power consumption | 176 | /// A high counter frequency may impact stop power consumption |
| 123 | pub frequency: Hertz, | 177 | pub frequency: Hertz, |
| 178 | |||
| 179 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 180 | /// Allow disabling the rtc, even when stop is configured | ||
| 181 | pub _disable_rtc: bool, | ||
| 124 | } | 182 | } |
| 125 | 183 | ||
| 126 | impl Default for RtcConfig { | 184 | impl Default for RtcConfig { |
| 127 | /// LSI with prescalers assuming 32.768 kHz. | 185 | /// LSI with prescalers assuming 32.768 kHz. |
| 128 | /// Raw sub-seconds in 1/256. | 186 | /// Raw sub-seconds in 1/256. |
| 129 | fn default() -> Self { | 187 | fn default() -> Self { |
| 130 | RtcConfig { frequency: Hertz(256) } | 188 | RtcConfig { |
| 189 | frequency: Hertz(256), | ||
| 190 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 191 | _disable_rtc: false, | ||
| 192 | } | ||
| 131 | } | 193 | } |
| 132 | } | 194 | } |
| 133 | 195 | ||
| @@ -145,8 +207,19 @@ pub enum RtcCalibrationCyclePeriod { | |||
| 145 | } | 207 | } |
| 146 | 208 | ||
| 147 | impl Rtc { | 209 | impl Rtc { |
| 210 | #[cfg(not(feature = "low-power"))] | ||
| 148 | /// Create a new RTC instance. | 211 | /// Create a new RTC instance. |
| 149 | pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> Self { | 212 | pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> (Self, RtcTimeProvider) { |
| 213 | (Self::new_inner(rtc_config), RtcTimeProvider::new()) | ||
| 214 | } | ||
| 215 | |||
| 216 | #[cfg(feature = "low-power")] | ||
| 217 | /// Create a new RTC instance. | ||
| 218 | pub fn new(_rtc: Peri<'static, RTC>) -> (RtcContainer, RtcTimeProvider) { | ||
| 219 | (RtcContainer::new(), RtcTimeProvider::new()) | ||
| 220 | } | ||
| 221 | |||
| 222 | pub(self) fn new_inner(rtc_config: RtcConfig) -> Self { | ||
| 150 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] | 223 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] |
| 151 | crate::rcc::enable_and_reset::<RTC>(); | 224 | crate::rcc::enable_and_reset::<RTC>(); |
| 152 | 225 | ||
| @@ -165,10 +238,13 @@ impl Rtc { | |||
| 165 | // Wait for the clock to update after initialization | 238 | // Wait for the clock to update after initialization |
| 166 | #[cfg(not(rtc_v2_f2))] | 239 | #[cfg(not(rtc_v2_f2))] |
| 167 | { | 240 | { |
| 168 | let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); | 241 | let now = RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap(); |
| 169 | while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} | 242 | while now == RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap() {} |
| 170 | } | 243 | } |
| 171 | 244 | ||
| 245 | #[cfg(feature = "low-power")] | ||
| 246 | this.enable_wakeup_line(); | ||
| 247 | |||
| 172 | this | 248 | this |
| 173 | } | 249 | } |
| 174 | 250 | ||
| @@ -177,11 +253,6 @@ impl Rtc { | |||
| 177 | freqs.rtc.to_hertz().unwrap() | 253 | freqs.rtc.to_hertz().unwrap() |
| 178 | } | 254 | } |
| 179 | 255 | ||
| 180 | /// Acquire a [`RtcTimeProvider`] instance. | ||
| 181 | pub const fn time_provider(&self) -> RtcTimeProvider { | ||
| 182 | RtcTimeProvider { _private: () } | ||
| 183 | } | ||
| 184 | |||
| 185 | /// Set the datetime to a new value. | 256 | /// Set the datetime to a new value. |
| 186 | /// | 257 | /// |
| 187 | /// # Errors | 258 | /// # Errors |
| @@ -225,15 +296,6 @@ impl Rtc { | |||
| 225 | Ok(()) | 296 | Ok(()) |
| 226 | } | 297 | } |
| 227 | 298 | ||
| 228 | /// Return the current datetime. | ||
| 229 | /// | ||
| 230 | /// # Errors | ||
| 231 | /// | ||
| 232 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. | ||
| 233 | pub fn now(&self) -> Result<DateTime, RtcError> { | ||
| 234 | self.time_provider().now() | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Check if daylight savings time is active. | 299 | /// Check if daylight savings time is active. |
| 238 | pub fn get_daylight_savings(&self) -> bool { | 300 | pub fn get_daylight_savings(&self) -> bool { |
| 239 | let cr = RTC::regs().cr().read(); | 301 | let cr = RTC::regs().cr().read(); |
| @@ -315,3 +377,18 @@ trait SealedInstance { | |||
| 315 | 377 | ||
| 316 | // fn apply_config(&mut self, rtc_config: RtcConfig); | 378 | // fn apply_config(&mut self, rtc_config: RtcConfig); |
| 317 | } | 379 | } |
| 380 | |||
| 381 | #[cfg(feature = "low-power")] | ||
| 382 | pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) { | ||
| 383 | use crate::time_driver::get_driver; | ||
| 384 | |||
| 385 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 386 | if config._disable_rtc { | ||
| 387 | return; | ||
| 388 | } | ||
| 389 | |||
| 390 | get_driver().set_rtc(cs, Rtc::new_inner(config)); | ||
| 391 | get_driver().set_min_stop_pause(cs, min_stop_pause); | ||
| 392 | |||
| 393 | trace!("low power: stop with rtc configured"); | ||
| 394 | } | ||
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 23f6ccb0c..8ac022536 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -93,7 +93,7 @@ impl super::Rtc { | |||
| 93 | }) | 93 | }) |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R | 96 | pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R |
| 97 | where | 97 | where |
| 98 | F: FnOnce(crate::pac::rtc::Rtc) -> R, | 98 | F: FnOnce(crate::pac::rtc::Rtc) -> R, |
| 99 | { | 99 | { |
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index d0b52049e..f7ebea73e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Key, Osel, Pol, TampalrmType}; | 1 | use stm32_metapac::rtc::vals::{Calp, Calw8, Calw16, Fmt, Key, Osel, Pol, TampalrmType}; |
| 2 | 2 | ||
| 3 | use super::RtcCalibrationCyclePeriod; | 3 | use super::RtcCalibrationCyclePeriod; |
| 4 | use crate::pac::rtc::Rtc; | 4 | use crate::pac::rtc::Rtc; |
| @@ -95,7 +95,7 @@ impl super::Rtc { | |||
| 95 | }) | 95 | }) |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R | 98 | pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R |
| 99 | where | 99 | where |
| 100 | F: FnOnce(crate::pac::rtc::Rtc) -> R, | 100 | F: FnOnce(crate::pac::rtc::Rtc) -> R, |
| 101 | { | 101 | { |
| @@ -131,7 +131,7 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 131 | 131 | ||
| 132 | #[cfg(feature = "low-power")] | 132 | #[cfg(feature = "low-power")] |
| 133 | cfg_if::cfg_if!( | 133 | cfg_if::cfg_if!( |
| 134 | if #[cfg(stm32g4)] { | 134 | if #[cfg(any(stm32g4, stm32wlex))] { |
| 135 | const EXTI_WAKEUP_LINE: usize = 20; | 135 | const EXTI_WAKEUP_LINE: usize = 20; |
| 136 | } else if #[cfg(stm32g0)] { | 136 | } else if #[cfg(stm32g0)] { |
| 137 | const EXTI_WAKEUP_LINE: usize = 19; | 137 | const EXTI_WAKEUP_LINE: usize = 19; |
| @@ -142,7 +142,7 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 142 | 142 | ||
| 143 | #[cfg(feature = "low-power")] | 143 | #[cfg(feature = "low-power")] |
| 144 | cfg_if::cfg_if!( | 144 | cfg_if::cfg_if!( |
| 145 | if #[cfg(stm32g4)] { | 145 | if #[cfg(any(stm32g4, stm32wlex))] { |
| 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; | 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; |
| 147 | } else if #[cfg(any(stm32g0, stm32u0))] { | 147 | } else if #[cfg(any(stm32g0, stm32u0))] { |
| 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; | 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; |
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index fb8b23b79..ce4bc43c3 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs | |||
| @@ -6,12 +6,12 @@ use core::marker::PhantomData; | |||
| 6 | use embassy_hal_internal::PeripheralType; | 6 | use embassy_hal_internal::PeripheralType; |
| 7 | 7 | ||
| 8 | pub use crate::dma::word; | 8 | pub use crate::dma::word; |
| 9 | use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; | 9 | use crate::dma::{Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer, ringbuffer}; |
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 11 | pub use crate::pac::sai::vals::Mckdiv as MasterClockDivider; | 11 | pub use crate::pac::sai::vals::Mckdiv as MasterClockDivider; |
| 12 | use crate::pac::sai::{vals, Sai as Regs}; | 12 | use crate::pac::sai::{Sai as Regs, vals}; |
| 13 | use crate::rcc::{self, RccPeripheral}; | 13 | use crate::rcc::{self, RccPeripheral}; |
| 14 | use crate::{peripherals, Peri}; | 14 | use crate::{Peri, peripherals}; |
| 15 | 15 | ||
| 16 | /// SAI error | 16 | /// SAI error |
| 17 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 17 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| @@ -391,7 +391,7 @@ pub struct Config { | |||
| 391 | pub frame_sync_polarity: FrameSyncPolarity, | 391 | pub frame_sync_polarity: FrameSyncPolarity, |
| 392 | pub frame_sync_active_level_length: word::U7, | 392 | pub frame_sync_active_level_length: word::U7, |
| 393 | pub frame_sync_definition: FrameSyncDefinition, | 393 | pub frame_sync_definition: FrameSyncDefinition, |
| 394 | pub frame_length: u8, | 394 | pub frame_length: u16, |
| 395 | pub clock_strobe: ClockStrobe, | 395 | pub clock_strobe: ClockStrobe, |
| 396 | pub output_drive: OutputDrive, | 396 | pub output_drive: OutputDrive, |
| 397 | pub master_clock_divider: Option<MasterClockDivider>, | 397 | pub master_clock_divider: Option<MasterClockDivider>, |
| @@ -696,7 +696,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 696 | w.set_fspol(config.frame_sync_polarity.fspol()); | 696 | w.set_fspol(config.frame_sync_polarity.fspol()); |
| 697 | w.set_fsdef(config.frame_sync_definition.fsdef()); | 697 | w.set_fsdef(config.frame_sync_definition.fsdef()); |
| 698 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); | 698 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); |
| 699 | w.set_frl(config.frame_length - 1); | 699 | w.set_frl((config.frame_length - 1).try_into().unwrap()); |
| 700 | }); | 700 | }); |
| 701 | 701 | ||
| 702 | ch.slotr().modify(|w| { | 702 | ch.slotr().modify(|w| { |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ccbd16cbf..e05131040 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -11,9 +11,9 @@ use embassy_hal_internal::drop::OnDrop; | |||
| 11 | use embassy_hal_internal::{Peri, PeripheralType}; | 11 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 12 | use embassy_sync::waitqueue::AtomicWaker; |
| 13 | use sdio_host::common_cmd::{self, Resp, ResponseLen}; | 13 | use sdio_host::common_cmd::{self, Resp, ResponseLen}; |
| 14 | use sdio_host::emmc::{ExtCSD, EMMC}; | 14 | use sdio_host::emmc::{EMMC, ExtCSD}; |
| 15 | use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}; | 15 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; |
| 16 | use sdio_host::{emmc_cmd, sd_cmd, Cmd}; | 16 | use sdio_host::{Cmd, emmc_cmd, sd_cmd}; |
| 17 | 17 | ||
| 18 | #[cfg(sdmmc_v1)] | 18 | #[cfg(sdmmc_v1)] |
| 19 | use crate::dma::ChannelAndRequest; | 19 | use crate::dma::ChannelAndRequest; |
| @@ -1032,12 +1032,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1032 | 1032 | ||
| 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. | 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. |
| 1034 | #[inline] | 1034 | #[inline] |
| 1035 | #[allow(unused)] | ||
| 1035 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { | 1036 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { |
| 1036 | let regs = T::regs(); | ||
| 1037 | |||
| 1038 | let res = poll_fn(|cx| { | 1037 | let res = poll_fn(|cx| { |
| 1038 | // Compiler might not be sufficiently constrained here | ||
| 1039 | // https://github.com/embassy-rs/embassy/issues/4723 | ||
| 1039 | T::state().register(cx.waker()); | 1040 | T::state().register(cx.waker()); |
| 1040 | let status = regs.star().read(); | 1041 | let status = T::regs().star().read(); |
| 1041 | 1042 | ||
| 1042 | if status.dcrcfail() { | 1043 | if status.dcrcfail() { |
| 1043 | return Poll::Ready(Err(Error::Crc)); | 1044 | return Poll::Ready(Err(Error::Crc)); |
| @@ -1052,10 +1053,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1052 | if status.stbiterr() { | 1053 | if status.stbiterr() { |
| 1053 | return Poll::Ready(Err(Error::StBitErr)); | 1054 | return Poll::Ready(Err(Error::StBitErr)); |
| 1054 | } | 1055 | } |
| 1056 | #[cfg(sdmmc_v1)] | ||
| 1055 | let done = match block { | 1057 | let done = match block { |
| 1056 | true => status.dbckend(), | 1058 | true => status.dbckend(), |
| 1057 | false => status.dataend(), | 1059 | false => status.dataend(), |
| 1058 | }; | 1060 | }; |
| 1061 | #[cfg(sdmmc_v2)] | ||
| 1062 | let done = status.dataend(); | ||
| 1059 | if done { | 1063 | if done { |
| 1060 | return Poll::Ready(Ok(())); | 1064 | return Poll::Ready(Ok(())); |
| 1061 | } | 1065 | } |
diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs index b0a32d5d1..6f2d24560 100644 --- a/embassy-stm32/src/spdifrx/mod.rs +++ b/embassy-stm32/src/spdifrx/mod.rs | |||
| @@ -13,7 +13,7 @@ use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; | |||
| 13 | use crate::interrupt::typelevel::Interrupt; | 13 | use crate::interrupt::typelevel::Interrupt; |
| 14 | use crate::pac::spdifrx::Spdifrx as Regs; | 14 | use crate::pac::spdifrx::Spdifrx as Regs; |
| 15 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 15 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 16 | use crate::{interrupt, peripherals, Peri}; | 16 | use crate::{Peri, interrupt, peripherals}; |
| 17 | 17 | ||
| 18 | /// Possible S/PDIF preamble types. | 18 | /// Possible S/PDIF preamble types. |
| 19 | #[allow(dead_code)] | 19 | #[allow(dead_code)] |
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c5373a54d..abb80ed26 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -6,15 +6,15 @@ use core::ptr; | |||
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| 8 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| 9 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | 9 | pub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity}; |
| 10 | 10 | ||
| 11 | use crate::dma::{word, ChannelAndRequest}; | 11 | use crate::Peri; |
| 12 | use crate::dma::{ChannelAndRequest, word}; | ||
| 12 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 13 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 13 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 14 | use crate::pac::spi::{regs, vals, Spi as Regs}; | 15 | use crate::pac::spi::{Spi as Regs, regs, vals}; |
| 15 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 16 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 16 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 17 | use crate::Peri; | ||
| 18 | 18 | ||
| 19 | /// SPI error. | 19 | /// SPI error. |
| 20 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 20 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| @@ -125,26 +125,69 @@ impl Config { | |||
| 125 | ) | 125 | ) |
| 126 | } | 126 | } |
| 127 | } | 127 | } |
| 128 | |||
| 129 | /// SPI communication mode | ||
| 130 | pub mod mode { | ||
| 131 | use stm32_metapac::spi::vals; | ||
| 132 | |||
| 133 | trait SealedMode {} | ||
| 134 | |||
| 135 | /// Trait for SPI communication mode operations. | ||
| 136 | #[allow(private_bounds)] | ||
| 137 | pub trait CommunicationMode: SealedMode { | ||
| 138 | /// Spi communication mode | ||
| 139 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 140 | const MASTER: vals::Mstr; | ||
| 141 | /// Spi communication mode | ||
| 142 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 143 | const MASTER: vals::Master; | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Mode allowing for SPI master operations. | ||
| 147 | pub struct Master; | ||
| 148 | /// Mode allowing for SPI slave operations. | ||
| 149 | pub struct Slave; | ||
| 150 | |||
| 151 | impl SealedMode for Master {} | ||
| 152 | impl CommunicationMode for Master { | ||
| 153 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 154 | const MASTER: vals::Mstr = vals::Mstr::MASTER; | ||
| 155 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 156 | const MASTER: vals::Master = vals::Master::MASTER; | ||
| 157 | } | ||
| 158 | |||
| 159 | impl SealedMode for Slave {} | ||
| 160 | impl CommunicationMode for Slave { | ||
| 161 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 162 | const MASTER: vals::Mstr = vals::Mstr::SLAVE; | ||
| 163 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 164 | const MASTER: vals::Master = vals::Master::SLAVE; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | use mode::{CommunicationMode, Master, Slave}; | ||
| 168 | |||
| 128 | /// SPI driver. | 169 | /// SPI driver. |
| 129 | pub struct Spi<'d, M: PeriMode> { | 170 | pub struct Spi<'d, M: PeriMode, CM: CommunicationMode> { |
| 130 | pub(crate) info: &'static Info, | 171 | pub(crate) info: &'static Info, |
| 131 | kernel_clock: Hertz, | 172 | kernel_clock: Hertz, |
| 132 | sck: Option<Peri<'d, AnyPin>>, | 173 | sck: Option<Peri<'d, AnyPin>>, |
| 133 | mosi: Option<Peri<'d, AnyPin>>, | 174 | mosi: Option<Peri<'d, AnyPin>>, |
| 134 | miso: Option<Peri<'d, AnyPin>>, | 175 | miso: Option<Peri<'d, AnyPin>>, |
| 176 | nss: Option<Peri<'d, AnyPin>>, | ||
| 135 | tx_dma: Option<ChannelAndRequest<'d>>, | 177 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 136 | rx_dma: Option<ChannelAndRequest<'d>>, | 178 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 137 | _phantom: PhantomData<M>, | 179 | _phantom: PhantomData<(M, CM)>, |
| 138 | current_word_size: word_impl::Config, | 180 | current_word_size: word_impl::Config, |
| 139 | gpio_speed: Speed, | 181 | gpio_speed: Speed, |
| 140 | } | 182 | } |
| 141 | 183 | ||
| 142 | impl<'d, M: PeriMode> Spi<'d, M> { | 184 | impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { |
| 143 | fn new_inner<T: Instance>( | 185 | fn new_inner<T: Instance>( |
| 144 | _peri: Peri<'d, T>, | 186 | _peri: Peri<'d, T>, |
| 145 | sck: Option<Peri<'d, AnyPin>>, | 187 | sck: Option<Peri<'d, AnyPin>>, |
| 146 | mosi: Option<Peri<'d, AnyPin>>, | 188 | mosi: Option<Peri<'d, AnyPin>>, |
| 147 | miso: Option<Peri<'d, AnyPin>>, | 189 | miso: Option<Peri<'d, AnyPin>>, |
| 190 | nss: Option<Peri<'d, AnyPin>>, | ||
| 148 | tx_dma: Option<ChannelAndRequest<'d>>, | 191 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 149 | rx_dma: Option<ChannelAndRequest<'d>>, | 192 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 150 | config: Config, | 193 | config: Config, |
| @@ -155,6 +198,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 155 | sck, | 198 | sck, |
| 156 | mosi, | 199 | mosi, |
| 157 | miso, | 200 | miso, |
| 201 | nss, | ||
| 158 | tx_dma, | 202 | tx_dma, |
| 159 | rx_dma, | 203 | rx_dma, |
| 160 | current_word_size: <u8 as SealedWord>::CONFIG, | 204 | current_word_size: <u8 as SealedWord>::CONFIG, |
| @@ -183,12 +227,12 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 183 | w.set_cpha(cpha); | 227 | w.set_cpha(cpha); |
| 184 | w.set_cpol(cpol); | 228 | w.set_cpol(cpol); |
| 185 | 229 | ||
| 186 | w.set_mstr(vals::Mstr::MASTER); | 230 | w.set_mstr(CM::MASTER); |
| 187 | w.set_br(br); | 231 | w.set_br(br); |
| 188 | w.set_spe(true); | 232 | w.set_spe(true); |
| 189 | w.set_lsbfirst(lsbfirst); | 233 | w.set_lsbfirst(lsbfirst); |
| 190 | w.set_ssi(true); | 234 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 191 | w.set_ssm(true); | 235 | w.set_ssm(CM::MASTER == vals::Mstr::MASTER); |
| 192 | w.set_crcen(false); | 236 | w.set_crcen(false); |
| 193 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 237 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 194 | // we're doing "fake rxonly", by actually writing one | 238 | // we're doing "fake rxonly", by actually writing one |
| @@ -210,11 +254,11 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 210 | w.set_cpha(cpha); | 254 | w.set_cpha(cpha); |
| 211 | w.set_cpol(cpol); | 255 | w.set_cpol(cpol); |
| 212 | 256 | ||
| 213 | w.set_mstr(vals::Mstr::MASTER); | 257 | w.set_mstr(CM::MASTER); |
| 214 | w.set_br(br); | 258 | w.set_br(br); |
| 215 | w.set_lsbfirst(lsbfirst); | 259 | w.set_lsbfirst(lsbfirst); |
| 216 | w.set_ssi(true); | 260 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 217 | w.set_ssm(true); | 261 | w.set_ssm(CM::MASTER == vals::Mstr::MASTER); |
| 218 | w.set_crcen(false); | 262 | w.set_crcen(false); |
| 219 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 263 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 220 | w.set_spe(true); | 264 | w.set_spe(true); |
| @@ -229,8 +273,8 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 229 | w.set_cpha(cpha); | 273 | w.set_cpha(cpha); |
| 230 | w.set_cpol(cpol); | 274 | w.set_cpol(cpol); |
| 231 | w.set_lsbfirst(lsbfirst); | 275 | w.set_lsbfirst(lsbfirst); |
| 232 | w.set_ssm(true); | 276 | w.set_ssm(CM::MASTER == vals::Master::MASTER); |
| 233 | w.set_master(vals::Master::MASTER); | 277 | w.set_master(CM::MASTER); |
| 234 | w.set_comm(vals::Comm::FULL_DUPLEX); | 278 | w.set_comm(vals::Comm::FULL_DUPLEX); |
| 235 | w.set_ssom(vals::Ssom::ASSERTED); | 279 | w.set_ssom(vals::Ssom::ASSERTED); |
| 236 | w.set_midi(0); | 280 | w.set_midi(0); |
| @@ -469,7 +513,30 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 469 | } | 513 | } |
| 470 | } | 514 | } |
| 471 | 515 | ||
| 472 | impl<'d> Spi<'d, Blocking> { | 516 | impl<'d> Spi<'d, Blocking, Slave> { |
| 517 | /// Create a new blocking SPI slave driver. | ||
| 518 | pub fn new_blocking_slave<T: Instance, #[cfg(afio)] A>( | ||
| 519 | peri: Peri<'d, T>, | ||
| 520 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 521 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 522 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 523 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 524 | config: Config, | ||
| 525 | ) -> Self { | ||
| 526 | Self::new_inner( | ||
| 527 | peri, | ||
| 528 | new_pin!(sck, config.sck_af()), | ||
| 529 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 530 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 531 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 532 | None, | ||
| 533 | None, | ||
| 534 | config, | ||
| 535 | ) | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | impl<'d> Spi<'d, Blocking, Master> { | ||
| 473 | /// Create a new blocking SPI driver. | 540 | /// Create a new blocking SPI driver. |
| 474 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( | 541 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 475 | peri: Peri<'d, T>, | 542 | peri: Peri<'d, T>, |
| @@ -485,6 +552,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 485 | new_pin!(miso, AfType::input(config.miso_pull)), | 552 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 486 | None, | 553 | None, |
| 487 | None, | 554 | None, |
| 555 | None, | ||
| 488 | config, | 556 | config, |
| 489 | ) | 557 | ) |
| 490 | } | 558 | } |
| @@ -503,6 +571,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 503 | new_pin!(miso, AfType::input(config.miso_pull)), | 571 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 504 | None, | 572 | None, |
| 505 | None, | 573 | None, |
| 574 | None, | ||
| 506 | config, | 575 | config, |
| 507 | ) | 576 | ) |
| 508 | } | 577 | } |
| @@ -521,6 +590,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 521 | None, | 590 | None, |
| 522 | None, | 591 | None, |
| 523 | None, | 592 | None, |
| 593 | None, | ||
| 524 | config, | 594 | config, |
| 525 | ) | 595 | ) |
| 526 | } | 596 | } |
| @@ -540,12 +610,38 @@ impl<'d> Spi<'d, Blocking> { | |||
| 540 | None, | 610 | None, |
| 541 | None, | 611 | None, |
| 542 | None, | 612 | None, |
| 613 | None, | ||
| 543 | config, | 614 | config, |
| 544 | ) | 615 | ) |
| 545 | } | 616 | } |
| 546 | } | 617 | } |
| 547 | 618 | ||
| 548 | impl<'d> Spi<'d, Async> { | 619 | impl<'d> Spi<'d, Async, Slave> { |
| 620 | /// Create a new SPI slave driver. | ||
| 621 | pub fn new_slave<T: Instance, #[cfg(afio)] A>( | ||
| 622 | peri: Peri<'d, T>, | ||
| 623 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 624 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 625 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 626 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 627 | tx_dma: Peri<'d, impl TxDma<T>>, | ||
| 628 | rx_dma: Peri<'d, impl RxDma<T>>, | ||
| 629 | config: Config, | ||
| 630 | ) -> Self { | ||
| 631 | Self::new_inner( | ||
| 632 | peri, | ||
| 633 | new_pin!(sck, config.sck_af()), | ||
| 634 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 635 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 636 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 637 | new_dma!(tx_dma), | ||
| 638 | new_dma!(rx_dma), | ||
| 639 | config, | ||
| 640 | ) | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | impl<'d> Spi<'d, Async, Master> { | ||
| 549 | /// Create a new SPI driver. | 645 | /// Create a new SPI driver. |
| 550 | pub fn new<T: Instance, #[cfg(afio)] A>( | 646 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 551 | peri: Peri<'d, T>, | 647 | peri: Peri<'d, T>, |
| @@ -561,6 +657,7 @@ impl<'d> Spi<'d, Async> { | |||
| 561 | new_pin!(sck, config.sck_af()), | 657 | new_pin!(sck, config.sck_af()), |
| 562 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 658 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 563 | new_pin!(miso, AfType::input(config.miso_pull)), | 659 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 660 | None, | ||
| 564 | new_dma!(tx_dma), | 661 | new_dma!(tx_dma), |
| 565 | new_dma!(rx_dma), | 662 | new_dma!(rx_dma), |
| 566 | config, | 663 | config, |
| @@ -581,6 +678,7 @@ impl<'d> Spi<'d, Async> { | |||
| 581 | new_pin!(sck, config.sck_af()), | 678 | new_pin!(sck, config.sck_af()), |
| 582 | None, | 679 | None, |
| 583 | new_pin!(miso, AfType::input(config.miso_pull)), | 680 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 681 | None, | ||
| 584 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 682 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 585 | new_dma!(tx_dma), | 683 | new_dma!(tx_dma), |
| 586 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 684 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| @@ -603,6 +701,7 @@ impl<'d> Spi<'d, Async> { | |||
| 603 | new_pin!(sck, config.sck_af()), | 701 | new_pin!(sck, config.sck_af()), |
| 604 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 702 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 605 | None, | 703 | None, |
| 704 | None, | ||
| 606 | new_dma!(tx_dma), | 705 | new_dma!(tx_dma), |
| 607 | None, | 706 | None, |
| 608 | config, | 707 | config, |
| @@ -623,6 +722,7 @@ impl<'d> Spi<'d, Async> { | |||
| 623 | None, | 722 | None, |
| 624 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 723 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 625 | None, | 724 | None, |
| 725 | None, | ||
| 626 | new_dma!(tx_dma), | 726 | new_dma!(tx_dma), |
| 627 | None, | 727 | None, |
| 628 | config, | 728 | config, |
| @@ -646,7 +746,7 @@ impl<'d> Spi<'d, Async> { | |||
| 646 | config.bit_order = BitOrder::MsbFirst; | 746 | config.bit_order = BitOrder::MsbFirst; |
| 647 | config.frequency = freq; | 747 | config.frequency = freq; |
| 648 | 748 | ||
| 649 | Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) | 749 | Self::new_inner(peri, None, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) |
| 650 | } | 750 | } |
| 651 | 751 | ||
| 652 | #[allow(dead_code)] | 752 | #[allow(dead_code)] |
| @@ -656,9 +756,11 @@ impl<'d> Spi<'d, Async> { | |||
| 656 | rx_dma: Option<ChannelAndRequest<'d>>, | 756 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 657 | config: Config, | 757 | config: Config, |
| 658 | ) -> Self { | 758 | ) -> Self { |
| 659 | Self::new_inner(peri, None, None, None, tx_dma, rx_dma, config) | 759 | Self::new_inner(peri, None, None, None, None, tx_dma, rx_dma, config) |
| 660 | } | 760 | } |
| 761 | } | ||
| 661 | 762 | ||
| 763 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | ||
| 662 | /// SPI write, using DMA. | 764 | /// SPI write, using DMA. |
| 663 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { | 765 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { |
| 664 | if data.is_empty() { | 766 | if data.is_empty() { |
| @@ -888,11 +990,12 @@ impl<'d> Spi<'d, Async> { | |||
| 888 | } | 990 | } |
| 889 | } | 991 | } |
| 890 | 992 | ||
| 891 | impl<'d, M: PeriMode> Drop for Spi<'d, M> { | 993 | impl<'d, M: PeriMode, CM: CommunicationMode> Drop for Spi<'d, M, CM> { |
| 892 | fn drop(&mut self) { | 994 | fn drop(&mut self) { |
| 893 | self.sck.as_ref().map(|x| x.set_as_disconnected()); | 995 | self.sck.as_ref().map(|x| x.set_as_disconnected()); |
| 894 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); | 996 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); |
| 895 | self.miso.as_ref().map(|x| x.set_as_disconnected()); | 997 | self.miso.as_ref().map(|x| x.set_as_disconnected()); |
| 998 | self.nss.as_ref().map(|x| x.set_as_disconnected()); | ||
| 896 | 999 | ||
| 897 | self.info.rcc.disable(); | 1000 | self.info.rcc.disable(); |
| 898 | } | 1001 | } |
| @@ -1127,7 +1230,7 @@ fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> { | |||
| 1127 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 | 1230 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 |
| 1128 | macro_rules! impl_blocking { | 1231 | macro_rules! impl_blocking { |
| 1129 | ($w:ident) => { | 1232 | ($w:ident) => { |
| 1130 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M> { | 1233 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M, CM> { |
| 1131 | type Error = Error; | 1234 | type Error = Error; |
| 1132 | 1235 | ||
| 1133 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { | 1236 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { |
| @@ -1135,7 +1238,7 @@ macro_rules! impl_blocking { | |||
| 1135 | } | 1238 | } |
| 1136 | } | 1239 | } |
| 1137 | 1240 | ||
| 1138 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M> { | 1241 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M, CM> { |
| 1139 | type Error = Error; | 1242 | type Error = Error; |
| 1140 | 1243 | ||
| 1141 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { | 1244 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { |
| @@ -1149,11 +1252,11 @@ macro_rules! impl_blocking { | |||
| 1149 | impl_blocking!(u8); | 1252 | impl_blocking!(u8); |
| 1150 | impl_blocking!(u16); | 1253 | impl_blocking!(u16); |
| 1151 | 1254 | ||
| 1152 | impl<'d, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { | 1255 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::ErrorType for Spi<'d, M, CM> { |
| 1153 | type Error = Error; | 1256 | type Error = Error; |
| 1154 | } | 1257 | } |
| 1155 | 1258 | ||
| 1156 | impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M> { | 1259 | impl<'d, W: Word, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M, CM> { |
| 1157 | fn flush(&mut self) -> Result<(), Self::Error> { | 1260 | fn flush(&mut self) -> Result<(), Self::Error> { |
| 1158 | Ok(()) | 1261 | Ok(()) |
| 1159 | } | 1262 | } |
| @@ -1186,7 +1289,7 @@ impl embedded_hal_1::spi::Error for Error { | |||
| 1186 | } | 1289 | } |
| 1187 | } | 1290 | } |
| 1188 | 1291 | ||
| 1189 | impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { | 1292 | impl<'d, W: Word, CM: CommunicationMode> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async, CM> { |
| 1190 | async fn flush(&mut self) -> Result<(), Self::Error> { | 1293 | async fn flush(&mut self) -> Result<(), Self::Error> { |
| 1191 | Ok(()) | 1294 | Ok(()) |
| 1192 | } | 1295 | } |
| @@ -1328,7 +1431,7 @@ foreach_peripheral!( | |||
| 1328 | }; | 1431 | }; |
| 1329 | ); | 1432 | ); |
| 1330 | 1433 | ||
| 1331 | impl<'d, M: PeriMode> SetConfig for Spi<'d, M> { | 1434 | impl<'d, M: PeriMode, CM: CommunicationMode> SetConfig for Spi<'d, M, CM> { |
| 1332 | type Config = Config; | 1435 | type Config = Config; |
| 1333 | type ConfigError = (); | 1436 | type ConfigError = (); |
| 1334 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | 1437 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 7db74bdf6..0b75aef92 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -1,14 +1,16 @@ | |||
| 1 | #![allow(non_snake_case)] | 1 | #![allow(non_snake_case)] |
| 2 | 2 | ||
| 3 | use core::cell::{Cell, RefCell}; | 3 | use core::cell::{Cell, RefCell}; |
| 4 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; | 4 | #[cfg(all(feature = "low-power", stm32wlex))] |
| 5 | use core::sync::atomic::AtomicU16; | ||
| 6 | use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; | ||
| 5 | 7 | ||
| 6 | use critical_section::CriticalSection; | 8 | use critical_section::CriticalSection; |
| 7 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 8 | use embassy_sync::blocking_mutex::Mutex; | 9 | use embassy_sync::blocking_mutex::Mutex; |
| 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 9 | use embassy_time_driver::{Driver, TICK_HZ}; | 11 | use embassy_time_driver::{Driver, TICK_HZ}; |
| 10 | use embassy_time_queue_utils::Queue; | 12 | use embassy_time_queue_utils::Queue; |
| 11 | use stm32_metapac::timer::{regs, TimGp16}; | 13 | use stm32_metapac::timer::{TimGp16, regs}; |
| 12 | 14 | ||
| 13 | use crate::interrupt::typelevel::Interrupt; | 15 | use crate::interrupt::typelevel::Interrupt; |
| 14 | use crate::pac::timer::vals; | 16 | use crate::pac::timer::vals; |
| @@ -194,6 +196,11 @@ fn calc_now(period: u32, counter: u16) -> u64 { | |||
| 194 | ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) | 196 | ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) |
| 195 | } | 197 | } |
| 196 | 198 | ||
| 199 | #[cfg(feature = "low-power")] | ||
| 200 | fn calc_period_counter(ticks: u64) -> (u32, u16) { | ||
| 201 | (2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16) | ||
| 202 | } | ||
| 203 | |||
| 197 | struct AlarmState { | 204 | struct AlarmState { |
| 198 | timestamp: Cell<u64>, | 205 | timestamp: Cell<u64>, |
| 199 | } | 206 | } |
| @@ -213,7 +220,13 @@ pub(crate) struct RtcDriver { | |||
| 213 | period: AtomicU32, | 220 | period: AtomicU32, |
| 214 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, | 221 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, |
| 215 | #[cfg(feature = "low-power")] | 222 | #[cfg(feature = "low-power")] |
| 216 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, | 223 | pub(crate) rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>, |
| 224 | #[cfg(feature = "low-power")] | ||
| 225 | /// The minimum pause time beyond which the executor will enter a low-power state. | ||
| 226 | min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>, | ||
| 227 | /// Saved count for the timer (its value is lost when entering STOP2) | ||
| 228 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 229 | saved_count: AtomicU16, | ||
| 217 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, | 230 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, |
| 218 | } | 231 | } |
| 219 | 232 | ||
| @@ -221,12 +234,18 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | |||
| 221 | period: AtomicU32::new(0), | 234 | period: AtomicU32::new(0), |
| 222 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), | 235 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 223 | #[cfg(feature = "low-power")] | 236 | #[cfg(feature = "low-power")] |
| 224 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), | 237 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)), |
| 238 | #[cfg(feature = "low-power")] | ||
| 239 | min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))), | ||
| 240 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 241 | saved_count: AtomicU16::new(0), | ||
| 225 | queue: Mutex::new(RefCell::new(Queue::new())) | 242 | queue: Mutex::new(RefCell::new(Queue::new())) |
| 226 | }); | 243 | }); |
| 227 | 244 | ||
| 228 | impl RtcDriver { | 245 | impl RtcDriver { |
| 229 | fn init(&'static self, cs: critical_section::CriticalSection) { | 246 | /// initialize the timer, but don't start it. Used for chips like stm32wle5 |
| 247 | /// for low power where the timer config is lost in STOP2. | ||
| 248 | pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) { | ||
| 230 | let r = regs_gp16(); | 249 | let r = regs_gp16(); |
| 231 | 250 | ||
| 232 | rcc::enable_and_reset_with_cs::<T>(cs); | 251 | rcc::enable_and_reset_with_cs::<T>(cs); |
| @@ -259,10 +278,16 @@ impl RtcDriver { | |||
| 259 | w.set_ccie(0, true); | 278 | w.set_ccie(0, true); |
| 260 | }); | 279 | }); |
| 261 | 280 | ||
| 281 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 282 | r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst))); | ||
| 283 | |||
| 262 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); | 284 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); |
| 263 | unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; | 285 | unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; |
| 286 | } | ||
| 264 | 287 | ||
| 265 | r.cr1().modify(|w| w.set_cen(true)); | 288 | fn init(&'static self, cs: CriticalSection) { |
| 289 | self.init_timer(cs); | ||
| 290 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | ||
| 266 | } | 291 | } |
| 267 | 292 | ||
| 268 | fn on_interrupt(&self) { | 293 | fn on_interrupt(&self) { |
| @@ -338,34 +363,10 @@ impl RtcDriver { | |||
| 338 | #[cfg(feature = "low-power")] | 363 | #[cfg(feature = "low-power")] |
| 339 | /// Add the given offset to the current time | 364 | /// Add the given offset to the current time |
| 340 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { | 365 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { |
| 341 | let offset = offset.as_ticks(); | 366 | let (period, counter) = calc_period_counter(self.now() + offset.as_ticks()); |
| 342 | let cnt = regs_gp16().cnt().read().cnt() as u32; | ||
| 343 | let period = self.period.load(Ordering::SeqCst); | ||
| 344 | |||
| 345 | // Correct the race, if it exists | ||
| 346 | let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { | ||
| 347 | period + 1 | ||
| 348 | } else { | ||
| 349 | period | ||
| 350 | }; | ||
| 351 | |||
| 352 | // Normalize to the full overflow | ||
| 353 | let period = (period / 2) * 2; | ||
| 354 | |||
| 355 | // Add the offset | ||
| 356 | let period = period + 2 * (offset / u16::MAX as u64) as u32; | ||
| 357 | let cnt = cnt + (offset % u16::MAX as u64) as u32; | ||
| 358 | |||
| 359 | let (cnt, period) = if cnt > u16::MAX as u32 { | ||
| 360 | (cnt - u16::MAX as u32, period + 2) | ||
| 361 | } else { | ||
| 362 | (cnt, period) | ||
| 363 | }; | ||
| 364 | |||
| 365 | let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; | ||
| 366 | 367 | ||
| 367 | self.period.store(period, Ordering::SeqCst); | 368 | self.period.store(period, Ordering::SeqCst); |
| 368 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); | 369 | regs_gp16().cnt().write(|w| w.set_cnt(counter)); |
| 369 | 370 | ||
| 370 | // Now, recompute alarm | 371 | // Now, recompute alarm |
| 371 | let alarm = self.alarm.borrow(cs); | 372 | let alarm = self.alarm.borrow(cs); |
| @@ -379,70 +380,64 @@ impl RtcDriver { | |||
| 379 | #[cfg(feature = "low-power")] | 380 | #[cfg(feature = "low-power")] |
| 380 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset | 381 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset |
| 381 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { | 382 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { |
| 382 | if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { | 383 | if !regs_gp16().cr1().read().cen() |
| 384 | && let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) | ||
| 385 | { | ||
| 383 | self.add_time(offset, cs); | 386 | self.add_time(offset, cs); |
| 384 | } | 387 | } |
| 385 | } | 388 | } |
| 386 | 389 | ||
| 387 | /* | 390 | /* |
| 388 | Low-power public functions: all create a critical section | 391 | Low-power public functions: all require a critical section |
| 389 | */ | 392 | */ |
| 390 | #[cfg(feature = "low-power")] | 393 | #[cfg(feature = "low-power")] |
| 391 | /// Set the rtc but panic if it's already been set | 394 | pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { |
| 392 | pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { | 395 | self.min_stop_pause.borrow(cs).replace(min_stop_pause); |
| 393 | critical_section::with(|cs| { | ||
| 394 | rtc.stop_wakeup_alarm(cs); | ||
| 395 | |||
| 396 | assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()) | ||
| 397 | }); | ||
| 398 | } | 396 | } |
| 399 | 397 | ||
| 400 | #[cfg(feature = "low-power")] | 398 | #[cfg(feature = "low-power")] |
| 401 | /// The minimum pause time beyond which the executor will enter a low-power state. | 399 | /// Set the rtc but panic if it's already been set |
| 402 | pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250); | 400 | pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) { |
| 401 | rtc.stop_wakeup_alarm(cs); | ||
| 402 | |||
| 403 | assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()); | ||
| 404 | } | ||
| 403 | 405 | ||
| 404 | #[cfg(feature = "low-power")] | 406 | #[cfg(feature = "low-power")] |
| 405 | /// Pause the timer if ready; return err if not | 407 | /// Pause the timer if ready; return err if not |
| 406 | pub(crate) fn pause_time(&self) -> Result<(), ()> { | 408 | pub(crate) fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> { |
| 407 | critical_section::with(|cs| { | 409 | self.stop_wakeup_alarm(cs); |
| 408 | /* | 410 | |
| 409 | If the wakeup timer is currently running, then we need to stop it and | 411 | let time_until_next_alarm = self.time_until_next_alarm(cs); |
| 410 | add the elapsed time to the current time, as this will impact the result | 412 | if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { |
| 411 | of `time_until_next_alarm`. | 413 | trace!( |
| 412 | */ | 414 | "time_until_next_alarm < self.min_stop_pause ({})", |
| 413 | self.stop_wakeup_alarm(cs); | 415 | time_until_next_alarm |
| 414 | 416 | ); | |
| 415 | let time_until_next_alarm = self.time_until_next_alarm(cs); | 417 | Err(()) |
| 416 | if time_until_next_alarm < Self::MIN_STOP_PAUSE { | 418 | } else { |
| 417 | Err(()) | 419 | self.rtc |
| 418 | } else { | 420 | .borrow(cs) |
| 419 | self.rtc | 421 | .borrow_mut() |
| 420 | .borrow(cs) | 422 | .as_mut() |
| 421 | .get() | 423 | .unwrap() |
| 422 | .unwrap() | 424 | .start_wakeup_alarm(time_until_next_alarm, cs); |
| 423 | .start_wakeup_alarm(time_until_next_alarm, cs); | 425 | |
| 424 | 426 | regs_gp16().cr1().modify(|w| w.set_cen(false)); | |
| 425 | regs_gp16().cr1().modify(|w| w.set_cen(false)); | 427 | // save the count for the timer as its lost in STOP2 for stm32wlex |
| 426 | 428 | #[cfg(stm32wlex)] | |
| 427 | Ok(()) | 429 | self.saved_count |
| 428 | } | 430 | .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); |
| 429 | }) | 431 | Ok(()) |
| 432 | } | ||
| 430 | } | 433 | } |
| 431 | 434 | ||
| 432 | #[cfg(feature = "low-power")] | 435 | #[cfg(feature = "low-power")] |
| 433 | /// Resume the timer with the given offset | 436 | /// Resume the timer with the given offset |
| 434 | pub(crate) fn resume_time(&self) { | 437 | pub(crate) fn resume_time(&self, cs: CriticalSection) { |
| 435 | if regs_gp16().cr1().read().cen() { | 438 | self.stop_wakeup_alarm(cs); |
| 436 | // Time isn't currently stopped | ||
| 437 | 439 | ||
| 438 | return; | 440 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| 439 | } | ||
| 440 | |||
| 441 | critical_section::with(|cs| { | ||
| 442 | self.stop_wakeup_alarm(cs); | ||
| 443 | |||
| 444 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | ||
| 445 | }) | ||
| 446 | } | 441 | } |
| 447 | 442 | ||
| 448 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { | 443 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { |
| @@ -514,7 +509,7 @@ impl Driver for RtcDriver { | |||
| 514 | } | 509 | } |
| 515 | 510 | ||
| 516 | #[cfg(feature = "low-power")] | 511 | #[cfg(feature = "low-power")] |
| 517 | pub(crate) fn get_driver() -> &'static RtcDriver { | 512 | pub(crate) const fn get_driver() -> &'static RtcDriver { |
| 518 | &DRIVER | 513 | &DRIVER |
| 519 | } | 514 | } |
| 520 | 515 | ||
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 484aae1d0..6d4c70dff 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -2,16 +2,16 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr}; | 5 | pub use stm32_metapac::timer::vals::{Ckd, Mms2, Ossi, Ossr}; |
| 6 | 6 | ||
| 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; | 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; |
| 8 | use super::simple_pwm::PwmPin; | 8 | use super::simple_pwm::PwmPin; |
| 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; | 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; |
| 10 | use crate::Peri; | ||
| 10 | use crate::gpio::{AnyPin, OutputType}; | 11 | use crate::gpio::{AnyPin, OutputType}; |
| 11 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 12 | use crate::timer::low_level::OutputCompareMode; | ||
| 13 | use crate::timer::TimerChannel; | 13 | use crate::timer::TimerChannel; |
| 14 | use crate::Peri; | 14 | use crate::timer::low_level::OutputCompareMode; |
| 15 | 15 | ||
| 16 | /// Complementary PWM pin wrapper. | 16 | /// Complementary PWM pin wrapper. |
| 17 | /// | 17 | /// |
| @@ -77,8 +77,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 77 | 77 | ||
| 78 | this.inner.set_counting_mode(counting_mode); | 78 | this.inner.set_counting_mode(counting_mode); |
| 79 | this.set_frequency(freq); | 79 | this.set_frequency(freq); |
| 80 | this.inner.start(); | ||
| 81 | |||
| 82 | this.inner.enable_outputs(); | 80 | this.inner.enable_outputs(); |
| 83 | 81 | ||
| 84 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 82 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| @@ -89,6 +87,10 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 89 | }); | 87 | }); |
| 90 | this.inner.set_autoreload_preload(true); | 88 | this.inner.set_autoreload_preload(true); |
| 91 | 89 | ||
| 90 | // Generate update event so pre-load registers are written to the shadow registers | ||
| 91 | this.inner.generate_update_event(); | ||
| 92 | this.inner.start(); | ||
| 93 | |||
| 92 | this | 94 | this |
| 93 | } | 95 | } |
| 94 | 96 | ||
| @@ -136,6 +138,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 136 | self.inner.get_moe() | 138 | self.inner.get_moe() |
| 137 | } | 139 | } |
| 138 | 140 | ||
| 141 | /// Set Master Slave Mode 2 | ||
| 142 | pub fn set_mms2(&mut self, mms2: Mms2) { | ||
| 143 | self.inner.set_mms2_selection(mms2); | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Set Repetition Counter | ||
| 147 | pub fn set_repetition_counter(&mut self, val: u16) { | ||
| 148 | self.inner.set_repetition_counter(val); | ||
| 149 | } | ||
| 150 | |||
| 139 | /// Enable the given channel. | 151 | /// Enable the given channel. |
| 140 | pub fn enable(&mut self, channel: Channel) { | 152 | pub fn enable(&mut self, channel: Channel) { |
| 141 | self.inner.enable_channel(channel, true); | 153 | self.inner.enable_channel(channel, true); |
| @@ -150,8 +162,8 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 150 | 162 | ||
| 151 | /// Set PWM frequency. | 163 | /// Set PWM frequency. |
| 152 | /// | 164 | /// |
| 153 | /// Note: when you call this, the max duty value changes, so you will have to | 165 | /// Note: that the frequency will not be applied in the timer until an update event |
| 154 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 166 | /// occurs. |
| 155 | pub fn set_frequency(&mut self, freq: Hertz) { | 167 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 156 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 168 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 157 | 2u8 | 169 | 2u8 |
| @@ -208,60 +220,57 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 208 | /// | 220 | /// |
| 209 | /// Note: | 221 | /// Note: |
| 210 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 222 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. |
| 223 | #[inline(always)] | ||
| 211 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 224 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { |
| 212 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 225 | self.inner.waveform_up(dma, channel, duty).await |
| 213 | let req = dma.request(); | 226 | } |
| 214 | |||
| 215 | let original_duty_state = self.inner.get_compare_value(channel); | ||
| 216 | let original_enable_state = self.inner.get_channel_enable_state(channel); | ||
| 217 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 218 | |||
| 219 | if !original_update_dma_state { | ||
| 220 | self.inner.enable_update_dma(true); | ||
| 221 | } | ||
| 222 | |||
| 223 | if !original_enable_state { | ||
| 224 | self.inner.enable_channel(channel, true); | ||
| 225 | } | ||
| 226 | |||
| 227 | unsafe { | ||
| 228 | #[cfg(not(any(bdma, gpdma)))] | ||
| 229 | use crate::dma::{Burst, FifoThreshold}; | ||
| 230 | use crate::dma::{Transfer, TransferOptions}; | ||
| 231 | |||
| 232 | let dma_transfer_option = TransferOptions { | ||
| 233 | #[cfg(not(any(bdma, gpdma)))] | ||
| 234 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 235 | #[cfg(not(any(bdma, gpdma)))] | ||
| 236 | mburst: Burst::Incr8, | ||
| 237 | ..Default::default() | ||
| 238 | }; | ||
| 239 | |||
| 240 | Transfer::new_write( | ||
| 241 | dma, | ||
| 242 | req, | ||
| 243 | duty, | ||
| 244 | self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 245 | dma_transfer_option, | ||
| 246 | ) | ||
| 247 | .await | ||
| 248 | }; | ||
| 249 | |||
| 250 | // restore output compare state | ||
| 251 | if !original_enable_state { | ||
| 252 | self.inner.enable_channel(channel, false); | ||
| 253 | } | ||
| 254 | 227 | ||
| 255 | self.inner.set_compare_value(channel, original_duty_state); | 228 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| 229 | /// | ||
| 230 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers | ||
| 231 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the | ||
| 232 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. | ||
| 233 | /// | ||
| 234 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row | ||
| 235 | /// represents a single update event and each column corresponds to a specific timer channel (starting | ||
| 236 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 237 | /// | ||
| 238 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 239 | /// | ||
| 240 | /// ```rust,ignore | ||
| 241 | /// let dma_buf: [u16; 16] = [ | ||
| 242 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 243 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 244 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 245 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 246 | /// ]; | ||
| 247 | /// ``` | ||
| 248 | /// | ||
| 249 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 250 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 251 | /// | ||
| 252 | /// Note: | ||
| 253 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 254 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 255 | /// switch this timer by using `time-driver-timX` feature. | ||
| 256 | /// | ||
| 257 | #[inline(always)] | ||
| 258 | pub async fn waveform_up_multi_channel( | ||
| 259 | &mut self, | ||
| 260 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 261 | starting_channel: Channel, | ||
| 262 | ending_channel: Channel, | ||
| 263 | duty: &[u16], | ||
| 264 | ) { | ||
| 265 | self.inner | ||
| 266 | .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) | ||
| 267 | .await; | ||
| 268 | } | ||
| 256 | 269 | ||
| 257 | // Since DMA is closed before timer update event trigger DMA is turn off, | 270 | /// Generate a sequence of PWM waveform |
| 258 | // this can almost always trigger a DMA FIFO error. | 271 | #[inline(always)] |
| 259 | // | 272 | pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { |
| 260 | // optional TODO: | 273 | self.inner.waveform(dma, duty).await; |
| 261 | // clean FEIF after disable UDE | ||
| 262 | if !original_update_dma_state { | ||
| 263 | self.inner.enable_update_dma(false); | ||
| 264 | } | ||
| 265 | } | 274 | } |
| 266 | } | 275 | } |
| 267 | 276 | ||
| @@ -388,7 +397,7 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) { | |||
| 388 | 397 | ||
| 389 | #[cfg(test)] | 398 | #[cfg(test)] |
| 390 | mod tests { | 399 | mod tests { |
| 391 | use super::{compute_dead_time_value, Ckd}; | 400 | use super::{Ckd, compute_dead_time_value}; |
| 392 | 401 | ||
| 393 | #[test] | 402 | #[test] |
| 394 | fn test_compute_dead_time_value() { | 403 | fn test_compute_dead_time_value() { |
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 7a25e6c21..9cf0f8c34 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs | |||
| @@ -8,11 +8,11 @@ use core::task::{Context, Poll}; | |||
| 8 | use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; | 8 | use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; |
| 9 | use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin}; | 9 | use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin}; |
| 10 | pub use super::{Ch1, Ch2, Ch3, Ch4}; | 10 | pub use super::{Ch1, Ch2, Ch3, Ch4}; |
| 11 | use crate::Peri; | ||
| 11 | use crate::gpio::{AfType, AnyPin, Pull}; | 12 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 12 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 13 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 13 | use crate::time::Hertz; | 14 | use crate::time::Hertz; |
| 14 | use crate::timer::TimerChannel; | 15 | use crate::timer::TimerChannel; |
| 15 | use crate::Peri; | ||
| 16 | 16 | ||
| 17 | /// Capture pin wrapper. | 17 | /// Capture pin wrapper. |
| 18 | /// | 18 | /// |
| @@ -60,6 +60,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 60 | this.inner.set_counting_mode(counting_mode); | 60 | this.inner.set_counting_mode(counting_mode); |
| 61 | this.inner.set_tick_freq(freq); | 61 | this.inner.set_tick_freq(freq); |
| 62 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 62 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 63 | this.inner.generate_update_event(); | ||
| 63 | this.inner.start(); | 64 | this.inner.start(); |
| 64 | 65 | ||
| 65 | // enable NVIC interrupt | 66 | // enable NVIC interrupt |
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index ac039bb0d..f0105ece8 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -10,12 +10,12 @@ use core::mem::ManuallyDrop; | |||
| 10 | 10 | ||
| 11 | use embassy_hal_internal::Peri; | 11 | use embassy_hal_internal::Peri; |
| 12 | // Re-export useful enums | 12 | // Re-export useful enums |
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; |
| 14 | 14 | ||
| 15 | use super::*; | 15 | use super::*; |
| 16 | use crate::pac::timer::vals; | 16 | use crate::pac::timer::vals; |
| 17 | use crate::rcc; | ||
| 18 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 18 | use crate::{dma, rcc}; | ||
| 19 | 19 | ||
| 20 | /// Input capture mode. | 20 | /// Input capture mode. |
| 21 | #[derive(Clone, Copy)] | 21 | #[derive(Clone, Copy)] |
| @@ -143,20 +143,69 @@ pub enum OutputCompareMode { | |||
| 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as | 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as |
| 144 | /// TIMx_CNT>TIMx_CCRx else inactive. | 144 | /// TIMx_CNT>TIMx_CCRx else inactive. |
| 145 | PwmMode2, | 145 | PwmMode2, |
| 146 | // TODO: there's more modes here depending on the chip family. | 146 | |
| 147 | #[cfg(timer_v2)] | ||
| 148 | /// In up-counting mode, the channel is active until a trigger | ||
| 149 | /// event is detected (on tim_trgi signal). Then, a comparison is performed as in PWM | ||
| 150 | /// mode 1 and the channels becomes active again at the next update. In down-counting | ||
| 151 | /// mode, the channel is inactive until a trigger event is detected (on tim_trgi signal). | ||
| 152 | /// Then, a comparison is performed as in PWM mode 1 and the channels becomes | ||
| 153 | /// inactive again at the next update. | ||
| 154 | OnePulseMode1, | ||
| 155 | |||
| 156 | #[cfg(timer_v2)] | ||
| 157 | /// In up-counting mode, the channel is inactive until a | ||
| 158 | /// trigger event is detected (on tim_trgi signal). Then, a comparison is performed as in | ||
| 159 | /// PWM mode 2 and the channels becomes inactive again at the next update. In down | ||
| 160 | /// counting mode, the channel is active until a trigger event is detected (on tim_trgi | ||
| 161 | /// signal). Then, a comparison is performed as in PWM mode 1 and the channels | ||
| 162 | /// becomes active again at the next update. | ||
| 163 | OnePulseMode2, | ||
| 164 | |||
| 165 | #[cfg(timer_v2)] | ||
| 166 | /// Combined PWM mode 1 - tim_oc1ref has the same behavior as in PWM mode 1. | ||
| 167 | /// tim_oc1refc is the logical OR between tim_oc1ref and tim_oc2ref. | ||
| 168 | CombinedPwmMode1, | ||
| 169 | |||
| 170 | #[cfg(timer_v2)] | ||
| 171 | /// Combined PWM mode 2 - tim_oc1ref has the same behavior as in PWM mode 2. | ||
| 172 | /// tim_oc1refc is the logical AND between tim_oc1ref and tim_oc2ref. | ||
| 173 | CombinedPwmMode2, | ||
| 174 | |||
| 175 | #[cfg(timer_v2)] | ||
| 176 | /// tim_oc1ref has the same behavior as in PWM mode 1. tim_oc1refc outputs tim_oc1ref | ||
| 177 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 178 | AsymmetricPwmMode1, | ||
| 179 | |||
| 180 | #[cfg(timer_v2)] | ||
| 181 | /// tim_oc1ref has the same behavior as in PWM mode 2. tim_oc1refc outputs tim_oc1ref | ||
| 182 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 183 | AsymmetricPwmMode2, | ||
| 147 | } | 184 | } |
| 148 | 185 | ||
| 149 | impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { | 186 | impl From<OutputCompareMode> for crate::pac::timer::vals::Ocm { |
| 150 | fn from(mode: OutputCompareMode) -> Self { | 187 | fn from(mode: OutputCompareMode) -> Self { |
| 151 | match mode { | 188 | match mode { |
| 152 | OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, | 189 | OutputCompareMode::Frozen => crate::pac::timer::vals::Ocm::FROZEN, |
| 153 | OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVE_ON_MATCH, | 190 | OutputCompareMode::ActiveOnMatch => crate::pac::timer::vals::Ocm::ACTIVE_ON_MATCH, |
| 154 | OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVE_ON_MATCH, | 191 | OutputCompareMode::InactiveOnMatch => crate::pac::timer::vals::Ocm::INACTIVE_ON_MATCH, |
| 155 | OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, | 192 | OutputCompareMode::Toggle => crate::pac::timer::vals::Ocm::TOGGLE, |
| 156 | OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCE_INACTIVE, | 193 | OutputCompareMode::ForceInactive => crate::pac::timer::vals::Ocm::FORCE_INACTIVE, |
| 157 | OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCE_ACTIVE, | 194 | OutputCompareMode::ForceActive => crate::pac::timer::vals::Ocm::FORCE_ACTIVE, |
| 158 | OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWM_MODE1, | 195 | OutputCompareMode::PwmMode1 => crate::pac::timer::vals::Ocm::PWM_MODE1, |
| 159 | OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWM_MODE2, | 196 | OutputCompareMode::PwmMode2 => crate::pac::timer::vals::Ocm::PWM_MODE2, |
| 197 | #[cfg(timer_v2)] | ||
| 198 | OutputCompareMode::OnePulseMode1 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_1, | ||
| 199 | #[cfg(timer_v2)] | ||
| 200 | OutputCompareMode::OnePulseMode2 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_2, | ||
| 201 | #[cfg(timer_v2)] | ||
| 202 | OutputCompareMode::CombinedPwmMode1 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_1, | ||
| 203 | #[cfg(timer_v2)] | ||
| 204 | OutputCompareMode::CombinedPwmMode2 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_2, | ||
| 205 | #[cfg(timer_v2)] | ||
| 206 | OutputCompareMode::AsymmetricPwmMode1 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_1, | ||
| 207 | #[cfg(timer_v2)] | ||
| 208 | OutputCompareMode::AsymmetricPwmMode2 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_2, | ||
| 160 | } | 209 | } |
| 161 | } | 210 | } |
| 162 | } | 211 | } |
| @@ -223,6 +272,17 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 223 | self.regs_core().cr1().modify(|r| r.set_cen(true)); | 272 | self.regs_core().cr1().modify(|r| r.set_cen(true)); |
| 224 | } | 273 | } |
| 225 | 274 | ||
| 275 | /// Generate timer update event from software. | ||
| 276 | /// | ||
| 277 | /// Set URS to avoid generating interrupt or DMA request. This update event is only | ||
| 278 | /// used to load value from pre-load registers. If called when the timer is running, | ||
| 279 | /// it may disrupt the output waveform. | ||
| 280 | pub fn generate_update_event(&self) { | ||
| 281 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 282 | self.regs_core().egr().write(|r| r.set_ug(true)); | ||
| 283 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 284 | } | ||
| 285 | |||
| 226 | /// Stop the timer. | 286 | /// Stop the timer. |
| 227 | pub fn stop(&self) { | 287 | pub fn stop(&self) { |
| 228 | self.regs_core().cr1().modify(|r| r.set_cen(false)); | 288 | self.regs_core().cr1().modify(|r| r.set_cen(false)); |
| @@ -273,10 +333,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 273 | let regs = self.regs_core(); | 333 | let regs = self.regs_core(); |
| 274 | regs.psc().write_value(psc); | 334 | regs.psc().write_value(psc); |
| 275 | regs.arr().write(|r| r.set_arr(arr)); | 335 | regs.arr().write(|r| r.set_arr(arr)); |
| 276 | |||
| 277 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 278 | regs.egr().write(|r| r.set_ug(true)); | ||
| 279 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 280 | } | 336 | } |
| 281 | #[cfg(not(stm32l0))] | 337 | #[cfg(not(stm32l0))] |
| 282 | TimerBits::Bits32 => { | 338 | TimerBits::Bits32 => { |
| @@ -286,10 +342,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 286 | let regs = self.regs_gp32_unchecked(); | 342 | let regs = self.regs_gp32_unchecked(); |
| 287 | regs.psc().write_value(psc); | 343 | regs.psc().write_value(psc); |
| 288 | regs.arr().write_value(arr); | 344 | regs.arr().write_value(arr); |
| 289 | |||
| 290 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 291 | regs.egr().write(|r| r.set_ug(true)); | ||
| 292 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 293 | } | 345 | } |
| 294 | } | 346 | } |
| 295 | } | 347 | } |
| @@ -607,6 +659,219 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 607 | } | 659 | } |
| 608 | } | 660 | } |
| 609 | 661 | ||
| 662 | /// Generate a sequence of PWM waveform | ||
| 663 | /// | ||
| 664 | /// Note: | ||
| 665 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 666 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | ||
| 667 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 668 | let req = dma.request(); | ||
| 669 | |||
| 670 | let original_update_dma_state = self.get_update_dma_state(); | ||
| 671 | |||
| 672 | if !original_update_dma_state { | ||
| 673 | self.enable_update_dma(true); | ||
| 674 | } | ||
| 675 | |||
| 676 | self.waveform_helper(dma, req, channel, duty).await; | ||
| 677 | |||
| 678 | // Since DMA is closed before timer update event trigger DMA is turn off, | ||
| 679 | // this can almost always trigger a DMA FIFO error. | ||
| 680 | // | ||
| 681 | // optional TODO: | ||
| 682 | // clean FEIF after disable UDE | ||
| 683 | if !original_update_dma_state { | ||
| 684 | self.enable_update_dma(false); | ||
| 685 | } | ||
| 686 | } | ||
| 687 | |||
| 688 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | ||
| 689 | /// | ||
| 690 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers | ||
| 691 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the | ||
| 692 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. | ||
| 693 | /// | ||
| 694 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row | ||
| 695 | /// represents a single update event and each column corresponds to a specific timer channel (starting | ||
| 696 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 697 | /// | ||
| 698 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 699 | /// | ||
| 700 | /// ```rust,ignore | ||
| 701 | /// let dma_buf: [u16; 16] = [ | ||
| 702 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 703 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 704 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 705 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 706 | /// ]; | ||
| 707 | /// ``` | ||
| 708 | /// | ||
| 709 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 710 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 711 | /// | ||
| 712 | /// Note: | ||
| 713 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 714 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 715 | /// switch this timer by using `time-driver-timX` feature. | ||
| 716 | /// | ||
| 717 | pub async fn waveform_up_multi_channel( | ||
| 718 | &mut self, | ||
| 719 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 720 | starting_channel: Channel, | ||
| 721 | ending_channel: Channel, | ||
| 722 | duty: &[u16], | ||
| 723 | ) { | ||
| 724 | let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; | ||
| 725 | let start_ch_index = starting_channel.index(); | ||
| 726 | let end_ch_index = ending_channel.index(); | ||
| 727 | |||
| 728 | assert!(start_ch_index <= end_ch_index); | ||
| 729 | |||
| 730 | let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 731 | self.regs_gp16() | ||
| 732 | .dcr() | ||
| 733 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 734 | self.regs_gp16() | ||
| 735 | .dcr() | ||
| 736 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | ||
| 737 | |||
| 738 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 739 | let req = dma.request(); | ||
| 740 | |||
| 741 | let original_update_dma_state = self.get_update_dma_state(); | ||
| 742 | if !original_update_dma_state { | ||
| 743 | self.enable_update_dma(true); | ||
| 744 | } | ||
| 745 | |||
| 746 | unsafe { | ||
| 747 | #[cfg(not(any(bdma, gpdma)))] | ||
| 748 | use crate::dma::{Burst, FifoThreshold}; | ||
| 749 | use crate::dma::{Transfer, TransferOptions}; | ||
| 750 | |||
| 751 | let dma_transfer_option = TransferOptions { | ||
| 752 | #[cfg(not(any(bdma, gpdma)))] | ||
| 753 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 754 | #[cfg(not(any(bdma, gpdma)))] | ||
| 755 | mburst: Burst::Incr4, | ||
| 756 | ..Default::default() | ||
| 757 | }; | ||
| 758 | |||
| 759 | Transfer::new_write( | ||
| 760 | dma, | ||
| 761 | req, | ||
| 762 | duty, | ||
| 763 | self.regs_gp16().dmar().as_ptr() as *mut u16, | ||
| 764 | dma_transfer_option, | ||
| 765 | ) | ||
| 766 | .await | ||
| 767 | }; | ||
| 768 | |||
| 769 | if !original_update_dma_state { | ||
| 770 | self.enable_update_dma(false); | ||
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 | /// Generate a sequence of PWM waveform | ||
| 775 | pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { | ||
| 776 | use crate::pac::timer::vals::Ccds; | ||
| 777 | |||
| 778 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 779 | let req = dma.request(); | ||
| 780 | |||
| 781 | let cc_channel = C::CHANNEL; | ||
| 782 | |||
| 783 | let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE; | ||
| 784 | let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel); | ||
| 785 | |||
| 786 | // redirect CC DMA request onto Update Event | ||
| 787 | if !original_cc_dma_on_update { | ||
| 788 | self.set_cc_dma_selection(Ccds::ON_UPDATE) | ||
| 789 | } | ||
| 790 | |||
| 791 | if !original_cc_dma_enabled { | ||
| 792 | self.set_cc_dma_enable_state(cc_channel, true); | ||
| 793 | } | ||
| 794 | |||
| 795 | self.waveform_helper(dma, req, cc_channel, duty).await; | ||
| 796 | |||
| 797 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | ||
| 798 | // this can almost always trigger a DMA FIFO error. | ||
| 799 | // | ||
| 800 | // optional TODO: | ||
| 801 | // clean FEIF after disable UDE | ||
| 802 | if !original_cc_dma_enabled { | ||
| 803 | self.set_cc_dma_enable_state(cc_channel, false); | ||
| 804 | } | ||
| 805 | |||
| 806 | if !original_cc_dma_on_update { | ||
| 807 | self.set_cc_dma_selection(Ccds::ON_COMPARE) | ||
| 808 | } | ||
| 809 | } | ||
| 810 | |||
| 811 | async fn waveform_helper( | ||
| 812 | &mut self, | ||
| 813 | dma: Peri<'_, impl dma::Channel>, | ||
| 814 | req: dma::Request, | ||
| 815 | channel: Channel, | ||
| 816 | duty: &[u16], | ||
| 817 | ) { | ||
| 818 | let original_duty_state = self.get_compare_value(channel); | ||
| 819 | let original_enable_state = self.get_channel_enable_state(channel); | ||
| 820 | |||
| 821 | if !original_enable_state { | ||
| 822 | self.enable_channel(channel, true); | ||
| 823 | } | ||
| 824 | |||
| 825 | unsafe { | ||
| 826 | #[cfg(not(any(bdma, gpdma)))] | ||
| 827 | use crate::dma::{Burst, FifoThreshold}; | ||
| 828 | use crate::dma::{Transfer, TransferOptions}; | ||
| 829 | |||
| 830 | let dma_transfer_option = TransferOptions { | ||
| 831 | #[cfg(not(any(bdma, gpdma)))] | ||
| 832 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 833 | #[cfg(not(any(bdma, gpdma)))] | ||
| 834 | mburst: Burst::Incr8, | ||
| 835 | ..Default::default() | ||
| 836 | }; | ||
| 837 | |||
| 838 | match self.bits() { | ||
| 839 | TimerBits::Bits16 => { | ||
| 840 | Transfer::new_write( | ||
| 841 | dma, | ||
| 842 | req, | ||
| 843 | duty, | ||
| 844 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 845 | dma_transfer_option, | ||
| 846 | ) | ||
| 847 | .await | ||
| 848 | } | ||
| 849 | #[cfg(not(any(stm32l0)))] | ||
| 850 | TimerBits::Bits32 => { | ||
| 851 | #[cfg(not(any(bdma, gpdma)))] | ||
| 852 | panic!("unsupported timer bits"); | ||
| 853 | |||
| 854 | #[cfg(any(bdma, gpdma))] | ||
| 855 | Transfer::new_write( | ||
| 856 | dma, | ||
| 857 | req, | ||
| 858 | duty, | ||
| 859 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, | ||
| 860 | dma_transfer_option, | ||
| 861 | ) | ||
| 862 | .await | ||
| 863 | } | ||
| 864 | }; | ||
| 865 | }; | ||
| 866 | |||
| 867 | // restore output compare state | ||
| 868 | if !original_enable_state { | ||
| 869 | self.enable_channel(channel, false); | ||
| 870 | } | ||
| 871 | |||
| 872 | self.set_compare_value(channel, original_duty_state); | ||
| 873 | } | ||
| 874 | |||
| 610 | /// Get capture value for a channel. | 875 | /// Get capture value for a channel. |
| 611 | pub fn get_capture_value(&self, channel: Channel) -> u32 { | 876 | pub fn get_capture_value(&self, channel: Channel) -> u32 { |
| 612 | self.get_compare_value(channel) | 877 | self.get_compare_value(channel) |
| @@ -640,6 +905,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 640 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) | 905 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) |
| 641 | } | 906 | } |
| 642 | 907 | ||
| 908 | /// Set Timer Master Mode | ||
| 909 | pub fn set_master_mode(&self, mms: MasterMode) { | ||
| 910 | self.regs_gp16().cr2().modify(|w| w.set_mms(mms)); | ||
| 911 | } | ||
| 912 | |||
| 643 | /// Set Timer Slave Mode | 913 | /// Set Timer Slave Mode |
| 644 | pub fn set_slave_mode(&self, sms: SlaveMode) { | 914 | pub fn set_slave_mode(&self, sms: SlaveMode) { |
| 645 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); | 915 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); |
| @@ -760,6 +1030,16 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { | |||
| 760 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); | 1030 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); |
| 761 | } | 1031 | } |
| 762 | 1032 | ||
| 1033 | /// Set master mode selection 2 | ||
| 1034 | pub fn set_mms2_selection(&self, mms2: vals::Mms2) { | ||
| 1035 | self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2)); | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | /// Set repetition counter | ||
| 1039 | pub fn set_repetition_counter(&self, val: u16) { | ||
| 1040 | self.regs_advanced().rcr().modify(|w| w.set_rep(val)); | ||
| 1041 | } | ||
| 1042 | |||
| 763 | /// Trigger software break 1 or 2 | 1043 | /// Trigger software break 1 or 2 |
| 764 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. | 1044 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. |
| 765 | pub fn trigger_software_break(&self, n: usize) { | 1045 | pub fn trigger_software_break(&self, n: usize) { |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index b09bc7166..804d1ef37 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -399,7 +399,7 @@ pub struct UpdateInterruptHandler<T: CoreInstance> { | |||
| 399 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { | 399 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { |
| 400 | unsafe fn on_interrupt() { | 400 | unsafe fn on_interrupt() { |
| 401 | #[cfg(feature = "low-power")] | 401 | #[cfg(feature = "low-power")] |
| 402 | crate::low_power::on_wakeup_irq(); | 402 | crate::low_power::Executor::on_wakeup_irq(); |
| 403 | 403 | ||
| 404 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); | 404 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); |
| 405 | 405 | ||
| @@ -429,7 +429,7 @@ impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompare | |||
| 429 | { | 429 | { |
| 430 | unsafe fn on_interrupt() { | 430 | unsafe fn on_interrupt() { |
| 431 | #[cfg(feature = "low-power")] | 431 | #[cfg(feature = "low-power")] |
| 432 | crate::low_power::on_wakeup_irq(); | 432 | crate::low_power::Executor::on_wakeup_irq(); |
| 433 | 433 | ||
| 434 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); | 434 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); |
| 435 | 435 | ||
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index a75b41bd7..fe8681356 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs | |||
| @@ -11,12 +11,12 @@ use super::low_level::{ | |||
| 11 | }; | 11 | }; |
| 12 | use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin}; | 12 | use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin}; |
| 13 | pub use super::{Ch1, Ch2}; | 13 | pub use super::{Ch1, Ch2}; |
| 14 | use crate::Peri; | ||
| 14 | use crate::gpio::{AfType, AnyPin, Pull}; | 15 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 15 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 16 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 16 | use crate::pac::timer::vals::Etp; | 17 | use crate::pac::timer::vals::Etp; |
| 17 | use crate::time::Hertz; | 18 | use crate::time::Hertz; |
| 18 | use crate::timer::TimerChannel; | 19 | use crate::timer::TimerChannel; |
| 19 | use crate::Peri; | ||
| 20 | 20 | ||
| 21 | /// External input marker type. | 21 | /// External input marker type. |
| 22 | pub enum Ext {} | 22 | pub enum Ext {} |
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 159b5a177..057ab011a 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs | |||
| @@ -2,9 +2,9 @@ | |||
| 2 | 2 | ||
| 3 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; | 3 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; |
| 4 | use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin}; | 4 | use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin}; |
| 5 | use crate::Peri; | ||
| 5 | use crate::gpio::{AfType, Pull}; | 6 | use crate::gpio::{AfType, Pull}; |
| 6 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 7 | use crate::Peri; | ||
| 8 | 8 | ||
| 9 | /// PWM Input driver. | 9 | /// PWM Input driver. |
| 10 | /// | 10 | /// |
| @@ -47,6 +47,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | |||
| 47 | inner.set_counting_mode(CountingMode::EdgeAlignedUp); | 47 | inner.set_counting_mode(CountingMode::EdgeAlignedUp); |
| 48 | inner.set_tick_freq(freq); | 48 | inner.set_tick_freq(freq); |
| 49 | inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 49 | inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 50 | inner.generate_update_event(); | ||
| 50 | inner.start(); | 51 | inner.start(); |
| 51 | 52 | ||
| 52 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 | 53 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 |
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index bb152731c..a547a2a19 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs | |||
| @@ -5,9 +5,9 @@ use stm32_metapac::timer::vals::{self, Sms}; | |||
| 5 | use super::low_level::Timer; | 5 | use super::low_level::Timer; |
| 6 | pub use super::{Ch1, Ch2}; | 6 | pub use super::{Ch1, Ch2}; |
| 7 | use super::{GeneralInstance4Channel, TimerPin}; | 7 | use super::{GeneralInstance4Channel, TimerPin}; |
| 8 | use crate::Peri; | ||
| 8 | use crate::gpio::{AfType, AnyPin, Pull}; | 9 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 9 | use crate::timer::TimerChannel; | 10 | use crate::timer::TimerChannel; |
| 10 | use crate::Peri; | ||
| 11 | 11 | ||
| 12 | /// Qei driver config. | 12 | /// Qei driver config. |
| 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index e6165e42b..6c9ef17e0 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -4,12 +4,12 @@ use core::marker::PhantomData; | |||
| 4 | use core::mem::ManuallyDrop; | 4 | use core::mem::ManuallyDrop; |
| 5 | 5 | ||
| 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; | 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; |
| 7 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; | 7 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; |
| 8 | use crate::Peri; | ||
| 8 | #[cfg(gpio_v2)] | 9 | #[cfg(gpio_v2)] |
| 9 | use crate::gpio::Pull; | 10 | use crate::gpio::Pull; |
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 11 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 11 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 12 | use crate::Peri; | ||
| 13 | 13 | ||
| 14 | /// PWM pin wrapper. | 14 | /// PWM pin wrapper. |
| 15 | /// | 15 | /// |
| @@ -198,7 +198,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 198 | this.inner.set_counting_mode(counting_mode); | 198 | this.inner.set_counting_mode(counting_mode); |
| 199 | this.set_frequency(freq); | 199 | this.set_frequency(freq); |
| 200 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 200 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 201 | this.inner.start(); | ||
| 202 | 201 | ||
| 203 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 202 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| 204 | .iter() | 203 | .iter() |
| @@ -207,6 +206,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 207 | 206 | ||
| 208 | this.inner.set_output_compare_preload(channel, true); | 207 | this.inner.set_output_compare_preload(channel, true); |
| 209 | }); | 208 | }); |
| 209 | this.inner.set_autoreload_preload(true); | ||
| 210 | |||
| 211 | // Generate update event so pre-load registers are written to the shadow registers | ||
| 212 | this.inner.generate_update_event(); | ||
| 213 | this.inner.start(); | ||
| 210 | 214 | ||
| 211 | this | 215 | this |
| 212 | } | 216 | } |
| @@ -285,8 +289,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 285 | 289 | ||
| 286 | /// Set PWM frequency. | 290 | /// Set PWM frequency. |
| 287 | /// | 291 | /// |
| 288 | /// Note: when you call this, the max duty value changes, so you will have to | 292 | /// Note: that the frequency will not be applied in the timer until an update event |
| 289 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 293 | /// occurs. |
| 290 | pub fn set_frequency(&mut self, freq: Hertz) { | 294 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 291 | // TODO: prevent ARR = u16::MAX? | 295 | // TODO: prevent ARR = u16::MAX? |
| 292 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 296 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| @@ -309,61 +313,12 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 309 | /// Generate a sequence of PWM waveform | 313 | /// Generate a sequence of PWM waveform |
| 310 | /// | 314 | /// |
| 311 | /// Note: | 315 | /// Note: |
| 312 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 316 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 317 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 318 | /// switch this timer by using `time-driver-timX` feature. | ||
| 319 | #[inline(always)] | ||
| 313 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 320 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { |
| 314 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 321 | self.inner.waveform_up(dma, channel, duty).await; |
| 315 | let req = dma.request(); | ||
| 316 | |||
| 317 | let original_duty_state = self.channel(channel).current_duty_cycle(); | ||
| 318 | let original_enable_state = self.channel(channel).is_enabled(); | ||
| 319 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 320 | |||
| 321 | if !original_update_dma_state { | ||
| 322 | self.inner.enable_update_dma(true); | ||
| 323 | } | ||
| 324 | |||
| 325 | if !original_enable_state { | ||
| 326 | self.channel(channel).enable(); | ||
| 327 | } | ||
| 328 | |||
| 329 | unsafe { | ||
| 330 | #[cfg(not(any(bdma, gpdma)))] | ||
| 331 | use crate::dma::{Burst, FifoThreshold}; | ||
| 332 | use crate::dma::{Transfer, TransferOptions}; | ||
| 333 | |||
| 334 | let dma_transfer_option = TransferOptions { | ||
| 335 | #[cfg(not(any(bdma, gpdma)))] | ||
| 336 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 337 | #[cfg(not(any(bdma, gpdma)))] | ||
| 338 | mburst: Burst::Incr8, | ||
| 339 | ..Default::default() | ||
| 340 | }; | ||
| 341 | |||
| 342 | Transfer::new_write( | ||
| 343 | dma, | ||
| 344 | req, | ||
| 345 | duty, | ||
| 346 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 347 | dma_transfer_option, | ||
| 348 | ) | ||
| 349 | .await | ||
| 350 | }; | ||
| 351 | |||
| 352 | // restore output compare state | ||
| 353 | if !original_enable_state { | ||
| 354 | self.channel(channel).disable(); | ||
| 355 | } | ||
| 356 | |||
| 357 | self.channel(channel).set_duty_cycle(original_duty_state); | ||
| 358 | |||
| 359 | // Since DMA is closed before timer update event trigger DMA is turn off, | ||
| 360 | // this can almost always trigger a DMA FIFO error. | ||
| 361 | // | ||
| 362 | // optional TODO: | ||
| 363 | // clean FEIF after disable UDE | ||
| 364 | if !original_update_dma_state { | ||
| 365 | self.inner.enable_update_dma(false); | ||
| 366 | } | ||
| 367 | } | 322 | } |
| 368 | 323 | ||
| 369 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | 324 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| @@ -378,18 +333,24 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 378 | /// | 333 | /// |
| 379 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | 334 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: |
| 380 | /// | 335 | /// |
| 336 | /// ```rust,ignore | ||
| 381 | /// let dma_buf: [u16; 16] = [ | 337 | /// let dma_buf: [u16; 16] = [ |
| 382 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | 338 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 |
| 383 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | 339 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 |
| 384 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | 340 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 |
| 385 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | 341 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 |
| 386 | /// ]; | 342 | /// ]; |
| 343 | /// ``` | ||
| 387 | /// | 344 | /// |
| 388 | /// Each group of N values (where N = number of channels) is transferred on one update event, | 345 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, |
| 389 | /// updating the duty cycles of all selected channels simultaneously. | 346 | /// updating the duty cycles of all selected channels simultaneously. |
| 390 | /// | 347 | /// |
| 391 | /// Note: | 348 | /// Note: |
| 392 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 349 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 350 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 351 | /// switch this timer by using `time-driver-timX` feature. | ||
| 352 | /// | ||
| 353 | #[inline(always)] | ||
| 393 | pub async fn waveform_up_multi_channel( | 354 | pub async fn waveform_up_multi_channel( |
| 394 | &mut self, | 355 | &mut self, |
| 395 | dma: Peri<'_, impl super::UpDma<T>>, | 356 | dma: Peri<'_, impl super::UpDma<T>>, |
| @@ -397,148 +358,15 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 397 | ending_channel: Channel, | 358 | ending_channel: Channel, |
| 398 | duty: &[u16], | 359 | duty: &[u16], |
| 399 | ) { | 360 | ) { |
| 400 | let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; | ||
| 401 | let start_ch_index = starting_channel.index(); | ||
| 402 | let end_ch_index = ending_channel.index(); | ||
| 403 | |||
| 404 | assert!(start_ch_index <= end_ch_index); | ||
| 405 | |||
| 406 | let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 407 | self.inner | ||
| 408 | .regs_gp16() | ||
| 409 | .dcr() | ||
| 410 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 411 | self.inner | 361 | self.inner |
| 412 | .regs_gp16() | 362 | .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) |
| 413 | .dcr() | 363 | .await; |
| 414 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | ||
| 415 | |||
| 416 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 417 | let req = dma.request(); | ||
| 418 | |||
| 419 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 420 | if !original_update_dma_state { | ||
| 421 | self.inner.enable_update_dma(true); | ||
| 422 | } | ||
| 423 | |||
| 424 | unsafe { | ||
| 425 | #[cfg(not(any(bdma, gpdma)))] | ||
| 426 | use crate::dma::{Burst, FifoThreshold}; | ||
| 427 | use crate::dma::{Transfer, TransferOptions}; | ||
| 428 | |||
| 429 | let dma_transfer_option = TransferOptions { | ||
| 430 | #[cfg(not(any(bdma, gpdma)))] | ||
| 431 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 432 | #[cfg(not(any(bdma, gpdma)))] | ||
| 433 | mburst: Burst::Incr4, | ||
| 434 | ..Default::default() | ||
| 435 | }; | ||
| 436 | |||
| 437 | Transfer::new_write( | ||
| 438 | dma, | ||
| 439 | req, | ||
| 440 | duty, | ||
| 441 | self.inner.regs_gp16().dmar().as_ptr() as *mut u16, | ||
| 442 | dma_transfer_option, | ||
| 443 | ) | ||
| 444 | .await | ||
| 445 | }; | ||
| 446 | |||
| 447 | if !original_update_dma_state { | ||
| 448 | self.inner.enable_update_dma(false); | ||
| 449 | } | ||
| 450 | } | 364 | } |
| 451 | } | ||
| 452 | 365 | ||
| 453 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | ||
| 454 | /// Generate a sequence of PWM waveform | 366 | /// Generate a sequence of PWM waveform |
| 367 | #[inline(always)] | ||
| 455 | pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { | 368 | pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { |
| 456 | use crate::pac::timer::vals::Ccds; | 369 | self.inner.waveform(dma, duty).await; |
| 457 | |||
| 458 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 459 | let req = dma.request(); | ||
| 460 | |||
| 461 | let cc_channel = C::CHANNEL; | ||
| 462 | |||
| 463 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); | ||
| 464 | let original_enable_state = self.channel(cc_channel).is_enabled(); | ||
| 465 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; | ||
| 466 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | ||
| 467 | |||
| 468 | // redirect CC DMA request onto Update Event | ||
| 469 | if !original_cc_dma_on_update { | ||
| 470 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) | ||
| 471 | } | ||
| 472 | |||
| 473 | if !original_cc_dma_enabled { | ||
| 474 | self.inner.set_cc_dma_enable_state(cc_channel, true); | ||
| 475 | } | ||
| 476 | |||
| 477 | if !original_enable_state { | ||
| 478 | self.channel(cc_channel).enable(); | ||
| 479 | } | ||
| 480 | |||
| 481 | unsafe { | ||
| 482 | #[cfg(not(any(bdma, gpdma)))] | ||
| 483 | use crate::dma::{Burst, FifoThreshold}; | ||
| 484 | use crate::dma::{Transfer, TransferOptions}; | ||
| 485 | |||
| 486 | let dma_transfer_option = TransferOptions { | ||
| 487 | #[cfg(not(any(bdma, gpdma)))] | ||
| 488 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 489 | #[cfg(not(any(bdma, gpdma)))] | ||
| 490 | mburst: Burst::Incr8, | ||
| 491 | ..Default::default() | ||
| 492 | }; | ||
| 493 | |||
| 494 | match self.inner.bits() { | ||
| 495 | TimerBits::Bits16 => { | ||
| 496 | Transfer::new_write( | ||
| 497 | dma, | ||
| 498 | req, | ||
| 499 | duty, | ||
| 500 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, | ||
| 501 | dma_transfer_option, | ||
| 502 | ) | ||
| 503 | .await | ||
| 504 | } | ||
| 505 | #[cfg(not(any(stm32l0)))] | ||
| 506 | TimerBits::Bits32 => { | ||
| 507 | #[cfg(not(any(bdma, gpdma)))] | ||
| 508 | panic!("unsupported timer bits"); | ||
| 509 | |||
| 510 | #[cfg(any(bdma, gpdma))] | ||
| 511 | Transfer::new_write( | ||
| 512 | dma, | ||
| 513 | req, | ||
| 514 | duty, | ||
| 515 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, | ||
| 516 | dma_transfer_option, | ||
| 517 | ) | ||
| 518 | .await | ||
| 519 | } | ||
| 520 | }; | ||
| 521 | }; | ||
| 522 | |||
| 523 | // restore output compare state | ||
| 524 | if !original_enable_state { | ||
| 525 | self.channel(cc_channel).disable(); | ||
| 526 | } | ||
| 527 | |||
| 528 | self.channel(cc_channel).set_duty_cycle(original_duty_state); | ||
| 529 | |||
| 530 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | ||
| 531 | // this can almost always trigger a DMA FIFO error. | ||
| 532 | // | ||
| 533 | // optional TODO: | ||
| 534 | // clean FEIF after disable UDE | ||
| 535 | if !original_cc_dma_enabled { | ||
| 536 | self.inner.set_cc_dma_enable_state(cc_channel, false); | ||
| 537 | } | ||
| 538 | |||
| 539 | if !original_cc_dma_on_update { | ||
| 540 | self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) | ||
| 541 | } | ||
| 542 | } | 370 | } |
| 543 | } | 371 | } |
| 544 | 372 | ||
diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs index 7d6442b48..097cf7942 100644 --- a/embassy-stm32/src/tsc/acquisition_banks.rs +++ b/embassy-stm32/src/tsc/acquisition_banks.rs | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | use super::TSC_NUM_GROUPS; | ||
| 1 | use super::io_pin::*; | 2 | use super::io_pin::*; |
| 2 | #[cfg(any(tsc_v2, tsc_v3))] | 3 | #[cfg(any(tsc_v2, tsc_v3))] |
| 3 | use super::pin_groups::G7; | 4 | use super::pin_groups::G7; |
| 4 | #[cfg(tsc_v3)] | 5 | #[cfg(tsc_v3)] |
| 5 | use super::pin_groups::G8; | 6 | use super::pin_groups::G8; |
| 6 | use super::pin_groups::{pin_roles, G1, G2, G3, G4, G5, G6}; | 7 | use super::pin_groups::{G1, G2, G3, G4, G5, G6, pin_roles}; |
| 7 | use super::types::{Group, GroupStatus}; | 8 | use super::types::{Group, GroupStatus}; |
| 8 | use super::TSC_NUM_GROUPS; | ||
| 9 | 9 | ||
| 10 | /// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank. | 10 | /// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank. |
| 11 | /// | 11 | /// |
diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs index 84421f7ff..9347e6bc0 100644 --- a/embassy-stm32/src/tsc/pin_groups.rs +++ b/embassy-stm32/src/tsc/pin_groups.rs | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::ops::BitOr; | 2 | use core::ops::BitOr; |
| 3 | 3 | ||
| 4 | use super::Instance; | ||
| 4 | use super::errors::GroupError; | 5 | use super::errors::GroupError; |
| 5 | use super::io_pin::*; | 6 | use super::io_pin::*; |
| 6 | use super::Instance; | ||
| 7 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | ||
| 8 | use crate::Peri; | 7 | use crate::Peri; |
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | ||
| 9 | 9 | ||
| 10 | /// Pin type definition to control IO parameters | 10 | /// Pin type definition to control IO parameters |
| 11 | #[derive(PartialEq, Clone, Copy)] | 11 | #[derive(PartialEq, Clone, Copy)] |
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 18aff4fbd..ae86d28f0 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs | |||
| @@ -19,8 +19,8 @@ use core::marker::PhantomData; | |||
| 19 | use core::sync::atomic::{AtomicBool, Ordering}; | 19 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 20 | use core::task::Poll; | 20 | use core::task::Poll; |
| 21 | 21 | ||
| 22 | use embassy_hal_internal::drop::OnDrop; | ||
| 23 | use embassy_hal_internal::PeripheralType; | 22 | use embassy_hal_internal::PeripheralType; |
| 23 | use embassy_hal_internal::drop::OnDrop; | ||
| 24 | use embassy_sync::waitqueue::AtomicWaker; | 24 | use embassy_sync::waitqueue::AtomicWaker; |
| 25 | 25 | ||
| 26 | use crate::dma::{ChannelAndRequest, TransferOptions}; | 26 | use crate::dma::{ChannelAndRequest, TransferOptions}; |
| @@ -28,11 +28,11 @@ use crate::interrupt::typelevel::Interrupt; | |||
| 28 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; | 28 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; |
| 29 | pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState}; | 29 | pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState}; |
| 30 | use crate::rcc::{self, RccPeripheral}; | 30 | use crate::rcc::{self, RccPeripheral}; |
| 31 | use crate::{interrupt, Peri}; | 31 | use crate::{Peri, interrupt}; |
| 32 | 32 | ||
| 33 | pub(crate) fn init( | 33 | pub(crate) fn init( |
| 34 | _cs: critical_section::CriticalSection, | 34 | _cs: critical_section::CriticalSection, |
| 35 | #[cfg(peri_ucpd1)] ucpd1_db_enable: bool, | 35 | #[cfg(all(peri_ucpd1, not(stm32n6)))] ucpd1_db_enable: bool, |
| 36 | #[cfg(peri_ucpd2)] ucpd2_db_enable: bool, | 36 | #[cfg(peri_ucpd2)] ucpd2_db_enable: bool, |
| 37 | ) { | 37 | ) { |
| 38 | #[cfg(stm32g0x1)] | 38 | #[cfg(stm32g0x1)] |
| @@ -349,6 +349,7 @@ impl<'d, T: Instance> CcPhy<'d, T> { | |||
| 349 | critical_section::with(|cs| { | 349 | critical_section::with(|cs| { |
| 350 | init( | 350 | init( |
| 351 | cs, | 351 | cs, |
| 352 | #[cfg(not(stm32n6))] | ||
| 352 | false, | 353 | false, |
| 353 | #[cfg(peri_ucpd2)] | 354 | #[cfg(peri_ucpd2)] |
| 354 | false, | 355 | false, |
diff --git a/embassy-stm32/src/uid.rs b/embassy-stm32/src/uid.rs index 5e38532bd..2d3e2b972 100644 --- a/embassy-stm32/src/uid.rs +++ b/embassy-stm32/src/uid.rs | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | //! Unique ID (UID) | 1 | //! Unique ID (UID) |
| 2 | 2 | ||
| 3 | /// Get this device's unique 96-bit ID. | 3 | /// Get this device's unique 96-bit ID. |
| 4 | pub fn uid() -> &'static [u8; 12] { | 4 | pub fn uid() -> [u8; 12] { |
| 5 | unsafe { &*crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() } | 5 | unsafe { *crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() } |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | /// Get this device's unique 96-bit ID, encoded into a string of 24 hexadecimal ASCII digits. | 8 | /// Get this device's unique 96-bit ID, encoded into a string of 24 hexadecimal ASCII digits. |
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 10dc02334..26d2b8991 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -5,16 +5,16 @@ use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}; | |||
| 5 | use core::task::Poll; | 5 | use core::task::Poll; |
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| 8 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | ||
| 9 | use embassy_hal_internal::Peri; | 8 | use embassy_hal_internal::Peri; |
| 9 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | ||
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | 11 | ||
| 12 | #[cfg(not(any(usart_v1, usart_v2)))] | 12 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 13 | use super::DePin; | 13 | use super::DePin; |
| 14 | use super::{ | 14 | use super::{ |
| 15 | Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexReadback, Info, Instance, Regs, RtsPin, RxPin, TxPin, | ||
| 15 | clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate, | 16 | clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate, |
| 16 | sr, tdr, Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexReadback, Info, Instance, Regs, RtsPin, RxPin, | 17 | sr, tdr, |
| 17 | TxPin, | ||
| 18 | }; | 18 | }; |
| 19 | use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; | 19 | use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; |
| 20 | use crate::interrupt::{self, InterruptExt}; | 20 | use crate::interrupt::{self, InterruptExt}; |
| @@ -87,7 +87,7 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { | |||
| 87 | // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) | 87 | // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) |
| 88 | // indicates that all bytes are pushed out from the FIFO. | 88 | // indicates that all bytes are pushed out from the FIFO. |
| 89 | // For other usart variants it shows that last byte from the buffer was just sent. | 89 | // For other usart variants it shows that last byte from the buffer was just sent. |
| 90 | if sr_val.tc() { | 90 | if sr_val.tc() && r.cr1().read().tcie() { |
| 91 | // For others it is cleared above with `clear_interrupt_flags`. | 91 | // For others it is cleared above with `clear_interrupt_flags`. |
| 92 | #[cfg(any(usart_v1, usart_v2))] | 92 | #[cfg(any(usart_v1, usart_v2))] |
| 93 | sr(r).modify(|w| w.set_tc(false)); | 93 | sr(r).modify(|w| w.set_tc(false)); |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0d2d86aca..0e7da634d 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -4,15 +4,16 @@ | |||
| 4 | 4 | ||
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; | 7 | use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering, compiler_fence}; |
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| 11 | use embassy_hal_internal::drop::OnDrop; | ||
| 12 | use embassy_hal_internal::PeripheralType; | 11 | use embassy_hal_internal::PeripheralType; |
| 12 | use embassy_hal_internal::drop::OnDrop; | ||
| 13 | use embassy_sync::waitqueue::AtomicWaker; | 13 | use embassy_sync::waitqueue::AtomicWaker; |
| 14 | use futures_util::future::{select, Either}; | 14 | use futures_util::future::{Either, select}; |
| 15 | 15 | ||
| 16 | use crate::Peri; | ||
| 16 | use crate::dma::ChannelAndRequest; | 17 | use crate::dma::ChannelAndRequest; |
| 17 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 18 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 18 | use crate::interrupt::typelevel::Interrupt as _; | 19 | use crate::interrupt::typelevel::Interrupt as _; |
| @@ -25,7 +26,6 @@ use crate::pac::usart::Usart as Regs; | |||
| 25 | use crate::pac::usart::{regs, vals}; | 26 | use crate::pac::usart::{regs, vals}; |
| 26 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 27 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 27 | use crate::time::Hertz; | 28 | use crate::time::Hertz; |
| 28 | use crate::Peri; | ||
| 29 | 29 | ||
| 30 | /// Interrupt handler. | 30 | /// Interrupt handler. |
| 31 | pub struct InterruptHandler<T: Instance> { | 31 | pub struct InterruptHandler<T: Instance> { |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 27071fb31..bac570d27 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -1,19 +1,21 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 3 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_embedded_hal::SetConfig; | 6 | use embassy_embedded_hal::SetConfig; |
| 7 | use embedded_io_async::ReadReady; | 7 | use embedded_io_async::ReadReady; |
| 8 | use futures_util::future::{select, Either}; | 8 | use futures_util::future::{Either, select}; |
| 9 | 9 | ||
| 10 | use super::{rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx}; | 10 | use super::{ |
| 11 | Config, ConfigError, Error, Info, State, UartRx, clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr, | ||
| 12 | }; | ||
| 13 | use crate::Peri; | ||
| 11 | use crate::dma::ReadableRingBuffer; | 14 | use crate::dma::ReadableRingBuffer; |
| 12 | use crate::gpio::{AnyPin, SealedPin as _}; | 15 | use crate::gpio::{AnyPin, SealedPin as _}; |
| 13 | use crate::mode::Async; | 16 | use crate::mode::Async; |
| 14 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 15 | use crate::usart::Regs; | 18 | use crate::usart::Regs; |
| 16 | use crate::Peri; | ||
| 17 | 19 | ||
| 18 | /// Rx-only Ring-buffered UART Driver | 20 | /// Rx-only Ring-buffered UART Driver |
| 19 | /// | 21 | /// |
| @@ -338,26 +340,16 @@ impl Drop for RingBufferedUartRx<'_> { | |||
| 338 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags | 340 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags |
| 339 | /// are cleared by a single read to the RDR register. | 341 | /// are cleared by a single read to the RDR register. |
| 340 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { | 342 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { |
| 341 | // Critical section is required so that the flags aren't set after read and before clear | 343 | // SAFETY: read only and we only use Rx related flags |
| 342 | let sr = critical_section::with(|_| { | 344 | let sr = sr(r).read(); |
| 343 | // SAFETY: read only and we only use Rx related flags | 345 | |
| 344 | let sr = sr(r).read(); | 346 | #[cfg(not(any(usart_v3, usart_v4)))] |
| 345 | 347 | unsafe { | |
| 346 | #[cfg(any(usart_v3, usart_v4))] | 348 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) |
| 347 | r.icr().write(|w| { | 349 | rdr(r).read_volatile() |
| 348 | w.set_idle(true); | 350 | }; |
| 349 | w.set_pe(true); | 351 | clear_interrupt_flags(r, sr); |
| 350 | w.set_fe(true); | 352 | |
| 351 | w.set_ne(true); | ||
| 352 | w.set_ore(true); | ||
| 353 | }); | ||
| 354 | #[cfg(not(any(usart_v3, usart_v4)))] | ||
| 355 | unsafe { | ||
| 356 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) | ||
| 357 | rdr(r).read_volatile() | ||
| 358 | }; | ||
| 359 | sr | ||
| 360 | }); | ||
| 361 | if sr.pe() { | 353 | if sr.pe() { |
| 362 | Err(Error::Parity) | 354 | Err(Error::Parity) |
| 363 | } else if sr.fe() { | 355 | } else if sr.fe() { |
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 5ce81b131..f6b1a81db 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs | |||
| @@ -2,18 +2,18 @@ use core::marker::PhantomData; | |||
| 2 | 2 | ||
| 3 | use embassy_hal_internal::PeripheralType; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | use embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported}; | 4 | use embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported}; |
| 5 | use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; | ||
| 6 | use embassy_usb_synopsys_otg::otg_v1::Otg; | ||
| 7 | pub use embassy_usb_synopsys_otg::Config; | 5 | pub use embassy_usb_synopsys_otg::Config; |
| 6 | use embassy_usb_synopsys_otg::otg_v1::Otg; | ||
| 7 | use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; | ||
| 8 | use embassy_usb_synopsys_otg::{ | 8 | use embassy_usb_synopsys_otg::{ |
| 9 | on_interrupt as on_interrupt_impl, Bus as OtgBus, ControlPipe, Driver as OtgDriver, Endpoint, In, OtgInstance, Out, | 9 | Bus as OtgBus, ControlPipe, Driver as OtgDriver, Endpoint, In, OtgInstance, Out, PhyType, State, |
| 10 | PhyType, State, | 10 | on_interrupt as on_interrupt_impl, |
| 11 | }; | 11 | }; |
| 12 | 12 | ||
| 13 | use crate::gpio::{AfType, OutputType, Speed}; | 13 | use crate::gpio::{AfType, OutputType, Speed}; |
| 14 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::rcc::{self, RccPeripheral}; | 15 | use crate::rcc::{self, RccPeripheral}; |
| 16 | use crate::{interrupt, Peri}; | 16 | use crate::{Peri, interrupt}; |
| 17 | 17 | ||
| 18 | const MAX_EP_COUNT: usize = 9; | 18 | const MAX_EP_COUNT: usize = 9; |
| 19 | 19 | ||
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 9e08d99b3..d405e4802 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs | |||
| @@ -12,11 +12,11 @@ use embassy_usb_driver::{ | |||
| 12 | Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, | 12 | Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| 15 | use crate::pac::USBRAM; | ||
| 15 | use crate::pac::usb::regs; | 16 | use crate::pac::usb::regs; |
| 16 | use crate::pac::usb::vals::{EpType, Stat}; | 17 | use crate::pac::usb::vals::{EpType, Stat}; |
| 17 | use crate::pac::USBRAM; | ||
| 18 | use crate::rcc::RccPeripheral; | 18 | use crate::rcc::RccPeripheral; |
| 19 | use crate::{interrupt, Peri}; | 19 | use crate::{Peri, interrupt}; |
| 20 | 20 | ||
| 21 | /// Interrupt handler. | 21 | /// Interrupt handler. |
| 22 | pub struct InterruptHandler<T: Instance> { | 22 | pub struct InterruptHandler<T: Instance> { |
diff --git a/embassy-stm32/src/vrefbuf/mod.rs b/embassy-stm32/src/vrefbuf/mod.rs index ccbd748d5..43dd9c800 100644 --- a/embassy-stm32/src/vrefbuf/mod.rs +++ b/embassy-stm32/src/vrefbuf/mod.rs | |||
| @@ -14,10 +14,10 @@ pub struct VoltageReferenceBuffer<'d, T: Instance> { | |||
| 14 | #[cfg(rcc_wba)] | 14 | #[cfg(rcc_wba)] |
| 15 | fn get_refbuf_trim(voltage_scale: Vrs) -> usize { | 15 | fn get_refbuf_trim(voltage_scale: Vrs) -> usize { |
| 16 | match voltage_scale { | 16 | match voltage_scale { |
| 17 | Vrs::VREF0 => 0x0BFA_07A8usize, | 17 | Vrs::VREF0 => 0x0BFA_07ABusize, |
| 18 | Vrs::VREF1 => 0x0BFA_07A9usize, | 18 | Vrs::VREF1 => 0x0BFA_07AAusize, |
| 19 | Vrs::VREF2 => 0x0BFA_07AAusize, | 19 | Vrs::VREF2 => 0x0BFA_07A9usize, |
| 20 | Vrs::VREF3 => 0x0BFA_07ABusize, | 20 | Vrs::VREF3 => 0x0BFA_07A8usize, |
| 21 | _ => panic!("Incorrect Vrs setting!"), | 21 | _ => panic!("Incorrect Vrs setting!"), |
| 22 | } | 22 | } |
| 23 | } | 23 | } |
| @@ -62,8 +62,7 @@ impl<'d, T: Instance> VoltageReferenceBuffer<'d, T> { | |||
| 62 | } | 62 | } |
| 63 | trace!( | 63 | trace!( |
| 64 | "Vrefbuf configured with voltage scale {} and impedance mode {}", | 64 | "Vrefbuf configured with voltage scale {} and impedance mode {}", |
| 65 | voltage_scale as u8, | 65 | voltage_scale as u8, impedance_mode as u8, |
| 66 | impedance_mode as u8, | ||
| 67 | ); | 66 | ); |
| 68 | VoltageReferenceBuffer { vrefbuf: PhantomData } | 67 | VoltageReferenceBuffer { vrefbuf: PhantomData } |
| 69 | } | 68 | } |
diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index fb5c3d930..1164739ff 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs | |||
| @@ -4,8 +4,8 @@ use core::marker::PhantomData; | |||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | use stm32_metapac::iwdg::vals::{Key, Pr}; | 5 | use stm32_metapac::iwdg::vals::{Key, Pr}; |
| 6 | 6 | ||
| 7 | use crate::rcc::LSI_FREQ; | ||
| 8 | use crate::Peri; | 7 | use crate::Peri; |
| 8 | use crate::rcc::LSI_FREQ; | ||
| 9 | 9 | ||
| 10 | /// Independent watchdog (IWDG) driver. | 10 | /// Independent watchdog (IWDG) driver. |
| 11 | pub struct IndependentWatchdog<'d, T: Instance> { | 11 | pub struct IndependentWatchdog<'d, T: Instance> { |
diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 6f224ab99..466e1a9b4 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs | |||
| @@ -11,15 +11,15 @@ use embassy_embedded_hal::{GetConfig, SetConfig}; | |||
| 11 | use embassy_hal_internal::PeripheralType; | 11 | use embassy_hal_internal::PeripheralType; |
| 12 | pub use enums::*; | 12 | pub use enums::*; |
| 13 | 13 | ||
| 14 | use crate::dma::{word, ChannelAndRequest}; | 14 | use crate::dma::{ChannelAndRequest, word}; |
| 15 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 15 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 16 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 16 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 17 | use crate::pac::xspi::vals::*; | ||
| 18 | use crate::pac::xspi::Xspi as Regs; | 17 | use crate::pac::xspi::Xspi as Regs; |
| 18 | use crate::pac::xspi::vals::*; | ||
| 19 | #[cfg(xspim_v1)] | 19 | #[cfg(xspim_v1)] |
| 20 | use crate::pac::xspim::Xspim; | 20 | use crate::pac::xspim::Xspim; |
| 21 | use crate::rcc::{self, RccPeripheral}; | 21 | use crate::rcc::{self, RccPeripheral}; |
| 22 | use crate::{peripherals, Peri}; | 22 | use crate::{Peri, peripherals}; |
| 23 | 23 | ||
| 24 | /// XPSI driver config. | 24 | /// XPSI driver config. |
| 25 | #[derive(Clone, Copy)] | 25 | #[derive(Clone, Copy)] |
