diff options
| author | Elias Hanelt <[email protected]> | 2025-11-25 13:18:00 -0800 |
|---|---|---|
| committer | Elias Hanelt <[email protected]> | 2025-11-25 13:18:00 -0800 |
| commit | 2b219b7cb59ec3e4370edc88538ea3ea996f37b9 (patch) | |
| tree | c602a4b7c39d50ada628f2d715c6cc59f2e35c3a /embassy-stm32/src | |
| parent | 576fb23faabf6df7f2c9ed2039e94d3586a3788f (diff) | |
| parent | 906eaee53f84381dd10583894edf2de67275f083 (diff) | |
Merge remote-tracking branch 'origin/main' into feature/spi-bidi
Diffstat (limited to 'embassy-stm32/src')
54 files changed, 4886 insertions, 3576 deletions
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index befa8ed4a..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::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; | 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)] |
| @@ -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,15 +231,15 @@ 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 { |
| @@ -214,20 +249,6 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 214 | ); | 249 | ); |
| 215 | } | 250 | } |
| 216 | 251 | ||
| 217 | let mut s = Self { adc }; | ||
| 218 | |||
| 219 | s.power_up(); | ||
| 220 | |||
| 221 | s.calibrate(); | ||
| 222 | blocking_delay_us(1); | ||
| 223 | |||
| 224 | s.enable(); | ||
| 225 | s.configure(); | ||
| 226 | |||
| 227 | s | ||
| 228 | } | ||
| 229 | |||
| 230 | fn power_up(&mut self) { | ||
| 231 | T::regs().isr().modify(|w| { | 252 | T::regs().isr().modify(|w| { |
| 232 | w.set_ldordy(true); | 253 | w.set_ldordy(true); |
| 233 | }); | 254 | }); |
| @@ -239,22 +260,15 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 239 | T::regs().isr().modify(|w| { | 260 | T::regs().isr().modify(|w| { |
| 240 | w.set_ldordy(true); | 261 | w.set_ldordy(true); |
| 241 | }); | 262 | }); |
| 242 | } | ||
| 243 | 263 | ||
| 244 | fn calibrate(&mut self) { | ||
| 245 | T::regs().cr().modify(|w| w.set_adcal(true)); | 264 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 246 | while T::regs().cr().read().adcal() {} | 265 | while T::regs().cr().read().adcal() {} |
| 247 | T::regs().isr().modify(|w| w.set_eocal(true)); | 266 | T::regs().isr().modify(|w| w.set_eocal(true)); |
| 248 | } | ||
| 249 | 267 | ||
| 250 | fn enable(&mut self) { | 268 | blocking_delay_us(1); |
| 251 | T::regs().isr().write(|w| w.set_adrdy(true)); | 269 | |
| 252 | T::regs().cr().modify(|w| w.set_aden(true)); | 270 | T::enable(); |
| 253 | while !T::regs().isr().read().adrdy() {} | ||
| 254 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 255 | } | ||
| 256 | 271 | ||
| 257 | fn configure(&mut self) { | ||
| 258 | // single conversion mode, software trigger | 272 | // single conversion mode, software trigger |
| 259 | T::regs().cfgr1().modify(|w| { | 273 | T::regs().cfgr1().modify(|w| { |
| 260 | #[cfg(stm32u5)] | 274 | #[cfg(stm32u5)] |
| @@ -280,61 +294,63 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 280 | w.set_smpsel(i, Smpsel::SMP1); | 294 | w.set_smpsel(i, Smpsel::SMP1); |
| 281 | } | 295 | } |
| 282 | }); | 296 | }); |
| 297 | |||
| 298 | Self { adc } | ||
| 283 | } | 299 | } |
| 284 | 300 | ||
| 285 | /// Enable reading the voltage reference internal channel. | 301 | /// Enable reading the voltage reference internal channel. |
| 286 | pub fn enable_vrefint(&self) -> VrefInt { | 302 | pub fn enable_vrefint_adc4(&self) -> super::VrefInt { |
| 287 | T::regs().ccr().modify(|w| { | 303 | T::regs().ccr().modify(|w| { |
| 288 | w.set_vrefen(true); | 304 | w.set_vrefen(true); |
| 289 | }); | 305 | }); |
| 290 | 306 | ||
| 291 | VrefInt {} | 307 | super::VrefInt {} |
| 292 | } | 308 | } |
| 293 | 309 | ||
| 294 | /// Enable reading the temperature internal channel. | 310 | /// Enable reading the temperature internal channel. |
| 295 | pub fn enable_temperature(&self) -> Temperature { | 311 | pub fn enable_temperature_adc4(&self) -> super::Temperature { |
| 296 | T::regs().ccr().modify(|w| { | 312 | T::regs().ccr().modify(|w| { |
| 297 | w.set_vsensesel(true); | 313 | w.set_vsensesel(true); |
| 298 | }); | 314 | }); |
| 299 | 315 | ||
| 300 | Temperature {} | 316 | super::Temperature {} |
| 301 | } | 317 | } |
| 302 | 318 | ||
| 303 | /// Enable reading the vbat internal channel. | 319 | /// Enable reading the vbat internal channel. |
| 304 | #[cfg(stm32u5)] | 320 | #[cfg(stm32u5)] |
| 305 | pub fn enable_vbat(&self) -> Vbat { | 321 | pub fn enable_vbat_adc4(&self) -> super::Vbat { |
| 306 | T::regs().ccr().modify(|w| { | 322 | T::regs().ccr().modify(|w| { |
| 307 | w.set_vbaten(true); | 323 | w.set_vbaten(true); |
| 308 | }); | 324 | }); |
| 309 | 325 | ||
| 310 | Vbat {} | 326 | super::Vbat {} |
| 311 | } | 327 | } |
| 312 | 328 | ||
| 313 | /// Enable reading the vbat internal channel. | 329 | /// Enable reading the vbat internal channel. |
| 314 | pub fn enable_vcore(&self) -> Vcore { | 330 | pub fn enable_vcore_adc4(&self) -> super::Vcore { |
| 315 | Vcore {} | 331 | super::Vcore {} |
| 316 | } | 332 | } |
| 317 | 333 | ||
| 318 | /// Enable reading the vbat internal channel. | 334 | /// Enable reading the vbat internal channel. |
| 319 | #[cfg(stm32u5)] | 335 | #[cfg(stm32u5)] |
| 320 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | 336 | pub fn enable_dac_channel_adc4(&self, dac: DacChannel) -> super::Dac { |
| 321 | let mux; | 337 | let mux; |
| 322 | match dac { | 338 | match dac { |
| 323 | DacChannel::OUT1 => mux = false, | 339 | DacChannel::OUT1 => mux = false, |
| 324 | DacChannel::OUT2 => mux = true, | 340 | DacChannel::OUT2 => mux = true, |
| 325 | } | 341 | } |
| 326 | T::regs().or().modify(|w| w.set_chn21sel(mux)); | 342 | T::regs().or().modify(|w| w.set_chn21sel(mux)); |
| 327 | Dac {} | 343 | super::Dac {} |
| 328 | } | 344 | } |
| 329 | 345 | ||
| 330 | /// Set the ADC resolution. | 346 | /// Set the ADC resolution. |
| 331 | pub fn set_resolution(&mut self, resolution: Resolution) { | 347 | pub fn set_resolution_adc4(&mut self, resolution: Resolution) { |
| 332 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); | 348 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); |
| 333 | } | 349 | } |
| 334 | 350 | ||
| 335 | /// Set hardware averaging. | 351 | /// Set hardware averaging. |
| 336 | #[cfg(stm32u5)] | 352 | #[cfg(stm32u5)] |
| 337 | pub fn set_averaging(&mut self, averaging: Averaging) { | 353 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 338 | let (enable, samples, right_shift) = match averaging { | 354 | let (enable, samples, right_shift) = match averaging { |
| 339 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), | 355 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), |
| 340 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), | 356 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), |
| @@ -354,7 +370,7 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 354 | }) | 370 | }) |
| 355 | } | 371 | } |
| 356 | #[cfg(stm32wba)] | 372 | #[cfg(stm32wba)] |
| 357 | pub fn set_averaging(&mut self, averaging: Averaging) { | 373 | pub fn set_averaging_adc4(&mut self, averaging: Averaging) { |
| 358 | let (enable, samples, right_shift) = match averaging { | 374 | let (enable, samples, right_shift) = match averaging { |
| 359 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), | 375 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), |
| 360 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), | 376 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), |
| @@ -373,168 +389,4 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 373 | w.set_ovse(enable) | 389 | w.set_ovse(enable) |
| 374 | }) | 390 | }) |
| 375 | } | 391 | } |
| 376 | |||
| 377 | /// Read an ADC channel. | ||
| 378 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 379 | T::regs().smpr().modify(|w| { | ||
| 380 | w.set_smp(0, sample_time); | ||
| 381 | }); | ||
| 382 | |||
| 383 | channel.setup(); | ||
| 384 | |||
| 385 | // Select channel | ||
| 386 | #[cfg(stm32wba)] | ||
| 387 | { | ||
| 388 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 389 | T::regs().chselr().modify(|w| { | ||
| 390 | w.set_chsel0(channel.channel() as usize, true); | ||
| 391 | }); | ||
| 392 | } | ||
| 393 | #[cfg(stm32u5)] | ||
| 394 | { | ||
| 395 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 396 | T::regs().chselrmod0().modify(|w| { | ||
| 397 | w.set_chsel(channel.channel() as usize, true); | ||
| 398 | }); | ||
| 399 | } | ||
| 400 | |||
| 401 | // Reset interrupts | ||
| 402 | T::regs().isr().modify(|reg| { | ||
| 403 | reg.set_eos(true); | ||
| 404 | reg.set_eoc(true); | ||
| 405 | }); | ||
| 406 | |||
| 407 | // Start conversion | ||
| 408 | T::regs().cr().modify(|reg| { | ||
| 409 | reg.set_adstart(true); | ||
| 410 | }); | ||
| 411 | |||
| 412 | while !T::regs().isr().read().eos() { | ||
| 413 | // spin | ||
| 414 | } | ||
| 415 | |||
| 416 | T::regs().dr().read().0 as u16 | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Read one or multiple ADC channels using DMA. | ||
| 420 | /// | ||
| 421 | /// `sequence` iterator and `readings` must have the same length. | ||
| 422 | /// The channels in `sequence` must be in ascending order. | ||
| 423 | /// | ||
| 424 | /// Example | ||
| 425 | /// ```rust,ignore | ||
| 426 | /// use embassy_stm32::adc::adc4; | ||
| 427 | /// use embassy_stm32::adc::AdcChannel; | ||
| 428 | /// | ||
| 429 | /// let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 430 | /// let mut adc4_pin1 = p.PC1; | ||
| 431 | /// let mut adc4_pin2 = p.PC0; | ||
| 432 | /// let mut.into()d41 = adc4_pin1.into(); | ||
| 433 | /// let mut.into()d42 = adc4_pin2.into(); | ||
| 434 | /// let mut measurements = [0u16; 2]; | ||
| 435 | /// // not that the channels must be in ascending order | ||
| 436 | /// adc4.read( | ||
| 437 | /// &mut p.GPDMA1_CH1, | ||
| 438 | /// [ | ||
| 439 | /// &mut.into()d42, | ||
| 440 | /// &mut.into()d41, | ||
| 441 | /// ] | ||
| 442 | /// .into_iter(), | ||
| 443 | /// &mut measurements, | ||
| 444 | /// ).await.unwrap(); | ||
| 445 | /// ``` | ||
| 446 | pub async fn read( | ||
| 447 | &mut self, | ||
| 448 | rx_dma: Peri<'_, impl RxDma4<T>>, | ||
| 449 | sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 450 | readings: &mut [u16], | ||
| 451 | ) -> Result<(), Adc4Error> { | ||
| 452 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 453 | assert!( | ||
| 454 | sequence.len() == readings.len(), | ||
| 455 | "Sequence length must be equal to readings length" | ||
| 456 | ); | ||
| 457 | |||
| 458 | // Ensure no conversions are ongoing | ||
| 459 | Self::cancel_conversions(); | ||
| 460 | |||
| 461 | T::regs().isr().modify(|reg| { | ||
| 462 | reg.set_ovr(true); | ||
| 463 | reg.set_eos(true); | ||
| 464 | reg.set_eoc(true); | ||
| 465 | }); | ||
| 466 | |||
| 467 | T::regs().cfgr1().modify(|reg| { | ||
| 468 | reg.set_dmaen(true); | ||
| 469 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 470 | #[cfg(stm32u5)] | ||
| 471 | reg.set_chselrmod(false); | ||
| 472 | #[cfg(stm32wba)] | ||
| 473 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 474 | }); | ||
| 475 | |||
| 476 | // Verify and activate sequence | ||
| 477 | let mut prev_channel: i16 = -1; | ||
| 478 | #[cfg(stm32wba)] | ||
| 479 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 480 | #[cfg(stm32u5)] | ||
| 481 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 482 | for channel in sequence { | ||
| 483 | let channel_num = channel.channel; | ||
| 484 | if channel_num as i16 <= prev_channel { | ||
| 485 | return Err(Adc4Error::InvalidSequence); | ||
| 486 | }; | ||
| 487 | prev_channel = channel_num as i16; | ||
| 488 | |||
| 489 | #[cfg(stm32wba)] | ||
| 490 | T::regs().chselr().modify(|w| { | ||
| 491 | w.set_chsel0(channel.channel as usize, true); | ||
| 492 | }); | ||
| 493 | #[cfg(stm32u5)] | ||
| 494 | T::regs().chselrmod0().modify(|w| { | ||
| 495 | w.set_chsel(channel.channel as usize, true); | ||
| 496 | }); | ||
| 497 | } | ||
| 498 | |||
| 499 | let request = rx_dma.request(); | ||
| 500 | let transfer = unsafe { | ||
| 501 | Transfer::new_read( | ||
| 502 | rx_dma, | ||
| 503 | request, | ||
| 504 | T::regs().dr().as_ptr() as *mut u16, | ||
| 505 | readings, | ||
| 506 | Default::default(), | ||
| 507 | ) | ||
| 508 | }; | ||
| 509 | |||
| 510 | // Start conversion | ||
| 511 | T::regs().cr().modify(|reg| { | ||
| 512 | reg.set_adstart(true); | ||
| 513 | }); | ||
| 514 | |||
| 515 | transfer.await; | ||
| 516 | |||
| 517 | // Ensure conversions are finished. | ||
| 518 | Self::cancel_conversions(); | ||
| 519 | |||
| 520 | // Reset configuration. | ||
| 521 | T::regs().cfgr1().modify(|reg| { | ||
| 522 | reg.set_dmaen(false); | ||
| 523 | }); | ||
| 524 | |||
| 525 | if T::regs().isr().read().ovr() { | ||
| 526 | Err(Adc4Error::DMAError) | ||
| 527 | } else { | ||
| 528 | Ok(()) | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | fn cancel_conversions() { | ||
| 533 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 534 | T::regs().cr().modify(|reg| { | ||
| 535 | reg.set_adstp(true); | ||
| 536 | }); | ||
| 537 | while T::regs().cr().read().adstart() {} | ||
| 538 | } | ||
| 539 | } | ||
| 540 | } | 392 | } |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index bc97a7c4b..3e109e429 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -1,12 +1,10 @@ | |||
| 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 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, | 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::{Peri, pac, rcc}; | 9 | use crate::{Peri, pac, rcc}; |
| 12 | 10 | ||
| @@ -19,7 +17,6 @@ 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 NUM_HW_CHANNELS: u8 = 22; | ||
| 23 | const CHSELR_SQ_SIZE: usize = 8; | 20 | const CHSELR_SQ_SIZE: usize = 8; |
| 24 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; | 21 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; |
| 25 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; | 22 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; |
| @@ -32,123 +29,169 @@ impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T { | |||
| 32 | const CHANNEL: u8 = 9; | 29 | const CHANNEL: u8 = 9; |
| 33 | } | 30 | } |
| 34 | 31 | ||
| 35 | #[derive(Copy, Clone, Debug)] | 32 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 36 | pub enum Prescaler { | 33 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 37 | NotDivided, | 34 | match raw_prescaler { |
| 38 | DividedBy2, | 35 | 0 => Presc::DIV1, |
| 39 | DividedBy4, | 36 | 1 => Presc::DIV2, |
| 40 | DividedBy6, | 37 | 2..=3 => Presc::DIV4, |
| 41 | DividedBy8, | 38 | 4..=5 => Presc::DIV6, |
| 42 | DividedBy10, | 39 | 6..=7 => Presc::DIV8, |
| 43 | DividedBy12, | 40 | 8..=9 => Presc::DIV10, |
| 44 | DividedBy16, | 41 | 10..=11 => Presc::DIV12, |
| 45 | DividedBy32, | 42 | _ => unimplemented!(), |
| 46 | DividedBy64, | 43 | } |
| 47 | DividedBy128, | ||
| 48 | DividedBy256, | ||
| 49 | } | 44 | } |
| 50 | 45 | ||
| 51 | impl Prescaler { | 46 | impl<T: Instance> super::SealedAnyInstance for T { |
| 52 | fn from_ker_ck(frequency: Hertz) -> Self { | 47 | fn dr() -> *mut u16 { |
| 53 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 48 | T::regs().dr().as_ptr() as *mut u16 |
| 54 | match raw_prescaler { | 49 | } |
| 55 | 0 => Self::NotDivided, | 50 | |
| 56 | 1 => Self::DividedBy2, | 51 | fn enable() { |
| 57 | 2..=3 => Self::DividedBy4, | 52 | T::regs().isr().modify(|w| w.set_adrdy(true)); |
| 58 | 4..=5 => Self::DividedBy6, | 53 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 59 | 6..=7 => Self::DividedBy8, | 54 | // ADRDY is "ADC ready". Wait until it will be True. |
| 60 | 8..=9 => Self::DividedBy10, | 55 | while !T::regs().isr().read().adrdy() {} |
| 61 | 10..=11 => Self::DividedBy12, | ||
| 62 | _ => unimplemented!(), | ||
| 63 | } | ||
| 64 | } | 56 | } |
| 65 | 57 | ||
| 66 | #[allow(unused)] | 58 | fn start() { |
| 67 | fn divisor(&self) -> u32 { | 59 | // Start conversion |
| 68 | match self { | 60 | T::regs().cr().modify(|reg| { |
| 69 | Prescaler::NotDivided => 1, | 61 | reg.set_adstart(true); |
| 70 | Prescaler::DividedBy2 => 2, | 62 | }); |
| 71 | Prescaler::DividedBy4 => 4, | 63 | } |
| 72 | Prescaler::DividedBy6 => 6, | 64 | |
| 73 | Prescaler::DividedBy8 => 8, | 65 | fn stop() { |
| 74 | Prescaler::DividedBy10 => 10, | 66 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 75 | Prescaler::DividedBy12 => 12, | 67 | T::regs().cr().modify(|reg| { |
| 76 | Prescaler::DividedBy16 => 16, | 68 | reg.set_adstp(Adstp::STOP); |
| 77 | Prescaler::DividedBy32 => 32, | 69 | }); |
| 78 | Prescaler::DividedBy64 => 64, | 70 | while T::regs().cr().read().adstart() {} |
| 79 | Prescaler::DividedBy128 => 128, | ||
| 80 | Prescaler::DividedBy256 => 256, | ||
| 81 | } | 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 | }); | ||
| 82 | } | 79 | } |
| 83 | 80 | ||
| 84 | fn presc(&self) -> Presc { | 81 | fn configure_dma(conversion_mode: super::ConversionMode) { |
| 85 | match self { | 82 | match conversion_mode { |
| 86 | Prescaler::NotDivided => Presc::DIV1, | 83 | ConversionMode::Singular => { |
| 87 | Prescaler::DividedBy2 => Presc::DIV2, | 84 | // Enable overrun control, so no new DMA requests will be generated until |
| 88 | Prescaler::DividedBy4 => Presc::DIV4, | 85 | // previous DR values is read. |
| 89 | Prescaler::DividedBy6 => Presc::DIV6, | 86 | T::regs().isr().modify(|reg| { |
| 90 | Prescaler::DividedBy8 => Presc::DIV8, | 87 | reg.set_ovr(true); |
| 91 | Prescaler::DividedBy10 => Presc::DIV10, | 88 | }); |
| 92 | Prescaler::DividedBy12 => Presc::DIV12, | 89 | |
| 93 | Prescaler::DividedBy16 => Presc::DIV16, | 90 | // Set continuous mode with oneshot dma. |
| 94 | Prescaler::DividedBy32 => Presc::DIV32, | 91 | T::regs().cfgr1().modify(|reg| { |
| 95 | Prescaler::DividedBy64 => Presc::DIV64, | 92 | reg.set_discen(false); |
| 96 | Prescaler::DividedBy128 => Presc::DIV128, | 93 | reg.set_cont(true); |
| 97 | Prescaler::DividedBy256 => Presc::DIV256, | 94 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); |
| 95 | reg.set_dmaen(true); | ||
| 96 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 97 | }); | ||
| 98 | } | ||
| 98 | } | 99 | } |
| 99 | } | 100 | } |
| 100 | } | ||
| 101 | 101 | ||
| 102 | #[cfg(feature = "defmt")] | 102 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) { |
| 103 | impl<'a> defmt::Format for Prescaler { | 103 | let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE; |
| 104 | fn format(&self, fmt: defmt::Formatter) { | 104 | let mut is_ordered_up = true; |
| 105 | match self { | 105 | let mut is_ordered_down = true; |
| 106 | Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), | 106 | |
| 107 | Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), | 107 | let sequence_len = sequence.len(); |
| 108 | Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), | 108 | let mut hw_channel_selection: u32 = 0; |
| 109 | Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), | 109 | let mut last_channel: u8 = 0; |
| 110 | Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), | 110 | let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5; |
| 111 | Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), | 111 | |
| 112 | Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), | 112 | T::regs().chselr_sq().write(|w| { |
| 113 | Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), | 113 | for (i, ((channel, _), _sample_time)) in sequence.enumerate() { |
| 114 | Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), | 114 | assert!( |
| 115 | Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), | 115 | sample_time == _sample_time || i == 0, |
| 116 | Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), | 116 | "C0 only supports one sample time for the sequence." |
| 117 | Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), | 117 | ); |
| 118 | |||
| 119 | sample_time = _sample_time; | ||
| 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; | ||
| 125 | |||
| 126 | if !needs_hw { | ||
| 127 | w.set_sq(i, channel); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | for i in sequence_len..CHSELR_SQ_SIZE { | ||
| 132 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 133 | } | ||
| 134 | }); | ||
| 135 | |||
| 136 | if needs_hw { | ||
| 137 | assert!( | ||
| 138 | sequence_len <= CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, | ||
| 139 | "Sequencer is required because of unordered channels, but read set cannot be more than {} in size.", | ||
| 140 | CHSELR_SQ_SIZE | ||
| 141 | ); | ||
| 142 | assert!( | ||
| 143 | sequence_len > CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, | ||
| 144 | "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.", | ||
| 145 | CHSELR_SQ_MAX_CHANNEL | ||
| 146 | ); | ||
| 147 | |||
| 148 | // Set required channels for multi-convert. | ||
| 149 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 118 | } | 150 | } |
| 151 | |||
| 152 | T::regs().smpr().modify(|w| { | ||
| 153 | w.smpsel(0); | ||
| 154 | w.set_smp1(sample_time); | ||
| 155 | }); | ||
| 156 | |||
| 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 | }); | ||
| 162 | |||
| 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() {} | ||
| 119 | } | 166 | } |
| 120 | } | ||
| 121 | 167 | ||
| 122 | /// Number of samples used for averaging. | 168 | fn convert() -> u16 { |
| 123 | /// TODO: Implement hardware averaging setting. | 169 | // Set single conversion mode. |
| 124 | #[allow(unused)] | 170 | T::regs().cfgr1().modify(|w| w.set_cont(false)); |
| 125 | #[derive(Copy, Clone, Debug)] | 171 | |
| 126 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 172 | // Start conversion |
| 127 | pub enum Averaging { | 173 | T::regs().cr().modify(|reg| { |
| 128 | Disabled, | 174 | reg.set_adstart(true); |
| 129 | Samples2, | 175 | }); |
| 130 | Samples4, | 176 | |
| 131 | Samples8, | 177 | // Waiting for End Of Conversion (EOC). |
| 132 | Samples16, | 178 | while !T::regs().isr().read().eoc() {} |
| 133 | Samples32, | 179 | |
| 134 | Samples64, | 180 | T::regs().dr().read().data() as u16 |
| 135 | Samples128, | 181 | } |
| 136 | Samples256, | ||
| 137 | Samples512, | ||
| 138 | Samples1024, | ||
| 139 | } | 182 | } |
| 140 | 183 | ||
| 141 | impl<'d, T: Instance> Adc<'d, T> { | 184 | impl<'d, T: AnyInstance> Adc<'d, T> { |
| 142 | /// Create a new ADC driver. | 185 | /// Create a new ADC driver. |
| 143 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { | 186 | pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { |
| 144 | rcc::enable_and_reset::<T>(); | 187 | rcc::enable_and_reset::<T>(); |
| 145 | 188 | ||
| 146 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | 189 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); |
| 147 | 190 | ||
| 148 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 191 | let prescaler = from_ker_ck(T::frequency()); |
| 149 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 192 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 150 | 193 | ||
| 151 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 194 | let frequency = T::frequency() / prescaler; |
| 152 | debug!("ADC frequency set to {}", frequency); | 195 | debug!("ADC frequency set to {}", frequency); |
| 153 | 196 | ||
| 154 | if frequency > MAX_ADC_CLK_FREQ { | 197 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -158,32 +201,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 158 | ); | 201 | ); |
| 159 | } | 202 | } |
| 160 | 203 | ||
| 161 | let mut s = Self { adc }; | ||
| 162 | |||
| 163 | s.power_up(); | ||
| 164 | |||
| 165 | s.set_resolution(resolution); | ||
| 166 | |||
| 167 | s.calibrate(); | ||
| 168 | |||
| 169 | s.enable(); | ||
| 170 | |||
| 171 | s.configure_default(); | ||
| 172 | |||
| 173 | s | ||
| 174 | } | ||
| 175 | |||
| 176 | fn power_up(&mut self) { | ||
| 177 | T::regs().cr().modify(|reg| { | 204 | T::regs().cr().modify(|reg| { |
| 178 | reg.set_advregen(true); | 205 | reg.set_advregen(true); |
| 179 | }); | 206 | }); |
| 180 | 207 | ||
| 181 | // "The software must wait for the ADC voltage regulator startup time." | 208 | // "The software must wait for the ADC voltage regulator startup time." |
| 182 | // See datasheet for the value. | 209 | // See datasheet for the value. |
| 183 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | 210 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US as u64 + 1); |
| 184 | } | 211 | |
| 212 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 185 | 213 | ||
| 186 | fn calibrate(&mut self) { | ||
| 187 | // 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. |
| 188 | let autoff_value = T::regs().cfgr1().read().autoff(); | 215 | let autoff_value = T::regs().cfgr1().read().autoff(); |
| 189 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | 216 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); |
| @@ -197,22 +224,17 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 197 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | 224 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); |
| 198 | 225 | ||
| 199 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | 226 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); |
| 200 | } | ||
| 201 | 227 | ||
| 202 | fn enable(&mut self) { | 228 | T::enable(); |
| 203 | T::regs().isr().modify(|w| w.set_adrdy(true)); | ||
| 204 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 205 | // ADRDY is "ADC ready". Wait until it will be True. | ||
| 206 | while !T::regs().isr().read().adrdy() {} | ||
| 207 | } | ||
| 208 | 229 | ||
| 209 | fn configure_default(&mut self) { | ||
| 210 | // single conversion mode, software trigger | 230 | // single conversion mode, software trigger |
| 211 | T::regs().cfgr1().modify(|w| { | 231 | T::regs().cfgr1().modify(|w| { |
| 212 | w.set_cont(false); | 232 | w.set_cont(false); |
| 213 | w.set_exten(Exten::DISABLED); | 233 | w.set_exten(Exten::DISABLED); |
| 214 | w.set_align(Align::RIGHT); | 234 | w.set_align(Align::RIGHT); |
| 215 | }); | 235 | }); |
| 236 | |||
| 237 | Self { adc } | ||
| 216 | } | 238 | } |
| 217 | 239 | ||
| 218 | /// Enable reading the voltage reference internal channel. | 240 | /// Enable reading the voltage reference internal channel. |
| @@ -233,219 +255,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 233 | 255 | ||
| 234 | super::Temperature {} | 256 | super::Temperature {} |
| 235 | } | 257 | } |
| 236 | |||
| 237 | /// Set the ADC sample time. | ||
| 238 | /// Shall only be called when ADC is not converting. | ||
| 239 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { | ||
| 240 | // Set all channels to use SMP1 field as source. | ||
| 241 | T::regs().smpr().modify(|w| { | ||
| 242 | w.smpsel(0); | ||
| 243 | w.set_smp1(sample_time); | ||
| 244 | }); | ||
| 245 | } | ||
| 246 | |||
| 247 | /// Set the ADC resolution. | ||
| 248 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 249 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Perform a single conversion. | ||
| 253 | fn convert(&mut self) -> u16 { | ||
| 254 | // Set single conversion mode. | ||
| 255 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 256 | |||
| 257 | // Start conversion | ||
| 258 | T::regs().cr().modify(|reg| { | ||
| 259 | reg.set_adstart(true); | ||
| 260 | }); | ||
| 261 | |||
| 262 | // Waiting for End Of Conversion (EOC). | ||
| 263 | while !T::regs().isr().read().eoc() {} | ||
| 264 | |||
| 265 | T::regs().dr().read().data() as u16 | ||
| 266 | } | ||
| 267 | |||
| 268 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 269 | self.set_sample_time_all_channels(sample_time); | ||
| 270 | |||
| 271 | Self::configure_channel(channel); | ||
| 272 | T::regs().cfgr1().write(|reg| { | ||
| 273 | reg.set_chselrmod(false); | ||
| 274 | reg.set_align(Align::RIGHT); | ||
| 275 | }); | ||
| 276 | self.convert() | ||
| 277 | } | ||
| 278 | |||
| 279 | fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator<Item = &'a mut AnyAdcChannel<T>>) { | ||
| 280 | assert!( | ||
| 281 | channel_sequence.len() <= CHSELR_SQ_SIZE, | ||
| 282 | "Seqenced read set cannot be more than {} in size.", | ||
| 283 | CHSELR_SQ_SIZE | ||
| 284 | ); | ||
| 285 | let mut last_sq_set: usize = 0; | ||
| 286 | T::regs().chselr_sq().write(|w| { | ||
| 287 | for (i, channel) in channel_sequence.enumerate() { | ||
| 288 | assert!( | ||
| 289 | channel.channel() <= CHSELR_SQ_MAX_CHANNEL, | ||
| 290 | "Sequencer only support HW channels smaller than {}.", | ||
| 291 | CHSELR_SQ_MAX_CHANNEL | ||
| 292 | ); | ||
| 293 | w.set_sq(i, channel.channel()); | ||
| 294 | last_sq_set = i; | ||
| 295 | } | ||
| 296 | |||
| 297 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 298 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 299 | } | ||
| 300 | }); | ||
| 301 | |||
| 302 | Self::apply_channel_conf() | ||
| 303 | } | ||
| 304 | |||
| 305 | async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma<T>>, readings: &mut [u16]) { | ||
| 306 | // Enable overrun control, so no new DMA requests will be generated until | ||
| 307 | // previous DR values is read. | ||
| 308 | T::regs().isr().modify(|reg| { | ||
| 309 | reg.set_ovr(true); | ||
| 310 | }); | ||
| 311 | |||
| 312 | // Set continuous mode with oneshot dma. | ||
| 313 | T::regs().cfgr1().modify(|reg| { | ||
| 314 | reg.set_discen(false); | ||
| 315 | reg.set_cont(true); | ||
| 316 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); | ||
| 317 | reg.set_dmaen(true); | ||
| 318 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 319 | }); | ||
| 320 | |||
| 321 | let request = rx_dma.request(); | ||
| 322 | let transfer = unsafe { | ||
| 323 | Transfer::new_read( | ||
| 324 | rx_dma, | ||
| 325 | request, | ||
| 326 | T::regs().dr().as_ptr() as *mut u16, | ||
| 327 | readings, | ||
| 328 | Default::default(), | ||
| 329 | ) | ||
| 330 | }; | ||
| 331 | |||
| 332 | // Start conversion. | ||
| 333 | T::regs().cr().modify(|reg| { | ||
| 334 | reg.set_adstart(true); | ||
| 335 | }); | ||
| 336 | |||
| 337 | // Wait for conversion sequence to finish. | ||
| 338 | transfer.await; | ||
| 339 | |||
| 340 | // Ensure conversions are finished. | ||
| 341 | Self::cancel_conversions(); | ||
| 342 | |||
| 343 | // Reset configuration. | ||
| 344 | T::regs().cfgr1().modify(|reg| { | ||
| 345 | reg.set_cont(false); | ||
| 346 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 347 | reg.set_dmaen(false); | ||
| 348 | }); | ||
| 349 | } | ||
| 350 | |||
| 351 | /// Read one or multiple ADC channels using DMA in hardware order. | ||
| 352 | /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. | ||
| 353 | /// Readings won't be in the same order as in the `set`! | ||
| 354 | /// | ||
| 355 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 356 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 357 | /// TODO(chudsaviet): externalize generic code and merge with read(). | ||
| 358 | pub async fn read_in_hw_order( | ||
| 359 | &mut self, | ||
| 360 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 361 | hw_channel_selection: u32, | ||
| 362 | scandir: Scandir, | ||
| 363 | readings: &mut [u16], | ||
| 364 | ) { | ||
| 365 | assert!( | ||
| 366 | hw_channel_selection != 0, | ||
| 367 | "Some bits in `hw_channel_selection` shall be set." | ||
| 368 | ); | ||
| 369 | assert!( | ||
| 370 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 371 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 372 | NUM_HW_CHANNELS | ||
| 373 | ); | ||
| 374 | // To check for correct readings slice size, we shall solve Hamming weight problem, | ||
| 375 | // which is either slow or memory consuming. | ||
| 376 | // Since we have limited resources, we don't do it here. | ||
| 377 | // Not doing this have a great potential for a bug through. | ||
| 378 | |||
| 379 | // Ensure no conversions are ongoing. | ||
| 380 | Self::cancel_conversions(); | ||
| 381 | |||
| 382 | T::regs().cfgr1().modify(|reg| { | ||
| 383 | reg.set_chselrmod(false); | ||
| 384 | reg.set_scandir(scandir); | ||
| 385 | reg.set_align(Align::RIGHT); | ||
| 386 | }); | ||
| 387 | |||
| 388 | // Set required channels for multi-convert. | ||
| 389 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 390 | |||
| 391 | Self::apply_channel_conf(); | ||
| 392 | |||
| 393 | self.dma_convert(rx_dma, readings).await | ||
| 394 | } | ||
| 395 | |||
| 396 | // Read ADC channels in specified order using DMA (CHSELRMOD = 1). | ||
| 397 | // In STM32C0, only lower 14 ADC channels can be read this way. | ||
| 398 | // For other channels, use `read_in_hw_order()` or blocking read. | ||
| 399 | pub async fn read( | ||
| 400 | &mut self, | ||
| 401 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 402 | channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 403 | readings: &mut [u16], | ||
| 404 | ) { | ||
| 405 | assert!( | ||
| 406 | channel_sequence.len() != 0, | ||
| 407 | "Asynchronous read channel sequence cannot be empty." | ||
| 408 | ); | ||
| 409 | assert!( | ||
| 410 | channel_sequence.len() == readings.len(), | ||
| 411 | "Channel sequence length must be equal to readings length." | ||
| 412 | ); | ||
| 413 | |||
| 414 | // Ensure no conversions are ongoing. | ||
| 415 | Self::cancel_conversions(); | ||
| 416 | |||
| 417 | T::regs().cfgr1().modify(|reg| { | ||
| 418 | reg.set_chselrmod(true); | ||
| 419 | reg.set_align(Align::RIGHT); | ||
| 420 | }); | ||
| 421 | |||
| 422 | Self::setup_channel_sequencer(channel_sequence); | ||
| 423 | |||
| 424 | self.dma_convert(rx_dma, readings).await | ||
| 425 | } | ||
| 426 | |||
| 427 | fn configure_channel(channel: &mut impl AdcChannel<T>) { | ||
| 428 | channel.setup(); | ||
| 429 | // write() because we want all other bits to be set to 0. | ||
| 430 | T::regs() | ||
| 431 | .chselr() | ||
| 432 | .write(|w| w.set_chsel(channel.channel().into(), true)); | ||
| 433 | |||
| 434 | Self::apply_channel_conf(); | ||
| 435 | } | ||
| 436 | |||
| 437 | fn apply_channel_conf() { | ||
| 438 | // Trigger and wait for the channel selection procedure to complete. | ||
| 439 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 440 | while !T::regs().isr().read().ccrdy() {} | ||
| 441 | } | ||
| 442 | |||
| 443 | fn cancel_conversions() { | ||
| 444 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 445 | T::regs().cr().modify(|reg| { | ||
| 446 | reg.set_adstp(Adstp::STOP); | ||
| 447 | }); | ||
| 448 | while T::regs().cr().read().adstart() {} | ||
| 449 | } | ||
| 450 | } | ||
| 451 | } | 258 | } |
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index f6220de78..d6c6f480b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -43,7 +43,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 43 | 43 | ||
| 44 | // 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’) |
| 45 | // for at least two ADC clock cycles. | 45 | // for at least two ADC clock cycles. |
| 46 | 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); |
| 47 | 47 | ||
| 48 | // Reset calibration | 48 | // Reset calibration |
| 49 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); | 49 | T::regs().cr2().modify(|reg| reg.set_rstcal(true)); |
| @@ -58,7 +58,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | // One cycle after calibration | 60 | // One cycle after calibration |
| 61 | 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); |
| 62 | 62 | ||
| 63 | T::Interrupt::unpend(); | 63 | T::Interrupt::unpend(); |
| 64 | unsafe { T::Interrupt::enable() }; | 64 | unsafe { T::Interrupt::enable() }; |
diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index 4a77f3c5b..29bfdac97 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -62,7 +62,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 62 | while T::regs().cr().read().adcal() {} | 62 | while T::regs().cr().read().adcal() {} |
| 63 | 63 | ||
| 64 | // 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). |
| 65 | 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); |
| 66 | 66 | ||
| 67 | // Enable the adc | 67 | // Enable the adc |
| 68 | T::regs().cr().modify(|w| w.set_aden(true)); | 68 | T::regs().cr().modify(|w| w.set_aden(true)); |
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 6430b0243..1767a3bb3 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,24 +1,21 @@ | |||
| 1 | use core::mem; | 1 | #[cfg(stm32g4)] |
| 2 | 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}; | ||
| 3 | #[allow(unused)] | 6 | #[allow(unused)] |
| 4 | #[cfg(stm32h7)] | 7 | #[cfg(stm32h7)] |
| 5 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 8 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| 6 | #[allow(unused)] | 9 | pub use pac::adccommon::vals::{Dual, Presc}; |
| 7 | #[cfg(stm32g4)] | 10 | |
| 8 | pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; | 11 | use super::{ |
| 9 | pub use pac::adccommon::vals::Presc; | 12 | Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, |
| 10 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | 13 | blocking_delay_us, |
| 11 | pub use stm32_metapac::adccommon::vals::Dual; | 14 | }; |
| 12 | 15 | use crate::adc::{AnyInstance, SealedAdcChannel}; | |
| 13 | use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; | ||
| 14 | use crate::adc::SealedAdcChannel; | ||
| 15 | use crate::dma::Transfer; | ||
| 16 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 17 | use crate::{Peri, pac, rcc}; | 17 | use crate::{Peri, pac, rcc}; |
| 18 | 18 | ||
| 19 | mod ringbuffered; | ||
| 20 | pub use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 22 | mod injected; | 19 | mod injected; |
| 23 | pub use injected::InjectedAdc; | 20 | pub use injected::InjectedAdc; |
| 24 | 21 | ||
| @@ -35,72 +32,31 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | |||
| 35 | #[cfg(stm32h7)] | 32 | #[cfg(stm32h7)] |
| 36 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 33 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 37 | 34 | ||
| 38 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 35 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 39 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 36 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 40 | #[allow(unused)] | 37 | match raw_prescaler { |
| 41 | enum Prescaler { | 38 | 0 => Presc::DIV1, |
| 42 | NotDivided, | 39 | 1 => Presc::DIV2, |
| 43 | DividedBy2, | 40 | 2..=3 => Presc::DIV4, |
| 44 | DividedBy4, | 41 | 4..=5 => Presc::DIV6, |
| 45 | DividedBy6, | 42 | 6..=7 => Presc::DIV8, |
| 46 | DividedBy8, | 43 | 8..=9 => Presc::DIV10, |
| 47 | DividedBy10, | 44 | 10..=11 => Presc::DIV12, |
| 48 | DividedBy12, | 45 | _ => unimplemented!(), |
| 49 | DividedBy16, | ||
| 50 | DividedBy32, | ||
| 51 | DividedBy64, | ||
| 52 | DividedBy128, | ||
| 53 | DividedBy256, | ||
| 54 | } | ||
| 55 | |||
| 56 | impl Prescaler { | ||
| 57 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 58 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 59 | match raw_prescaler { | ||
| 60 | 0 => Self::NotDivided, | ||
| 61 | 1 => Self::DividedBy2, | ||
| 62 | 2..=3 => Self::DividedBy4, | ||
| 63 | 4..=5 => Self::DividedBy6, | ||
| 64 | 6..=7 => Self::DividedBy8, | ||
| 65 | 8..=9 => Self::DividedBy10, | ||
| 66 | 10..=11 => Self::DividedBy12, | ||
| 67 | _ => unimplemented!(), | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | fn divisor(&self) -> u32 { | ||
| 72 | match self { | ||
| 73 | Prescaler::NotDivided => 1, | ||
| 74 | Prescaler::DividedBy2 => 2, | ||
| 75 | Prescaler::DividedBy4 => 4, | ||
| 76 | Prescaler::DividedBy6 => 6, | ||
| 77 | Prescaler::DividedBy8 => 8, | ||
| 78 | Prescaler::DividedBy10 => 10, | ||
| 79 | Prescaler::DividedBy12 => 12, | ||
| 80 | Prescaler::DividedBy16 => 16, | ||
| 81 | Prescaler::DividedBy32 => 32, | ||
| 82 | Prescaler::DividedBy64 => 64, | ||
| 83 | Prescaler::DividedBy128 => 128, | ||
| 84 | Prescaler::DividedBy256 => 256, | ||
| 85 | } | ||
| 86 | } | 46 | } |
| 47 | } | ||
| 87 | 48 | ||
| 88 | fn presc(&self) -> Presc { | 49 | /// ADC configuration |
| 89 | match self { | 50 | #[derive(Default)] |
| 90 | Prescaler::NotDivided => Presc::DIV1, | 51 | pub struct AdcConfig { |
| 91 | Prescaler::DividedBy2 => Presc::DIV2, | 52 | pub dual_mode: Option<Dual>, |
| 92 | Prescaler::DividedBy4 => Presc::DIV4, | 53 | pub resolution: Option<Resolution>, |
| 93 | Prescaler::DividedBy6 => Presc::DIV6, | 54 | #[cfg(stm32g4)] |
| 94 | Prescaler::DividedBy8 => Presc::DIV8, | 55 | pub oversampling_shift: Option<u8>, |
| 95 | Prescaler::DividedBy10 => Presc::DIV10, | 56 | #[cfg(stm32g4)] |
| 96 | Prescaler::DividedBy12 => Presc::DIV12, | 57 | pub oversampling_ratio: Option<u8>, |
| 97 | Prescaler::DividedBy16 => Presc::DIV16, | 58 | #[cfg(stm32g4)] |
| 98 | Prescaler::DividedBy32 => Presc::DIV32, | 59 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, |
| 99 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 100 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 101 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | 60 | } |
| 105 | 61 | ||
| 106 | // Trigger source for ADC conversions¨ | 62 | // Trigger source for ADC conversions¨ |
| @@ -112,25 +68,189 @@ pub struct ConversionTrigger { | |||
| 112 | pub edge: Exten, | 68 | pub edge: Exten, |
| 113 | } | 69 | } |
| 114 | 70 | ||
| 115 | // Conversion mode for regular ADC channels | 71 | impl<T: Instance> super::SealedAnyInstance for T { |
| 116 | #[derive(Copy, Clone)] | 72 | fn dr() -> *mut u16 { |
| 117 | pub enum RegularConversionMode { | 73 | T::regs().dr().as_ptr() as *mut u16 |
| 118 | // Samples as fast as possible | 74 | } |
| 119 | Continuous, | 75 | |
| 120 | // Sample at rate determined by external trigger | 76 | fn enable() { |
| 121 | Triggered(ConversionTrigger), | 77 | // Make sure bits are off |
| 78 | while T::regs().cr().read().addis() { | ||
| 79 | // spin | ||
| 80 | } | ||
| 81 | |||
| 82 | if !T::regs().cr().read().aden() { | ||
| 83 | // Enable ADC | ||
| 84 | T::regs().isr().modify(|reg| { | ||
| 85 | reg.set_adrdy(true); | ||
| 86 | }); | ||
| 87 | T::regs().cr().modify(|reg| { | ||
| 88 | reg.set_aden(true); | ||
| 89 | }); | ||
| 90 | |||
| 91 | while !T::regs().isr().read().adrdy() { | ||
| 92 | // spin | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | fn start() { | ||
| 98 | T::regs().cr().modify(|reg| { | ||
| 99 | reg.set_adstart(true); | ||
| 100 | }); | ||
| 101 | } | ||
| 102 | |||
| 103 | fn stop() { | ||
| 104 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 105 | T::regs().cr().modify(|reg| { | ||
| 106 | reg.set_adstp(Adstp::STOP); | ||
| 107 | }); | ||
| 108 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 109 | // ADC is completely stopped | ||
| 110 | while T::regs().cr().read().adstart() {} | ||
| 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 | }); | ||
| 118 | } | ||
| 119 | |||
| 120 | fn convert() -> u16 { | ||
| 121 | T::regs().isr().modify(|reg| { | ||
| 122 | reg.set_eos(true); | ||
| 123 | reg.set_eoc(true); | ||
| 124 | }); | ||
| 125 | |||
| 126 | // Start conversion | ||
| 127 | T::regs().cr().modify(|reg| { | ||
| 128 | reg.set_adstart(true); | ||
| 129 | }); | ||
| 130 | |||
| 131 | while !T::regs().isr().read().eos() { | ||
| 132 | // spin | ||
| 133 | } | ||
| 134 | |||
| 135 | T::regs().dr().read().0 as u16 | ||
| 136 | } | ||
| 137 | |||
| 138 | fn configure_dma(conversion_mode: ConversionMode) { | ||
| 139 | T::regs().isr().modify(|reg| { | ||
| 140 | reg.set_ovr(true); | ||
| 141 | }); | ||
| 142 | |||
| 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 | }); | ||
| 151 | |||
| 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 | }); | ||
| 163 | |||
| 164 | T::regs().cfgr().modify(|r| { | ||
| 165 | r.set_extsel(trigger.channel); | ||
| 166 | r.set_exten(trigger.edge); | ||
| 167 | }); | ||
| 168 | |||
| 169 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 170 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 177 | T::regs().cr().modify(|w| w.set_aden(false)); | ||
| 178 | |||
| 179 | // Set sequence length | ||
| 180 | T::regs().sqr1().modify(|w| { | ||
| 181 | w.set_l(sequence.len() as u8 - 1); | ||
| 182 | }); | ||
| 183 | |||
| 184 | #[cfg(stm32g4)] | ||
| 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(); | ||
| 192 | |||
| 193 | // Configure channels and ranks | ||
| 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 | } | ||
| 201 | |||
| 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); | ||
| 241 | } | ||
| 122 | } | 242 | } |
| 123 | 243 | ||
| 124 | impl<'d, T: Instance> Adc<'d, T> { | 244 | impl<'d, T: Instance + AnyInstance> Adc<'d, T> { |
| 125 | /// Create a new ADC driver. | 245 | /// Create a new ADC driver. |
| 126 | pub fn new(adc: Peri<'d, T>) -> Self { | 246 | pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 127 | rcc::enable_and_reset::<T>(); | 247 | rcc::enable_and_reset::<T>(); |
| 128 | 248 | ||
| 129 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 249 | let prescaler = from_ker_ck(T::frequency()); |
| 130 | 250 | ||
| 131 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 251 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 132 | 252 | ||
| 133 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 253 | let frequency = T::frequency() / prescaler; |
| 134 | trace!("ADC frequency set to {}", frequency); | 254 | trace!("ADC frequency set to {}", frequency); |
| 135 | 255 | ||
| 136 | if frequency > MAX_ADC_CLK_FREQ { | 256 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -173,7 +293,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 173 | 293 | ||
| 174 | blocking_delay_us(20); | 294 | blocking_delay_us(20); |
| 175 | 295 | ||
| 176 | Self::enable(); | 296 | T::enable(); |
| 177 | 297 | ||
| 178 | // single conversion mode, software trigger | 298 | // single conversion mode, software trigger |
| 179 | T::regs().cfgr().modify(|w| { | 299 | T::regs().cfgr().modify(|w| { |
| @@ -181,28 +301,34 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 181 | w.set_exten(Exten::DISABLED); | 301 | w.set_exten(Exten::DISABLED); |
| 182 | }); | 302 | }); |
| 183 | 303 | ||
| 184 | Self { adc } | 304 | if let Some(dual) = config.dual_mode { |
| 185 | } | 305 | T::common_regs().ccr().modify(|reg| { |
| 306 | reg.set_dual(dual); | ||
| 307 | }) | ||
| 308 | } | ||
| 186 | 309 | ||
| 187 | fn enable() { | 310 | if let Some(resolution) = config.resolution { |
| 188 | // Make sure bits are off | 311 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| 189 | while T::regs().cr().read().addis() { | ||
| 190 | // spin | ||
| 191 | } | 312 | } |
| 192 | 313 | ||
| 193 | if !T::regs().cr().read().aden() { | 314 | #[cfg(stm32g4)] |
| 194 | // Enable ADC | 315 | if let Some(shift) = config.oversampling_shift { |
| 195 | T::regs().isr().modify(|reg| { | 316 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 196 | reg.set_adrdy(true); | 317 | } |
| 197 | }); | ||
| 198 | T::regs().cr().modify(|reg| { | ||
| 199 | reg.set_aden(true); | ||
| 200 | }); | ||
| 201 | 318 | ||
| 202 | while !T::regs().isr().read().adrdy() { | 319 | #[cfg(stm32g4)] |
| 203 | // spin | 320 | if let Some(ratio) = config.oversampling_ratio { |
| 204 | } | 321 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); |
| 205 | } | 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 } | ||
| 206 | } | 332 | } |
| 207 | 333 | ||
| 208 | /// Enable reading the voltage reference internal channel. | 334 | /// Enable reading the voltage reference internal channel. |
| @@ -241,26 +367,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 241 | super::Vbat {} | 367 | super::Vbat {} |
| 242 | } | 368 | } |
| 243 | 369 | ||
| 244 | /// Set oversampling shift. | ||
| 245 | #[cfg(stm32g4)] | ||
| 246 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 247 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 248 | } | ||
| 249 | |||
| 250 | /// Set oversampling ratio. | ||
| 251 | #[cfg(stm32g4)] | ||
| 252 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 253 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 254 | } | ||
| 255 | |||
| 256 | /// Enable oversampling in regular mode. | ||
| 257 | #[cfg(stm32g4)] | ||
| 258 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 259 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 260 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 261 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 262 | } | ||
| 263 | |||
| 264 | // Reads that are not implemented as INJECTED in "blocking_read" | 370 | // Reads that are not implemented as INJECTED in "blocking_read" |
| 265 | // #[cfg(stm32g4)] | 371 | // #[cfg(stm32g4)] |
| 266 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | 372 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { |
| @@ -274,311 +380,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 274 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | 380 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); |
| 275 | // } | 381 | // } |
| 276 | 382 | ||
| 277 | /// Set the ADC resolution. | ||
| 278 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 279 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Perform a single conversion. | ||
| 283 | fn convert(&mut self) -> u16 { | ||
| 284 | T::regs().isr().modify(|reg| { | ||
| 285 | reg.set_eos(true); | ||
| 286 | reg.set_eoc(true); | ||
| 287 | }); | ||
| 288 | |||
| 289 | // Start conversion | ||
| 290 | T::regs().cr().modify(|reg| { | ||
| 291 | reg.set_adstart(true); | ||
| 292 | }); | ||
| 293 | |||
| 294 | while !T::regs().isr().read().eos() { | ||
| 295 | // spin | ||
| 296 | } | ||
| 297 | |||
| 298 | T::regs().dr().read().0 as u16 | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Read an ADC pin. | ||
| 302 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 303 | channel.setup(); | ||
| 304 | |||
| 305 | Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); | ||
| 306 | |||
| 307 | #[cfg(stm32h7)] | ||
| 308 | { | ||
| 309 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 310 | T::regs() | ||
| 311 | .pcsel() | ||
| 312 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); | ||
| 313 | } | ||
| 314 | |||
| 315 | self.convert() | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Start regular adc conversion | ||
| 319 | pub(super) fn start() { | ||
| 320 | T::regs().cr().modify(|reg| { | ||
| 321 | reg.set_adstart(true); | ||
| 322 | }); | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Stop regular conversions | ||
| 326 | pub(super) fn stop() { | ||
| 327 | Self::stop_regular_conversions(); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Teardown method for stopping regular ADC conversions | ||
| 331 | pub(super) fn teardown_dma() { | ||
| 332 | Self::stop_regular_conversions(); | ||
| 333 | |||
| 334 | // Disable dma control | ||
| 335 | T::regs().cfgr().modify(|reg| { | ||
| 336 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 337 | }); | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Read one or multiple ADC regular channels using DMA. | ||
| 341 | /// | ||
| 342 | /// `sequence` iterator and `readings` must have the same length. | ||
| 343 | /// | ||
| 344 | /// Example | ||
| 345 | /// ```rust,ignore | ||
| 346 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 347 | /// | ||
| 348 | /// let mut adc = Adc::new(p.ADC1); | ||
| 349 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 350 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 351 | /// let mut measurements = [0u16; 2]; | ||
| 352 | /// | ||
| 353 | /// adc.read( | ||
| 354 | /// p.DMA1_CH2.reborrow(), | ||
| 355 | /// [ | ||
| 356 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 357 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 358 | /// ] | ||
| 359 | /// .into_iter(), | ||
| 360 | /// &mut measurements, | ||
| 361 | /// ) | ||
| 362 | /// .await; | ||
| 363 | /// defmt::info!("measurements: {}", measurements); | ||
| 364 | /// ``` | ||
| 365 | /// | ||
| 366 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 367 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 368 | pub async fn read( | ||
| 369 | &mut self, | ||
| 370 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 371 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 372 | readings: &mut [u16], | ||
| 373 | ) { | ||
| 374 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 375 | assert!( | ||
| 376 | sequence.len() == readings.len(), | ||
| 377 | "Sequence length must be equal to readings length" | ||
| 378 | ); | ||
| 379 | assert!( | ||
| 380 | sequence.len() <= 16, | ||
| 381 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 382 | ); | ||
| 383 | |||
| 384 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 385 | Self::stop_regular_conversions(); | ||
| 386 | Self::enable(); | ||
| 387 | |||
| 388 | Self::configure_sequence( | ||
| 389 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 390 | ); | ||
| 391 | |||
| 392 | // Set continuous mode with oneshot dma. | ||
| 393 | // Clear overrun flag before starting transfer. | ||
| 394 | T::regs().isr().modify(|reg| { | ||
| 395 | reg.set_ovr(true); | ||
| 396 | }); | ||
| 397 | |||
| 398 | T::regs().cfgr().modify(|reg| { | ||
| 399 | reg.set_discen(false); | ||
| 400 | reg.set_cont(true); | ||
| 401 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 402 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 403 | }); | ||
| 404 | |||
| 405 | let request = rx_dma.request(); | ||
| 406 | let transfer = unsafe { | ||
| 407 | Transfer::new_read( | ||
| 408 | rx_dma, | ||
| 409 | request, | ||
| 410 | T::regs().dr().as_ptr() as *mut u16, | ||
| 411 | readings, | ||
| 412 | Default::default(), | ||
| 413 | ) | ||
| 414 | }; | ||
| 415 | |||
| 416 | // Start conversion | ||
| 417 | T::regs().cr().modify(|reg| { | ||
| 418 | reg.set_adstart(true); | ||
| 419 | }); | ||
| 420 | |||
| 421 | // Wait for conversion sequence to finish. | ||
| 422 | transfer.await; | ||
| 423 | |||
| 424 | // Ensure conversions are finished. | ||
| 425 | Self::stop_regular_conversions(); | ||
| 426 | |||
| 427 | // Reset configuration. | ||
| 428 | T::regs().cfgr().modify(|reg| { | ||
| 429 | reg.set_cont(false); | ||
| 430 | }); | ||
| 431 | } | ||
| 432 | |||
| 433 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 434 | // Set sequence length | ||
| 435 | T::regs().sqr1().modify(|w| { | ||
| 436 | w.set_l(sequence.len() as u8 - 1); | ||
| 437 | }); | ||
| 438 | |||
| 439 | // Configure channels and ranks | ||
| 440 | for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { | ||
| 441 | let sample_time = sample_time.into(); | ||
| 442 | if ch <= 9 { | ||
| 443 | T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 444 | } else { | ||
| 445 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 446 | } | ||
| 447 | |||
| 448 | match _i { | ||
| 449 | 0..=3 => { | ||
| 450 | T::regs().sqr1().modify(|w| { | ||
| 451 | w.set_sq(_i, ch); | ||
| 452 | }); | ||
| 453 | } | ||
| 454 | 4..=8 => { | ||
| 455 | T::regs().sqr2().modify(|w| { | ||
| 456 | w.set_sq(_i - 4, ch); | ||
| 457 | }); | ||
| 458 | } | ||
| 459 | 9..=13 => { | ||
| 460 | T::regs().sqr3().modify(|w| { | ||
| 461 | w.set_sq(_i - 9, ch); | ||
| 462 | }); | ||
| 463 | } | ||
| 464 | 14..=15 => { | ||
| 465 | T::regs().sqr4().modify(|w| { | ||
| 466 | w.set_sq(_i - 14, ch); | ||
| 467 | }); | ||
| 468 | } | ||
| 469 | _ => unreachable!(), | ||
| 470 | } | ||
| 471 | |||
| 472 | #[cfg(stm32g4)] | ||
| 473 | { | ||
| 474 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc | ||
| 475 | |||
| 476 | T::regs().difsel().modify(|w| { | ||
| 477 | w.set_difsel( | ||
| 478 | ch.into(), | ||
| 479 | if is_differential { | ||
| 480 | Difsel::DIFFERENTIAL | ||
| 481 | } else { | ||
| 482 | Difsel::SINGLE_ENDED | ||
| 483 | }, | ||
| 484 | ); | ||
| 485 | }); | ||
| 486 | |||
| 487 | T::regs().cr().modify(|w| w.set_aden(true)); // enable adc | ||
| 488 | } | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | /// Set external trigger for regular conversion sequence | ||
| 493 | fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 494 | T::regs().cfgr().modify(|r| { | ||
| 495 | r.set_extsel(trigger.channel); | ||
| 496 | r.set_exten(trigger.edge); | ||
| 497 | }); | ||
| 498 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 499 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 500 | } | ||
| 501 | |||
| 502 | // Dual ADC mode selection | ||
| 503 | pub fn configure_dual_mode(&mut self, val: Dual) { | ||
| 504 | T::common_regs().ccr().modify(|reg| { | ||
| 505 | reg.set_dual(val); | ||
| 506 | }) | ||
| 507 | } | ||
| 508 | |||
| 509 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 510 | /// | ||
| 511 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 512 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 513 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 514 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 515 | /// defines the period at which the buffer should be read. | ||
| 516 | /// | ||
| 517 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 518 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 519 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 520 | /// the buffer length should be `3 * 40 = 120`. | ||
| 521 | /// | ||
| 522 | /// # Parameters | ||
| 523 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 524 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 525 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 526 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 527 | /// | ||
| 528 | /// # Returns | ||
| 529 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 530 | pub fn into_ring_buffered<'a>( | ||
| 531 | mut self, | ||
| 532 | dma: Peri<'a, impl RxDma<T>>, | ||
| 533 | dma_buf: &'a mut [u16], | ||
| 534 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 535 | mode: RegularConversionMode, | ||
| 536 | ) -> RingBufferedAdc<'a, T> { | ||
| 537 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 538 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 539 | assert!( | ||
| 540 | sequence.len() <= 16, | ||
| 541 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 542 | ); | ||
| 543 | // reset conversions and enable the adc | ||
| 544 | Self::stop_regular_conversions(); | ||
| 545 | Self::enable(); | ||
| 546 | |||
| 547 | //adc side setup | ||
| 548 | Self::configure_sequence( | ||
| 549 | sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), | ||
| 550 | ); | ||
| 551 | |||
| 552 | // Clear overrun flag before starting transfer. | ||
| 553 | T::regs().isr().modify(|reg| { | ||
| 554 | reg.set_ovr(true); | ||
| 555 | }); | ||
| 556 | |||
| 557 | T::regs().cfgr().modify(|reg| { | ||
| 558 | reg.set_discen(false); // Convert all channels for each trigger | ||
| 559 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 560 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 561 | }); | ||
| 562 | |||
| 563 | match mode { | ||
| 564 | RegularConversionMode::Continuous => { | ||
| 565 | T::regs().cfgr().modify(|reg| { | ||
| 566 | reg.set_cont(true); | ||
| 567 | }); | ||
| 568 | } | ||
| 569 | RegularConversionMode::Triggered(trigger) => { | ||
| 570 | T::regs().cfgr().modify(|r| { | ||
| 571 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 572 | }); | ||
| 573 | self.set_regular_conversion_trigger(trigger); | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | mem::forget(self); | ||
| 578 | |||
| 579 | RingBufferedAdc::new(dma, dma_buf) | ||
| 580 | } | ||
| 581 | |||
| 582 | /// Configures the ADC for injected conversions. | 383 | /// Configures the ADC for injected conversions. |
| 583 | /// | 384 | /// |
| 584 | /// Injected conversions are separate from the regular conversion sequence and are typically | 385 | /// Injected conversions are separate from the regular conversion sequence and are typically |
| @@ -607,7 +408,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 607 | /// - Accessing samples beyond `N` will result in a panic; use the returned type | 408 | /// - Accessing samples beyond `N` will result in a panic; use the returned type |
| 608 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. | 409 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. |
| 609 | pub fn setup_injected_conversions<'a, const N: usize>( | 410 | pub fn setup_injected_conversions<'a, const N: usize>( |
| 610 | mut self, | 411 | self, |
| 611 | sequence: [(AnyAdcChannel<T>, SampleTime); N], | 412 | sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 612 | trigger: ConversionTrigger, | 413 | trigger: ConversionTrigger, |
| 613 | interrupt: bool, | 414 | interrupt: bool, |
| @@ -619,8 +420,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 619 | NR_INJECTED_RANKS | 420 | NR_INJECTED_RANKS |
| 620 | ); | 421 | ); |
| 621 | 422 | ||
| 622 | Self::stop_regular_conversions(); | 423 | T::enable(); |
| 623 | Self::enable(); | ||
| 624 | 424 | ||
| 625 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); | 425 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); |
| 626 | 426 | ||
| @@ -649,8 +449,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 649 | 449 | ||
| 650 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); | 450 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); |
| 651 | 451 | ||
| 652 | self.set_injected_conversion_trigger(trigger); | 452 | // Set external trigger for injected conversion sequence |
| 653 | self.enable_injected_eos_interrupt(interrupt); | 453 | // Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 454 | T::regs().jsqr().modify(|r| { | ||
| 455 | r.set_jextsel(trigger.channel); | ||
| 456 | r.set_jexten(trigger.edge); | ||
| 457 | }); | ||
| 458 | |||
| 459 | // Enable end of injected sequence interrupt | ||
| 460 | T::regs().ier().modify(|r| r.set_jeosie(interrupt)); | ||
| 461 | |||
| 654 | Self::start_injected_conversions(); | 462 | Self::start_injected_conversions(); |
| 655 | 463 | ||
| 656 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels | 464 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels |
| @@ -683,12 +491,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 683 | self, | 491 | self, |
| 684 | dma: Peri<'a, impl RxDma<T>>, | 492 | dma: Peri<'a, impl RxDma<T>>, |
| 685 | dma_buf: &'a mut [u16], | 493 | dma_buf: &'a mut [u16], |
| 686 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | 494 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, |
| 687 | regular_conversion_mode: RegularConversionMode, | 495 | regular_conversion_mode: RegularConversionMode, |
| 688 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], | 496 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 689 | injected_trigger: ConversionTrigger, | 497 | injected_trigger: ConversionTrigger, |
| 690 | injected_interrupt: bool, | 498 | injected_interrupt: bool, |
| 691 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | 499 | ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { |
| 692 | unsafe { | 500 | unsafe { |
| 693 | ( | 501 | ( |
| 694 | Self { | 502 | Self { |
| @@ -721,32 +529,6 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 721 | reg.set_jadstart(true); | 529 | reg.set_jadstart(true); |
| 722 | }); | 530 | }); |
| 723 | } | 531 | } |
| 724 | |||
| 725 | /// Set external trigger for injected conversion sequence | ||
| 726 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 | ||
| 727 | fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 728 | T::regs().jsqr().modify(|r| { | ||
| 729 | r.set_jextsel(trigger.channel); | ||
| 730 | r.set_jexten(trigger.edge); | ||
| 731 | }); | ||
| 732 | } | ||
| 733 | |||
| 734 | /// Enable end of injected sequence interrupt | ||
| 735 | fn enable_injected_eos_interrupt(&mut self, enable: bool) { | ||
| 736 | T::regs().ier().modify(|r| r.set_jeosie(enable)); | ||
| 737 | } | ||
| 738 | |||
| 739 | // Stop regular conversions | ||
| 740 | fn stop_regular_conversions() { | ||
| 741 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 742 | T::regs().cr().modify(|reg| { | ||
| 743 | reg.set_adstp(Adstp::STOP); | ||
| 744 | }); | ||
| 745 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 746 | // ADC is completely stopped | ||
| 747 | while T::regs().cr().read().adstart() {} | ||
| 748 | } | ||
| 749 | } | ||
| 750 | } | 532 | } |
| 751 | 533 | ||
| 752 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | 534 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index f9f1bba2a..ccaa5d1b2 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -5,9 +5,9 @@ use core::sync::atomic::{Ordering, compiler_fence}; | |||
| 5 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 6 | 6 | ||
| 7 | use super::{AnyAdcChannel, SampleTime}; | 7 | use super::{AnyAdcChannel, SampleTime}; |
| 8 | use crate::adc::Adc; | ||
| 9 | #[allow(unused_imports)] | 8 | #[allow(unused_imports)] |
| 10 | use crate::adc::Instance; | 9 | use crate::adc::Instance; |
| 10 | use crate::adc::{Adc, AnyInstance}; | ||
| 11 | 11 | ||
| 12 | /// Injected ADC sequence with owned channels. | 12 | /// Injected ADC sequence with owned channels. |
| 13 | pub struct InjectedAdc<T: Instance, const N: usize> { | 13 | pub struct InjectedAdc<T: Instance, const N: usize> { |
| @@ -36,9 +36,9 @@ impl<T: Instance, const N: usize> InjectedAdc<T, N> { | |||
| 36 | } | 36 | } |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { | 39 | impl<T: Instance + AnyInstance, const N: usize> Drop for InjectedAdc<T, N> { |
| 40 | fn drop(&mut self) { | 40 | fn drop(&mut self) { |
| 41 | Adc::<T>::teardown_dma(); | 41 | T::stop(); |
| 42 | compiler_fence(Ordering::SeqCst); | 42 | compiler_fence(Ordering::SeqCst); |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index e321c4fa1..6d53d9b91 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -17,6 +17,9 @@ | |||
| 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)] |
| @@ -25,26 +28,25 @@ pub use _version::*; | |||
| 25 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 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 | } | 52 | } |
| @@ -87,21 +89,262 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 87 | } | 89 | } |
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | /// Performs a busy-wait delay for a specified number of microseconds. | 92 | // Temporary patch for ADCs that have not implemented the standard iface yet |
| 91 | #[allow(unused)] | 93 | #[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] |
| 92 | pub(crate) fn blocking_delay_us(us: u32) { | 94 | trait_set::trait_set! { |
| 93 | cfg_if::cfg_if! { | 95 | pub trait AnyInstance = Instance; |
| 94 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called | 96 | } |
| 95 | // as in sometimes 15 us (1 tick) would take > 20 seconds. | 97 | |
| 96 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { | 98 | #[cfg(any( |
| 97 | let duration = embassy_time::Duration::from_micros(us 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 |
| 98 | embassy_time::block_for(duration); | 100 | ))] |
| 99 | } else { | 101 | pub trait BasicAnyInstance { |
| 100 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | 102 | type SampleTime; |
| 101 | let us = us as u64; | 103 | } |
| 102 | let cycles = freq * us / 1_000_000; | 104 | |
| 103 | cortex_m::asm::delay(cycles as u32); | 105 | #[cfg(any( |
| 104 | } | 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) | ||
| 105 | } | 348 | } |
| 106 | } | 349 | } |
| 107 | 350 | ||
| @@ -143,6 +386,14 @@ impl SpecialChannel for Temperature {} | |||
| 143 | pub struct Vbat; | 386 | pub struct Vbat; |
| 144 | impl SpecialChannel for Vbat {} | 387 | impl SpecialChannel for Vbat {} |
| 145 | 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 | |||
| 146 | /// ADC instance. | 397 | /// ADC instance. |
| 147 | #[cfg(not(any( | 398 | #[cfg(not(any( |
| 148 | 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, |
| @@ -187,9 +438,9 @@ pub struct AnyAdcChannel<T> { | |||
| 187 | is_differential: bool, | 438 | is_differential: bool, |
| 188 | _phantom: PhantomData<T>, | 439 | _phantom: PhantomData<T>, |
| 189 | } | 440 | } |
| 190 | impl_peripheral!(AnyAdcChannel<T: Instance>); | 441 | impl_peripheral!(AnyAdcChannel<T: AnyInstance>); |
| 191 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} | 442 | impl<T: AnyInstance> AdcChannel<T> for AnyAdcChannel<T> {} |
| 192 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | 443 | impl<T: AnyInstance> SealedAdcChannel<T> for AnyAdcChannel<T> { |
| 193 | fn channel(&self) -> u8 { | 444 | fn channel(&self) -> u8 { |
| 194 | self.channel | 445 | self.channel |
| 195 | } | 446 | } |
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 024c6acdc..5437866d3 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs | |||
| @@ -4,7 +4,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; | |||
| 4 | #[allow(unused_imports)] | 4 | #[allow(unused_imports)] |
| 5 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 6 | 6 | ||
| 7 | use crate::adc::Adc; | 7 | use crate::adc::AnyInstance; |
| 8 | #[allow(unused_imports)] | 8 | #[allow(unused_imports)] |
| 9 | use crate::adc::{Instance, RxDma}; | 9 | use crate::adc::{Instance, RxDma}; |
| 10 | #[allow(unused_imports)] | 10 | #[allow(unused_imports)] |
| @@ -19,7 +19,7 @@ pub struct RingBufferedAdc<'d, T: Instance> { | |||
| 19 | ring_buf: ReadableRingBuffer<'d, u16>, | 19 | ring_buf: ReadableRingBuffer<'d, u16>, |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | impl<'d, T: Instance> RingBufferedAdc<'d, T> { | 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 { | 23 | pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self { |
| 24 | //dma side setup | 24 | //dma side setup |
| 25 | let opts = TransferOptions { | 25 | let opts = TransferOptions { |
| @@ -45,12 +45,10 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 45 | compiler_fence(Ordering::SeqCst); | 45 | compiler_fence(Ordering::SeqCst); |
| 46 | self.ring_buf.start(); | 46 | self.ring_buf.start(); |
| 47 | 47 | ||
| 48 | Adc::<T>::start(); | 48 | T::start(); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | pub fn stop(&mut self) { | 51 | pub fn stop(&mut self) { |
| 52 | Adc::<T>::stop(); | ||
| 53 | |||
| 54 | self.ring_buf.request_pause(); | 52 | self.ring_buf.request_pause(); |
| 55 | 53 | ||
| 56 | compiler_fence(Ordering::SeqCst); | 54 | compiler_fence(Ordering::SeqCst); |
| @@ -161,7 +159,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 161 | return Ok(len); | 159 | return Ok(len); |
| 162 | } | 160 | } |
| 163 | Err(_) => { | 161 | Err(_) => { |
| 164 | self.stop(); | 162 | self.ring_buf.request_pause(); |
| 165 | 163 | ||
| 166 | return Err(OverrunError); | 164 | return Err(OverrunError); |
| 167 | } | 165 | } |
| @@ -170,9 +168,9 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 170 | } | 168 | } |
| 171 | } | 169 | } |
| 172 | 170 | ||
| 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | 171 | impl<T: Instance + AnyInstance> Drop for RingBufferedAdc<'_, T> { |
| 174 | fn drop(&mut self) { | 172 | fn drop(&mut self) { |
| 175 | Adc::<T>::teardown_dma(); | 173 | T::stop(); |
| 176 | 174 | ||
| 177 | compiler_fence(Ordering::SeqCst); | 175 | compiler_fence(Ordering::SeqCst); |
| 178 | 176 | ||
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index efa1cc68c..3c4431ae0 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,15 +1,12 @@ | |||
| 1 | use core::mem; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 3 | 2 | ||
| 4 | use super::{Temperature, Vbat, VrefInt, blocking_delay_us}; | 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 5 | use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; | 4 | use crate::adc::{Adc, Instance, Resolution, SampleTime}; |
| 6 | use crate::pac::adc::vals; | 5 | use crate::pac::adc::vals; |
| 6 | pub use crate::pac::adccommon::vals::Adcpre; | ||
| 7 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 8 | use crate::{Peri, rcc}; | 8 | use crate::{Peri, rcc}; |
| 9 | 9 | ||
| 10 | mod ringbuffered; | ||
| 11 | pub use ringbuffered::RingBufferedAdc; | ||
| 12 | |||
| 13 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | 10 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 14 | r.sr().modify(|regs| { | 11 | r.sr().modify(|regs| { |
| 15 | regs.set_eoc(false); | 12 | regs.set_eoc(false); |
| @@ -54,156 +51,75 @@ impl Temperature { | |||
| 54 | } | 51 | } |
| 55 | } | 52 | } |
| 56 | 53 | ||
| 57 | enum Prescaler { | 54 | fn from_pclk2(freq: Hertz) -> Adcpre { |
| 58 | Div2, | 55 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). |
| 59 | Div4, | 56 | #[cfg(stm32f2)] |
| 60 | Div6, | 57 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); |
| 61 | Div8, | 58 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. |
| 62 | } | 59 | #[cfg(not(stm32f2))] |
| 63 | 60 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | |
| 64 | impl Prescaler { | 61 | let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0); |
| 65 | fn from_pclk2(freq: Hertz) -> Self { | 62 | match raw_div { |
| 66 | // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). | 63 | 0..=1 => Adcpre::DIV2, |
| 67 | #[cfg(stm32f2)] | 64 | 2..=3 => Adcpre::DIV4, |
| 68 | const MAX_FREQUENCY: Hertz = Hertz(30_000_000); | 65 | 4..=5 => Adcpre::DIV6, |
| 69 | // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. | 66 | 6..=7 => Adcpre::DIV8, |
| 70 | #[cfg(not(stm32f2))] | 67 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), |
| 71 | const MAX_FREQUENCY: Hertz = Hertz(36_000_000); | ||
| 72 | let raw_div = freq.0 / MAX_FREQUENCY.0; | ||
| 73 | match raw_div { | ||
| 74 | 0..=1 => Self::Div2, | ||
| 75 | 2..=3 => Self::Div4, | ||
| 76 | 4..=5 => Self::Div6, | ||
| 77 | 6..=7 => Self::Div8, | ||
| 78 | _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), | ||
| 79 | } | ||
| 80 | } | 68 | } |
| 69 | } | ||
| 81 | 70 | ||
| 82 | fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { | 71 | /// ADC configuration |
| 83 | match self { | 72 | #[derive(Default)] |
| 84 | Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, | 73 | pub struct AdcConfig { |
| 85 | Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, | 74 | resolution: Option<Resolution>, |
| 86 | Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, | ||
| 87 | Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | 75 | } |
| 91 | 76 | ||
| 92 | impl<'d, T> Adc<'d, T> | 77 | impl<T: Instance> super::SealedAnyInstance for T { |
| 93 | where | 78 | fn dr() -> *mut u16 { |
| 94 | T: Instance, | 79 | T::regs().dr().as_ptr() as *mut u16 |
| 95 | { | 80 | } |
| 96 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 97 | rcc::enable_and_reset::<T>(); | ||
| 98 | 81 | ||
| 99 | let presc = Prescaler::from_pclk2(T::frequency()); | 82 | fn enable() { |
| 100 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); | ||
| 101 | T::regs().cr2().modify(|reg| { | 83 | T::regs().cr2().modify(|reg| { |
| 102 | reg.set_adon(true); | 84 | reg.set_adon(true); |
| 103 | }); | 85 | }); |
| 104 | 86 | ||
| 105 | blocking_delay_us(3); | 87 | blocking_delay_us(3); |
| 106 | |||
| 107 | Self { adc } | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 111 | /// | ||
| 112 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 113 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 114 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 115 | /// | ||
| 116 | /// `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. | ||
| 117 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 118 | /// | ||
| 119 | /// [`read`]: #method.read | ||
| 120 | pub fn into_ring_buffered<'a>( | ||
| 121 | self, | ||
| 122 | dma: Peri<'d, impl RxDma<T>>, | ||
| 123 | dma_buf: &'d mut [u16], | ||
| 124 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 125 | ) -> RingBufferedAdc<'d, T> { | ||
| 126 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 127 | |||
| 128 | Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { | ||
| 129 | channel.setup(); | ||
| 130 | |||
| 131 | (channel.channel, sample_time) | ||
| 132 | })); | ||
| 133 | compiler_fence(Ordering::SeqCst); | ||
| 134 | |||
| 135 | Self::setup_dma(); | ||
| 136 | |||
| 137 | // Don't disable the clock | ||
| 138 | mem::forget(self); | ||
| 139 | |||
| 140 | RingBufferedAdc::new(dma, dma_buf) | ||
| 141 | } | ||
| 142 | |||
| 143 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 144 | channel.setup(); | ||
| 145 | |||
| 146 | // Configure ADC | ||
| 147 | let channel = channel.channel(); | ||
| 148 | |||
| 149 | Self::configure_sequence([(channel, sample_time)].into_iter()); | ||
| 150 | Self::blocking_convert() | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Enables internal voltage reference and returns [VrefInt], which can be used in | ||
| 154 | /// [Adc::read_internal()] to perform conversion. | ||
| 155 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 156 | T::common_regs().ccr().modify(|reg| { | ||
| 157 | reg.set_tsvrefe(true); | ||
| 158 | }); | ||
| 159 | |||
| 160 | VrefInt {} | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Enables internal temperature sensor and returns [Temperature], which can be used in | ||
| 164 | /// [Adc::read_internal()] to perform conversion. | ||
| 165 | /// | ||
| 166 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, | ||
| 167 | /// temperature sensor will return vbat value. | ||
| 168 | pub fn enable_temperature(&self) -> Temperature { | ||
| 169 | T::common_regs().ccr().modify(|reg| { | ||
| 170 | reg.set_tsvrefe(true); | ||
| 171 | }); | ||
| 172 | |||
| 173 | Temperature {} | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Enables vbat input and returns [Vbat], which can be used in | ||
| 177 | /// [Adc::read_internal()] to perform conversion. | ||
| 178 | pub fn enable_vbat(&self) -> Vbat { | ||
| 179 | T::common_regs().ccr().modify(|reg| { | ||
| 180 | reg.set_vbate(true); | ||
| 181 | }); | ||
| 182 | |||
| 183 | Vbat {} | ||
| 184 | } | 88 | } |
| 185 | 89 | ||
| 186 | pub(super) fn start() { | 90 | fn start() { |
| 187 | // Begin ADC conversions | 91 | // Begin ADC conversions |
| 188 | T::regs().cr2().modify(|reg| { | 92 | T::regs().cr2().modify(|reg| { |
| 189 | reg.set_adon(true); | ||
| 190 | reg.set_swstart(true); | 93 | reg.set_swstart(true); |
| 191 | }); | 94 | }); |
| 192 | } | 95 | } |
| 193 | 96 | ||
| 194 | pub(super) fn stop() { | 97 | fn stop() { |
| 98 | let r = T::regs(); | ||
| 99 | |||
| 195 | // Stop ADC | 100 | // Stop ADC |
| 196 | T::regs().cr2().modify(|reg| { | 101 | r.cr2().modify(|reg| { |
| 197 | // Stop ADC | 102 | // Stop ADC |
| 198 | reg.set_swstart(false); | 103 | reg.set_swstart(false); |
| 104 | // Stop ADC | ||
| 105 | reg.set_adon(false); | ||
| 106 | // Stop DMA | ||
| 107 | reg.set_dma(false); | ||
| 199 | }); | 108 | }); |
| 200 | } | ||
| 201 | 109 | ||
| 202 | pub fn set_resolution(&mut self, resolution: Resolution) { | 110 | r.cr1().modify(|w| { |
| 203 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); | 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); | ||
| 204 | } | 120 | } |
| 205 | 121 | ||
| 206 | pub(super) fn blocking_convert() -> u16 { | 122 | fn convert() -> u16 { |
| 207 | // clear end of conversion flag | 123 | // clear end of conversion flag |
| 208 | T::regs().sr().modify(|reg| { | 124 | T::regs().sr().modify(|reg| { |
| 209 | reg.set_eoc(false); | 125 | reg.set_eoc(false); |
| @@ -224,7 +140,44 @@ where | |||
| 224 | T::regs().dr().read().0 as u16 | 140 | T::regs().dr().read().0 as u16 |
| 225 | } | 141 | } |
| 226 | 142 | ||
| 227 | pub(super) fn configure_sequence(sequence: impl ExactSizeIterator<Item = (u8, SampleTime)>) { | 143 | fn configure_dma(conversion_mode: ConversionMode) { |
| 144 | match conversion_mode { | ||
| 145 | ConversionMode::Repeated(_) => { | ||
| 146 | let r = T::regs(); | ||
| 147 | |||
| 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 | } | ||
| 179 | |||
| 180 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { | ||
| 228 | T::regs().cr2().modify(|reg| { | 181 | T::regs().cr2().modify(|reg| { |
| 229 | reg.set_adon(true); | 182 | reg.set_adon(true); |
| 230 | }); | 183 | }); |
| @@ -234,7 +187,7 @@ where | |||
| 234 | r.set_l((sequence.len() - 1).try_into().unwrap()); | 187 | r.set_l((sequence.len() - 1).try_into().unwrap()); |
| 235 | }); | 188 | }); |
| 236 | 189 | ||
| 237 | for (i, (ch, sample_time)) in sequence.enumerate() { | 190 | for (i, ((ch, _), sample_time)) in sequence.enumerate() { |
| 238 | // Set the channel in the right sequence field. | 191 | // Set the channel in the right sequence field. |
| 239 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); | 192 | T::regs().sqr3().modify(|w| w.set_sq(i, ch)); |
| 240 | 193 | ||
| @@ -246,63 +199,61 @@ where | |||
| 246 | } | 199 | } |
| 247 | } | 200 | } |
| 248 | } | 201 | } |
| 202 | } | ||
| 249 | 203 | ||
| 250 | pub(super) fn setup_dma() { | 204 | impl<'d, T> Adc<'d, T> |
| 251 | let r = T::regs(); | 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()) | ||
| 210 | } | ||
| 252 | 211 | ||
| 253 | // Clear all interrupts | 212 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 254 | r.sr().modify(|regs| { | 213 | rcc::enable_and_reset::<T>(); |
| 255 | regs.set_eoc(false); | ||
| 256 | regs.set_ovr(false); | ||
| 257 | regs.set_strt(false); | ||
| 258 | }); | ||
| 259 | 214 | ||
| 260 | r.cr1().modify(|w| { | 215 | let presc = from_pclk2(T::frequency()); |
| 261 | // Enable interrupt for end of conversion | 216 | T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); |
| 262 | w.set_eocie(true); | 217 | T::enable(); |
| 263 | // Enable interrupt for overrun | ||
| 264 | w.set_ovrie(true); | ||
| 265 | // Scanning converisons of multiple channels | ||
| 266 | w.set_scan(true); | ||
| 267 | // Continuous conversion mode | ||
| 268 | w.set_discen(false); | ||
| 269 | }); | ||
| 270 | 218 | ||
| 271 | r.cr2().modify(|w| { | 219 | if let Some(resolution) = config.resolution { |
| 272 | // Enable DMA mode | 220 | T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); |
| 273 | w.set_dma(true); | 221 | } |
| 274 | // Enable continuous conversions | ||
| 275 | w.set_cont(true); | ||
| 276 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 277 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 278 | // EOC flag is set at the end of each conversion. | ||
| 279 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 280 | }); | ||
| 281 | } | ||
| 282 | 222 | ||
| 283 | pub(super) fn teardown_dma() { | 223 | Self { adc } |
| 284 | let r = T::regs(); | 224 | } |
| 285 | 225 | ||
| 286 | // Stop ADC | 226 | /// Enables internal voltage reference and returns [VrefInt], which can be used in |
| 287 | r.cr2().modify(|reg| { | 227 | /// [Adc::read_internal()] to perform conversion. |
| 288 | // Stop ADC | 228 | pub fn enable_vrefint(&self) -> VrefInt { |
| 289 | reg.set_swstart(false); | 229 | T::common_regs().ccr().modify(|reg| { |
| 290 | // Stop ADC | 230 | reg.set_tsvrefe(true); |
| 291 | reg.set_adon(false); | ||
| 292 | // Stop DMA | ||
| 293 | reg.set_dma(false); | ||
| 294 | }); | 231 | }); |
| 295 | 232 | ||
| 296 | r.cr1().modify(|w| { | 233 | VrefInt {} |
| 297 | // Disable interrupt for end of conversion | 234 | } |
| 298 | w.set_eocie(false); | 235 | |
| 299 | // Disable interrupt for overrun | 236 | /// Enables internal temperature sensor and returns [Temperature], which can be used in |
| 300 | w.set_ovrie(false); | 237 | /// [Adc::read_internal()] to perform conversion. |
| 238 | /// | ||
| 239 | /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, | ||
| 240 | /// temperature sensor will return vbat value. | ||
| 241 | pub fn enable_temperature(&self) -> Temperature { | ||
| 242 | T::common_regs().ccr().modify(|reg| { | ||
| 243 | reg.set_tsvrefe(true); | ||
| 301 | }); | 244 | }); |
| 302 | 245 | ||
| 303 | clear_interrupt_flags(r); | 246 | Temperature {} |
| 247 | } | ||
| 304 | 248 | ||
| 305 | compiler_fence(Ordering::SeqCst); | 249 | /// Enables vbat input and returns [Vbat], which can be used in |
| 250 | /// [Adc::read_internal()] to perform conversion. | ||
| 251 | pub fn enable_vbat(&self) -> Vbat { | ||
| 252 | T::common_regs().ccr().modify(|reg| { | ||
| 253 | reg.set_vbate(true); | ||
| 254 | }); | ||
| 255 | |||
| 256 | Vbat {} | ||
| 306 | } | 257 | } |
| 307 | } | 258 | } |
| 308 | 259 | ||
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index cbc217545..b270588c4 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 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)] |
| @@ -11,18 +11,8 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | |||
| 11 | 11 | ||
| 12 | #[allow(unused_imports)] | 12 | #[allow(unused_imports)] |
| 13 | use super::SealedAdcChannel; | 13 | use super::SealedAdcChannel; |
| 14 | use super::{ | 14 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 15 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, Temperature, Vbat, VrefInt, | 15 | use crate::adc::ConversionMode; |
| 16 | blocking_delay_us, | ||
| 17 | }; | ||
| 18 | |||
| 19 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 20 | mod ringbuffered; | ||
| 21 | |||
| 22 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 23 | use ringbuffered::RingBufferedAdc; | ||
| 24 | |||
| 25 | use crate::dma::Transfer; | ||
| 26 | use crate::{Peri, pac, rcc}; | 16 | use crate::{Peri, pac, rcc}; |
| 27 | 17 | ||
| 28 | /// Default VREF voltage used for sample conversion to millivolts. | 18 | /// Default VREF voltage used for sample conversion to millivolts. |
| @@ -75,7 +65,7 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | |||
| 75 | } | 65 | } |
| 76 | #[cfg(any(adc_h5, adc_h7rs))] | 66 | #[cfg(any(adc_h5, adc_h7rs))] |
| 77 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | 67 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| 78 | const CHANNEL: u8 = 2; | 68 | const CHANNEL: u8 = 16; |
| 79 | } | 69 | } |
| 80 | #[cfg(adc_u0)] | 70 | #[cfg(adc_u0)] |
| 81 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | 71 | impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { |
| @@ -89,10 +79,10 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | |||
| 89 | cfg_if! { | 79 | cfg_if! { |
| 90 | if #[cfg(any(adc_h5, adc_h7rs))] { | 80 | if #[cfg(any(adc_h5, adc_h7rs))] { |
| 91 | pub struct VddCore; | 81 | pub struct VddCore; |
| 92 | impl<T: Instance> AdcChannel<T> for VddCore {} | 82 | impl<T: Instance> super::AdcChannel<T> for VddCore {} |
| 93 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { | 83 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { |
| 94 | fn channel(&self) -> u8 { | 84 | fn channel(&self) -> u8 { |
| 95 | 6 | 85 | 17 |
| 96 | } | 86 | } |
| 97 | } | 87 | } |
| 98 | } | 88 | } |
| @@ -101,7 +91,7 @@ cfg_if! { | |||
| 101 | cfg_if! { | 91 | cfg_if! { |
| 102 | if #[cfg(adc_u0)] { | 92 | if #[cfg(adc_u0)] { |
| 103 | pub struct DacOut; | 93 | pub struct DacOut; |
| 104 | impl<T: Instance> AdcChannel<T> for DacOut {} | 94 | impl<T: Instance> super::AdcChannel<T> for DacOut {} |
| 105 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { | 95 | impl<T: Instance> super::SealedAdcChannel<T> for DacOut { |
| 106 | fn channel(&self) -> u8 { | 96 | fn channel(&self) -> u8 { |
| 107 | 19 | 97 | 19 |
| @@ -110,21 +100,6 @@ cfg_if! { | |||
| 110 | } | 100 | } |
| 111 | } | 101 | } |
| 112 | 102 | ||
| 113 | /// Number of samples used for averaging. | ||
| 114 | #[derive(Copy, Clone, Debug)] | ||
| 115 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 116 | pub enum Averaging { | ||
| 117 | Disabled, | ||
| 118 | Samples2, | ||
| 119 | Samples4, | ||
| 120 | Samples8, | ||
| 121 | Samples16, | ||
| 122 | Samples32, | ||
| 123 | Samples64, | ||
| 124 | Samples128, | ||
| 125 | Samples256, | ||
| 126 | } | ||
| 127 | |||
| 128 | cfg_if! { if #[cfg(adc_g0)] { | 103 | cfg_if! { if #[cfg(adc_g0)] { |
| 129 | 104 | ||
| 130 | /// Synchronous PCLK prescaler | 105 | /// Synchronous PCLK prescaler |
| @@ -145,310 +120,148 @@ pub enum Clock { | |||
| 145 | 120 | ||
| 146 | }} | 121 | }} |
| 147 | 122 | ||
| 148 | impl<'d, T: Instance> Adc<'d, T> { | 123 | #[cfg(adc_u0)] |
| 149 | /// Enable the voltage regulator | 124 | type Ovss = u8; |
| 150 | fn init_regulator() { | 125 | #[cfg(adc_u0)] |
| 151 | rcc::enable_and_reset::<T>(); | 126 | type Ovsr = u8; |
| 152 | T::regs().cr().modify(|reg| { | 127 | #[cfg(adc_v3)] |
| 153 | #[cfg(not(any(adc_g0, adc_u0)))] | 128 | type Ovss = OversamplingShift; |
| 154 | reg.set_deeppwd(false); | 129 | #[cfg(adc_v3)] |
| 155 | reg.set_advregen(true); | 130 | type Ovsr = OversamplingRatio; |
| 156 | }); | 131 | |
| 157 | 132 | /// Adc configuration | |
| 158 | // If this is false then each ADC_CHSELR bit enables an input channel. | 133 | #[derive(Default)] |
| 159 | // This is the reset value, so has no effect. | 134 | pub struct AdcConfig { |
| 160 | #[cfg(any(adc_g0, adc_u0))] | 135 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 161 | T::regs().cfgr1().modify(|reg| { | 136 | pub oversampling_shift: Option<Ovss>, |
| 162 | reg.set_chselrmod(false); | 137 | #[cfg(any(adc_u0, adc_g0, adc_v3))] |
| 163 | }); | 138 | pub oversampling_ratio: Option<Ovsr>, |
| 139 | #[cfg(any(adc_u0, adc_g0))] | ||
| 140 | pub oversampling_enable: Option<bool>, | ||
| 141 | #[cfg(adc_v3)] | ||
| 142 | pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, | ||
| 143 | #[cfg(adc_g0)] | ||
| 144 | pub clock: Option<Clock>, | ||
| 145 | pub resolution: Option<Resolution>, | ||
| 146 | pub averaging: Option<Averaging>, | ||
| 147 | } | ||
| 164 | 148 | ||
| 165 | blocking_delay_us(20); | 149 | impl<T: Instance> super::SealedAnyInstance for T { |
| 150 | fn dr() -> *mut u16 { | ||
| 151 | T::regs().dr().as_ptr() as *mut u16 | ||
| 166 | } | 152 | } |
| 167 | 153 | ||
| 168 | /// Calibrate to remove conversion offset | 154 | // Enable ADC only when it is not already running. |
| 169 | fn init_calibrate() { | 155 | fn enable() { |
| 170 | T::regs().cr().modify(|reg| { | 156 | // Make sure bits are off |
| 171 | reg.set_adcal(true); | 157 | while T::regs().cr().read().addis() { |
| 172 | }); | ||
| 173 | |||
| 174 | while T::regs().cr().read().adcal() { | ||
| 175 | // spin | 158 | // spin |
| 176 | } | 159 | } |
| 177 | 160 | ||
| 178 | blocking_delay_us(1); | 161 | if !T::regs().cr().read().aden() { |
| 162 | // Enable ADC | ||
| 163 | T::regs().isr().modify(|reg| { | ||
| 164 | reg.set_adrdy(true); | ||
| 165 | }); | ||
| 166 | T::regs().cr().modify(|reg| { | ||
| 167 | reg.set_aden(true); | ||
| 168 | }); | ||
| 169 | |||
| 170 | while !T::regs().isr().read().adrdy() { | ||
| 171 | // spin | ||
| 172 | } | ||
| 173 | } | ||
| 179 | } | 174 | } |
| 180 | 175 | ||
| 181 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 176 | fn start() { |
| 182 | pub(super) fn start() { | ||
| 183 | // Start adc conversion | ||
| 184 | T::regs().cr().modify(|reg| { | 177 | T::regs().cr().modify(|reg| { |
| 185 | reg.set_adstart(true); | 178 | reg.set_adstart(true); |
| 186 | }); | 179 | }); |
| 187 | } | 180 | } |
| 188 | 181 | ||
| 189 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 182 | fn stop() { |
| 190 | pub(super) fn stop() { | 183 | // Ensure conversions are finished. |
| 191 | // Stop adc conversion | ||
| 192 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 184 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 193 | T::regs().cr().modify(|reg| { | 185 | T::regs().cr().modify(|reg| { |
| 194 | reg.set_adstp(true); | 186 | reg.set_adstp(true); |
| 195 | }); | 187 | }); |
| 196 | while T::regs().cr().read().adstart() {} | 188 | while T::regs().cr().read().adstart() {} |
| 197 | } | 189 | } |
| 198 | } | ||
| 199 | 190 | ||
| 200 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | 191 | // Reset configuration. |
| 201 | pub(super) fn teardown_dma() { | ||
| 202 | //disable dma control | ||
| 203 | #[cfg(not(any(adc_g0, adc_u0)))] | 192 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 204 | T::regs().cfgr().modify(|reg| { | 193 | T::regs().cfgr().modify(|reg| { |
| 194 | reg.set_cont(false); | ||
| 205 | reg.set_dmaen(false); | 195 | reg.set_dmaen(false); |
| 206 | }); | 196 | }); |
| 207 | #[cfg(any(adc_g0, adc_u0))] | 197 | #[cfg(any(adc_g0, adc_u0))] |
| 208 | T::regs().cfgr1().modify(|reg| { | 198 | T::regs().cfgr1().modify(|reg| { |
| 199 | reg.set_cont(false); | ||
| 209 | reg.set_dmaen(false); | 200 | reg.set_dmaen(false); |
| 210 | }); | 201 | }); |
| 211 | } | 202 | } |
| 212 | 203 | ||
| 213 | /// Initialize the ADC leaving any analog clock at reset value. | 204 | /// Perform a single conversion. |
| 214 | /// For G0 and WL, this is the async clock without prescaler. | 205 | fn convert() -> u16 { |
| 215 | pub fn new(adc: Peri<'d, T>) -> Self { | 206 | // Some models are affected by an erratum: |
| 216 | Self::init_regulator(); | 207 | // If we perform conversions slower than 1 kHz, the first read ADC value can be |
| 217 | Self::init_calibrate(); | 208 | // corrupted, so we discard it and measure again. |
| 218 | Self { adc } | 209 | // |
| 219 | } | 210 | // STM32L471xx: Section 2.7.3 |
| 220 | 211 | // STM32G4: Section 2.7.3 | |
| 221 | #[cfg(adc_g0)] | 212 | #[cfg(any(rcc_l4, rcc_g4))] |
| 222 | /// Initialize ADC with explicit clock for the analog ADC | 213 | let len = 2; |
| 223 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | ||
| 224 | Self::init_regulator(); | ||
| 225 | |||
| 226 | #[cfg(any(stm32wl5x))] | ||
| 227 | { | ||
| 228 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 229 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 230 | match clock { | ||
| 231 | Clock::Async { div: _ } => { | ||
| 232 | assert!(async_clock_available); | ||
| 233 | } | ||
| 234 | Clock::Sync { div: _ } => { | ||
| 235 | if async_clock_available { | ||
| 236 | warn!("Not using configured ADC clock"); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | } | ||
| 240 | } | ||
| 241 | match clock { | ||
| 242 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 243 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 244 | reg.set_ckmode(match div { | ||
| 245 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 246 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 247 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 248 | }) | ||
| 249 | }), | ||
| 250 | } | ||
| 251 | |||
| 252 | Self::init_calibrate(); | ||
| 253 | |||
| 254 | Self { adc } | ||
| 255 | } | ||
| 256 | 214 | ||
| 257 | // Enable ADC only when it is not already running. | 215 | #[cfg(not(any(rcc_l4, rcc_g4)))] |
| 258 | fn enable(&mut self) { | 216 | let len = 1; |
| 259 | // Make sure bits are off | ||
| 260 | while T::regs().cr().read().addis() { | ||
| 261 | // spin | ||
| 262 | } | ||
| 263 | 217 | ||
| 264 | if !T::regs().cr().read().aden() { | 218 | for _ in 0..len { |
| 265 | // Enable ADC | ||
| 266 | T::regs().isr().modify(|reg| { | 219 | T::regs().isr().modify(|reg| { |
| 267 | reg.set_adrdy(true); | 220 | reg.set_eos(true); |
| 221 | reg.set_eoc(true); | ||
| 268 | }); | 222 | }); |
| 223 | |||
| 224 | // Start conversion | ||
| 269 | T::regs().cr().modify(|reg| { | 225 | T::regs().cr().modify(|reg| { |
| 270 | reg.set_aden(true); | 226 | reg.set_adstart(true); |
| 271 | }); | 227 | }); |
| 272 | 228 | ||
| 273 | while !T::regs().isr().read().adrdy() { | 229 | while !T::regs().isr().read().eos() { |
| 274 | // spin | 230 | // spin |
| 275 | } | 231 | } |
| 276 | } | 232 | } |
| 277 | } | ||
| 278 | |||
| 279 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 280 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 281 | T::common_regs().ccr().modify(|reg| { | ||
| 282 | reg.set_vrefen(true); | ||
| 283 | }); | ||
| 284 | #[cfg(any(adc_g0, adc_u0))] | ||
| 285 | T::regs().ccr().modify(|reg| { | ||
| 286 | reg.set_vrefen(true); | ||
| 287 | }); | ||
| 288 | |||
| 289 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us | ||
| 290 | // to stabilize the internal voltage reference. | ||
| 291 | blocking_delay_us(15); | ||
| 292 | |||
| 293 | VrefInt {} | ||
| 294 | } | ||
| 295 | |||
| 296 | pub fn enable_temperature(&self) -> Temperature { | ||
| 297 | cfg_if! { | ||
| 298 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 299 | T::regs().ccr().modify(|reg| { | ||
| 300 | reg.set_tsen(true); | ||
| 301 | }); | ||
| 302 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 303 | T::common_regs().ccr().modify(|reg| { | ||
| 304 | reg.set_tsen(true); | ||
| 305 | }); | ||
| 306 | } else { | ||
| 307 | T::common_regs().ccr().modify(|reg| { | ||
| 308 | reg.set_ch17sel(true); | ||
| 309 | }); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | Temperature {} | ||
| 314 | } | ||
| 315 | 233 | ||
| 316 | pub fn enable_vbat(&self) -> Vbat { | 234 | T::regs().dr().read().0 as u16 |
| 317 | cfg_if! { | ||
| 318 | if #[cfg(any(adc_g0, adc_u0))] { | ||
| 319 | T::regs().ccr().modify(|reg| { | ||
| 320 | reg.set_vbaten(true); | ||
| 321 | }); | ||
| 322 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 323 | T::common_regs().ccr().modify(|reg| { | ||
| 324 | reg.set_vbaten(true); | ||
| 325 | }); | ||
| 326 | } else { | ||
| 327 | T::common_regs().ccr().modify(|reg| { | ||
| 328 | reg.set_ch18sel(true); | ||
| 329 | }); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | Vbat {} | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Set the ADC resolution. | ||
| 337 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 338 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 339 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 340 | #[cfg(any(adc_g0, adc_u0))] | ||
| 341 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 342 | } | ||
| 343 | |||
| 344 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 345 | let (enable, samples, right_shift) = match averaging { | ||
| 346 | Averaging::Disabled => (false, 0, 0), | ||
| 347 | Averaging::Samples2 => (true, 0, 1), | ||
| 348 | Averaging::Samples4 => (true, 1, 2), | ||
| 349 | Averaging::Samples8 => (true, 2, 3), | ||
| 350 | Averaging::Samples16 => (true, 3, 4), | ||
| 351 | Averaging::Samples32 => (true, 4, 5), | ||
| 352 | Averaging::Samples64 => (true, 5, 6), | ||
| 353 | Averaging::Samples128 => (true, 6, 7), | ||
| 354 | Averaging::Samples256 => (true, 7, 8), | ||
| 355 | }; | ||
| 356 | T::regs().cfgr2().modify(|reg| { | ||
| 357 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 358 | reg.set_rovse(enable); | ||
| 359 | #[cfg(any(adc_g0, adc_u0))] | ||
| 360 | reg.set_ovse(enable); | ||
| 361 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 362 | reg.set_ovsr(samples.into()); | ||
| 363 | #[cfg(not(any(adc_h5, adc_h7rs)))] | ||
| 364 | reg.set_ovsr(samples.into()); | ||
| 365 | reg.set_ovss(right_shift.into()); | ||
| 366 | }) | ||
| 367 | } | ||
| 368 | /* | ||
| 369 | /// Convert a raw sample from the `Temperature` to deg C | ||
| 370 | pub fn to_degrees_centigrade(sample: u16) -> f32 { | ||
| 371 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) | ||
| 372 | * (sample as f32 - VtempCal30::get().read() as f32) | ||
| 373 | + 30.0 | ||
| 374 | } | 235 | } |
| 375 | */ | ||
| 376 | 236 | ||
| 377 | /// Perform a single conversion. | 237 | fn configure_dma(conversion_mode: ConversionMode) { |
| 378 | fn convert(&mut self) -> u16 { | 238 | // Set continuous mode with oneshot dma. |
| 239 | // Clear overrun flag before starting transfer. | ||
| 379 | T::regs().isr().modify(|reg| { | 240 | T::regs().isr().modify(|reg| { |
| 380 | reg.set_eos(true); | 241 | reg.set_ovr(true); |
| 381 | reg.set_eoc(true); | ||
| 382 | }); | ||
| 383 | |||
| 384 | // Start conversion | ||
| 385 | T::regs().cr().modify(|reg| { | ||
| 386 | reg.set_adstart(true); | ||
| 387 | }); | 242 | }); |
| 388 | 243 | ||
| 389 | while !T::regs().isr().read().eos() { | 244 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 390 | // spin | 245 | let regs = T::regs().cfgr(); |
| 391 | } | ||
| 392 | 246 | ||
| 393 | T::regs().dr().read().0 as u16 | 247 | #[cfg(any(adc_g0, adc_u0))] |
| 394 | } | 248 | let regs = T::regs().cfgr1(); |
| 395 | 249 | ||
| 396 | /// Read an ADC channel. | 250 | regs.modify(|reg| { |
| 397 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 251 | reg.set_discen(false); |
| 398 | self.read_channel(channel, sample_time) | 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 | }); | ||
| 399 | } | 260 | } |
| 400 | 261 | ||
| 401 | /// Read one or multiple ADC channels using DMA. | 262 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 402 | /// | 263 | #[cfg(adc_h5)] |
| 403 | /// `readings` must have a length that is a multiple of the length of the | 264 | T::regs().cr().modify(|w| w.set_aden(false)); |
| 404 | /// `sequence` iterator. | ||
| 405 | /// | ||
| 406 | /// Note: The order of values in `readings` is defined by the pin ADC | ||
| 407 | /// channel number and not the pin order in `sequence`. | ||
| 408 | /// | ||
| 409 | /// Example | ||
| 410 | /// ```rust,ignore | ||
| 411 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 412 | /// | ||
| 413 | /// let mut adc = Adc::new(p.ADC1); | ||
| 414 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 415 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 416 | /// let mut measurements = [0u16; 2]; | ||
| 417 | /// | ||
| 418 | /// adc.read( | ||
| 419 | /// p.DMA1_CH2.reborrow(), | ||
| 420 | /// [ | ||
| 421 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 422 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 423 | /// ] | ||
| 424 | /// .into_iter(), | ||
| 425 | /// &mut measurements, | ||
| 426 | /// ) | ||
| 427 | /// .await; | ||
| 428 | /// defmt::info!("measurements: {}", measurements); | ||
| 429 | /// ``` | ||
| 430 | pub async fn read( | ||
| 431 | &mut self, | ||
| 432 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 433 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 434 | readings: &mut [u16], | ||
| 435 | ) { | ||
| 436 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 437 | assert!( | ||
| 438 | readings.len() % sequence.len() == 0, | ||
| 439 | "Readings length must be a multiple of sequence length" | ||
| 440 | ); | ||
| 441 | assert!( | ||
| 442 | sequence.len() <= 16, | ||
| 443 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 444 | ); | ||
| 445 | |||
| 446 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 447 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 448 | |||
| 449 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 450 | Self::cancel_conversions(); | ||
| 451 | self.enable(); | ||
| 452 | 265 | ||
| 453 | // Set sequence length | 266 | // Set sequence length |
| 454 | #[cfg(not(any(adc_g0, adc_u0)))] | 267 | #[cfg(not(any(adc_g0, adc_u0)))] |
| @@ -462,10 +275,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 462 | 275 | ||
| 463 | T::regs().chselr().write(|chselr| { | 276 | T::regs().chselr().write(|chselr| { |
| 464 | T::regs().smpr().write(|smpr| { | 277 | T::regs().smpr().write(|smpr| { |
| 465 | for (channel, sample_time) in sequence { | 278 | for ((channel, _), sample_time) in sequence { |
| 466 | chselr.set_chsel(channel.channel.into(), true); | 279 | chselr.set_chsel(channel.into(), true); |
| 467 | 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) { |
| 468 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | 281 | smpr.set_smpsel(channel.into(), (i as u8).into()); |
| 469 | } else { | 282 | } else { |
| 470 | smpr.set_sample_time(sample_times.len(), sample_time); | 283 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 471 | if let Err(_) = sample_times.push(sample_time) { | 284 | if let Err(_) = sample_times.push(sample_time) { |
| @@ -484,42 +297,86 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 484 | #[cfg(adc_u0)] | 297 | #[cfg(adc_u0)] |
| 485 | let mut channel_mask = 0; | 298 | let mut channel_mask = 0; |
| 486 | 299 | ||
| 300 | #[cfg(adc_h5)] | ||
| 301 | let mut difsel = 0u32; | ||
| 302 | |||
| 487 | // Configure channels and ranks | 303 | // Configure channels and ranks |
| 488 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 304 | for (_i, ((channel, _is_differential), sample_time)) in sequence.enumerate() { |
| 489 | 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 | } | ||
| 490 | 339 | ||
| 491 | // Each channel is sampled according to sequence | 340 | // Each channel is sampled according to sequence |
| 492 | #[cfg(not(any(adc_g0, adc_u0)))] | 341 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 493 | match _i { | 342 | match _i { |
| 494 | 0..=3 => { | 343 | 0..=3 => { |
| 495 | T::regs().sqr1().modify(|w| { | 344 | T::regs().sqr1().modify(|w| { |
| 496 | w.set_sq(_i, channel.channel()); | 345 | w.set_sq(_i, channel); |
| 497 | }); | 346 | }); |
| 498 | } | 347 | } |
| 499 | 4..=8 => { | 348 | 4..=8 => { |
| 500 | T::regs().sqr2().modify(|w| { | 349 | T::regs().sqr2().modify(|w| { |
| 501 | w.set_sq(_i - 4, channel.channel()); | 350 | w.set_sq(_i - 4, channel); |
| 502 | }); | 351 | }); |
| 503 | } | 352 | } |
| 504 | 9..=13 => { | 353 | 9..=13 => { |
| 505 | T::regs().sqr3().modify(|w| { | 354 | T::regs().sqr3().modify(|w| { |
| 506 | w.set_sq(_i - 9, channel.channel()); | 355 | w.set_sq(_i - 9, channel); |
| 507 | }); | 356 | }); |
| 508 | } | 357 | } |
| 509 | 14..=15 => { | 358 | 14..=15 => { |
| 510 | T::regs().sqr4().modify(|w| { | 359 | T::regs().sqr4().modify(|w| { |
| 511 | w.set_sq(_i - 14, channel.channel()); | 360 | w.set_sq(_i - 14, channel); |
| 512 | }); | 361 | }); |
| 513 | } | 362 | } |
| 514 | _ => unreachable!(), | 363 | _ => unreachable!(), |
| 515 | } | 364 | } |
| 516 | 365 | ||
| 366 | #[cfg(adc_h5)] | ||
| 367 | { | ||
| 368 | difsel |= (_is_differential as u32) << channel; | ||
| 369 | } | ||
| 370 | |||
| 517 | #[cfg(adc_u0)] | 371 | #[cfg(adc_u0)] |
| 518 | { | 372 | { |
| 519 | channel_mask |= 1 << channel.channel(); | 373 | channel_mask |= 1 << channel; |
| 520 | } | 374 | } |
| 521 | } | 375 | } |
| 522 | 376 | ||
| 377 | #[cfg(adc_h5)] | ||
| 378 | T::regs().difsel().write(|w| w.set_difsel(difsel)); | ||
| 379 | |||
| 523 | // 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. |
| 524 | // 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. |
| 525 | // 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. |
| @@ -528,312 +385,234 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 528 | reg.set_chsel(channel_mask); | 385 | reg.set_chsel(channel_mask); |
| 529 | }); | 386 | }); |
| 530 | } | 387 | } |
| 531 | // Set continuous mode with oneshot dma. | 388 | } |
| 532 | // Clear overrun flag before starting transfer. | 389 | } |
| 533 | T::regs().isr().modify(|reg| { | ||
| 534 | reg.set_ovr(true); | ||
| 535 | }); | ||
| 536 | 390 | ||
| 537 | #[cfg(not(any(adc_g0, adc_u0)))] | 391 | impl<'d, T: Instance> Adc<'d, T> { |
| 538 | T::regs().cfgr().modify(|reg| { | 392 | /// Enable the voltage regulator |
| 539 | reg.set_discen(false); | 393 | fn init_regulator() { |
| 540 | reg.set_cont(true); | 394 | rcc::enable_and_reset::<T>(); |
| 541 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | 395 | T::regs().cr().modify(|reg| { |
| 542 | reg.set_dmaen(true); | 396 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 397 | reg.set_deeppwd(false); | ||
| 398 | reg.set_advregen(true); | ||
| 543 | }); | 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. | ||
| 544 | #[cfg(any(adc_g0, adc_u0))] | 403 | #[cfg(any(adc_g0, adc_u0))] |
| 545 | T::regs().cfgr1().modify(|reg| { | 404 | T::regs().cfgr1().modify(|reg| { |
| 546 | reg.set_discen(false); | 405 | reg.set_chselrmod(false); |
| 547 | reg.set_cont(true); | ||
| 548 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 549 | reg.set_dmaen(true); | ||
| 550 | }); | 406 | }); |
| 551 | 407 | ||
| 552 | let request = rx_dma.request(); | 408 | blocking_delay_us(20); |
| 553 | let transfer = unsafe { | 409 | } |
| 554 | Transfer::new_read( | ||
| 555 | rx_dma, | ||
| 556 | request, | ||
| 557 | T::regs().dr().as_ptr() as *mut u16, | ||
| 558 | readings, | ||
| 559 | Default::default(), | ||
| 560 | ) | ||
| 561 | }; | ||
| 562 | 410 | ||
| 563 | // Start conversion | 411 | /// Calibrate to remove conversion offset |
| 412 | fn init_calibrate() { | ||
| 564 | T::regs().cr().modify(|reg| { | 413 | T::regs().cr().modify(|reg| { |
| 565 | reg.set_adstart(true); | 414 | reg.set_adcal(true); |
| 566 | }); | 415 | }); |
| 567 | 416 | ||
| 568 | // Wait for conversion sequence to finish. | 417 | while T::regs().cr().read().adcal() { |
| 569 | transfer.await; | 418 | // spin |
| 570 | 419 | } | |
| 571 | // Ensure conversions are finished. | ||
| 572 | Self::cancel_conversions(); | ||
| 573 | 420 | ||
| 574 | // Reset configuration. | 421 | blocking_delay_us(1); |
| 575 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 576 | T::regs().cfgr().modify(|reg| { | ||
| 577 | reg.set_cont(false); | ||
| 578 | }); | ||
| 579 | #[cfg(any(adc_g0, adc_u0))] | ||
| 580 | T::regs().cfgr1().modify(|reg| { | ||
| 581 | reg.set_cont(false); | ||
| 582 | }); | ||
| 583 | } | 422 | } |
| 584 | 423 | ||
| 585 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 424 | /// Initialize the ADC leaving any analog clock at reset value. |
| 586 | /// | 425 | /// For G0 and WL, this is the async clock without prescaler. |
| 587 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | 426 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 588 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | 427 | Self::init_regulator(); |
| 589 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | 428 | Self::init_calibrate(); |
| 590 | /// | 429 | Self { adc } |
| 591 | /// `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. | 430 | } |
| 592 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 593 | /// | ||
| 594 | /// [`read`]: #method.read | ||
| 595 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 596 | pub fn into_ring_buffered<'a>( | ||
| 597 | &mut self, | ||
| 598 | dma: Peri<'a, impl RxDma<T>>, | ||
| 599 | dma_buf: &'a mut [u16], | ||
| 600 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 601 | ) -> RingBufferedAdc<'a, T> { | ||
| 602 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 603 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 604 | assert!( | ||
| 605 | sequence.len() <= 16, | ||
| 606 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 607 | ); | ||
| 608 | // reset conversions and enable the adc | ||
| 609 | Self::cancel_conversions(); | ||
| 610 | self.enable(); | ||
| 611 | |||
| 612 | //adc side setup | ||
| 613 | 431 | ||
| 614 | // Set sequence length | 432 | pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { |
| 615 | #[cfg(not(any(adc_g0, adc_u0)))] | 433 | #[cfg(not(adc_g0))] |
| 616 | T::regs().sqr1().modify(|w| { | 434 | let s = Self::new(adc); |
| 617 | w.set_l(sequence.len() as u8 - 1); | ||
| 618 | }); | ||
| 619 | 435 | ||
| 620 | #[cfg(adc_g0)] | 436 | #[cfg(adc_g0)] |
| 621 | { | 437 | let s = match config.clock { |
| 622 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | 438 | Some(clock) => Self::new_with_clock(adc, clock), |
| 439 | None => Self::new(adc), | ||
| 440 | }; | ||
| 623 | 441 | ||
| 624 | T::regs().chselr().write(|chselr| { | 442 | #[cfg(any(adc_g0, adc_u0, adc_v3))] |
| 625 | T::regs().smpr().write(|smpr| { | 443 | if let Some(shift) = config.oversampling_shift { |
| 626 | for (channel, sample_time) in sequence { | 444 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 627 | chselr.set_chsel(channel.channel.into(), true); | ||
| 628 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | ||
| 629 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | ||
| 630 | } else { | ||
| 631 | smpr.set_sample_time(sample_times.len(), sample_time); | ||
| 632 | if let Err(_) = sample_times.push(sample_time) { | ||
| 633 | panic!( | ||
| 634 | "Implementation is limited to {} unique sample times among all channels.", | ||
| 635 | SAMPLE_TIMES_CAPACITY | ||
| 636 | ); | ||
| 637 | } | ||
| 638 | } | ||
| 639 | } | ||
| 640 | }) | ||
| 641 | }); | ||
| 642 | } | 445 | } |
| 643 | #[cfg(not(adc_g0))] | ||
| 644 | { | ||
| 645 | #[cfg(adc_u0)] | ||
| 646 | let mut channel_mask = 0; | ||
| 647 | 446 | ||
| 648 | // Configure channels and ranks | 447 | #[cfg(any(adc_g0, adc_u0, adc_v3))] |
| 649 | for (_i, (mut channel, sample_time)) in sequence.enumerate() { | 448 | if let Some(ratio) = config.oversampling_ratio { |
| 650 | Self::configure_channel(&mut channel, sample_time); | 449 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); |
| 450 | } | ||
| 651 | 451 | ||
| 652 | // Each channel is sampled according to sequence | 452 | #[cfg(any(adc_g0, adc_u0))] |
| 653 | #[cfg(not(any(adc_g0, adc_u0)))] | 453 | if let Some(enable) = config.oversampling_enable { |
| 654 | match _i { | 454 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); |
| 655 | 0..=3 => { | 455 | } |
| 656 | T::regs().sqr1().modify(|w| { | ||
| 657 | w.set_sq(_i, channel.channel()); | ||
| 658 | }); | ||
| 659 | } | ||
| 660 | 4..=8 => { | ||
| 661 | T::regs().sqr2().modify(|w| { | ||
| 662 | w.set_sq(_i - 4, channel.channel()); | ||
| 663 | }); | ||
| 664 | } | ||
| 665 | 9..=13 => { | ||
| 666 | T::regs().sqr3().modify(|w| { | ||
| 667 | w.set_sq(_i - 9, channel.channel()); | ||
| 668 | }); | ||
| 669 | } | ||
| 670 | 14..=15 => { | ||
| 671 | T::regs().sqr4().modify(|w| { | ||
| 672 | w.set_sq(_i - 14, channel.channel()); | ||
| 673 | }); | ||
| 674 | } | ||
| 675 | _ => unreachable!(), | ||
| 676 | } | ||
| 677 | 456 | ||
| 678 | #[cfg(adc_u0)] | 457 | #[cfg(adc_v3)] |
| 679 | { | 458 | if let Some((mode, trig_mode, enable)) = config.oversampling_mode { |
| 680 | channel_mask |= 1 << channel.channel(); | 459 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); |
| 681 | } | 460 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); |
| 682 | } | 461 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); |
| 462 | } | ||
| 683 | 463 | ||
| 684 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | 464 | if let Some(resolution) = config.resolution { |
| 685 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | 465 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 686 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | 466 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); |
| 687 | #[cfg(adc_u0)] | 467 | #[cfg(any(adc_g0, adc_u0))] |
| 688 | T::regs().chselr().modify(|reg| { | 468 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); |
| 689 | reg.set_chsel(channel_mask); | ||
| 690 | }); | ||
| 691 | } | 469 | } |
| 692 | // Set continuous mode with Circular dma. | ||
| 693 | // Clear overrun flag before starting transfer. | ||
| 694 | T::regs().isr().modify(|reg| { | ||
| 695 | reg.set_ovr(true); | ||
| 696 | }); | ||
| 697 | 470 | ||
| 698 | #[cfg(not(any(adc_g0, adc_u0)))] | 471 | if let Some(averaging) = config.averaging { |
| 699 | T::regs().cfgr().modify(|reg| { | 472 | let (enable, samples, right_shift) = match averaging { |
| 700 | reg.set_discen(false); | 473 | Averaging::Disabled => (false, 0, 0), |
| 701 | reg.set_cont(true); | 474 | Averaging::Samples2 => (true, 0, 1), |
| 702 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 475 | Averaging::Samples4 => (true, 1, 2), |
| 703 | reg.set_dmaen(true); | 476 | Averaging::Samples8 => (true, 2, 3), |
| 704 | }); | 477 | Averaging::Samples16 => (true, 3, 4), |
| 705 | #[cfg(any(adc_g0, adc_u0))] | 478 | Averaging::Samples32 => (true, 4, 5), |
| 706 | T::regs().cfgr1().modify(|reg| { | 479 | Averaging::Samples64 => (true, 5, 6), |
| 707 | reg.set_discen(false); | 480 | Averaging::Samples128 => (true, 6, 7), |
| 708 | reg.set_cont(true); | 481 | Averaging::Samples256 => (true, 7, 8), |
| 709 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 482 | }; |
| 710 | reg.set_dmaen(true); | 483 | T::regs().cfgr2().modify(|reg| { |
| 711 | }); | 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 | }) | ||
| 494 | } | ||
| 712 | 495 | ||
| 713 | RingBufferedAdc::new(dma, dma_buf) | 496 | s |
| 714 | } | 497 | } |
| 715 | 498 | ||
| 716 | #[cfg(not(adc_g0))] | 499 | #[cfg(adc_g0)] |
| 717 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 500 | /// Initialize ADC with explicit clock for the analog ADC |
| 718 | // RM0492, RM0481, etc. | 501 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { |
| 719 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 502 | Self::init_regulator(); |
| 720 | #[cfg(any(adc_h5, adc_h7rs))] | 503 | |
| 721 | if channel.channel() == 0 { | 504 | #[cfg(any(stm32wl5x))] |
| 722 | T::regs().or().modify(|reg| reg.set_op0(true)); | 505 | { |
| 506 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 507 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 508 | match clock { | ||
| 509 | Clock::Async { div: _ } => { | ||
| 510 | assert!(async_clock_available); | ||
| 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 | }), | ||
| 723 | } | 528 | } |
| 724 | 529 | ||
| 725 | // Configure channel | 530 | Self::init_calibrate(); |
| 726 | Self::set_channel_sample_time(channel.channel(), sample_time); | 531 | |
| 532 | Self { adc } | ||
| 727 | } | 533 | } |
| 728 | 534 | ||
| 729 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | 535 | pub fn enable_vrefint(&self) -> VrefInt { |
| 730 | self.enable(); | ||
| 731 | #[cfg(not(adc_g0))] | ||
| 732 | Self::configure_channel(channel, sample_time); | ||
| 733 | #[cfg(adc_g0)] | ||
| 734 | T::regs().smpr().write(|reg| { | ||
| 735 | reg.set_sample_time(0, sample_time); | ||
| 736 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | ||
| 737 | }); | ||
| 738 | // Select channel | ||
| 739 | #[cfg(not(any(adc_g0, adc_u0)))] | 536 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 740 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 537 | T::common_regs().ccr().modify(|reg| { |
| 538 | reg.set_vrefen(true); | ||
| 539 | }); | ||
| 741 | #[cfg(any(adc_g0, adc_u0))] | 540 | #[cfg(any(adc_g0, adc_u0))] |
| 742 | T::regs().chselr().write(|reg| { | 541 | T::regs().ccr().modify(|reg| { |
| 743 | #[cfg(adc_g0)] | 542 | reg.set_vrefen(true); |
| 744 | reg.set_chsel(channel.channel().into(), true); | ||
| 745 | #[cfg(adc_u0)] | ||
| 746 | reg.set_chsel(1 << channel.channel()); | ||
| 747 | }); | 543 | }); |
| 748 | 544 | ||
| 749 | // Some models are affected by an erratum: | 545 | // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us |
| 750 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | 546 | // to stabilize the internal voltage reference. |
| 751 | // corrupted, so we discard it and measure again. | 547 | blocking_delay_us(15); |
| 752 | // | ||
| 753 | // STM32L471xx: Section 2.7.3 | ||
| 754 | // STM32G4: Section 2.7.3 | ||
| 755 | #[cfg(any(rcc_l4, rcc_g4))] | ||
| 756 | let _ = self.convert(); | ||
| 757 | let val = self.convert(); | ||
| 758 | |||
| 759 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 760 | |||
| 761 | // RM0492, RM0481, etc. | ||
| 762 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | ||
| 763 | #[cfg(any(adc_h5, adc_h7rs))] | ||
| 764 | if channel.channel() == 0 { | ||
| 765 | T::regs().or().modify(|reg| reg.set_op0(false)); | ||
| 766 | } | ||
| 767 | |||
| 768 | val | ||
| 769 | } | ||
| 770 | |||
| 771 | #[cfg(adc_g0)] | ||
| 772 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | ||
| 773 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 774 | } | ||
| 775 | #[cfg(adc_u0)] | ||
| 776 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 777 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 778 | } | ||
| 779 | 548 | ||
| 780 | #[cfg(adc_g0)] | 549 | VrefInt {} |
| 781 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | ||
| 782 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 783 | } | ||
| 784 | #[cfg(adc_u0)] | ||
| 785 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 786 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 787 | } | 550 | } |
| 788 | 551 | ||
| 789 | #[cfg(any(adc_g0, adc_u0))] | 552 | pub fn enable_temperature(&self) -> Temperature { |
| 790 | pub fn oversampling_enable(&mut self, enable: bool) { | 553 | cfg_if! { |
| 791 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | 554 | if #[cfg(any(adc_g0, adc_u0))] { |
| 792 | } | 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 | } | ||
| 793 | 568 | ||
| 794 | #[cfg(adc_v3)] | 569 | Temperature {} |
| 795 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 796 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 797 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 798 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 799 | } | 570 | } |
| 800 | 571 | ||
| 801 | #[cfg(adc_v3)] | 572 | pub fn enable_vbat(&self) -> Vbat { |
| 802 | pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { | 573 | cfg_if! { |
| 803 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 574 | if #[cfg(any(adc_g0, adc_u0))] { |
| 804 | } | 575 | T::regs().ccr().modify(|reg| { |
| 576 | reg.set_vbaten(true); | ||
| 577 | }); | ||
| 578 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | ||
| 579 | T::common_regs().ccr().modify(|reg| { | ||
| 580 | reg.set_vbaten(true); | ||
| 581 | }); | ||
| 582 | } else { | ||
| 583 | T::common_regs().ccr().modify(|reg| { | ||
| 584 | reg.set_ch18sel(true); | ||
| 585 | }); | ||
| 586 | } | ||
| 587 | } | ||
| 805 | 588 | ||
| 806 | #[cfg(adc_v3)] | 589 | Vbat {} |
| 807 | pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { | ||
| 808 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 809 | } | 590 | } |
| 810 | 591 | ||
| 811 | #[cfg(not(adc_g0))] | 592 | pub fn disable_vbat(&self) { |
| 812 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | ||
| 813 | cfg_if! { | 593 | cfg_if! { |
| 814 | if #[cfg(adc_u0)] { | 594 | if #[cfg(any(adc_g0, adc_u0))] { |
| 815 | // On G0 and U6 all channels use the same sampling time. | 595 | T::regs().ccr().modify(|reg| { |
| 816 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 596 | reg.set_vbaten(false); |
| 597 | }); | ||
| 817 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 598 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 818 | match _ch { | 599 | T::common_regs().ccr().modify(|reg| { |
| 819 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 600 | reg.set_vbaten(false); |
| 820 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 601 | }); |
| 821 | } | ||
| 822 | } else { | 602 | } else { |
| 823 | let sample_time = sample_time.into(); | 603 | T::common_regs().ccr().modify(|reg| { |
| 824 | T::regs() | 604 | reg.set_ch18sel(false); |
| 825 | .smpr(_ch as usize / 10) | 605 | }); |
| 826 | .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); | ||
| 827 | } | 606 | } |
| 828 | } | 607 | } |
| 829 | } | 608 | } |
| 830 | 609 | ||
| 831 | fn cancel_conversions() { | 610 | /* |
| 832 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 611 | /// Convert a raw sample from the `Temperature` to deg C |
| 833 | T::regs().cr().modify(|reg| { | 612 | pub fn to_degrees_centigrade(sample: u16) -> f32 { |
| 834 | reg.set_adstp(true); | 613 | (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) |
| 835 | }); | 614 | * (sample as f32 - VtempCal30::get().read() as f32) |
| 836 | while T::regs().cr().read().adstart() {} | 615 | + 30.0 |
| 837 | } | ||
| 838 | } | 616 | } |
| 617 | */ | ||
| 839 | } | 618 | } |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 1d5d3fb92..a3d9e6176 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -4,11 +4,8 @@ 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 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, Temperature, Vbat, | 8 | use crate::adc::ConversionMode; |
| 9 | VrefInt, blocking_delay_us, | ||
| 10 | }; | ||
| 11 | use crate::dma::Transfer; | ||
| 12 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 13 | use crate::{Peri, pac, rcc}; | 10 | use crate::{Peri, pac, rcc}; |
| 14 | 11 | ||
| @@ -62,101 +59,191 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { | |||
| 62 | const CHANNEL: u8 = 18; | 59 | const CHANNEL: u8 = 18; |
| 63 | } | 60 | } |
| 64 | 61 | ||
| 65 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | 62 | fn from_ker_ck(frequency: Hertz) -> Presc { |
| 66 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | 63 | let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); |
| 67 | #[allow(unused)] | 64 | match raw_prescaler { |
| 68 | enum Prescaler { | 65 | 0 => Presc::DIV1, |
| 69 | NotDivided, | 66 | 1 => Presc::DIV2, |
| 70 | DividedBy2, | 67 | 2..=3 => Presc::DIV4, |
| 71 | DividedBy4, | 68 | 4..=5 => Presc::DIV6, |
| 72 | DividedBy6, | 69 | 6..=7 => Presc::DIV8, |
| 73 | DividedBy8, | 70 | 8..=9 => Presc::DIV10, |
| 74 | DividedBy10, | 71 | 10..=11 => Presc::DIV12, |
| 75 | DividedBy12, | 72 | _ => unimplemented!(), |
| 76 | DividedBy16, | 73 | } |
| 77 | DividedBy32, | 74 | } |
| 78 | DividedBy64, | 75 | |
| 79 | DividedBy128, | 76 | /// Adc configuration |
| 80 | DividedBy256, | 77 | #[derive(Default)] |
| 78 | pub struct AdcConfig { | ||
| 79 | pub resolution: Option<Resolution>, | ||
| 80 | pub averaging: Option<Averaging>, | ||
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | impl Prescaler { | 83 | impl<T: Instance> super::SealedAnyInstance for T { |
| 84 | fn from_ker_ck(frequency: Hertz) -> Self { | 84 | fn dr() -> *mut u16 { |
| 85 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | 85 | T::regs().dr().as_ptr() as *mut u16 |
| 86 | match raw_prescaler { | 86 | } |
| 87 | 0 => Self::NotDivided, | 87 | |
| 88 | 1 => Self::DividedBy2, | 88 | fn enable() { |
| 89 | 2..=3 => Self::DividedBy4, | 89 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 90 | 4..=5 => Self::DividedBy6, | 90 | T::regs().cr().modify(|w| w.set_aden(true)); |
| 91 | 6..=7 => Self::DividedBy8, | 91 | while !T::regs().isr().read().adrdy() {} |
| 92 | 8..=9 => Self::DividedBy10, | 92 | T::regs().isr().write(|w| w.set_adrdy(true)); |
| 93 | 10..=11 => Self::DividedBy12, | 93 | } |
| 94 | _ => unimplemented!(), | 94 | |
| 95 | fn start() { | ||
| 96 | // Start conversion | ||
| 97 | T::regs().cr().modify(|reg| { | ||
| 98 | reg.set_adstart(true); | ||
| 99 | }); | ||
| 100 | } | ||
| 101 | |||
| 102 | fn stop() { | ||
| 103 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 104 | T::regs().cr().modify(|reg| { | ||
| 105 | reg.set_adstp(Adstp::STOP); | ||
| 106 | }); | ||
| 107 | while T::regs().cr().read().adstart() {} | ||
| 95 | } | 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 | }); | ||
| 96 | } | 115 | } |
| 97 | 116 | ||
| 98 | fn divisor(&self) -> u32 { | 117 | fn convert() -> u16 { |
| 99 | match self { | 118 | T::regs().isr().modify(|reg| { |
| 100 | Prescaler::NotDivided => 1, | 119 | reg.set_eos(true); |
| 101 | Prescaler::DividedBy2 => 2, | 120 | reg.set_eoc(true); |
| 102 | Prescaler::DividedBy4 => 4, | 121 | }); |
| 103 | Prescaler::DividedBy6 => 6, | 122 | |
| 104 | Prescaler::DividedBy8 => 8, | 123 | // Start conversion |
| 105 | Prescaler::DividedBy10 => 10, | 124 | T::regs().cr().modify(|reg| { |
| 106 | Prescaler::DividedBy12 => 12, | 125 | reg.set_adstart(true); |
| 107 | Prescaler::DividedBy16 => 16, | 126 | }); |
| 108 | Prescaler::DividedBy32 => 32, | 127 | |
| 109 | Prescaler::DividedBy64 => 64, | 128 | while !T::regs().isr().read().eos() { |
| 110 | Prescaler::DividedBy128 => 128, | 129 | // spin |
| 111 | Prescaler::DividedBy256 => 256, | ||
| 112 | } | 130 | } |
| 131 | |||
| 132 | T::regs().dr().read().0 as u16 | ||
| 113 | } | 133 | } |
| 114 | 134 | ||
| 115 | fn presc(&self) -> Presc { | 135 | fn configure_dma(conversion_mode: ConversionMode) { |
| 116 | match self { | 136 | match conversion_mode { |
| 117 | Prescaler::NotDivided => Presc::DIV1, | 137 | ConversionMode::Singular => { |
| 118 | Prescaler::DividedBy2 => Presc::DIV2, | 138 | T::regs().isr().modify(|reg| { |
| 119 | Prescaler::DividedBy4 => Presc::DIV4, | 139 | reg.set_ovr(true); |
| 120 | Prescaler::DividedBy6 => Presc::DIV6, | 140 | }); |
| 121 | Prescaler::DividedBy8 => Presc::DIV8, | 141 | T::regs().cfgr().modify(|reg| { |
| 122 | Prescaler::DividedBy10 => Presc::DIV10, | 142 | reg.set_cont(true); |
| 123 | Prescaler::DividedBy12 => Presc::DIV12, | 143 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); |
| 124 | Prescaler::DividedBy16 => Presc::DIV16, | 144 | }); |
| 125 | Prescaler::DividedBy32 => Presc::DIV32, | 145 | } |
| 126 | Prescaler::DividedBy64 => Presc::DIV64, | 146 | #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] |
| 127 | Prescaler::DividedBy128 => Presc::DIV128, | 147 | _ => unreachable!(), |
| 128 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 129 | } | 148 | } |
| 130 | } | 149 | } |
| 131 | } | ||
| 132 | 150 | ||
| 133 | /// Number of samples used for averaging. | 151 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { |
| 134 | #[derive(Copy, Clone, Debug)] | 152 | // Set sequence length |
| 135 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 153 | T::regs().sqr1().modify(|w| { |
| 136 | pub enum Averaging { | 154 | w.set_l(sequence.len() as u8 - 1); |
| 137 | Disabled, | 155 | }); |
| 138 | Samples2, | 156 | |
| 139 | Samples4, | 157 | // Configure channels and ranks |
| 140 | Samples8, | 158 | for (i, ((channel, _), sample_time)) in sequence.enumerate() { |
| 141 | Samples16, | 159 | let sample_time = sample_time.into(); |
| 142 | Samples32, | 160 | if channel <= 9 { |
| 143 | Samples64, | 161 | T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); |
| 144 | Samples128, | 162 | } else { |
| 145 | Samples256, | 163 | T::regs() |
| 146 | Samples512, | 164 | .smpr(1) |
| 147 | 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 | } | ||
| 148 | } | 201 | } |
| 149 | 202 | ||
| 150 | 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 | |||
| 151 | /// Create a new ADC driver. | 238 | /// Create a new ADC driver. |
| 152 | pub fn new(adc: Peri<'d, T>) -> Self { | 239 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 153 | rcc::enable_and_reset::<T>(); | 240 | rcc::enable_and_reset::<T>(); |
| 154 | 241 | ||
| 155 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 242 | let prescaler = from_ker_ck(T::frequency()); |
| 156 | 243 | ||
| 157 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 244 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); |
| 158 | 245 | ||
| 159 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 246 | let frequency = T::frequency() / prescaler; |
| 160 | info!("ADC frequency set to {}", frequency); | 247 | info!("ADC frequency set to {}", frequency); |
| 161 | 248 | ||
| 162 | if frequency > MAX_ADC_CLK_FREQ { | 249 | if frequency > MAX_ADC_CLK_FREQ { |
| @@ -179,37 +266,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 179 | }; | 266 | }; |
| 180 | T::regs().cr().modify(|w| w.set_boost(boost)); | 267 | T::regs().cr().modify(|w| w.set_boost(boost)); |
| 181 | } | 268 | } |
| 182 | let mut s = Self { adc }; | ||
| 183 | s.power_up(); | ||
| 184 | s.configure_differential_inputs(); | ||
| 185 | 269 | ||
| 186 | s.calibrate(); | ||
| 187 | blocking_delay_us(1); | ||
| 188 | |||
| 189 | s.enable(); | ||
| 190 | s.configure(); | ||
| 191 | |||
| 192 | s | ||
| 193 | } | ||
| 194 | |||
| 195 | fn power_up(&mut self) { | ||
| 196 | T::regs().cr().modify(|reg| { | 270 | T::regs().cr().modify(|reg| { |
| 197 | reg.set_deeppwd(false); | 271 | reg.set_deeppwd(false); |
| 198 | reg.set_advregen(true); | 272 | reg.set_advregen(true); |
| 199 | }); | 273 | }); |
| 200 | 274 | ||
| 201 | blocking_delay_us(10); | 275 | blocking_delay_us(10); |
| 202 | } | ||
| 203 | 276 | ||
| 204 | fn configure_differential_inputs(&mut self) { | ||
| 205 | T::regs().difsel().modify(|w| { | 277 | T::regs().difsel().modify(|w| { |
| 206 | for n in 0..20 { | 278 | for n in 0..20 { |
| 207 | w.set_difsel(n, Difsel::SINGLE_ENDED); | 279 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 208 | } | 280 | } |
| 209 | }); | 281 | }); |
| 210 | } | ||
| 211 | 282 | ||
| 212 | fn calibrate(&mut self) { | ||
| 213 | T::regs().cr().modify(|w| { | 283 | T::regs().cr().modify(|w| { |
| 214 | #[cfg(not(adc_u5))] | 284 | #[cfg(not(adc_u5))] |
| 215 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | 285 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| @@ -219,21 +289,18 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 219 | T::regs().cr().modify(|w| w.set_adcal(true)); | 289 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 220 | 290 | ||
| 221 | while T::regs().cr().read().adcal() {} | 291 | while T::regs().cr().read().adcal() {} |
| 222 | } | ||
| 223 | 292 | ||
| 224 | fn enable(&mut self) { | 293 | blocking_delay_us(1); |
| 225 | T::regs().isr().write(|w| w.set_adrdy(true)); | 294 | |
| 226 | T::regs().cr().modify(|w| w.set_aden(true)); | 295 | T::enable(); |
| 227 | while !T::regs().isr().read().adrdy() {} | ||
| 228 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 229 | } | ||
| 230 | 296 | ||
| 231 | fn configure(&mut self) { | ||
| 232 | // single conversion mode, software trigger | 297 | // single conversion mode, software trigger |
| 233 | T::regs().cfgr().modify(|w| { | 298 | T::regs().cfgr().modify(|w| { |
| 234 | w.set_cont(false); | 299 | w.set_cont(false); |
| 235 | w.set_exten(Exten::DISABLED); | 300 | w.set_exten(Exten::DISABLED); |
| 236 | }); | 301 | }); |
| 302 | |||
| 303 | Self { adc } | ||
| 237 | } | 304 | } |
| 238 | 305 | ||
| 239 | /// Enable reading the voltage reference internal channel. | 306 | /// Enable reading the voltage reference internal channel. |
| @@ -262,218 +329,4 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 262 | 329 | ||
| 263 | Vbat {} | 330 | Vbat {} |
| 264 | } | 331 | } |
| 265 | |||
| 266 | /// Set the ADC resolution. | ||
| 267 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 268 | T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); | ||
| 269 | } | ||
| 270 | |||
| 271 | /// Set hardware averaging. | ||
| 272 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 273 | let (enable, samples, right_shift) = match averaging { | ||
| 274 | Averaging::Disabled => (false, 0, 0), | ||
| 275 | Averaging::Samples2 => (true, 1, 1), | ||
| 276 | Averaging::Samples4 => (true, 3, 2), | ||
| 277 | Averaging::Samples8 => (true, 7, 3), | ||
| 278 | Averaging::Samples16 => (true, 15, 4), | ||
| 279 | Averaging::Samples32 => (true, 31, 5), | ||
| 280 | Averaging::Samples64 => (true, 63, 6), | ||
| 281 | Averaging::Samples128 => (true, 127, 7), | ||
| 282 | Averaging::Samples256 => (true, 255, 8), | ||
| 283 | Averaging::Samples512 => (true, 511, 9), | ||
| 284 | Averaging::Samples1024 => (true, 1023, 10), | ||
| 285 | }; | ||
| 286 | |||
| 287 | T::regs().cfgr2().modify(|reg| { | ||
| 288 | reg.set_rovse(enable); | ||
| 289 | reg.set_ovsr(samples); | ||
| 290 | reg.set_ovss(right_shift); | ||
| 291 | }) | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Perform a single conversion. | ||
| 295 | fn convert(&mut self) -> u16 { | ||
| 296 | T::regs().isr().modify(|reg| { | ||
| 297 | reg.set_eos(true); | ||
| 298 | reg.set_eoc(true); | ||
| 299 | }); | ||
| 300 | |||
| 301 | // Start conversion | ||
| 302 | T::regs().cr().modify(|reg| { | ||
| 303 | reg.set_adstart(true); | ||
| 304 | }); | ||
| 305 | |||
| 306 | while !T::regs().isr().read().eos() { | ||
| 307 | // spin | ||
| 308 | } | ||
| 309 | |||
| 310 | T::regs().dr().read().0 as u16 | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Read an ADC channel. | ||
| 314 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 315 | self.read_channel(channel, sample_time) | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Read one or multiple ADC channels using DMA. | ||
| 319 | /// | ||
| 320 | /// `sequence` iterator and `readings` must have the same length. | ||
| 321 | /// | ||
| 322 | /// Example | ||
| 323 | /// ```rust,ignore | ||
| 324 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 325 | /// | ||
| 326 | /// let mut adc = Adc::new(p.ADC1); | ||
| 327 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 328 | /// let mut adc_pin2 = p.PA2.into(); | ||
| 329 | /// let mut measurements = [0u16; 2]; | ||
| 330 | /// | ||
| 331 | /// adc.read( | ||
| 332 | /// p.DMA2_CH0.reborrow(), | ||
| 333 | /// [ | ||
| 334 | /// (&mut *adc_pin0, SampleTime::CYCLES112), | ||
| 335 | /// (&mut *adc_pin2, SampleTime::CYCLES112), | ||
| 336 | /// ] | ||
| 337 | /// .into_iter(), | ||
| 338 | /// &mut measurements, | ||
| 339 | /// ) | ||
| 340 | /// .await; | ||
| 341 | /// defmt::info!("measurements: {}", measurements); | ||
| 342 | /// ``` | ||
| 343 | pub async fn read( | ||
| 344 | &mut self, | ||
| 345 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 346 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 347 | readings: &mut [u16], | ||
| 348 | ) { | ||
| 349 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 350 | assert!( | ||
| 351 | sequence.len() == readings.len(), | ||
| 352 | "Sequence length must be equal to readings length" | ||
| 353 | ); | ||
| 354 | assert!( | ||
| 355 | sequence.len() <= 16, | ||
| 356 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 357 | ); | ||
| 358 | |||
| 359 | // Ensure no conversions are ongoing | ||
| 360 | Self::cancel_conversions(); | ||
| 361 | |||
| 362 | // Set sequence length | ||
| 363 | T::regs().sqr1().modify(|w| { | ||
| 364 | w.set_l(sequence.len() as u8 - 1); | ||
| 365 | }); | ||
| 366 | |||
| 367 | // Configure channels and ranks | ||
| 368 | for (i, (channel, sample_time)) in sequence.enumerate() { | ||
| 369 | Self::configure_channel(channel, sample_time); | ||
| 370 | match i { | ||
| 371 | 0..=3 => { | ||
| 372 | T::regs().sqr1().modify(|w| { | ||
| 373 | w.set_sq(i, channel.channel()); | ||
| 374 | }); | ||
| 375 | } | ||
| 376 | 4..=8 => { | ||
| 377 | T::regs().sqr2().modify(|w| { | ||
| 378 | w.set_sq(i - 4, channel.channel()); | ||
| 379 | }); | ||
| 380 | } | ||
| 381 | 9..=13 => { | ||
| 382 | T::regs().sqr3().modify(|w| { | ||
| 383 | w.set_sq(i - 9, channel.channel()); | ||
| 384 | }); | ||
| 385 | } | ||
| 386 | 14..=15 => { | ||
| 387 | T::regs().sqr4().modify(|w| { | ||
| 388 | w.set_sq(i - 14, channel.channel()); | ||
| 389 | }); | ||
| 390 | } | ||
| 391 | _ => unreachable!(), | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | // Set continuous mode with oneshot dma. | ||
| 396 | // Clear overrun flag before starting transfer. | ||
| 397 | |||
| 398 | T::regs().isr().modify(|reg| { | ||
| 399 | reg.set_ovr(true); | ||
| 400 | }); | ||
| 401 | T::regs().cfgr().modify(|reg| { | ||
| 402 | reg.set_cont(true); | ||
| 403 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); | ||
| 404 | }); | ||
| 405 | |||
| 406 | let request = rx_dma.request(); | ||
| 407 | let transfer = unsafe { | ||
| 408 | Transfer::new_read( | ||
| 409 | rx_dma, | ||
| 410 | request, | ||
| 411 | T::regs().dr().as_ptr() as *mut u16, | ||
| 412 | readings, | ||
| 413 | Default::default(), | ||
| 414 | ) | ||
| 415 | }; | ||
| 416 | |||
| 417 | // Start conversion | ||
| 418 | T::regs().cr().modify(|reg| { | ||
| 419 | reg.set_adstart(true); | ||
| 420 | }); | ||
| 421 | |||
| 422 | // Wait for conversion sequence to finish. | ||
| 423 | transfer.await; | ||
| 424 | |||
| 425 | // Ensure conversions are finished. | ||
| 426 | Self::cancel_conversions(); | ||
| 427 | |||
| 428 | // Reset configuration. | ||
| 429 | T::regs().cfgr().modify(|reg| { | ||
| 430 | reg.set_cont(false); | ||
| 431 | reg.set_dmngt(Dmngt::from_bits(0)); | ||
| 432 | }); | ||
| 433 | } | ||
| 434 | |||
| 435 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 436 | channel.setup(); | ||
| 437 | |||
| 438 | let channel = channel.channel(); | ||
| 439 | |||
| 440 | Self::set_channel_sample_time(channel, sample_time); | ||
| 441 | |||
| 442 | #[cfg(any(stm32h7, stm32u5))] | ||
| 443 | { | ||
| 444 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | ||
| 445 | T::regs() | ||
| 446 | .pcsel() | ||
| 447 | .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 { | ||
| 452 | Self::configure_channel(channel, sample_time); | ||
| 453 | |||
| 454 | T::regs().sqr1().modify(|reg| { | ||
| 455 | reg.set_sq(0, channel.channel()); | ||
| 456 | reg.set_l(0); | ||
| 457 | }); | ||
| 458 | |||
| 459 | self.convert() | ||
| 460 | } | ||
| 461 | |||
| 462 | fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 463 | let sample_time = sample_time.into(); | ||
| 464 | if ch <= 9 { | ||
| 465 | T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 466 | } else { | ||
| 467 | T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | fn cancel_conversions() { | ||
| 472 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 473 | T::regs().cr().modify(|reg| { | ||
| 474 | reg.set_adstp(Adstp::STOP); | ||
| 475 | }); | ||
| 476 | while T::regs().cr().read().adstart() {} | ||
| 477 | } | ||
| 478 | } | ||
| 479 | } | 332 | } |
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 90dbf4f09..b46ae2813 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs | |||
| @@ -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 106558d20..383c74a78 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs | |||
| @@ -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; |
| @@ -408,7 +409,7 @@ impl AnyChannel { | |||
| 408 | /// Linked-list DMA transfer. | 409 | /// Linked-list DMA transfer. |
| 409 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 410 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 410 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { | 411 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { |
| 411 | channel: Peri<'a, AnyChannel>, | 412 | channel: BusyChannel<'a>, |
| 412 | } | 413 | } |
| 413 | 414 | ||
| 414 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | 415 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { |
| @@ -429,7 +430,9 @@ impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | |||
| 429 | channel.configure_linked_list(&table, options); | 430 | channel.configure_linked_list(&table, options); |
| 430 | channel.start(); | 431 | channel.start(); |
| 431 | 432 | ||
| 432 | Self { channel } | 433 | Self { |
| 434 | channel: BusyChannel::new(channel), | ||
| 435 | } | ||
| 433 | } | 436 | } |
| 434 | 437 | ||
| 435 | /// 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. |
| @@ -505,7 +508,7 @@ impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> | |||
| 505 | /// DMA transfer. | 508 | /// DMA transfer. |
| 506 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 509 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 507 | pub struct Transfer<'a> { | 510 | pub struct Transfer<'a> { |
| 508 | channel: Peri<'a, AnyChannel>, | 511 | channel: BusyChannel<'a>, |
| 509 | } | 512 | } |
| 510 | 513 | ||
| 511 | impl<'a> Transfer<'a> { | 514 | impl<'a> Transfer<'a> { |
| @@ -625,7 +628,9 @@ impl<'a> Transfer<'a> { | |||
| 625 | ); | 628 | ); |
| 626 | channel.start(); | 629 | channel.start(); |
| 627 | 630 | ||
| 628 | Self { channel } | 631 | Self { |
| 632 | channel: BusyChannel::new(channel), | ||
| 633 | } | ||
| 629 | } | 634 | } |
| 630 | 635 | ||
| 631 | /// 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 94c597e0d..54e4d5f71 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs | |||
| @@ -12,7 +12,7 @@ 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 de7a2c175..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)] |
| @@ -48,6 +51,8 @@ pub type Request = (); | |||
| 48 | pub(crate) trait SealedChannel { | 51 | pub(crate) trait SealedChannel { |
| 49 | #[cfg(not(stm32n6))] | 52 | #[cfg(not(stm32n6))] |
| 50 | fn id(&self) -> u8; | 53 | fn id(&self) -> u8; |
| 54 | #[cfg(feature = "low-power")] | ||
| 55 | fn stop_mode(&self) -> crate::rcc::StopMode; | ||
| 51 | } | 56 | } |
| 52 | 57 | ||
| 53 | #[cfg(not(stm32n6))] | 58 | #[cfg(not(stm32n6))] |
| @@ -62,15 +67,25 @@ pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static { | |||
| 62 | 67 | ||
| 63 | #[cfg(not(stm32n6))] | 68 | #[cfg(not(stm32n6))] |
| 64 | macro_rules! dma_channel_impl { | 69 | macro_rules! dma_channel_impl { |
| 65 | ($channel_peri:ident, $index:expr) => { | 70 | ($channel_peri:ident, $index:expr, $stop_mode:ident) => { |
| 66 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { | 71 | impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { |
| 67 | fn id(&self) -> u8 { | 72 | fn id(&self) -> u8 { |
| 68 | $index | 73 | $index |
| 69 | } | 74 | } |
| 75 | |||
| 76 | #[cfg(feature = "low-power")] | ||
| 77 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 78 | crate::rcc::StopMode::$stop_mode | ||
| 79 | } | ||
| 70 | } | 80 | } |
| 71 | impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { | 81 | impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { |
| 72 | unsafe fn on_irq() { | 82 | unsafe fn on_irq() { |
| 73 | 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(); | ||
| 74 | } | 89 | } |
| 75 | } | 90 | } |
| 76 | 91 | ||
| @@ -80,15 +95,57 @@ macro_rules! dma_channel_impl { | |||
| 80 | fn from(val: crate::peripherals::$channel_peri) -> Self { | 95 | fn from(val: crate::peripherals::$channel_peri) -> Self { |
| 81 | Self { | 96 | Self { |
| 82 | 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), | ||
| 83 | } | 100 | } |
| 84 | } | 101 | } |
| 85 | } | 102 | } |
| 86 | }; | 103 | }; |
| 87 | } | 104 | } |
| 88 | 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 | |||
| 89 | /// Type-erased DMA channel. | 144 | /// Type-erased DMA channel. |
| 90 | pub struct AnyChannel { | 145 | pub struct AnyChannel { |
| 91 | pub(crate) id: u8, | 146 | pub(crate) id: u8, |
| 147 | #[cfg(feature = "low-power")] | ||
| 148 | pub(crate) stop_mode: crate::rcc::StopMode, | ||
| 92 | } | 149 | } |
| 93 | impl_peripheral!(AnyChannel); | 150 | impl_peripheral!(AnyChannel); |
| 94 | 151 | ||
| @@ -103,6 +160,11 @@ impl SealedChannel for AnyChannel { | |||
| 103 | fn id(&self) -> u8 { | 160 | fn id(&self) -> u8 { |
| 104 | self.id | 161 | self.id |
| 105 | } | 162 | } |
| 163 | |||
| 164 | #[cfg(feature = "low-power")] | ||
| 165 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 166 | self.stop_mode | ||
| 167 | } | ||
| 106 | } | 168 | } |
| 107 | impl Channel for AnyChannel {} | 169 | impl Channel for AnyChannel {} |
| 108 | 170 | ||
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index 59a2cbcdb..b8945820c 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -5,18 +5,11 @@ 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::{Peri, peripherals}; | 11 | use crate::{Peri, peripherals}; |
| 11 | 12 | ||
| 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 | |||
| 20 | /// PacketTypes extracted from CubeMX | 13 | /// PacketTypes extracted from CubeMX |
| 21 | #[repr(u8)] | 14 | #[repr(u8)] |
| 22 | #[allow(dead_code)] | 15 | #[allow(dead_code)] |
| @@ -334,7 +327,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 334 | if T::regs().gpsr().read().cmdfe() { | 327 | if T::regs().gpsr().read().cmdfe() { |
| 335 | return Ok(()); | 328 | return Ok(()); |
| 336 | } | 329 | } |
| 337 | blocking_delay_ms(1); | 330 | block_for_us(1_000); |
| 338 | } | 331 | } |
| 339 | Err(Error::FifoTimeout) | 332 | Err(Error::FifoTimeout) |
| 340 | } | 333 | } |
| @@ -345,7 +338,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 345 | if !T::regs().gpsr().read().cmdff() { | 338 | if !T::regs().gpsr().read().cmdff() { |
| 346 | return Ok(()); | 339 | return Ok(()); |
| 347 | } | 340 | } |
| 348 | blocking_delay_ms(1); | 341 | block_for_us(1_000); |
| 349 | } | 342 | } |
| 350 | Err(Error::FifoTimeout) | 343 | Err(Error::FifoTimeout) |
| 351 | } | 344 | } |
| @@ -356,7 +349,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 356 | if !self.read_busy() { | 349 | if !self.read_busy() { |
| 357 | return Ok(()); | 350 | return Ok(()); |
| 358 | } | 351 | } |
| 359 | blocking_delay_ms(1); | 352 | block_for_us(1_000); |
| 360 | } | 353 | } |
| 361 | Err(Error::ReadTimeout) | 354 | Err(Error::ReadTimeout) |
| 362 | } | 355 | } |
| @@ -367,7 +360,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 367 | if !T::regs().gpsr().read().prdfe() { | 360 | if !T::regs().gpsr().read().prdfe() { |
| 368 | return Ok(()); | 361 | return Ok(()); |
| 369 | } | 362 | } |
| 370 | blocking_delay_ms(1); | 363 | block_for_us(1_000); |
| 371 | } | 364 | } |
| 372 | Err(Error::FifoTimeout) | 365 | Err(Error::FifoTimeout) |
| 373 | } | 366 | } |
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 a77eb8719..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; | ||
| 7 | use core::sync::atomic::{Ordering, fence}; | 6 | use core::sync::atomic::{Ordering, fence}; |
| 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 | ||
| @@ -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,58 +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!( | 364 | config_pins!( |
| 354 | 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 | 365 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en |
| 355 | ); | 366 | ); |
| 356 | 367 | ||
| 357 | let pins = Pins::Mii([ | 368 | let pins = Pins::Mii([ |
| 358 | rx_clk.into(), | 369 | rx_clk.into(), |
| 359 | tx_clk.into(), | 370 | tx_clk.into(), |
| 360 | mdio.into(), | ||
| 361 | mdc.into(), | ||
| 362 | rxdv.into(), | 371 | rxdv.into(), |
| 363 | rx_d0.into(), | 372 | rx_d0.into(), |
| 364 | rx_d1.into(), | 373 | rx_d1.into(), |
| @@ -371,43 +380,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 371 | tx_en.into(), | 380 | tx_en.into(), |
| 372 | ]); | 381 | ]); |
| 373 | 382 | ||
| 374 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 383 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false) |
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Ethernet station management interface. | ||
| 379 | pub(crate) struct EthernetStationManagement<T: Instance> { | ||
| 380 | peri: PhantomData<T>, | ||
| 381 | clock_range: Cr, | ||
| 382 | } | ||
| 383 | |||
| 384 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 385 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 386 | let mac = T::regs().ethernet_mac(); | ||
| 387 | |||
| 388 | mac.macmiiar().modify(|w| { | ||
| 389 | w.set_pa(phy_addr); | ||
| 390 | w.set_mr(reg); | ||
| 391 | w.set_mw(Mw::READ); // read operation | ||
| 392 | w.set_cr(self.clock_range); | ||
| 393 | w.set_mb(MbProgress::BUSY); // indicate that operation is in progress | ||
| 394 | }); | ||
| 395 | while mac.macmiiar().read().mb() == MbProgress::BUSY {} | ||
| 396 | mac.macmiidr().read().md() | ||
| 397 | } | ||
| 398 | |||
| 399 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 400 | let mac = T::regs().ethernet_mac(); | ||
| 401 | |||
| 402 | mac.macmiidr().write(|w| w.set_md(val)); | ||
| 403 | mac.macmiiar().modify(|w| { | ||
| 404 | w.set_pa(phy_addr); | ||
| 405 | w.set_mr(reg); | ||
| 406 | w.set_mw(Mw::WRITE); // write | ||
| 407 | w.set_cr(self.clock_range); | ||
| 408 | w.set_mb(MbProgress::BUSY); | ||
| 409 | }); | ||
| 410 | while mac.macmiiar().read().mb() == MbProgress::BUSY {} | ||
| 411 | } | 384 | } |
| 412 | } | 385 | } |
| 413 | 386 | ||
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 39a6e8b0f..7f92e351c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | mod descriptors; | 1 | mod descriptors; |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | ||
| 4 | use core::sync::atomic::{Ordering, fence}; | 3 | use core::sync::atomic::{Ordering, fence}; |
| 5 | 4 | ||
| 6 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| @@ -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,31 +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 | ||
| 135 | critical_section::with(|_| { | ||
| 136 | crate::pac::RCC.ahb1enr().modify(|w| { | ||
| 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!( | 184 | config_pins!( |
| 148 | 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 | 185 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en |
| 149 | ); | 186 | ); |
| 150 | 187 | ||
| 151 | let pins = Pins::Mii([ | 188 | let pins = Pins::Mii([ |
| 152 | rx_clk.into(), | 189 | rx_clk.into(), |
| 153 | tx_clk.into(), | 190 | tx_clk.into(), |
| 154 | mdio.into(), | ||
| 155 | mdc.into(), | ||
| 156 | rxdv.into(), | 191 | rxdv.into(), |
| 157 | rx_d0.into(), | 192 | rx_d0.into(), |
| 158 | rx_d1.into(), | 193 | rx_d1.into(), |
| @@ -165,7 +200,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 165 | tx_en.into(), | 200 | tx_en.into(), |
| 166 | ]); | 201 | ]); |
| 167 | 202 | ||
| 168 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 203 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII) |
| 169 | } | 204 | } |
| 170 | 205 | ||
| 171 | fn new_inner<const TX: usize, const RX: usize>( | 206 | fn new_inner<const TX: usize, const RX: usize>( |
| @@ -175,7 +210,19 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 175 | pins: Pins<'d>, | 210 | pins: Pins<'d>, |
| 176 | phy: P, | 211 | phy: P, |
| 177 | mac_addr: [u8; 6], | 212 | mac_addr: [u8; 6], |
| 213 | eth_sel_phy: EthSelPhy, | ||
| 178 | ) -> 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 | |||
| 179 | let dma = T::regs().ethernet_dma(); | 226 | let dma = T::regs().ethernet_dma(); |
| 180 | let mac = T::regs().ethernet_mac(); | 227 | let mac = T::regs().ethernet_mac(); |
| 181 | let mtl = T::regs().ethernet_mtl(); | 228 | let mtl = T::regs().ethernet_mtl(); |
| @@ -237,32 +284,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 237 | w.set_rbsz(RX_BUFFER_SIZE as u16); | 284 | w.set_rbsz(RX_BUFFER_SIZE as u16); |
| 238 | }); | 285 | }); |
| 239 | 286 | ||
| 240 | let hclk = <T as SealedRccPeripheral>::frequency(); | ||
| 241 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 242 | |||
| 243 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 244 | let clock_range = match hclk_mhz { | ||
| 245 | 0..=34 => 2, // Divide by 16 | ||
| 246 | 35..=59 => 3, // Divide by 26 | ||
| 247 | 60..=99 => 0, // Divide by 42 | ||
| 248 | 100..=149 => 1, // Divide by 62 | ||
| 249 | 150..=249 => 4, // Divide by 102 | ||
| 250 | 250..=310 => 5, // Divide by 124 | ||
| 251 | _ => { | ||
| 252 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 253 | } | ||
| 254 | }; | ||
| 255 | |||
| 256 | let mut this = Self { | 287 | let mut this = Self { |
| 257 | _peri: peri, | 288 | _peri: peri, |
| 258 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 289 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 259 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 290 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| 260 | pins, | 291 | pins, |
| 261 | phy, | 292 | phy, |
| 262 | station_management: EthernetStationManagement { | ||
| 263 | peri: PhantomData, | ||
| 264 | clock_range: clock_range, | ||
| 265 | }, | ||
| 266 | mac_addr, | 293 | mac_addr, |
| 267 | }; | 294 | }; |
| 268 | 295 | ||
| @@ -288,8 +315,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 288 | w.set_tie(true); | 315 | w.set_tie(true); |
| 289 | }); | 316 | }); |
| 290 | 317 | ||
| 291 | this.phy.phy_reset(&mut this.station_management); | 318 | this.phy.phy_reset(); |
| 292 | this.phy.phy_init(&mut this.station_management); | 319 | this.phy.phy_init(); |
| 293 | 320 | ||
| 294 | interrupt::ETH.unpend(); | 321 | interrupt::ETH.unpend(); |
| 295 | unsafe { interrupt::ETH.enable() }; | 322 | unsafe { interrupt::ETH.enable() }; |
| @@ -298,42 +325,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 298 | } | 325 | } |
| 299 | } | 326 | } |
| 300 | 327 | ||
| 301 | /// Ethernet SMI driver. | ||
| 302 | pub struct EthernetStationManagement<T: Instance> { | ||
| 303 | peri: PhantomData<T>, | ||
| 304 | clock_range: u8, | ||
| 305 | } | ||
| 306 | |||
| 307 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 308 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 309 | let mac = T::regs().ethernet_mac(); | ||
| 310 | |||
| 311 | mac.macmdioar().modify(|w| { | ||
| 312 | w.set_pa(phy_addr); | ||
| 313 | w.set_rda(reg); | ||
| 314 | w.set_goc(0b11); // read | ||
| 315 | w.set_cr(self.clock_range); | ||
| 316 | w.set_mb(true); | ||
| 317 | }); | ||
| 318 | while mac.macmdioar().read().mb() {} | ||
| 319 | mac.macmdiodr().read().md() | ||
| 320 | } | ||
| 321 | |||
| 322 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 323 | let mac = T::regs().ethernet_mac(); | ||
| 324 | |||
| 325 | mac.macmdiodr().write(|w| w.set_md(val)); | ||
| 326 | mac.macmdioar().modify(|w| { | ||
| 327 | w.set_pa(phy_addr); | ||
| 328 | w.set_rda(reg); | ||
| 329 | w.set_goc(0b01); // write | ||
| 330 | w.set_cr(self.clock_range); | ||
| 331 | w.set_mb(true); | ||
| 332 | }); | ||
| 333 | while mac.macmdioar().read().mb() {} | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | 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> { |
| 338 | fn drop(&mut self) { | 329 | fn drop(&mut self) { |
| 339 | 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 cb46d362c..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::{PeripheralType, impl_peripheral}; | 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, PinNumber, Pull}; | 12 | use crate::gpio::{AnyPin, ExtiPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; |
| 13 | use crate::interrupt::Interrupt as InterruptEnum; | ||
| 14 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt as InterruptType}; | ||
| 12 | use crate::pac::EXTI; | 15 | use crate::pac::EXTI; |
| 13 | use crate::pac::exti::regs::Lines; | 16 | use crate::pac::exti::regs::Lines; |
| 14 | use crate::{Peri, interrupt, pac, peripherals}; | 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]; |
| @@ -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 | ||
| @@ -227,11 +258,12 @@ impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> { | |||
| 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: PinNumber, | 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: PinNumber, port: PinNumber, 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)); |
| @@ -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) -> PinNumber; | 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 { |
| 392 | #[allow(unused)] | ||
| 344 | number: PinNumber, | 393 | number: PinNumber, |
| 345 | } | 394 | } |
| 346 | 395 | ||
| 347 | impl_peripheral!(AnyChannel); | ||
| 348 | impl SealedChannel for AnyChannel {} | ||
| 349 | impl Channel for AnyChannel { | ||
| 350 | fn number(&self) -> PinNumber { | ||
| 351 | self.number | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 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) -> PinNumber { | 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 PinNumber, | 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/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 b595938a6..60d00e766 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs | |||
| @@ -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/g.rs b/embassy-stm32/src/flash/g.rs index d026541a4..d7ba2f571 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs | |||
| @@ -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 | }); |
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 8a43cce3f..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::{Ordering, fence}; | 2 | use core::sync::atomic::{Ordering, fence}; |
| 3 | 3 | ||
| 4 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 5 | use pac::flash::regs::Sr; | ||
| 6 | |||
| 4 | use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; | 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/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/gpio.rs b/embassy-stm32/src/gpio.rs index 17c5a9962..e7d4e9ad3 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs | |||
| @@ -812,15 +812,19 @@ pub type PinNumber = u8; | |||
| 812 | #[cfg(stm32n6)] | 812 | #[cfg(stm32n6)] |
| 813 | pub type PinNumber = u16; | 813 | pub type PinNumber = u16; |
| 814 | 814 | ||
| 815 | /// GPIO pin trait. | 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")] | ||
| 816 | #[allow(private_bounds)] | 817 | #[allow(private_bounds)] |
| 817 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | 818 | pub trait ExtiPin: PeripheralType + SealedPin { |
| 818 | /// EXTI channel assigned to this pin. | 819 | /// EXTI channel assigned to this pin. |
| 819 | /// | 820 | /// |
| 820 | /// For example, PC4 uses EXTI4. | 821 | /// For example, PC4 uses EXTI4. |
| 821 | #[cfg(feature = "exti")] | ||
| 822 | type ExtiChannel: crate::exti::Channel; | 822 | type ExtiChannel: crate::exti::Channel; |
| 823 | } | ||
| 823 | 824 | ||
| 825 | /// GPIO pin trait. | ||
| 826 | #[allow(private_bounds)] | ||
| 827 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | ||
| 824 | /// Number of the pin within the port (0..31) | 828 | /// Number of the pin within the port (0..31) |
| 825 | #[inline] | 829 | #[inline] |
| 826 | fn pin(&self) -> PinNumber { | 830 | fn pin(&self) -> PinNumber { |
| @@ -834,7 +838,7 @@ pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | |||
| 834 | } | 838 | } |
| 835 | } | 839 | } |
| 836 | 840 | ||
| 837 | /// Type-erased GPIO pin | 841 | /// Type-erased GPIO pin. |
| 838 | pub struct AnyPin { | 842 | pub struct AnyPin { |
| 839 | pin_port: PinNumber, | 843 | pin_port: PinNumber, |
| 840 | } | 844 | } |
| @@ -862,10 +866,7 @@ impl AnyPin { | |||
| 862 | } | 866 | } |
| 863 | 867 | ||
| 864 | impl_peripheral!(AnyPin); | 868 | impl_peripheral!(AnyPin); |
| 865 | impl Pin for AnyPin { | 869 | impl Pin for AnyPin {} |
| 866 | #[cfg(feature = "exti")] | ||
| 867 | type ExtiChannel = crate::exti::AnyChannel; | ||
| 868 | } | ||
| 869 | impl SealedPin for AnyPin { | 870 | impl SealedPin for AnyPin { |
| 870 | #[inline] | 871 | #[inline] |
| 871 | fn pin_port(&self) -> PinNumber { | 872 | fn pin_port(&self) -> PinNumber { |
| @@ -878,7 +879,9 @@ impl SealedPin for AnyPin { | |||
| 878 | foreach_pin!( | 879 | foreach_pin!( |
| 879 | ($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) => { |
| 880 | impl Pin for peripherals::$pin_name { | 881 | impl Pin for peripherals::$pin_name { |
| 881 | #[cfg(feature = "exti")] | 882 | } |
| 883 | #[cfg(feature = "exti")] | ||
| 884 | impl ExtiPin for peripherals::$pin_name { | ||
| 882 | type ExtiChannel = peripherals::$exti_ch; | 885 | type ExtiChannel = peripherals::$exti_ch; |
| 883 | } | 886 | } |
| 884 | impl SealedPin for peripherals::$pin_name { | 887 | impl SealedPin for peripherals::$pin_name { |
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 4d3a5d68d..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 | // 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. |
| 6 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, | 14 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, |
| 7 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), | 15 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), |
| 8 | // which is not yet supported by this code. | 16 | // which is not yet supported by this code. |
| 9 | use crate::Peri; | 17 | use crate::Peri; |
| 10 | use crate::pac; | ||
| 11 | use crate::rcc::{self, RccPeripheral}; | 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)); | ||
| 161 | |||
| 162 | match self.try_lock(process_id) { | ||
| 163 | Some(mutex) => Poll::Ready(mutex), | ||
| 164 | None => Poll::Pending, | ||
| 165 | } | ||
| 166 | }) | ||
| 167 | .await | ||
| 168 | } | ||
| 84 | 169 | ||
| 85 | HardwareSemaphore { _peri: peripheral } | 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 69baa708e..1d3560678 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs | |||
| @@ -391,7 +391,7 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 391 | while T::REGS.sr().read().busy() {} | 391 | while T::REGS.sr().read().busy() {} |
| 392 | 392 | ||
| 393 | T::REGS.cr().modify(|w| { | 393 | T::REGS.cr().modify(|w| { |
| 394 | w.set_fmode(0.into()); | 394 | w.set_fmode(FunctionalMode::IndirectWrite.into()); |
| 395 | }); | 395 | }); |
| 396 | 396 | ||
| 397 | // Configure alternate bytes | 397 | // Configure alternate bytes |
| @@ -498,7 +498,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 498 | w.set_dmaen(false); | 498 | w.set_dmaen(false); |
| 499 | }); | 499 | }); |
| 500 | 500 | ||
| 501 | self.configure_command(&transaction, Some(buf.len()))?; | 501 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 502 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 502 | 503 | ||
| 503 | let current_address = T::REGS.ar().read().address(); | 504 | let current_address = T::REGS.ar().read().address(); |
| 504 | let current_instruction = T::REGS.ir().read().instruction(); | 505 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -537,7 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 537 | w.set_dmaen(false); | 538 | w.set_dmaen(false); |
| 538 | }); | 539 | }); |
| 539 | 540 | ||
| 540 | self.configure_command(&transaction, Some(buf.len()))?; | 541 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 542 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 541 | 543 | ||
| 542 | T::REGS | 544 | T::REGS |
| 543 | .cr() | 545 | .cr() |
| @@ -767,7 +769,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 767 | // Wait for peripheral to be free | 769 | // Wait for peripheral to be free |
| 768 | while T::REGS.sr().read().busy() {} | 770 | while T::REGS.sr().read().busy() {} |
| 769 | 771 | ||
| 770 | self.configure_command(&transaction, Some(buf.len()))?; | 772 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 773 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 771 | 774 | ||
| 772 | let current_address = T::REGS.ar().read().address(); | 775 | let current_address = T::REGS.ar().read().address(); |
| 773 | let current_instruction = T::REGS.ir().read().instruction(); | 776 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -782,16 +785,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 782 | T::REGS.ar().write(|v| v.set_address(current_address)); | 785 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 783 | } | 786 | } |
| 784 | 787 | ||
| 785 | let transfer = unsafe { | 788 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 786 | self.dma | 789 | let transfer = unsafe { |
| 787 | .as_mut() | 790 | self.dma |
| 788 | .unwrap() | 791 | .as_mut() |
| 789 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 792 | .unwrap() |
| 790 | }; | 793 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 794 | }; | ||
| 791 | 795 | ||
| 792 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 796 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 793 | 797 | ||
| 794 | transfer.blocking_wait(); | 798 | transfer.blocking_wait(); |
| 799 | } | ||
| 795 | 800 | ||
| 796 | finish_dma(T::REGS); | 801 | finish_dma(T::REGS); |
| 797 | 802 | ||
| @@ -807,21 +812,24 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 807 | // Wait for peripheral to be free | 812 | // Wait for peripheral to be free |
| 808 | while T::REGS.sr().read().busy() {} | 813 | while T::REGS.sr().read().busy() {} |
| 809 | 814 | ||
| 810 | self.configure_command(&transaction, Some(buf.len()))?; | 815 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 816 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 811 | T::REGS | 817 | T::REGS |
| 812 | .cr() | 818 | .cr() |
| 813 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | 819 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); |
| 814 | 820 | ||
| 815 | let transfer = unsafe { | 821 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 816 | self.dma | 822 | let transfer = unsafe { |
| 817 | .as_mut() | 823 | self.dma |
| 818 | .unwrap() | 824 | .as_mut() |
| 819 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 825 | .unwrap() |
| 820 | }; | 826 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) |
| 827 | }; | ||
| 821 | 828 | ||
| 822 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 829 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 823 | 830 | ||
| 824 | transfer.blocking_wait(); | 831 | transfer.blocking_wait(); |
| 832 | } | ||
| 825 | 833 | ||
| 826 | finish_dma(T::REGS); | 834 | finish_dma(T::REGS); |
| 827 | 835 | ||
| @@ -837,7 +845,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 837 | // Wait for peripheral to be free | 845 | // Wait for peripheral to be free |
| 838 | while T::REGS.sr().read().busy() {} | 846 | while T::REGS.sr().read().busy() {} |
| 839 | 847 | ||
| 840 | self.configure_command(&transaction, Some(buf.len()))?; | 848 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 849 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 841 | 850 | ||
| 842 | let current_address = T::REGS.ar().read().address(); | 851 | let current_address = T::REGS.ar().read().address(); |
| 843 | let current_instruction = T::REGS.ir().read().instruction(); | 852 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -852,16 +861,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 852 | T::REGS.ar().write(|v| v.set_address(current_address)); | 861 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 853 | } | 862 | } |
| 854 | 863 | ||
| 855 | let transfer = unsafe { | 864 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 856 | self.dma | 865 | let transfer = unsafe { |
| 857 | .as_mut() | 866 | self.dma |
| 858 | .unwrap() | 867 | .as_mut() |
| 859 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 868 | .unwrap() |
| 860 | }; | 869 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 870 | }; | ||
| 861 | 871 | ||
| 862 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 872 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 863 | 873 | ||
| 864 | transfer.await; | 874 | transfer.await; |
| 875 | } | ||
| 865 | 876 | ||
| 866 | finish_dma(T::REGS); | 877 | finish_dma(T::REGS); |
| 867 | 878 | ||
| @@ -877,21 +888,25 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { | |||
| 877 | // Wait for peripheral to be free | 888 | // Wait for peripheral to be free |
| 878 | while T::REGS.sr().read().busy() {} | 889 | while T::REGS.sr().read().busy() {} |
| 879 | 890 | ||
| 880 | self.configure_command(&transaction, Some(buf.len()))?; | 891 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 892 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 881 | T::REGS | 893 | T::REGS |
| 882 | .cr() | 894 | .cr() |
| 883 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | 895 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); |
| 884 | 896 | ||
| 885 | let transfer = unsafe { | 897 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 886 | self.dma | 898 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 887 | .as_mut() | 899 | let transfer = unsafe { |
| 888 | .unwrap() | 900 | self.dma |
| 889 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 901 | .as_mut() |
| 890 | }; | 902 | .unwrap() |
| 903 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 904 | }; | ||
| 891 | 905 | ||
| 892 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 906 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 893 | 907 | ||
| 894 | transfer.await; | 908 | transfer.await; |
| 909 | } | ||
| 895 | 910 | ||
| 896 | finish_dma(T::REGS); | 911 | finish_dma(T::REGS); |
| 897 | 912 | ||
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 4527e55b9..b2ba94e21 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -98,6 +98,27 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() { | |||
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | 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 | |||
| 101 | pub(crate) fn init(&mut self, config: Config) { | 122 | pub(crate) fn init(&mut self, config: Config) { |
| 102 | self.info.regs.cr1().modify(|reg| { | 123 | self.info.regs.cr1().modify(|reg| { |
| 103 | reg.set_pe(false); | 124 | reg.set_pe(false); |
| @@ -147,12 +168,6 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 147 | // `buffer`. The START bit can be set even if the bus | 168 | // `buffer`. The START bit can be set even if the bus |
| 148 | // is BUSY or I2C is in slave mode. | 169 | // is BUSY or I2C is in slave mode. |
| 149 | 170 | ||
| 150 | let reload = if reload { | ||
| 151 | i2c::vals::Reload::NOT_COMPLETED | ||
| 152 | } else { | ||
| 153 | i2c::vals::Reload::COMPLETED | ||
| 154 | }; | ||
| 155 | |||
| 156 | info.regs.cr2().modify(|w| { | 171 | info.regs.cr2().modify(|w| { |
| 157 | w.set_sadd(address.addr() << 1); | 172 | w.set_sadd(address.addr() << 1); |
| 158 | w.set_add10(address.add_mode()); | 173 | w.set_add10(address.add_mode()); |
| @@ -160,7 +175,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 160 | w.set_nbytes(length as u8); | 175 | w.set_nbytes(length as u8); |
| 161 | w.set_start(true); | 176 | w.set_start(true); |
| 162 | w.set_autoend(stop.autoend()); | 177 | w.set_autoend(stop.autoend()); |
| 163 | w.set_reload(reload); | 178 | w.set_reload(Self::to_reload(reload)); |
| 164 | }); | 179 | }); |
| 165 | 180 | ||
| 166 | Ok(()) | 181 | Ok(()) |
| @@ -172,28 +187,25 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 172 | length: usize, | 187 | length: usize, |
| 173 | stop: Stop, | 188 | stop: Stop, |
| 174 | reload: bool, | 189 | reload: bool, |
| 190 | restart: bool, | ||
| 175 | timeout: Timeout, | 191 | timeout: Timeout, |
| 176 | ) -> Result<(), Error> { | 192 | ) -> Result<(), Error> { |
| 177 | assert!(length < 256); | 193 | assert!(length < 256); |
| 178 | 194 | ||
| 179 | // Wait for any previous address sequence to end | 195 | if !restart { |
| 180 | // automatically. This could be up to 50% of a bus | 196 | // Wait for any previous address sequence to end |
| 181 | // cycle (ie. up to 0.5/freq) | 197 | // automatically. This could be up to 50% of a bus |
| 182 | while info.regs.cr2().read().start() { | 198 | // cycle (ie. up to 0.5/freq) |
| 183 | timeout.check()?; | 199 | while info.regs.cr2().read().start() { |
| 184 | } | 200 | timeout.check()?; |
| 201 | } | ||
| 185 | 202 | ||
| 186 | // Wait for the bus to be free | 203 | // Wait for the bus to be free |
| 187 | while info.regs.isr().read().busy() { | 204 | while info.regs.isr().read().busy() { |
| 188 | timeout.check()?; | 205 | timeout.check()?; |
| 206 | } | ||
| 189 | } | 207 | } |
| 190 | 208 | ||
| 191 | let reload = if reload { | ||
| 192 | i2c::vals::Reload::NOT_COMPLETED | ||
| 193 | } else { | ||
| 194 | i2c::vals::Reload::COMPLETED | ||
| 195 | }; | ||
| 196 | |||
| 197 | // Set START and prepare to send `bytes`. The | 209 | // Set START and prepare to send `bytes`. The |
| 198 | // 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 |
| 199 | // I2C is in slave mode. | 211 | // I2C is in slave mode. |
| @@ -204,28 +216,36 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 204 | w.set_nbytes(length as u8); | 216 | w.set_nbytes(length as u8); |
| 205 | w.set_start(true); | 217 | w.set_start(true); |
| 206 | w.set_autoend(stop.autoend()); | 218 | w.set_autoend(stop.autoend()); |
| 207 | w.set_reload(reload); | 219 | w.set_reload(Self::to_reload(reload)); |
| 208 | }); | 220 | }); |
| 209 | 221 | ||
| 210 | Ok(()) | 222 | Ok(()) |
| 211 | } | 223 | } |
| 212 | 224 | ||
| 213 | 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> { | ||
| 214 | assert!(length < 256 && length > 0); | 232 | assert!(length < 256 && length > 0); |
| 215 | 233 | ||
| 216 | 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 | } | ||
| 217 | timeout.check()?; | 242 | timeout.check()?; |
| 218 | } | 243 | } |
| 219 | 244 | ||
| 220 | let will_reload = if will_reload { | ||
| 221 | i2c::vals::Reload::NOT_COMPLETED | ||
| 222 | } else { | ||
| 223 | i2c::vals::Reload::COMPLETED | ||
| 224 | }; | ||
| 225 | |||
| 226 | info.regs.cr2().modify(|w| { | 245 | info.regs.cr2().modify(|w| { |
| 227 | w.set_nbytes(length as u8); | 246 | w.set_nbytes(length as u8); |
| 228 | w.set_reload(will_reload); | 247 | w.set_reload(Self::to_reload(will_reload)); |
| 248 | w.set_autoend(stop.autoend()); | ||
| 229 | }); | 249 | }); |
| 230 | 250 | ||
| 231 | Ok(()) | 251 | Ok(()) |
| @@ -369,7 +389,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 369 | loop { | 389 | loop { |
| 370 | let isr = self.info.regs.isr().read(); | 390 | let isr = self.info.regs.isr().read(); |
| 371 | self.error_occurred(&isr, timeout)?; | 391 | self.error_occurred(&isr, timeout)?; |
| 372 | 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() { | ||
| 373 | return Ok(()); | 395 | return Ok(()); |
| 374 | } | 396 | } |
| 375 | timeout.check()?; | 397 | timeout.check()?; |
| @@ -396,14 +418,20 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 396 | address, | 418 | address, |
| 397 | read.len().min(255), | 419 | read.len().min(255), |
| 398 | Stop::Automatic, | 420 | Stop::Automatic, |
| 399 | last_chunk_idx != 0, | 421 | last_chunk_idx != 0, // reload |
| 400 | restart, | 422 | restart, |
| 401 | timeout, | 423 | timeout, |
| 402 | )?; | 424 | )?; |
| 403 | 425 | ||
| 404 | for (number, chunk) in read.chunks_mut(255).enumerate() { | 426 | for (number, chunk) in read.chunks_mut(255).enumerate() { |
| 405 | if number != 0 { | 427 | if number != 0 { |
| 406 | 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 | )?; | ||
| 407 | } | 435 | } |
| 408 | 436 | ||
| 409 | for byte in chunk { | 437 | for byte in chunk { |
| @@ -441,6 +469,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 441 | write.len().min(255), | 469 | write.len().min(255), |
| 442 | Stop::Software, | 470 | Stop::Software, |
| 443 | last_chunk_idx != 0, | 471 | last_chunk_idx != 0, |
| 472 | false, // restart | ||
| 444 | timeout, | 473 | timeout, |
| 445 | ) { | 474 | ) { |
| 446 | if send_stop { | 475 | if send_stop { |
| @@ -451,7 +480,13 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 451 | 480 | ||
| 452 | for (number, chunk) in write.chunks(255).enumerate() { | 481 | for (number, chunk) in write.chunks(255).enumerate() { |
| 453 | if number != 0 { | 482 | if number != 0 { |
| 454 | 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 | )?; | ||
| 455 | } | 490 | } |
| 456 | 491 | ||
| 457 | for byte in chunk { | 492 | for byte in chunk { |
| @@ -507,9 +542,215 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 507 | /// | 542 | /// |
| 508 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 543 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 509 | 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> { |
| 510 | let _ = addr; | 545 | if operations.is_empty() { |
| 511 | let _ = operations; | 546 | return Err(Error::ZeroLengthTransfer); |
| 512 | 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(()) | ||
| 513 | } | 754 | } |
| 514 | 755 | ||
| 515 | /// Blocking write multiple buffers. | 756 | /// Blocking write multiple buffers. |
| @@ -531,6 +772,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 531 | first_length.min(255), | 772 | first_length.min(255), |
| 532 | Stop::Software, | 773 | Stop::Software, |
| 533 | (first_length > 255) || (last_slice_index != 0), | 774 | (first_length > 255) || (last_slice_index != 0), |
| 775 | false, // restart | ||
| 534 | timeout, | 776 | timeout, |
| 535 | ) { | 777 | ) { |
| 536 | self.master_stop(); | 778 | self.master_stop(); |
| @@ -552,6 +794,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 552 | self.info, | 794 | self.info, |
| 553 | slice_len.min(255), | 795 | slice_len.min(255), |
| 554 | (idx != last_slice_index) || (slice_len > 255), | 796 | (idx != last_slice_index) || (slice_len > 255), |
| 797 | Stop::Software, | ||
| 555 | timeout, | 798 | timeout, |
| 556 | ) { | 799 | ) { |
| 557 | if err != Error::Nack { | 800 | if err != Error::Nack { |
| @@ -567,6 +810,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 567 | self.info, | 810 | self.info, |
| 568 | chunk.len(), | 811 | chunk.len(), |
| 569 | (number != last_chunk_idx) || (idx != last_slice_index), | 812 | (number != last_chunk_idx) || (idx != last_slice_index), |
| 813 | Stop::Software, | ||
| 570 | timeout, | 814 | timeout, |
| 571 | ) { | 815 | ) { |
| 572 | if err != Error::Nack { | 816 | if err != Error::Nack { |
| @@ -610,6 +854,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 610 | first_slice: bool, | 854 | first_slice: bool, |
| 611 | last_slice: bool, | 855 | last_slice: bool, |
| 612 | send_stop: bool, | 856 | send_stop: bool, |
| 857 | restart: bool, | ||
| 613 | timeout: Timeout, | 858 | timeout: Timeout, |
| 614 | ) -> Result<(), Error> { | 859 | ) -> Result<(), Error> { |
| 615 | let total_len = write.len(); | 860 | let total_len = write.len(); |
| @@ -676,10 +921,17 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 676 | total_len.min(255), | 921 | total_len.min(255), |
| 677 | Stop::Software, | 922 | Stop::Software, |
| 678 | (total_len > 255) || !last_slice, | 923 | (total_len > 255) || !last_slice, |
| 924 | restart, | ||
| 679 | timeout, | 925 | timeout, |
| 680 | )?; | 926 | )?; |
| 681 | } else { | 927 | } else { |
| 682 | 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 | )?; | ||
| 683 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 935 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 684 | } | 936 | } |
| 685 | } else if !(isr.tcr() || isr.tc()) { | 937 | } else if !(isr.tcr() || isr.tc()) { |
| @@ -688,9 +940,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 688 | } else if remaining_len == 0 { | 940 | } else if remaining_len == 0 { |
| 689 | return Poll::Ready(Ok(())); | 941 | return Poll::Ready(Ok(())); |
| 690 | } else { | 942 | } else { |
| 691 | let last_piece = (remaining_len <= 255) && last_slice; | 943 | if let Err(e) = Self::reload( |
| 692 | 944 | self.info, | |
| 693 | 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 | ) { | ||
| 694 | return Poll::Ready(Err(e)); | 950 | return Poll::Ready(Err(e)); |
| 695 | } | 951 | } |
| 696 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 952 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| @@ -702,10 +958,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 702 | .await?; | 958 | .await?; |
| 703 | 959 | ||
| 704 | dma_transfer.await; | 960 | dma_transfer.await; |
| 705 | if last_slice { | 961 | |
| 706 | // This should be done already | 962 | // Always wait for TC after DMA completes - needed for consecutive buffers |
| 707 | self.wait_tc(timeout)?; | 963 | self.wait_tc(timeout)?; |
| 708 | } | ||
| 709 | 964 | ||
| 710 | if last_slice & send_stop { | 965 | if last_slice & send_stop { |
| 711 | self.master_stop(); | 966 | self.master_stop(); |
| @@ -780,7 +1035,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 780 | address, | 1035 | address, |
| 781 | total_len.min(255), | 1036 | total_len.min(255), |
| 782 | Stop::Automatic, | 1037 | Stop::Automatic, |
| 783 | total_len > 255, | 1038 | total_len > 255, // reload |
| 784 | restart, | 1039 | restart, |
| 785 | timeout, | 1040 | timeout, |
| 786 | )?; | 1041 | )?; |
| @@ -788,12 +1043,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 788 | return Poll::Ready(Ok(())); | 1043 | return Poll::Ready(Ok(())); |
| 789 | } | 1044 | } |
| 790 | } else if isr.tcr() { | 1045 | } else if isr.tcr() { |
| 791 | // poll_fn was woken without an interrupt present | 1046 | // Transfer Complete Reload - need to set up next chunk |
| 792 | return Poll::Pending; | ||
| 793 | } else { | ||
| 794 | let last_piece = remaining_len <= 255; | 1047 | let last_piece = remaining_len <= 255; |
| 795 | 1048 | ||
| 796 | 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) { |
| 797 | return Poll::Ready(Err(e)); | 1050 | return Poll::Ready(Err(e)); |
| 798 | } | 1051 | } |
| 799 | // Return here if we are on last chunk, | 1052 | // Return here if we are on last chunk, |
| @@ -802,6 +1055,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 802 | return Poll::Ready(Ok(())); | 1055 | return Poll::Ready(Ok(())); |
| 803 | } | 1056 | } |
| 804 | 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; | ||
| 805 | } | 1061 | } |
| 806 | 1062 | ||
| 807 | remaining_len = remaining_len.saturating_sub(255); | 1063 | remaining_len = remaining_len.saturating_sub(255); |
| @@ -819,14 +1075,12 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 819 | 1075 | ||
| 820 | /// Write. | 1076 | /// Write. |
| 821 | 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> { |
| 822 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 823 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 824 | let timeout = self.timeout(); | 1078 | let timeout = self.timeout(); |
| 825 | if write.is_empty() { | 1079 | if write.is_empty() { |
| 826 | self.write_internal(address.into(), write, true, timeout) | 1080 | self.write_internal(address.into(), write, true, timeout) |
| 827 | } else { | 1081 | } else { |
| 828 | timeout | 1082 | timeout |
| 829 | .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) | 1083 | .with(self.write_dma_internal(address.into(), write, true, true, true, false, timeout)) |
| 830 | .await | 1084 | .await |
| 831 | } | 1085 | } |
| 832 | } | 1086 | } |
| @@ -835,23 +1089,29 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 835 | /// | 1089 | /// |
| 836 | /// The buffers are concatenated in a single write transaction. | 1090 | /// The buffers are concatenated in a single write transaction. |
| 837 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { | 1091 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { |
| 838 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 839 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 840 | let timeout = self.timeout(); | 1092 | let timeout = self.timeout(); |
| 841 | 1093 | ||
| 842 | if write.is_empty() { | 1094 | if write.is_empty() { |
| 843 | return Err(Error::ZeroLengthTransfer); | 1095 | return Err(Error::ZeroLengthTransfer); |
| 844 | } | 1096 | } |
| 845 | let mut iter = write.iter(); | ||
| 846 | 1097 | ||
| 1098 | let mut iter = write.iter(); | ||
| 847 | let mut first = true; | 1099 | let mut first = true; |
| 848 | let mut current = iter.next(); | 1100 | let mut current = iter.next(); |
| 1101 | |||
| 849 | while let Some(c) = current { | 1102 | while let Some(c) = current { |
| 850 | let next = iter.next(); | 1103 | let next = iter.next(); |
| 851 | let is_last = next.is_none(); | 1104 | let is_last = next.is_none(); |
| 852 | 1105 | ||
| 853 | let fut = self.write_dma_internal(address, c, first, is_last, is_last, timeout); | 1106 | let fut = self.write_dma_internal( |
| 1107 | address, c, first, // first_slice | ||
| 1108 | is_last, // last_slice | ||
| 1109 | is_last, // send_stop (only on last buffer) | ||
| 1110 | false, // restart (false for all - they're one continuous write) | ||
| 1111 | timeout, | ||
| 1112 | ); | ||
| 854 | timeout.with(fut).await?; | 1113 | timeout.with(fut).await?; |
| 1114 | |||
| 855 | first = false; | 1115 | first = false; |
| 856 | current = next; | 1116 | current = next; |
| 857 | } | 1117 | } |
| @@ -860,8 +1120,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 860 | 1120 | ||
| 861 | /// Read. | 1121 | /// Read. |
| 862 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 1122 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| 863 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 864 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 865 | let timeout = self.timeout(); | 1123 | let timeout = self.timeout(); |
| 866 | 1124 | ||
| 867 | if buffer.is_empty() { | 1125 | if buffer.is_empty() { |
| @@ -874,14 +1132,12 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 874 | 1132 | ||
| 875 | /// Write, restart, read. | 1133 | /// Write, restart, read. |
| 876 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 1134 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 877 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 878 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 879 | let timeout = self.timeout(); | 1135 | let timeout = self.timeout(); |
| 880 | 1136 | ||
| 881 | if write.is_empty() { | 1137 | if write.is_empty() { |
| 882 | self.write_internal(address.into(), write, false, timeout)?; | 1138 | self.write_internal(address.into(), write, false, timeout)?; |
| 883 | } else { | 1139 | } else { |
| 884 | let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); | 1140 | let fut = self.write_dma_internal(address.into(), write, true, true, false, false, timeout); |
| 885 | timeout.with(fut).await?; | 1141 | timeout.with(fut).await?; |
| 886 | } | 1142 | } |
| 887 | 1143 | ||
| @@ -901,11 +1157,298 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 901 | /// | 1157 | /// |
| 902 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 1158 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 903 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 1159 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 904 | #[cfg(all(feature = "low-power", stm32wlex))] | 1160 | if operations.is_empty() { |
| 905 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | 1161 | return Err(Error::ZeroLengthTransfer); |
| 906 | let _ = addr; | 1162 | } |
| 907 | let _ = operations; | 1163 | |
| 908 | todo!() | 1164 | let address = addr.into(); |
| 1165 | let timeout = self.timeout(); | ||
| 1166 | |||
| 1167 | // Group consecutive operations of the same type | ||
| 1168 | let mut op_idx = 0; | ||
| 1169 | let mut is_first_group = true; | ||
| 1170 | |||
| 1171 | while op_idx < operations.len() { | ||
| 1172 | // Determine the type of current group and find all consecutive operations of same type | ||
| 1173 | let is_read = matches!(operations[op_idx], Operation::Read(_)); | ||
| 1174 | let group_start = op_idx; | ||
| 1175 | |||
| 1176 | // Find end of this group (consecutive operations of same type) | ||
| 1177 | while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { | ||
| 1178 | op_idx += 1; | ||
| 1179 | } | ||
| 1180 | let group_end = op_idx; | ||
| 1181 | let is_last_group = op_idx >= operations.len(); | ||
| 1182 | |||
| 1183 | // Execute this group of operations | ||
| 1184 | if is_read { | ||
| 1185 | self.execute_read_group_async( | ||
| 1186 | address, | ||
| 1187 | &mut operations[group_start..group_end], | ||
| 1188 | is_first_group, | ||
| 1189 | is_last_group, | ||
| 1190 | timeout, | ||
| 1191 | ) | ||
| 1192 | .await?; | ||
| 1193 | } else { | ||
| 1194 | self.execute_write_group_async( | ||
| 1195 | address, | ||
| 1196 | &operations[group_start..group_end], | ||
| 1197 | is_first_group, | ||
| 1198 | is_last_group, | ||
| 1199 | timeout, | ||
| 1200 | ) | ||
| 1201 | .await?; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | is_first_group = false; | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | Ok(()) | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | async fn execute_write_group_async( | ||
| 1211 | &mut self, | ||
| 1212 | address: Address, | ||
| 1213 | operations: &[Operation<'_>], | ||
| 1214 | is_first_group: bool, | ||
| 1215 | is_last_group: bool, | ||
| 1216 | timeout: Timeout, | ||
| 1217 | ) -> Result<(), Error> { | ||
| 1218 | // Calculate total bytes across all operations in this group | ||
| 1219 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1220 | |||
| 1221 | if total_bytes == 0 { | ||
| 1222 | // Handle empty write group using blocking call | ||
| 1223 | if is_first_group { | ||
| 1224 | Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; | ||
| 1225 | } | ||
| 1226 | if is_last_group { | ||
| 1227 | self.master_stop(); | ||
| 1228 | } | ||
| 1229 | return Ok(()); | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | // Collect all write buffers | ||
| 1233 | let mut write_buffers: heapless::Vec<&[u8], 16> = heapless::Vec::new(); | ||
| 1234 | for operation in operations { | ||
| 1235 | if let Operation::Write(buffer) = operation { | ||
| 1236 | if !buffer.is_empty() { | ||
| 1237 | let _ = write_buffers.push(buffer); | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | if write_buffers.is_empty() { | ||
| 1243 | return Ok(()); | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | // Send each buffer using DMA | ||
| 1247 | let num_buffers = write_buffers.len(); | ||
| 1248 | for (idx, buffer) in write_buffers.iter().enumerate() { | ||
| 1249 | let is_first_buffer = idx == 0; | ||
| 1250 | let is_last_buffer = idx == num_buffers - 1; | ||
| 1251 | |||
| 1252 | let fut = self.write_dma_internal( | ||
| 1253 | address, | ||
| 1254 | buffer, | ||
| 1255 | is_first_buffer, // first_slice | ||
| 1256 | is_last_buffer, // last_slice | ||
| 1257 | is_last_buffer && is_last_group, // send_stop | ||
| 1258 | is_first_buffer && !is_first_group, // restart (only for first buffer if not first group) | ||
| 1259 | timeout, | ||
| 1260 | ); | ||
| 1261 | timeout.with(fut).await?; | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | Ok(()) | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | async fn execute_read_group_async( | ||
| 1268 | &mut self, | ||
| 1269 | address: Address, | ||
| 1270 | operations: &mut [Operation<'_>], | ||
| 1271 | is_first_group: bool, | ||
| 1272 | is_last_group: bool, | ||
| 1273 | timeout: Timeout, | ||
| 1274 | ) -> Result<(), Error> { | ||
| 1275 | // Calculate total bytes across all operations in this group | ||
| 1276 | let total_bytes = Self::total_operation_bytes(operations); | ||
| 1277 | |||
| 1278 | if total_bytes == 0 { | ||
| 1279 | // Handle empty read group using blocking call | ||
| 1280 | if is_first_group { | ||
| 1281 | Self::master_read( | ||
| 1282 | self.info, | ||
| 1283 | address, | ||
| 1284 | 0, | ||
| 1285 | if is_last_group { Stop::Automatic } else { Stop::Software }, | ||
| 1286 | false, // reload | ||
| 1287 | !is_first_group, | ||
| 1288 | timeout, | ||
| 1289 | )?; | ||
| 1290 | } | ||
| 1291 | if is_last_group { | ||
| 1292 | self.wait_stop(timeout)?; | ||
| 1293 | } | ||
| 1294 | return Ok(()); | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | // Use DMA for read operations - need to handle multiple buffers | ||
| 1298 | let restart = !is_first_group; | ||
| 1299 | let mut total_remaining = total_bytes; | ||
| 1300 | let mut is_first_in_group = true; | ||
| 1301 | |||
| 1302 | for operation in operations { | ||
| 1303 | if let Operation::Read(buffer) = operation { | ||
| 1304 | if buffer.is_empty() { | ||
| 1305 | // Skip empty buffers | ||
| 1306 | continue; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | let buf_len = buffer.len(); | ||
| 1310 | total_remaining -= buf_len; | ||
| 1311 | let is_last_in_group = total_remaining == 0; | ||
| 1312 | |||
| 1313 | // Perform DMA read | ||
| 1314 | if is_first_in_group { | ||
| 1315 | // First buffer: use read_dma_internal which handles restart properly | ||
| 1316 | // Only use Automatic stop if this is the last buffer in the last group | ||
| 1317 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1318 | Stop::Automatic | ||
| 1319 | } else { | ||
| 1320 | Stop::Software | ||
| 1321 | }; | ||
| 1322 | |||
| 1323 | // We need a custom DMA read that respects our stop mode | ||
| 1324 | self.read_dma_group_internal(address, buffer, restart, stop_mode, timeout) | ||
| 1325 | .await?; | ||
| 1326 | is_first_in_group = false; | ||
| 1327 | } else { | ||
| 1328 | // Subsequent buffers: need to reload and continue | ||
| 1329 | let stop_mode = if is_last_group && is_last_in_group { | ||
| 1330 | Stop::Automatic | ||
| 1331 | } else { | ||
| 1332 | Stop::Software | ||
| 1333 | }; | ||
| 1334 | |||
| 1335 | self.read_dma_group_internal( | ||
| 1336 | address, buffer, false, // no restart for subsequent buffers in same group | ||
| 1337 | stop_mode, timeout, | ||
| 1338 | ) | ||
| 1339 | .await?; | ||
| 1340 | } | ||
| 1341 | } | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | // Wait for transfer to complete | ||
| 1345 | if is_last_group { | ||
| 1346 | self.wait_stop(timeout)?; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | Ok(()) | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | /// Internal DMA read helper for transaction groups | ||
| 1353 | async fn read_dma_group_internal( | ||
| 1354 | &mut self, | ||
| 1355 | address: Address, | ||
| 1356 | buffer: &mut [u8], | ||
| 1357 | restart: bool, | ||
| 1358 | stop_mode: Stop, | ||
| 1359 | timeout: Timeout, | ||
| 1360 | ) -> Result<(), Error> { | ||
| 1361 | let total_len = buffer.len(); | ||
| 1362 | |||
| 1363 | let dma_transfer = unsafe { | ||
| 1364 | let regs = self.info.regs; | ||
| 1365 | regs.cr1().modify(|w| { | ||
| 1366 | w.set_rxdmaen(true); | ||
| 1367 | w.set_tcie(true); | ||
| 1368 | w.set_nackie(true); | ||
| 1369 | w.set_errie(true); | ||
| 1370 | }); | ||
| 1371 | let src = regs.rxdr().as_ptr() as *mut u8; | ||
| 1372 | |||
| 1373 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1374 | }; | ||
| 1375 | |||
| 1376 | let mut remaining_len = total_len; | ||
| 1377 | |||
| 1378 | let on_drop = OnDrop::new(|| { | ||
| 1379 | let regs = self.info.regs; | ||
| 1380 | regs.cr1().modify(|w| { | ||
| 1381 | w.set_rxdmaen(false); | ||
| 1382 | w.set_tcie(false); | ||
| 1383 | w.set_nackie(false); | ||
| 1384 | w.set_errie(false); | ||
| 1385 | }); | ||
| 1386 | regs.icr().write(|w| { | ||
| 1387 | w.set_nackcf(true); | ||
| 1388 | w.set_berrcf(true); | ||
| 1389 | w.set_arlocf(true); | ||
| 1390 | w.set_ovrcf(true); | ||
| 1391 | }); | ||
| 1392 | }); | ||
| 1393 | |||
| 1394 | poll_fn(|cx| { | ||
| 1395 | self.state.waker.register(cx.waker()); | ||
| 1396 | |||
| 1397 | let isr = self.info.regs.isr().read(); | ||
| 1398 | |||
| 1399 | if isr.nackf() { | ||
| 1400 | return Poll::Ready(Err(Error::Nack)); | ||
| 1401 | } | ||
| 1402 | if isr.arlo() { | ||
| 1403 | return Poll::Ready(Err(Error::Arbitration)); | ||
| 1404 | } | ||
| 1405 | if isr.berr() { | ||
| 1406 | return Poll::Ready(Err(Error::Bus)); | ||
| 1407 | } | ||
| 1408 | if isr.ovr() { | ||
| 1409 | return Poll::Ready(Err(Error::Overrun)); | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | if remaining_len == total_len { | ||
| 1413 | Self::master_read( | ||
| 1414 | self.info, | ||
| 1415 | address, | ||
| 1416 | total_len.min(255), | ||
| 1417 | stop_mode, | ||
| 1418 | total_len > 255, // reload | ||
| 1419 | restart, | ||
| 1420 | timeout, | ||
| 1421 | )?; | ||
| 1422 | if total_len <= 255 { | ||
| 1423 | return Poll::Ready(Ok(())); | ||
| 1424 | } | ||
| 1425 | } else if isr.tcr() { | ||
| 1426 | // Transfer Complete Reload - need to set up next chunk | ||
| 1427 | let last_piece = remaining_len <= 255; | ||
| 1428 | |||
| 1429 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, stop_mode, timeout) { | ||
| 1430 | return Poll::Ready(Err(e)); | ||
| 1431 | } | ||
| 1432 | // Return here if we are on last chunk, | ||
| 1433 | // end of transfer will be awaited with the DMA below | ||
| 1434 | if last_piece { | ||
| 1435 | return Poll::Ready(Ok(())); | ||
| 1436 | } | ||
| 1437 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | ||
| 1438 | } else { | ||
| 1439 | // poll_fn was woken without TCR interrupt | ||
| 1440 | return Poll::Pending; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1444 | Poll::Pending | ||
| 1445 | }) | ||
| 1446 | .await?; | ||
| 1447 | |||
| 1448 | dma_transfer.await; | ||
| 1449 | drop(on_drop); | ||
| 1450 | |||
| 1451 | Ok(()) | ||
| 909 | } | 1452 | } |
| 910 | } | 1453 | } |
| 911 | 1454 | ||
| @@ -1043,7 +1586,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1043 | if number == 0 { | 1586 | if number == 0 { |
| 1044 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1587 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1045 | } else { | 1588 | } else { |
| 1046 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1589 | Self::reload( |
| 1590 | self.info, | ||
| 1591 | chunk.len(), | ||
| 1592 | number != last_chunk_idx, | ||
| 1593 | Stop::Software, | ||
| 1594 | timeout, | ||
| 1595 | )?; | ||
| 1047 | } | 1596 | } |
| 1048 | 1597 | ||
| 1049 | let mut index = 0; | 1598 | let mut index = 0; |
| @@ -1092,7 +1641,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1092 | if number == 0 { | 1641 | if number == 0 { |
| 1093 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | 1642 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); |
| 1094 | } else { | 1643 | } else { |
| 1095 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 1644 | Self::reload( |
| 1645 | self.info, | ||
| 1646 | chunk.len(), | ||
| 1647 | number != last_chunk_idx, | ||
| 1648 | Stop::Software, | ||
| 1649 | timeout, | ||
| 1650 | )?; | ||
| 1096 | } | 1651 | } |
| 1097 | 1652 | ||
| 1098 | let mut index = 0; | 1653 | let mut index = 0; |
| @@ -1228,7 +1783,13 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1228 | Poll::Pending | 1783 | Poll::Pending |
| 1229 | } else if isr.tcr() { | 1784 | } else if isr.tcr() { |
| 1230 | let is_last_slice = remaining_len <= 255; | 1785 | let is_last_slice = remaining_len <= 255; |
| 1231 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1786 | if let Err(e) = Self::reload( |
| 1787 | self.info, | ||
| 1788 | remaining_len.min(255), | ||
| 1789 | !is_last_slice, | ||
| 1790 | Stop::Software, | ||
| 1791 | timeout, | ||
| 1792 | ) { | ||
| 1232 | return Poll::Ready(Err(e)); | 1793 | return Poll::Ready(Err(e)); |
| 1233 | } | 1794 | } |
| 1234 | remaining_len = remaining_len.saturating_sub(255); | 1795 | remaining_len = remaining_len.saturating_sub(255); |
| @@ -1292,7 +1853,13 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1292 | Poll::Pending | 1853 | Poll::Pending |
| 1293 | } else if isr.tcr() { | 1854 | } else if isr.tcr() { |
| 1294 | let is_last_slice = remaining_len <= 255; | 1855 | let is_last_slice = remaining_len <= 255; |
| 1295 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | 1856 | if let Err(e) = Self::reload( |
| 1857 | self.info, | ||
| 1858 | remaining_len.min(255), | ||
| 1859 | !is_last_slice, | ||
| 1860 | Stop::Software, | ||
| 1861 | timeout, | ||
| 1862 | ) { | ||
| 1296 | return Poll::Ready(Err(e)); | 1863 | return Poll::Ready(Err(e)); |
| 1297 | } | 1864 | } |
| 1298 | remaining_len = remaining_len.saturating_sub(255); | 1865 | remaining_len = remaining_len.saturating_sub(255); |
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index e1d8b1c2a..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::marker::PhantomData; | ||
| 4 | use core::sync::atomic::{Ordering, compiler_fence}; | 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 680edf433..2f783bf64 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -95,6 +95,8 @@ pub mod i2c; | |||
| 95 | pub mod i2s; | 95 | pub mod i2s; |
| 96 | #[cfg(stm32wb)] | 96 | #[cfg(stm32wb)] |
| 97 | pub mod ipcc; | 97 | pub mod ipcc; |
| 98 | #[cfg(lcd)] | ||
| 99 | pub mod lcd; | ||
| 98 | #[cfg(feature = "low-power")] | 100 | #[cfg(feature = "low-power")] |
| 99 | pub mod low_power; | 101 | pub mod low_power; |
| 100 | #[cfg(lptim)] | 102 | #[cfg(lptim)] |
| @@ -151,7 +153,7 @@ pub use crate::_generated::interrupt; | |||
| 151 | /// Macro to bind interrupts to handlers. | 153 | /// Macro to bind interrupts to handlers. |
| 152 | /// | 154 | /// |
| 153 | /// 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;`) |
| 154 | /// 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 |
| 155 | /// prove at compile-time that the right interrupts have been bound. | 157 | /// prove at compile-time that the right interrupts have been bound. |
| 156 | /// | 158 | /// |
| 157 | /// Example of how to bind one interrupt: | 159 | /// Example of how to bind one interrupt: |
| @@ -178,6 +180,10 @@ pub use crate::_generated::interrupt; | |||
| 178 | /// } | 180 | /// } |
| 179 | /// ); | 181 | /// ); |
| 180 | /// ``` | 182 | /// ``` |
| 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. | ||
| 181 | // 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`. |
| 182 | #[macro_export] | 188 | #[macro_export] |
| 183 | macro_rules! bind_interrupts { | 189 | macro_rules! bind_interrupts { |
| @@ -649,12 +655,26 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 649 | rcc::init_rcc(cs, config.rcc); | 655 | rcc::init_rcc(cs, config.rcc); |
| 650 | 656 | ||
| 651 | #[cfg(feature = "low-power")] | 657 | #[cfg(feature = "low-power")] |
| 652 | crate::rtc::init_rtc(cs, config.rtc); | 658 | rtc::init_rtc(cs, config.rtc, config.min_stop_pause); |
| 653 | 659 | ||
| 654 | #[cfg(feature = "low-power")] | 660 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 655 | crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause); | 661 | hsem::init_hsem(cs); |
| 656 | } | 662 | } |
| 657 | 663 | ||
| 658 | p | 664 | p |
| 659 | }) | 665 | }) |
| 660 | } | 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 696dfe83f..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,21 +22,16 @@ | |||
| 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); |
| @@ -45,11 +40,9 @@ | |||
| 45 | //! } | 40 | //! } |
| 46 | //! ``` | 41 | //! ``` |
| 47 | 42 | ||
| 48 | // TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.` | ||
| 49 | #![allow(static_mut_refs)] | ||
| 50 | |||
| 51 | use core::arch::asm; | 43 | use core::arch::asm; |
| 52 | use core::marker::PhantomData; | 44 | use core::marker::PhantomData; |
| 45 | use core::mem; | ||
| 53 | use core::sync::atomic::{Ordering, compiler_fence}; | 46 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 54 | 47 | ||
| 55 | use cortex_m::peripheral::SCB; | 48 | use cortex_m::peripheral::SCB; |
| @@ -57,11 +50,13 @@ use critical_section::CriticalSection; | |||
| 57 | use embassy_executor::*; | 50 | use embassy_executor::*; |
| 58 | 51 | ||
| 59 | use crate::interrupt; | 52 | use crate::interrupt; |
| 53 | pub use crate::rcc::StopMode; | ||
| 54 | use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount}; | ||
| 60 | use crate::time_driver::get_driver; | 55 | use crate::time_driver::get_driver; |
| 61 | 56 | ||
| 62 | const THREAD_PENDER: usize = usize::MAX; | 57 | const THREAD_PENDER: usize = usize::MAX; |
| 63 | 58 | ||
| 64 | static mut EXECUTOR: Option<Executor> = None; | 59 | static mut EXECUTOR_TAKEN: bool = false; |
| 65 | 60 | ||
| 66 | /// Prevent the device from going into the stop mode if held | 61 | /// Prevent the device from going into the stop mode if held |
| 67 | pub struct DeviceBusy(StopMode); | 62 | pub struct DeviceBusy(StopMode); |
| @@ -79,15 +74,8 @@ impl DeviceBusy { | |||
| 79 | 74 | ||
| 80 | /// Create a new DeviceBusy. | 75 | /// Create a new DeviceBusy. |
| 81 | pub fn new(stop_mode: StopMode) -> Self { | 76 | pub fn new(stop_mode: StopMode) -> Self { |
| 82 | critical_section::with(|_| unsafe { | 77 | critical_section::with(|cs| { |
| 83 | match stop_mode { | 78 | increment_stop_refcount(cs, stop_mode); |
| 84 | StopMode::Stop1 => { | ||
| 85 | crate::rcc::REFCOUNT_STOP1 += 1; | ||
| 86 | } | ||
| 87 | StopMode::Stop2 => { | ||
| 88 | crate::rcc::REFCOUNT_STOP2 += 1; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | }); | 79 | }); |
| 92 | 80 | ||
| 93 | Self(stop_mode) | 81 | Self(stop_mode) |
| @@ -96,15 +84,8 @@ impl DeviceBusy { | |||
| 96 | 84 | ||
| 97 | impl Drop for DeviceBusy { | 85 | impl Drop for DeviceBusy { |
| 98 | fn drop(&mut self) { | 86 | fn drop(&mut self) { |
| 99 | critical_section::with(|_| unsafe { | 87 | critical_section::with(|cs| { |
| 100 | match self.0 { | 88 | decrement_stop_refcount(cs, self.0); |
| 101 | StopMode::Stop1 => { | ||
| 102 | crate::rcc::REFCOUNT_STOP1 -= 1; | ||
| 103 | } | ||
| 104 | StopMode::Stop2 => { | ||
| 105 | crate::rcc::REFCOUNT_STOP2 -= 1; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | }); | 89 | }); |
| 109 | } | 90 | } |
| 110 | } | 91 | } |
| @@ -137,34 +118,24 @@ foreach_interrupt! { | |||
| 137 | /// prevents entering the given stop mode. | 118 | /// prevents entering the given stop mode. |
| 138 | pub fn stop_ready(stop_mode: StopMode) -> bool { | 119 | pub fn stop_ready(stop_mode: StopMode) -> bool { |
| 139 | critical_section::with(|cs| match Executor::stop_mode(cs) { | 120 | critical_section::with(|cs| match Executor::stop_mode(cs) { |
| 140 | Some(StopMode::Stop2) => true, | 121 | Some(StopMode::Standby | StopMode::Stop2) => true, |
| 141 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, | 122 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, |
| 142 | None => false, | 123 | None => false, |
| 143 | }) | 124 | }) |
| 144 | } | 125 | } |
| 145 | 126 | ||
| 146 | /// Available Stop modes. | 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] |
| 147 | #[non_exhaustive] | 128 | use crate::pac::pwr::vals::Lpms; |
| 148 | #[derive(PartialEq)] | ||
| 149 | pub enum StopMode { | ||
| 150 | /// STOP 1 | ||
| 151 | Stop1, | ||
| 152 | /// STOP 2 | ||
| 153 | Stop2, | ||
| 154 | } | ||
| 155 | 129 | ||
| 156 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] | 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] |
| 157 | use stm32_metapac::pwr::vals::Lpms; | ||
| 158 | |||
| 159 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] | ||
| 160 | impl Into<Lpms> for StopMode { | 131 | impl Into<Lpms> for StopMode { |
| 161 | fn into(self) -> Lpms { | 132 | fn into(self) -> Lpms { |
| 162 | match self { | 133 | match self { |
| 163 | StopMode::Stop1 => Lpms::STOP1, | 134 | StopMode::Stop1 => Lpms::STOP1, |
| 164 | #[cfg(not(stm32wba))] | 135 | #[cfg(not(any(stm32wb, stm32wba)))] |
| 165 | StopMode::Stop2 => Lpms::STOP2, | 136 | StopMode::Standby | StopMode::Stop2 => Lpms::STOP2, |
| 166 | #[cfg(stm32wba)] | 137 | #[cfg(any(stm32wb, stm32wba))] |
| 167 | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? | 138 | StopMode::Standby | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? |
| 168 | } | 139 | } |
| 169 | } | 140 | } |
| 170 | } | 141 | } |
| @@ -182,42 +153,47 @@ impl Into<Lpms> for StopMode { | |||
| 182 | pub struct Executor { | 153 | pub struct Executor { |
| 183 | inner: raw::Executor, | 154 | inner: raw::Executor, |
| 184 | not_send: PhantomData<*mut ()>, | 155 | not_send: PhantomData<*mut ()>, |
| 185 | scb: SCB, | ||
| 186 | } | 156 | } |
| 187 | 157 | ||
| 188 | impl Executor { | 158 | impl Executor { |
| 189 | /// Create a new Executor. | 159 | /// Create a new Executor. |
| 190 | pub fn take() -> &'static mut Self { | 160 | pub fn new() -> Self { |
| 191 | critical_section::with(|_| unsafe { | 161 | unsafe { |
| 192 | assert!(EXECUTOR.is_none()); | 162 | if EXECUTOR_TAKEN { |
| 193 | 163 | panic!("Low power executor can only be taken once."); | |
| 194 | EXECUTOR = Some(Self { | 164 | } else { |
| 195 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), | 165 | EXECUTOR_TAKEN = true; |
| 196 | not_send: PhantomData, | 166 | } |
| 197 | scb: cortex_m::Peripherals::steal().SCB, | 167 | } |
| 198 | }); | ||
| 199 | |||
| 200 | let executor = EXECUTOR.as_mut().unwrap(); | ||
| 201 | 168 | ||
| 202 | executor | 169 | Self { |
| 203 | }) | 170 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), |
| 171 | not_send: PhantomData, | ||
| 172 | } | ||
| 204 | } | 173 | } |
| 205 | 174 | ||
| 206 | pub(crate) unsafe fn on_wakeup_irq() { | 175 | pub(crate) unsafe fn on_wakeup_irq() { |
| 207 | critical_section::with(|cs| { | 176 | critical_section::with(|cs| { |
| 208 | #[cfg(stm32wlex)] | 177 | #[cfg(stm32wlex)] |
| 209 | { | 178 | { |
| 210 | let extscr = crate::pac::PWR.extscr().read(); | 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(); | ||
| 211 | if extscr.c1stop2f() || extscr.c1stopf() { | 184 | if extscr.c1stop2f() || extscr.c1stopf() { |
| 212 | // when we wake from any stop mode we need to re-initialize the rcc | 185 | // when we wake from any stop mode we need to re-initialize the rcc |
| 213 | crate::rcc::apply_resume_config(); | 186 | while RCC.cfgr().read().sws() != Sw::MSI {} |
| 187 | |||
| 188 | init_rcc(RCC_CONFIG.unwrap()); | ||
| 189 | |||
| 214 | if extscr.c1stop2f() { | 190 | if extscr.c1stop2f() { |
| 215 | // when we wake from STOP2, we need to re-initialize the time driver | 191 | // when we wake from STOP2, we need to re-initialize the time driver |
| 216 | crate::time_driver::init_timer(cs); | 192 | get_driver().init_timer(cs); |
| 217 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) | 193 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) |
| 218 | // and given that we just woke from STOP2, we can reset them | 194 | // and given that we just woke from STOP2, we can reset them |
| 219 | crate::rcc::REFCOUNT_STOP2 = 0; | 195 | REFCOUNT_STOP2 = 0; |
| 220 | crate::rcc::REFCOUNT_STOP1 = 0; | 196 | REFCOUNT_STOP1 = 0; |
| 221 | } | 197 | } |
| 222 | } | 198 | } |
| 223 | } | 199 | } |
| @@ -226,19 +202,90 @@ impl Executor { | |||
| 226 | }); | 202 | }); |
| 227 | } | 203 | } |
| 228 | 204 | ||
| 205 | const fn get_scb() -> SCB { | ||
| 206 | unsafe { mem::transmute(()) } | ||
| 207 | } | ||
| 208 | |||
| 229 | fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { | 209 | fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { |
| 230 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && 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"); | ||
| 231 | Some(StopMode::Stop2) | 213 | Some(StopMode::Stop2) |
| 232 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 214 | } else if unsafe { REFCOUNT_STOP1 == 0 } { |
| 215 | trace!("low power: stop 1"); | ||
| 233 | Some(StopMode::Stop1) | 216 | Some(StopMode::Stop1) |
| 234 | } else { | 217 | } else { |
| 218 | trace!("low power: not ready to stop (refcount_stop1: {})", unsafe { | ||
| 219 | REFCOUNT_STOP1 | ||
| 220 | }); | ||
| 235 | None | 221 | None |
| 236 | } | 222 | } |
| 237 | } | 223 | } |
| 238 | 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 | |||
| 239 | #[allow(unused_variables)] | 283 | #[allow(unused_variables)] |
| 240 | fn configure_stop(&mut self, stop_mode: StopMode) { | 284 | fn configure_stop(&self, _cs: CriticalSection, stop_mode: StopMode) -> Result<(), ()> { |
| 241 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] | 285 | #[cfg(all(stm32wb, feature = "low-power"))] |
| 286 | self.configure_stop_stm32wb(_cs)?; | ||
| 287 | |||
| 288 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb, stm32wba, stm32wlex))] | ||
| 242 | 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())); |
| 243 | #[cfg(stm32h5)] | 290 | #[cfg(stm32h5)] |
| 244 | crate::pac::PWR.pmcr().modify(|v| { | 291 | crate::pac::PWR.pmcr().modify(|v| { |
| @@ -246,10 +293,12 @@ impl Executor { | |||
| 246 | v.set_lpms(vals::Lpms::STOP); | 293 | v.set_lpms(vals::Lpms::STOP); |
| 247 | v.set_svos(vals::Svos::SCALE3); | 294 | v.set_svos(vals::Svos::SCALE3); |
| 248 | }); | 295 | }); |
| 296 | |||
| 297 | Ok(()) | ||
| 249 | } | 298 | } |
| 250 | 299 | ||
| 251 | fn configure_pwr(&mut self) { | 300 | fn configure_pwr(&self) { |
| 252 | self.scb.clear_sleepdeep(); | 301 | Self::get_scb().clear_sleepdeep(); |
| 253 | // Clear any previous stop flags | 302 | // Clear any previous stop flags |
| 254 | #[cfg(stm32wlex)] | 303 | #[cfg(stm32wlex)] |
| 255 | crate::pac::PWR.extscr().modify(|w| { | 304 | crate::pac::PWR.extscr().modify(|w| { |
| @@ -258,27 +307,18 @@ impl Executor { | |||
| 258 | 307 | ||
| 259 | compiler_fence(Ordering::SeqCst); | 308 | compiler_fence(Ordering::SeqCst); |
| 260 | 309 | ||
| 261 | let stop_mode = critical_section::with(|cs| Self::stop_mode(cs)); | 310 | critical_section::with(|cs| { |
| 262 | 311 | let _ = unsafe { RCC_CONFIG }?; | |
| 263 | if stop_mode.is_none() { | 312 | let stop_mode = Self::stop_mode(cs)?; |
| 264 | trace!("low power: not ready to stop"); | 313 | get_driver().pause_time(cs).ok()?; |
| 265 | return; | 314 | self.configure_stop(cs, stop_mode).ok()?; |
| 266 | } | ||
| 267 | |||
| 268 | if get_driver().pause_time().is_err() { | ||
| 269 | trace!("low power: failed to pause time"); | ||
| 270 | return; | ||
| 271 | } | ||
| 272 | |||
| 273 | let stop_mode = stop_mode.unwrap(); | ||
| 274 | match stop_mode { | ||
| 275 | StopMode::Stop1 => trace!("low power: stop 1"), | ||
| 276 | StopMode::Stop2 => trace!("low power: stop 2"), | ||
| 277 | } | ||
| 278 | self.configure_stop(stop_mode); | ||
| 279 | 315 | ||
| 280 | #[cfg(not(feature = "low-power-debug-with-sleep"))] | 316 | Some(()) |
| 281 | self.scb.set_sleepdeep(); | 317 | }) |
| 318 | .map(|_| { | ||
| 319 | #[cfg(not(feature = "low-power-debug-with-sleep"))] | ||
| 320 | Self::get_scb().set_sleepdeep(); | ||
| 321 | }); | ||
| 282 | } | 322 | } |
| 283 | 323 | ||
| 284 | /// Run the executor. | 324 | /// Run the executor. |
| @@ -300,12 +340,11 @@ impl Executor { | |||
| 300 | /// | 340 | /// |
| 301 | /// This function never returns. | 341 | /// This function never returns. |
| 302 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 342 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 303 | let executor = unsafe { EXECUTOR.as_mut().unwrap() }; | 343 | init(self.inner.spawner()); |
| 304 | init(executor.inner.spawner()); | ||
| 305 | 344 | ||
| 306 | loop { | 345 | loop { |
| 307 | unsafe { | 346 | unsafe { |
| 308 | executor.inner.poll(); | 347 | self.inner.poll(); |
| 309 | self.configure_pwr(); | 348 | self.configure_pwr(); |
| 310 | asm!("wfe"); | 349 | asm!("wfe"); |
| 311 | #[cfg(stm32wlex)] | 350 | #[cfg(stm32wlex)] |
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index ac8d5de21..4a55f5bd3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -4,19 +4,12 @@ | |||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::Peri; | 6 | use crate::Peri; |
| 7 | #[cfg(opamp_v5)] | ||
| 8 | use crate::block_for_us; | ||
| 7 | use crate::pac::opamp::vals::*; | 9 | use crate::pac::opamp::vals::*; |
| 8 | #[cfg(not(any(stm32g4, stm32f3)))] | 10 | #[cfg(not(any(stm32g4, stm32f3)))] |
| 9 | use crate::rcc::RccInfo; | 11 | use crate::rcc::RccInfo; |
| 10 | 12 | ||
| 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 | |||
| 20 | /// Gain | 13 | /// Gain |
| 21 | #[allow(missing_docs)] | 14 | #[allow(missing_docs)] |
| 22 | #[derive(Clone, Copy)] | 15 | #[derive(Clone, Copy)] |
| @@ -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 592a8594a..2d5dbd95a 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs | |||
| @@ -451,7 +451,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 451 | } | 451 | } |
| 452 | 452 | ||
| 453 | T::REGS.cr().modify(|w| { | 453 | T::REGS.cr().modify(|w| { |
| 454 | w.set_fmode(0.into()); | 454 | w.set_fmode(vals::FunctionalMode::INDIRECT_WRITE); |
| 455 | }); | 455 | }); |
| 456 | 456 | ||
| 457 | // Configure alternate bytes | 457 | // Configure alternate bytes |
| @@ -577,7 +577,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 577 | w.set_dmaen(false); | 577 | w.set_dmaen(false); |
| 578 | }); | 578 | }); |
| 579 | 579 | ||
| 580 | self.configure_command(&transaction, Some(buf.len()))?; | 580 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 581 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 581 | 582 | ||
| 582 | let current_address = T::REGS.ar().read().address(); | 583 | let current_address = T::REGS.ar().read().address(); |
| 583 | let current_instruction = T::REGS.ir().read().instruction(); | 584 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -616,7 +617,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 616 | w.set_dmaen(false); | 617 | w.set_dmaen(false); |
| 617 | }); | 618 | }); |
| 618 | 619 | ||
| 619 | self.configure_command(&transaction, Some(buf.len()))?; | 620 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 621 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 620 | 622 | ||
| 621 | T::REGS | 623 | T::REGS |
| 622 | .cr() | 624 | .cr() |
| @@ -1153,7 +1155,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1153 | // Wait for peripheral to be free | 1155 | // Wait for peripheral to be free |
| 1154 | while T::REGS.sr().read().busy() {} | 1156 | while T::REGS.sr().read().busy() {} |
| 1155 | 1157 | ||
| 1156 | self.configure_command(&transaction, Some(buf.len()))?; | 1158 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1159 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1157 | 1160 | ||
| 1158 | let current_address = T::REGS.ar().read().address(); | 1161 | let current_address = T::REGS.ar().read().address(); |
| 1159 | let current_instruction = T::REGS.ir().read().instruction(); | 1162 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1168,16 +1171,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1168 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1171 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1169 | } | 1172 | } |
| 1170 | 1173 | ||
| 1171 | let transfer = unsafe { | 1174 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1172 | self.dma | 1175 | let transfer = unsafe { |
| 1173 | .as_mut() | 1176 | self.dma |
| 1174 | .unwrap() | 1177 | .as_mut() |
| 1175 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1178 | .unwrap() |
| 1176 | }; | 1179 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1180 | }; | ||
| 1177 | 1181 | ||
| 1178 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1182 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1179 | 1183 | ||
| 1180 | transfer.blocking_wait(); | 1184 | transfer.blocking_wait(); |
| 1185 | } | ||
| 1181 | 1186 | ||
| 1182 | finish_dma(T::REGS); | 1187 | finish_dma(T::REGS); |
| 1183 | 1188 | ||
| @@ -1193,13 +1198,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1193 | // Wait for peripheral to be free | 1198 | // Wait for peripheral to be free |
| 1194 | while T::REGS.sr().read().busy() {} | 1199 | while T::REGS.sr().read().busy() {} |
| 1195 | 1200 | ||
| 1196 | self.configure_command(&transaction, Some(buf.len()))?; | 1201 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1202 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1197 | T::REGS | 1203 | T::REGS |
| 1198 | .cr() | 1204 | .cr() |
| 1199 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); | 1205 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1200 | 1206 | ||
| 1201 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. | 1207 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1202 | for chunk in buf.chunks(0xFFFF) { | 1208 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1203 | let transfer = unsafe { | 1209 | let transfer = unsafe { |
| 1204 | self.dma | 1210 | self.dma |
| 1205 | .as_mut() | 1211 | .as_mut() |
| @@ -1226,7 +1232,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1226 | // Wait for peripheral to be free | 1232 | // Wait for peripheral to be free |
| 1227 | while T::REGS.sr().read().busy() {} | 1233 | while T::REGS.sr().read().busy() {} |
| 1228 | 1234 | ||
| 1229 | self.configure_command(&transaction, Some(buf.len()))?; | 1235 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1236 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1230 | 1237 | ||
| 1231 | let current_address = T::REGS.ar().read().address(); | 1238 | let current_address = T::REGS.ar().read().address(); |
| 1232 | let current_instruction = T::REGS.ir().read().instruction(); | 1239 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1241,16 +1248,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1241 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1248 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1242 | } | 1249 | } |
| 1243 | 1250 | ||
| 1244 | let transfer = unsafe { | 1251 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1245 | self.dma | 1252 | let transfer = unsafe { |
| 1246 | .as_mut() | 1253 | self.dma |
| 1247 | .unwrap() | 1254 | .as_mut() |
| 1248 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1255 | .unwrap() |
| 1249 | }; | 1256 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1257 | }; | ||
| 1250 | 1258 | ||
| 1251 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1259 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1252 | 1260 | ||
| 1253 | transfer.await; | 1261 | transfer.await; |
| 1262 | } | ||
| 1254 | 1263 | ||
| 1255 | finish_dma(T::REGS); | 1264 | finish_dma(T::REGS); |
| 1256 | 1265 | ||
| @@ -1266,13 +1275,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1266 | // Wait for peripheral to be free | 1275 | // Wait for peripheral to be free |
| 1267 | while T::REGS.sr().read().busy() {} | 1276 | while T::REGS.sr().read().busy() {} |
| 1268 | 1277 | ||
| 1269 | self.configure_command(&transaction, Some(buf.len()))?; | 1278 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1279 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1270 | T::REGS | 1280 | T::REGS |
| 1271 | .cr() | 1281 | .cr() |
| 1272 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); | 1282 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1273 | 1283 | ||
| 1274 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. | 1284 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1275 | for chunk in buf.chunks(0xFFFF) { | 1285 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1276 | let transfer = unsafe { | 1286 | let transfer = unsafe { |
| 1277 | self.dma | 1287 | self.dma |
| 1278 | .as_mut() | 1288 | .as_mut() |
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 584957c6d..2e1cbd702 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -1,6 +1,3 @@ | |||
| 1 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 2 | use core::mem::MaybeUninit; | ||
| 3 | |||
| 4 | #[cfg(any(stm32l0, stm32l1))] | 1 | #[cfg(any(stm32l0, stm32l1))] |
| 5 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 2 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 6 | use crate::pac::rcc::regs::Cfgr; | 3 | use crate::pac::rcc::regs::Cfgr; |
| @@ -14,42 +11,6 @@ use crate::time::Hertz; | |||
| 14 | /// HSI speed | 11 | /// HSI speed |
| 15 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 12 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 16 | 13 | ||
| 17 | /// Saved RCC Config | ||
| 18 | /// | ||
| 19 | /// Used when exiting STOP2 to re-enable clocks to their last configured state | ||
| 20 | /// for chips that need it. | ||
| 21 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 22 | static mut RESUME_RCC_CONFIG: MaybeUninit<Config> = MaybeUninit::uninit(); | ||
| 23 | |||
| 24 | /// Set the rcc config to be restored when exiting STOP2 | ||
| 25 | /// | ||
| 26 | /// Safety: Sets a mutable global. | ||
| 27 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 28 | pub(crate) unsafe fn set_resume_config(config: Config) { | ||
| 29 | trace!("rcc set_resume_config()"); | ||
| 30 | RESUME_RCC_CONFIG = MaybeUninit::new(config); | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Get the rcc config to be restored when exiting STOP2 | ||
| 34 | /// | ||
| 35 | /// Safety: Reads a mutable global. | ||
| 36 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 37 | pub(crate) unsafe fn get_resume_config() -> Config { | ||
| 38 | *(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref() | ||
| 39 | } | ||
| 40 | |||
| 41 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 42 | /// Safety: should only be called from low power executable just after resuming from STOP2 | ||
| 43 | pub(crate) unsafe fn apply_resume_config() { | ||
| 44 | trace!("rcc apply_resume_config()"); | ||
| 45 | |||
| 46 | while RCC.cfgr().read().sws() != Sysclk::MSI {} | ||
| 47 | |||
| 48 | let config = get_resume_config(); | ||
| 49 | |||
| 50 | init(config); | ||
| 51 | } | ||
| 52 | |||
| 53 | #[derive(Clone, Copy, Eq, PartialEq)] | 14 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 54 | pub enum HseMode { | 15 | pub enum HseMode { |
| 55 | /// crystal/ceramic oscillator (HSEBYP=0) | 16 | /// crystal/ceramic oscillator (HSEBYP=0) |
| @@ -193,10 +154,6 @@ fn msi_enable(range: MSIRange) { | |||
| 193 | } | 154 | } |
| 194 | 155 | ||
| 195 | pub(crate) unsafe fn init(config: Config) { | 156 | pub(crate) unsafe fn init(config: Config) { |
| 196 | // save the rcc config because if we enter stop 2 we need to re-apply it on wakeup | ||
| 197 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 198 | set_resume_config(config); | ||
| 199 | |||
| 200 | // Switch to MSI to prevent problems with PLL configuration. | 157 | // Switch to MSI to prevent problems with PLL configuration. |
| 201 | if !RCC.cr().read().msion() { | 158 | if !RCC.cr().read().msion() { |
| 202 | // Turn on MSI and configure it to 4MHz. | 159 | // Turn on MSI and configure it to 4MHz. |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 592890777..85434fa83 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -49,6 +49,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; | |||
| 49 | /// May be read without a critical section | 49 | /// May be read without a critical section |
| 50 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 50 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 51 | 51 | ||
| 52 | #[cfg(feature = "low-power")] | ||
| 53 | pub(crate) static mut RCC_CONFIG: Option<Config> = None; | ||
| 54 | |||
| 52 | #[cfg(backup_sram)] | 55 | #[cfg(backup_sram)] |
| 53 | pub(crate) static mut BKSRAM_RETAINED: bool = false; | 56 | pub(crate) static mut BKSRAM_RETAINED: bool = false; |
| 54 | 57 | ||
| @@ -108,6 +111,32 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo | |||
| 108 | unsafe { get_freqs() } | 111 | unsafe { get_freqs() } |
| 109 | } | 112 | } |
| 110 | 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 | |||
| 111 | pub(crate) trait SealedRccPeripheral { | 140 | pub(crate) trait SealedRccPeripheral { |
| 112 | fn frequency() -> Hertz; | 141 | fn frequency() -> Hertz; |
| 113 | #[allow(dead_code)] | 142 | #[allow(dead_code)] |
| @@ -138,12 +167,19 @@ pub(crate) struct RccInfo { | |||
| 138 | stop_mode: StopMode, | 167 | stop_mode: StopMode, |
| 139 | } | 168 | } |
| 140 | 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. | ||
| 141 | #[cfg(feature = "low-power")] | 172 | #[cfg(feature = "low-power")] |
| 142 | #[allow(dead_code)] | 173 | #[allow(dead_code)] |
| 143 | pub(crate) enum StopMode { | 174 | #[derive(Debug, Clone, Copy, PartialEq, Default)] |
| 144 | Standby, | 175 | pub enum StopMode { |
| 145 | Stop2, | 176 | #[default] |
| 177 | /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1 | ||
| 146 | 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, | ||
| 147 | } | 183 | } |
| 148 | 184 | ||
| 149 | impl RccInfo { | 185 | impl RccInfo { |
| @@ -199,15 +235,7 @@ impl RccInfo { | |||
| 199 | } | 235 | } |
| 200 | 236 | ||
| 201 | #[cfg(feature = "low-power")] | 237 | #[cfg(feature = "low-power")] |
| 202 | match self.stop_mode { | 238 | increment_stop_refcount(_cs, self.stop_mode); |
| 203 | StopMode::Standby => {} | ||
| 204 | StopMode::Stop2 => unsafe { | ||
| 205 | REFCOUNT_STOP2 += 1; | ||
| 206 | }, | ||
| 207 | StopMode::Stop1 => unsafe { | ||
| 208 | REFCOUNT_STOP1 += 1; | ||
| 209 | }, | ||
| 210 | } | ||
| 211 | 239 | ||
| 212 | // set the xxxRST bit | 240 | // set the xxxRST bit |
| 213 | let reset_ptr = self.reset_ptr(); | 241 | let reset_ptr = self.reset_ptr(); |
| @@ -265,15 +293,7 @@ impl RccInfo { | |||
| 265 | } | 293 | } |
| 266 | 294 | ||
| 267 | #[cfg(feature = "low-power")] | 295 | #[cfg(feature = "low-power")] |
| 268 | match self.stop_mode { | 296 | decrement_stop_refcount(_cs, self.stop_mode); |
| 269 | StopMode::Standby => {} | ||
| 270 | StopMode::Stop2 => unsafe { | ||
| 271 | REFCOUNT_STOP2 -= 1; | ||
| 272 | }, | ||
| 273 | StopMode::Stop1 => unsafe { | ||
| 274 | REFCOUNT_STOP1 -= 1; | ||
| 275 | }, | ||
| 276 | } | ||
| 277 | 297 | ||
| 278 | // clear the xxxEN bit | 298 | // clear the xxxEN bit |
| 279 | let enable_ptr = self.enable_ptr(); | 299 | let enable_ptr = self.enable_ptr(); |
| @@ -408,8 +428,39 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { | |||
| 408 | 428 | ||
| 409 | #[cfg(feature = "low-power")] | 429 | #[cfg(feature = "low-power")] |
| 410 | { | 430 | { |
| 431 | RCC_CONFIG = Some(config); | ||
| 411 | REFCOUNT_STOP2 = 0; | 432 | REFCOUNT_STOP2 = 0; |
| 412 | REFCOUNT_STOP1 = 0; | 433 | REFCOUNT_STOP1 = 0; |
| 413 | } | 434 | } |
| 414 | } | 435 | } |
| 415 | } | 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/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index e5bf30927..f049d6b12 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -3,6 +3,7 @@ use embassy_time::{Duration, TICK_HZ}; | |||
| 3 | 3 | ||
| 4 | use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; | 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::{RtcTimeProvider, SealedInstance}; | 8 | use crate::rtc::{RtcTimeProvider, SealedInstance}; |
| 8 | 9 | ||
| @@ -58,60 +59,16 @@ 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, stm32wlex | ||
| 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, stm32wlex | ||
| 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 { |
| @@ -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)); |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 116b3c7ed..e88bd7ab2 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -379,13 +379,16 @@ trait SealedInstance { | |||
| 379 | } | 379 | } |
| 380 | 380 | ||
| 381 | #[cfg(feature = "low-power")] | 381 | #[cfg(feature = "low-power")] |
| 382 | pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) { | 382 | pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) { |
| 383 | use crate::time_driver::get_driver; | ||
| 384 | |||
| 383 | #[cfg(feature = "_allow-disable-rtc")] | 385 | #[cfg(feature = "_allow-disable-rtc")] |
| 384 | if config._disable_rtc { | 386 | if config._disable_rtc { |
| 385 | return; | 387 | return; |
| 386 | } | 388 | } |
| 387 | 389 | ||
| 388 | crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config)); | 390 | get_driver().set_rtc(cs, Rtc::new_inner(config)); |
| 391 | get_driver().set_min_stop_pause(cs, min_stop_pause); | ||
| 389 | 392 | ||
| 390 | trace!("low power: stop with rtc configured"); | 393 | trace!("low power: stop with rtc configured"); |
| 391 | } | 394 | } |
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 726d1729a..ce4bc43c3 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs | |||
| @@ -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/time_driver.rs b/embassy-stm32/src/time_driver.rs index 7db51d72e..0b75aef92 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -196,6 +196,11 @@ fn calc_now(period: u32, counter: u16) -> u64 { | |||
| 196 | ((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) |
| 197 | } | 197 | } |
| 198 | 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 | |||
| 199 | struct AlarmState { | 204 | struct AlarmState { |
| 200 | timestamp: Cell<u64>, | 205 | timestamp: Cell<u64>, |
| 201 | } | 206 | } |
| @@ -240,7 +245,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | |||
| 240 | impl RtcDriver { | 245 | impl RtcDriver { |
| 241 | /// initialize the timer, but don't start it. Used for chips like stm32wle5 | 246 | /// initialize the timer, but don't start it. Used for chips like stm32wle5 |
| 242 | /// for low power where the timer config is lost in STOP2. | 247 | /// for low power where the timer config is lost in STOP2. |
| 243 | fn init_timer(&'static self, cs: critical_section::CriticalSection) { | 248 | pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) { |
| 244 | let r = regs_gp16(); | 249 | let r = regs_gp16(); |
| 245 | 250 | ||
| 246 | rcc::enable_and_reset_with_cs::<T>(cs); | 251 | rcc::enable_and_reset_with_cs::<T>(cs); |
| @@ -358,34 +363,10 @@ impl RtcDriver { | |||
| 358 | #[cfg(feature = "low-power")] | 363 | #[cfg(feature = "low-power")] |
| 359 | /// Add the given offset to the current time | 364 | /// Add the given offset to the current time |
| 360 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { | 365 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { |
| 361 | let offset = offset.as_ticks(); | 366 | let (period, counter) = calc_period_counter(self.now() + offset.as_ticks()); |
| 362 | let cnt = regs_gp16().cnt().read().cnt() as u32; | ||
| 363 | let period = self.period.load(Ordering::SeqCst); | ||
| 364 | |||
| 365 | // Correct the race, if it exists | ||
| 366 | let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { | ||
| 367 | period + 1 | ||
| 368 | } else { | ||
| 369 | period | ||
| 370 | }; | ||
| 371 | |||
| 372 | // Normalize to the full overflow | ||
| 373 | let period = (period / 2) * 2; | ||
| 374 | |||
| 375 | // Add the offset | ||
| 376 | let period = period + 2 * (offset / u16::MAX as u64) as u32; | ||
| 377 | let cnt = cnt + (offset % u16::MAX as u64) as u32; | ||
| 378 | |||
| 379 | let (cnt, period) = if cnt > u16::MAX as u32 { | ||
| 380 | (cnt - u16::MAX as u32, period + 2) | ||
| 381 | } else { | ||
| 382 | (cnt, period) | ||
| 383 | }; | ||
| 384 | |||
| 385 | let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; | ||
| 386 | 367 | ||
| 387 | self.period.store(period, Ordering::SeqCst); | 368 | self.period.store(period, Ordering::SeqCst); |
| 388 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); | 369 | regs_gp16().cnt().write(|w| w.set_cnt(counter)); |
| 389 | 370 | ||
| 390 | // Now, recompute alarm | 371 | // Now, recompute alarm |
| 391 | let alarm = self.alarm.borrow(cs); | 372 | let alarm = self.alarm.borrow(cs); |
| @@ -399,13 +380,15 @@ impl RtcDriver { | |||
| 399 | #[cfg(feature = "low-power")] | 380 | #[cfg(feature = "low-power")] |
| 400 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset | 381 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset |
| 401 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { | 382 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { |
| 402 | if let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().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 | { | ||
| 403 | self.add_time(offset, cs); | 386 | self.add_time(offset, cs); |
| 404 | } | 387 | } |
| 405 | } | 388 | } |
| 406 | 389 | ||
| 407 | /* | 390 | /* |
| 408 | Low-power public functions: all create or require a critical section | 391 | Low-power public functions: all require a critical section |
| 409 | */ | 392 | */ |
| 410 | #[cfg(feature = "low-power")] | 393 | #[cfg(feature = "low-power")] |
| 411 | pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { | 394 | pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { |
| @@ -422,49 +405,36 @@ impl RtcDriver { | |||
| 422 | 405 | ||
| 423 | #[cfg(feature = "low-power")] | 406 | #[cfg(feature = "low-power")] |
| 424 | /// Pause the timer if ready; return err if not | 407 | /// Pause the timer if ready; return err if not |
| 425 | pub(crate) fn pause_time(&self) -> Result<(), ()> { | 408 | pub(crate) fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> { |
| 426 | critical_section::with(|cs| { | 409 | self.stop_wakeup_alarm(cs); |
| 427 | /* | 410 | |
| 428 | 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); |
| 429 | 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() { |
| 430 | of `time_until_next_alarm`. | 413 | trace!( |
| 431 | */ | 414 | "time_until_next_alarm < self.min_stop_pause ({})", |
| 432 | self.stop_wakeup_alarm(cs); | 415 | time_until_next_alarm |
| 433 | 416 | ); | |
| 434 | let time_until_next_alarm = self.time_until_next_alarm(cs); | 417 | Err(()) |
| 435 | if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { | 418 | } else { |
| 436 | trace!( | 419 | self.rtc |
| 437 | "time_until_next_alarm < self.min_stop_pause ({})", | 420 | .borrow(cs) |
| 438 | time_until_next_alarm | 421 | .borrow_mut() |
| 439 | ); | 422 | .as_mut() |
| 440 | Err(()) | 423 | .unwrap() |
| 441 | } else { | 424 | .start_wakeup_alarm(time_until_next_alarm, cs); |
| 442 | self.rtc | 425 | |
| 443 | .borrow(cs) | 426 | regs_gp16().cr1().modify(|w| w.set_cen(false)); |
| 444 | .borrow_mut() | 427 | // save the count for the timer as its lost in STOP2 for stm32wlex |
| 445 | .as_mut() | 428 | #[cfg(stm32wlex)] |
| 446 | .unwrap() | 429 | self.saved_count |
| 447 | .start_wakeup_alarm(time_until_next_alarm, cs); | 430 | .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); |
| 448 | 431 | Ok(()) | |
| 449 | regs_gp16().cr1().modify(|w| w.set_cen(false)); | 432 | } |
| 450 | // save the count for the timer as its lost in STOP2 for stm32wlex | ||
| 451 | #[cfg(stm32wlex)] | ||
| 452 | self.saved_count | ||
| 453 | .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); | ||
| 454 | Ok(()) | ||
| 455 | } | ||
| 456 | }) | ||
| 457 | } | 433 | } |
| 458 | 434 | ||
| 459 | #[cfg(feature = "low-power")] | 435 | #[cfg(feature = "low-power")] |
| 460 | /// Resume the timer with the given offset | 436 | /// Resume the timer with the given offset |
| 461 | pub(crate) fn resume_time(&self, cs: CriticalSection) { | 437 | pub(crate) fn resume_time(&self, cs: CriticalSection) { |
| 462 | if regs_gp16().cr1().read().cen() { | ||
| 463 | // Time isn't currently stopped | ||
| 464 | |||
| 465 | return; | ||
| 466 | } | ||
| 467 | |||
| 468 | self.stop_wakeup_alarm(cs); | 438 | self.stop_wakeup_alarm(cs); |
| 469 | 439 | ||
| 470 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | 440 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| @@ -546,8 +516,3 @@ pub(crate) const fn get_driver() -> &'static RtcDriver { | |||
| 546 | pub(crate) fn init(cs: CriticalSection) { | 516 | pub(crate) fn init(cs: CriticalSection) { |
| 547 | DRIVER.init(cs) | 517 | DRIVER.init(cs) |
| 548 | } | 518 | } |
| 549 | |||
| 550 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 551 | pub(crate) fn init_timer(cs: CriticalSection) { | ||
| 552 | DRIVER.init_timer(cs) | ||
| 553 | } | ||
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 9a56a41fb..77f19a37b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -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 | ||
| @@ -160,8 +162,8 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 160 | 162 | ||
| 161 | /// Set PWM frequency. | 163 | /// Set PWM frequency. |
| 162 | /// | 164 | /// |
| 163 | /// 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 |
| 164 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 166 | /// occurs. |
| 165 | pub fn set_frequency(&mut self, freq: Hertz) { | 167 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 166 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 168 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 167 | 2u8 | 169 | 2u8 |
| @@ -219,59 +221,53 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 219 | /// Note: | 221 | /// Note: |
| 220 | /// 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. |
| 221 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 223 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { |
| 222 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 224 | self.inner.enable_channel(channel, true); |
| 223 | let req = dma.request(); | 225 | self.inner.enable_update_dma(true); |
| 224 | 226 | self.inner.setup_update_dma(dma, channel, duty).await; | |
| 225 | let original_duty_state = self.inner.get_compare_value(channel); | 227 | self.inner.enable_update_dma(false); |
| 226 | let original_enable_state = self.inner.get_channel_enable_state(channel); | 228 | } |
| 227 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 228 | |||
| 229 | if !original_update_dma_state { | ||
| 230 | self.inner.enable_update_dma(true); | ||
| 231 | } | ||
| 232 | |||
| 233 | if !original_enable_state { | ||
| 234 | self.inner.enable_channel(channel, true); | ||
| 235 | } | ||
| 236 | |||
| 237 | unsafe { | ||
| 238 | #[cfg(not(any(bdma, gpdma)))] | ||
| 239 | use crate::dma::{Burst, FifoThreshold}; | ||
| 240 | use crate::dma::{Transfer, TransferOptions}; | ||
| 241 | |||
| 242 | let dma_transfer_option = TransferOptions { | ||
| 243 | #[cfg(not(any(bdma, gpdma)))] | ||
| 244 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 245 | #[cfg(not(any(bdma, gpdma)))] | ||
| 246 | mburst: Burst::Incr8, | ||
| 247 | ..Default::default() | ||
| 248 | }; | ||
| 249 | |||
| 250 | Transfer::new_write( | ||
| 251 | dma, | ||
| 252 | req, | ||
| 253 | duty, | ||
| 254 | self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 255 | dma_transfer_option, | ||
| 256 | ) | ||
| 257 | .await | ||
| 258 | }; | ||
| 259 | |||
| 260 | // restore output compare state | ||
| 261 | if !original_enable_state { | ||
| 262 | self.inner.enable_channel(channel, false); | ||
| 263 | } | ||
| 264 | |||
| 265 | self.inner.set_compare_value(channel, original_duty_state); | ||
| 266 | 229 | ||
| 267 | // Since DMA is closed before timer update event trigger DMA is turn off, | 230 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| 268 | // this can almost always trigger a DMA FIFO error. | 231 | /// |
| 269 | // | 232 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers |
| 270 | // optional TODO: | 233 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the |
| 271 | // clean FEIF after disable UDE | 234 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. |
| 272 | if !original_update_dma_state { | 235 | /// |
| 273 | self.inner.enable_update_dma(false); | 236 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row |
| 274 | } | 237 | /// represents a single update event and each column corresponds to a specific timer channel (starting |
| 238 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 239 | /// | ||
| 240 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 241 | /// | ||
| 242 | /// ```rust,ignore | ||
| 243 | /// let dma_buf: [u16; 16] = [ | ||
| 244 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 245 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 246 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 247 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 248 | /// ]; | ||
| 249 | /// ``` | ||
| 250 | /// | ||
| 251 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 252 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 253 | /// | ||
| 254 | /// Note: | ||
| 255 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 256 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 257 | /// switch this timer by using `time-driver-timX` feature. | ||
| 258 | /// | ||
| 259 | pub async fn waveform_up_multi_channel( | ||
| 260 | &mut self, | ||
| 261 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 262 | starting_channel: Channel, | ||
| 263 | ending_channel: Channel, | ||
| 264 | duty: &[u16], | ||
| 265 | ) { | ||
| 266 | self.inner.enable_update_dma(true); | ||
| 267 | self.inner | ||
| 268 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) | ||
| 269 | .await; | ||
| 270 | self.inner.enable_update_dma(false); | ||
| 275 | } | 271 | } |
| 276 | } | 272 | } |
| 277 | 273 | ||
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 2a4ec2db0..9cf0f8c34 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs | |||
| @@ -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 0122fe4f7..aba08081f 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -13,6 +13,7 @@ use embassy_hal_internal::Peri; | |||
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, 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::dma::{Transfer, WritableRingBuffer}; | ||
| 16 | use crate::pac::timer::vals; | 17 | use crate::pac::timer::vals; |
| 17 | use crate::rcc; | 18 | use crate::rcc; |
| 18 | use crate::time::Hertz; | 19 | use crate::time::Hertz; |
| @@ -272,6 +273,17 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 272 | self.regs_core().cr1().modify(|r| r.set_cen(true)); | 273 | self.regs_core().cr1().modify(|r| r.set_cen(true)); |
| 273 | } | 274 | } |
| 274 | 275 | ||
| 276 | /// Generate timer update event from software. | ||
| 277 | /// | ||
| 278 | /// Set URS to avoid generating interrupt or DMA request. This update event is only | ||
| 279 | /// used to load value from pre-load registers. If called when the timer is running, | ||
| 280 | /// it may disrupt the output waveform. | ||
| 281 | pub fn generate_update_event(&self) { | ||
| 282 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 283 | self.regs_core().egr().write(|r| r.set_ug(true)); | ||
| 284 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 285 | } | ||
| 286 | |||
| 275 | /// Stop the timer. | 287 | /// Stop the timer. |
| 276 | pub fn stop(&self) { | 288 | pub fn stop(&self) { |
| 277 | self.regs_core().cr1().modify(|r| r.set_cen(false)); | 289 | self.regs_core().cr1().modify(|r| r.set_cen(false)); |
| @@ -322,10 +334,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 322 | let regs = self.regs_core(); | 334 | let regs = self.regs_core(); |
| 323 | regs.psc().write_value(psc); | 335 | regs.psc().write_value(psc); |
| 324 | regs.arr().write(|r| r.set_arr(arr)); | 336 | regs.arr().write(|r| r.set_arr(arr)); |
| 325 | |||
| 326 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 327 | regs.egr().write(|r| r.set_ug(true)); | ||
| 328 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 329 | } | 337 | } |
| 330 | #[cfg(not(stm32l0))] | 338 | #[cfg(not(stm32l0))] |
| 331 | TimerBits::Bits32 => { | 339 | TimerBits::Bits32 => { |
| @@ -335,10 +343,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 335 | let regs = self.regs_gp32_unchecked(); | 343 | let regs = self.regs_gp32_unchecked(); |
| 336 | regs.psc().write_value(psc); | 344 | regs.psc().write_value(psc); |
| 337 | regs.arr().write_value(arr); | 345 | regs.arr().write_value(arr); |
| 338 | |||
| 339 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 340 | regs.egr().write(|r| r.set_ug(true)); | ||
| 341 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 342 | } | 346 | } |
| 343 | } | 347 | } |
| 344 | } | 348 | } |
| @@ -656,6 +660,167 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 656 | } | 660 | } |
| 657 | } | 661 | } |
| 658 | 662 | ||
| 663 | /// Setup a ring buffer for the channel | ||
| 664 | pub fn setup_ring_buffer<'a>( | ||
| 665 | &mut self, | ||
| 666 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 667 | channel: Channel, | ||
| 668 | dma_buf: &'a mut [u16], | ||
| 669 | ) -> WritableRingBuffer<'a, u16> { | ||
| 670 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 671 | let req = dma.request(); | ||
| 672 | |||
| 673 | unsafe { | ||
| 674 | use crate::dma::TransferOptions; | ||
| 675 | #[cfg(not(any(bdma, gpdma)))] | ||
| 676 | use crate::dma::{Burst, FifoThreshold}; | ||
| 677 | |||
| 678 | let dma_transfer_option = TransferOptions { | ||
| 679 | #[cfg(not(any(bdma, gpdma)))] | ||
| 680 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 681 | #[cfg(not(any(bdma, gpdma)))] | ||
| 682 | mburst: Burst::Incr8, | ||
| 683 | ..Default::default() | ||
| 684 | }; | ||
| 685 | |||
| 686 | WritableRingBuffer::new( | ||
| 687 | dma, | ||
| 688 | req, | ||
| 689 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 690 | dma_buf, | ||
| 691 | dma_transfer_option, | ||
| 692 | ) | ||
| 693 | } | ||
| 694 | } | ||
| 695 | |||
| 696 | /// Generate a sequence of PWM waveform | ||
| 697 | /// | ||
| 698 | /// Note: | ||
| 699 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 700 | pub fn setup_update_dma<'a>( | ||
| 701 | &mut self, | ||
| 702 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 703 | channel: Channel, | ||
| 704 | duty: &'a [u16], | ||
| 705 | ) -> Transfer<'a> { | ||
| 706 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 707 | let req = dma.request(); | ||
| 708 | |||
| 709 | unsafe { | ||
| 710 | #[cfg(not(any(bdma, gpdma)))] | ||
| 711 | use crate::dma::{Burst, FifoThreshold}; | ||
| 712 | use crate::dma::{Transfer, TransferOptions}; | ||
| 713 | |||
| 714 | let dma_transfer_option = TransferOptions { | ||
| 715 | #[cfg(not(any(bdma, gpdma)))] | ||
| 716 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 717 | #[cfg(not(any(bdma, gpdma)))] | ||
| 718 | mburst: Burst::Incr8, | ||
| 719 | ..Default::default() | ||
| 720 | }; | ||
| 721 | |||
| 722 | match self.bits() { | ||
| 723 | TimerBits::Bits16 => Transfer::new_write( | ||
| 724 | dma, | ||
| 725 | req, | ||
| 726 | duty, | ||
| 727 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 728 | dma_transfer_option, | ||
| 729 | ), | ||
| 730 | #[cfg(not(any(stm32l0)))] | ||
| 731 | TimerBits::Bits32 => { | ||
| 732 | #[cfg(not(any(bdma, gpdma)))] | ||
| 733 | panic!("unsupported timer bits"); | ||
| 734 | |||
| 735 | #[cfg(any(bdma, gpdma))] | ||
| 736 | Transfer::new_write( | ||
| 737 | dma, | ||
| 738 | req, | ||
| 739 | duty, | ||
| 740 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, | ||
| 741 | dma_transfer_option, | ||
| 742 | ) | ||
| 743 | } | ||
| 744 | } | ||
| 745 | } | ||
| 746 | } | ||
| 747 | |||
| 748 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | ||
| 749 | /// | ||
| 750 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers | ||
| 751 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the | ||
| 752 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. | ||
| 753 | /// | ||
| 754 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row | ||
| 755 | /// represents a single update event and each column corresponds to a specific timer channel (starting | ||
| 756 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 757 | /// | ||
| 758 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 759 | /// | ||
| 760 | /// ```rust,ignore | ||
| 761 | /// let dma_buf: [u16; 16] = [ | ||
| 762 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 763 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 764 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 765 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 766 | /// ]; | ||
| 767 | /// ``` | ||
| 768 | /// | ||
| 769 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 770 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 771 | /// | ||
| 772 | /// Note: | ||
| 773 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 774 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 775 | /// switch this timer by using `time-driver-timX` feature. | ||
| 776 | /// | ||
| 777 | pub fn setup_update_dma_burst<'a>( | ||
| 778 | &mut self, | ||
| 779 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 780 | starting_channel: Channel, | ||
| 781 | ending_channel: Channel, | ||
| 782 | duty: &'a [u16], | ||
| 783 | ) -> Transfer<'a> { | ||
| 784 | let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; | ||
| 785 | let start_ch_index = starting_channel.index(); | ||
| 786 | let end_ch_index = ending_channel.index(); | ||
| 787 | |||
| 788 | assert!(start_ch_index <= end_ch_index); | ||
| 789 | |||
| 790 | let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 791 | self.regs_gp16() | ||
| 792 | .dcr() | ||
| 793 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 794 | self.regs_gp16() | ||
| 795 | .dcr() | ||
| 796 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | ||
| 797 | |||
| 798 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 799 | let req = dma.request(); | ||
| 800 | |||
| 801 | unsafe { | ||
| 802 | #[cfg(not(any(bdma, gpdma)))] | ||
| 803 | use crate::dma::{Burst, FifoThreshold}; | ||
| 804 | use crate::dma::{Transfer, TransferOptions}; | ||
| 805 | |||
| 806 | let dma_transfer_option = TransferOptions { | ||
| 807 | #[cfg(not(any(bdma, gpdma)))] | ||
| 808 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 809 | #[cfg(not(any(bdma, gpdma)))] | ||
| 810 | mburst: Burst::Incr4, | ||
| 811 | ..Default::default() | ||
| 812 | }; | ||
| 813 | |||
| 814 | Transfer::new_write( | ||
| 815 | dma, | ||
| 816 | req, | ||
| 817 | duty, | ||
| 818 | self.regs_gp16().dmar().as_ptr() as *mut u16, | ||
| 819 | dma_transfer_option, | ||
| 820 | ) | ||
| 821 | } | ||
| 822 | } | ||
| 823 | |||
| 659 | /// Get capture value for a channel. | 824 | /// Get capture value for a channel. |
| 660 | pub fn get_capture_value(&self, channel: Channel) -> u32 { | 825 | pub fn get_capture_value(&self, channel: Channel) -> u32 { |
| 661 | self.get_compare_value(channel) | 826 | self.get_compare_value(channel) |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 804d1ef37..3fa363881 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -12,6 +12,7 @@ pub mod low_level; | |||
| 12 | pub mod one_pulse; | 12 | pub mod one_pulse; |
| 13 | pub mod pwm_input; | 13 | pub mod pwm_input; |
| 14 | pub mod qei; | 14 | pub mod qei; |
| 15 | pub mod ringbuffered; | ||
| 15 | pub mod simple_pwm; | 16 | pub mod simple_pwm; |
| 16 | 17 | ||
| 17 | use crate::interrupt; | 18 | use crate::interrupt; |
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index da8a79b09..057ab011a 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs | |||
| @@ -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/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs new file mode 100644 index 000000000..e8f97bf59 --- /dev/null +++ b/embassy-stm32/src/timer/ringbuffered.rs | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | //! RingBuffered PWM driver. | ||
| 2 | |||
| 3 | use core::mem::ManuallyDrop; | ||
| 4 | use core::task::Waker; | ||
| 5 | |||
| 6 | use super::low_level::Timer; | ||
| 7 | use super::{Channel, GeneralInstance4Channel}; | ||
| 8 | use crate::dma::WritableRingBuffer; | ||
| 9 | use crate::dma::ringbuffer::Error; | ||
| 10 | |||
| 11 | /// A PWM channel that uses a DMA ring buffer for continuous waveform generation. | ||
| 12 | /// | ||
| 13 | /// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU. | ||
| 14 | /// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling | ||
| 15 | /// through duty cycle values stored in memory. | ||
| 16 | /// | ||
| 17 | /// You can write new duty cycle values to the ring buffer while it's running, enabling | ||
| 18 | /// dynamic waveform generation for applications like motor control, LED dimming, or audio output. | ||
| 19 | /// | ||
| 20 | /// # Example | ||
| 21 | /// ```ignore | ||
| 22 | /// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer); | ||
| 23 | /// channel.start(); // Start DMA transfer | ||
| 24 | /// channel.write(&[100, 200, 300]).ok(); // Update duty cycles | ||
| 25 | /// ``` | ||
| 26 | pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { | ||
| 27 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 28 | ring_buf: WritableRingBuffer<'d, u16>, | ||
| 29 | channel: Channel, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { | ||
| 33 | pub(crate) fn new( | ||
| 34 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 35 | channel: Channel, | ||
| 36 | ring_buf: WritableRingBuffer<'d, u16>, | ||
| 37 | ) -> Self { | ||
| 38 | Self { | ||
| 39 | timer, | ||
| 40 | ring_buf, | ||
| 41 | channel, | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Start the ring buffer operation. | ||
| 46 | /// | ||
| 47 | /// You must call this after creating it for it to work. | ||
| 48 | pub fn start(&mut self) { | ||
| 49 | self.ring_buf.start() | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Clear all data in the ring buffer. | ||
| 53 | pub fn clear(&mut self) { | ||
| 54 | self.ring_buf.clear() | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. | ||
| 58 | pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { | ||
| 59 | self.ring_buf.write_immediate(buf) | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Write elements from the ring buffer | ||
| 63 | /// Return a tuple of the length written and the length remaining in the buffer | ||
| 64 | pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { | ||
| 65 | self.ring_buf.write(buf) | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Write an exact number of elements to the ringbuffer. | ||
| 69 | pub async fn write_exact(&mut self, buffer: &[u16]) -> Result<usize, Error> { | ||
| 70 | self.ring_buf.write_exact(buffer).await | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Wait for any ring buffer write error. | ||
| 74 | pub async fn wait_write_error(&mut self) -> Result<usize, Error> { | ||
| 75 | self.ring_buf.wait_write_error().await | ||
| 76 | } | ||
| 77 | |||
| 78 | /// The current length of the ringbuffer | ||
| 79 | pub fn len(&mut self) -> Result<usize, Error> { | ||
| 80 | self.ring_buf.len() | ||
| 81 | } | ||
| 82 | |||
| 83 | /// The capacity of the ringbuffer | ||
| 84 | pub const fn capacity(&self) -> usize { | ||
| 85 | self.ring_buf.capacity() | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Set a waker to be woken when at least one byte is send. | ||
| 89 | pub fn set_waker(&mut self, waker: &Waker) { | ||
| 90 | self.ring_buf.set_waker(waker) | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Request the DMA to reset. The configuration for this channel will not be preserved. | ||
| 94 | /// | ||
| 95 | /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. | ||
| 96 | pub fn request_reset(&mut self) { | ||
| 97 | self.ring_buf.request_reset() | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 101 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 102 | /// | ||
| 103 | /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. | ||
| 104 | pub fn request_pause(&mut self) { | ||
| 105 | self.ring_buf.request_pause() | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Return whether DMA is still running. | ||
| 109 | /// | ||
| 110 | /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop. | ||
| 111 | pub fn is_running(&mut self) -> bool { | ||
| 112 | self.ring_buf.is_running() | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Stop the DMA transfer and await until the buffer is empty. | ||
| 116 | /// | ||
| 117 | /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written. | ||
| 118 | /// | ||
| 119 | /// This is designed to be used with streaming output data such as the I2S/SAI or DAC. | ||
| 120 | pub async fn stop(&mut self) { | ||
| 121 | self.ring_buf.stop().await | ||
| 122 | } | ||
| 123 | |||
| 124 | /// Enable the given channel. | ||
| 125 | pub fn enable(&mut self) { | ||
| 126 | self.timer.enable_channel(self.channel, true); | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Disable the given channel. | ||
| 130 | pub fn disable(&mut self) { | ||
| 131 | self.timer.enable_channel(self.channel, false); | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Check whether given channel is enabled | ||
| 135 | pub fn is_enabled(&self) -> bool { | ||
| 136 | self.timer.get_channel_enable_state(self.channel) | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Get max duty value. | ||
| 140 | /// | ||
| 141 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | ||
| 142 | pub fn max_duty_cycle(&self) -> u16 { | ||
| 143 | let max = self.timer.get_max_compare_value(); | ||
| 144 | assert!(max < u16::MAX as u32); | ||
| 145 | max as u16 + 1 | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Set the output polarity for a given channel. | ||
| 149 | pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) { | ||
| 150 | self.timer.set_output_polarity(self.channel, polarity); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Set the output compare mode for a given channel. | ||
| 154 | pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) { | ||
| 155 | self.timer.set_output_compare_mode(self.channel, mode); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. | ||
| 160 | pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { | ||
| 161 | /// Channel 1 | ||
| 162 | pub ch1: RingBufferedPwmChannel<'d, T>, | ||
| 163 | /// Channel 2 | ||
| 164 | pub ch2: RingBufferedPwmChannel<'d, T>, | ||
| 165 | /// Channel 3 | ||
| 166 | pub ch3: RingBufferedPwmChannel<'d, T>, | ||
| 167 | /// Channel 4 | ||
| 168 | pub ch4: RingBufferedPwmChannel<'d, T>, | ||
| 169 | } | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 36303aeb4..484e9fd81 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -4,7 +4,8 @@ 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::ringbuffered::RingBufferedPwmChannel; |
| 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; | ||
| 8 | use crate::Peri; | 9 | use crate::Peri; |
| 9 | #[cfg(gpio_v2)] | 10 | #[cfg(gpio_v2)] |
| 10 | use crate::gpio::Pull; | 11 | use crate::gpio::Pull; |
| @@ -158,6 +159,33 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | |||
| 158 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { | 159 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { |
| 159 | self.timer.set_output_compare_mode(self.channel, mode); | 160 | self.timer.set_output_compare_mode(self.channel, mode); |
| 160 | } | 161 | } |
| 162 | |||
| 163 | /// Convert this PWM channel into a ring-buffered PWM channel. | ||
| 164 | /// | ||
| 165 | /// This allows continuous PWM waveform generation using a DMA ring buffer. | ||
| 166 | /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking. | ||
| 167 | /// | ||
| 168 | /// # Arguments | ||
| 169 | /// * `tx_dma` - The DMA channel to use for transferring duty cycle values | ||
| 170 | /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements) | ||
| 171 | /// | ||
| 172 | /// # Panics | ||
| 173 | /// Panics if `dma_buf` is empty or longer than 65535 elements. | ||
| 174 | pub fn into_ring_buffered_channel( | ||
| 175 | mut self, | ||
| 176 | tx_dma: Peri<'d, impl super::UpDma<T>>, | ||
| 177 | dma_buf: &'d mut [u16], | ||
| 178 | ) -> RingBufferedPwmChannel<'d, T> { | ||
| 179 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 180 | |||
| 181 | self.timer.enable_update_dma(true); | ||
| 182 | |||
| 183 | RingBufferedPwmChannel::new( | ||
| 184 | unsafe { self.timer.clone_unchecked() }, | ||
| 185 | self.channel, | ||
| 186 | self.timer.setup_ring_buffer(tx_dma, self.channel, dma_buf), | ||
| 187 | ) | ||
| 188 | } | ||
| 161 | } | 189 | } |
| 162 | 190 | ||
| 163 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. | 191 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. |
| @@ -198,7 +226,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 198 | this.inner.set_counting_mode(counting_mode); | 226 | this.inner.set_counting_mode(counting_mode); |
| 199 | this.set_frequency(freq); | 227 | this.set_frequency(freq); |
| 200 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details | 228 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
| 201 | this.inner.start(); | ||
| 202 | 229 | ||
| 203 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 230 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| 204 | .iter() | 231 | .iter() |
| @@ -207,6 +234,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 207 | 234 | ||
| 208 | this.inner.set_output_compare_preload(channel, true); | 235 | this.inner.set_output_compare_preload(channel, true); |
| 209 | }); | 236 | }); |
| 237 | this.inner.set_autoreload_preload(true); | ||
| 238 | |||
| 239 | // Generate update event so pre-load registers are written to the shadow registers | ||
| 240 | this.inner.generate_update_event(); | ||
| 241 | this.inner.start(); | ||
| 210 | 242 | ||
| 211 | this | 243 | this |
| 212 | } | 244 | } |
| @@ -285,8 +317,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 285 | 317 | ||
| 286 | /// Set PWM frequency. | 318 | /// Set PWM frequency. |
| 287 | /// | 319 | /// |
| 288 | /// Note: when you call this, the max duty value changes, so you will have to | 320 | /// 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. | 321 | /// occurs. |
| 290 | pub fn set_frequency(&mut self, freq: Hertz) { | 322 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 291 | // TODO: prevent ARR = u16::MAX? | 323 | // TODO: prevent ARR = u16::MAX? |
| 292 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 324 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| @@ -309,80 +341,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 309 | /// Generate a sequence of PWM waveform | 341 | /// Generate a sequence of PWM waveform |
| 310 | /// | 342 | /// |
| 311 | /// Note: | 343 | /// Note: |
| 312 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 344 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 345 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 346 | /// switch this timer by using `time-driver-timX` feature. | ||
| 313 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 347 | 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 | 348 | self.inner.enable_channel(channel, true); |
| 315 | let req = dma.request(); | 349 | self.inner.enable_update_dma(true); |
| 316 | 350 | self.inner.setup_update_dma(dma, channel, duty).await; | |
| 317 | let original_duty_state = self.channel(channel).current_duty_cycle(); | 351 | self.inner.enable_update_dma(false); |
| 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 | match self.inner.bits() { | ||
| 343 | TimerBits::Bits16 => { | ||
| 344 | Transfer::new_write( | ||
| 345 | dma, | ||
| 346 | req, | ||
| 347 | duty, | ||
| 348 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 349 | dma_transfer_option, | ||
| 350 | ) | ||
| 351 | .await | ||
| 352 | } | ||
| 353 | #[cfg(not(any(stm32l0)))] | ||
| 354 | TimerBits::Bits32 => { | ||
| 355 | #[cfg(not(any(bdma, gpdma)))] | ||
| 356 | panic!("unsupported timer bits"); | ||
| 357 | |||
| 358 | #[cfg(any(bdma, gpdma))] | ||
| 359 | Transfer::new_write( | ||
| 360 | dma, | ||
| 361 | req, | ||
| 362 | duty, | ||
| 363 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, | ||
| 364 | dma_transfer_option, | ||
| 365 | ) | ||
| 366 | .await | ||
| 367 | } | ||
| 368 | }; | ||
| 369 | }; | ||
| 370 | |||
| 371 | // restore output compare state | ||
| 372 | if !original_enable_state { | ||
| 373 | self.channel(channel).disable(); | ||
| 374 | } | ||
| 375 | |||
| 376 | self.channel(channel).set_duty_cycle(original_duty_state); | ||
| 377 | |||
| 378 | // Since DMA is closed before timer update event trigger DMA is turn off, | ||
| 379 | // this can almost always trigger a DMA FIFO error. | ||
| 380 | // | ||
| 381 | // optional TODO: | ||
| 382 | // clean FEIF after disable UDE | ||
| 383 | if !original_update_dma_state { | ||
| 384 | self.inner.enable_update_dma(false); | ||
| 385 | } | ||
| 386 | } | 352 | } |
| 387 | 353 | ||
| 388 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | 354 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| @@ -397,18 +363,23 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 397 | /// | 363 | /// |
| 398 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | 364 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: |
| 399 | /// | 365 | /// |
| 366 | /// ```rust,ignore | ||
| 400 | /// let dma_buf: [u16; 16] = [ | 367 | /// let dma_buf: [u16; 16] = [ |
| 401 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | 368 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 |
| 402 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | 369 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 |
| 403 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | 370 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 |
| 404 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | 371 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 |
| 405 | /// ]; | 372 | /// ]; |
| 373 | /// ``` | ||
| 406 | /// | 374 | /// |
| 407 | /// Each group of N values (where N = number of channels) is transferred on one update event, | 375 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, |
| 408 | /// updating the duty cycles of all selected channels simultaneously. | 376 | /// updating the duty cycles of all selected channels simultaneously. |
| 409 | /// | 377 | /// |
| 410 | /// Note: | 378 | /// Note: |
| 411 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 379 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. |
| 380 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 381 | /// switch this timer by using `time-driver-timX` feature. | ||
| 382 | /// | ||
| 412 | pub async fn waveform_up_multi_channel( | 383 | pub async fn waveform_up_multi_channel( |
| 413 | &mut self, | 384 | &mut self, |
| 414 | dma: Peri<'_, impl super::UpDma<T>>, | 385 | dma: Peri<'_, impl super::UpDma<T>>, |
| @@ -416,148 +387,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 416 | ending_channel: Channel, | 387 | ending_channel: Channel, |
| 417 | duty: &[u16], | 388 | duty: &[u16], |
| 418 | ) { | 389 | ) { |
| 419 | let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; | 390 | self.inner.enable_update_dma(true); |
| 420 | let start_ch_index = starting_channel.index(); | ||
| 421 | let end_ch_index = ending_channel.index(); | ||
| 422 | |||
| 423 | assert!(start_ch_index <= end_ch_index); | ||
| 424 | |||
| 425 | let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 426 | self.inner | ||
| 427 | .regs_gp16() | ||
| 428 | .dcr() | ||
| 429 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 430 | self.inner | 391 | self.inner |
| 431 | .regs_gp16() | 392 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) |
| 432 | .dcr() | 393 | .await; |
| 433 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | 394 | self.inner.enable_update_dma(false); |
| 434 | |||
| 435 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 436 | let req = dma.request(); | ||
| 437 | |||
| 438 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 439 | if !original_update_dma_state { | ||
| 440 | self.inner.enable_update_dma(true); | ||
| 441 | } | ||
| 442 | |||
| 443 | unsafe { | ||
| 444 | #[cfg(not(any(bdma, gpdma)))] | ||
| 445 | use crate::dma::{Burst, FifoThreshold}; | ||
| 446 | use crate::dma::{Transfer, TransferOptions}; | ||
| 447 | |||
| 448 | let dma_transfer_option = TransferOptions { | ||
| 449 | #[cfg(not(any(bdma, gpdma)))] | ||
| 450 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 451 | #[cfg(not(any(bdma, gpdma)))] | ||
| 452 | mburst: Burst::Incr4, | ||
| 453 | ..Default::default() | ||
| 454 | }; | ||
| 455 | |||
| 456 | Transfer::new_write( | ||
| 457 | dma, | ||
| 458 | req, | ||
| 459 | duty, | ||
| 460 | self.inner.regs_gp16().dmar().as_ptr() as *mut u16, | ||
| 461 | dma_transfer_option, | ||
| 462 | ) | ||
| 463 | .await | ||
| 464 | }; | ||
| 465 | |||
| 466 | if !original_update_dma_state { | ||
| 467 | self.inner.enable_update_dma(false); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | ||
| 473 | /// Generate a sequence of PWM waveform | ||
| 474 | pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { | ||
| 475 | use crate::pac::timer::vals::Ccds; | ||
| 476 | |||
| 477 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 478 | let req = dma.request(); | ||
| 479 | |||
| 480 | let cc_channel = C::CHANNEL; | ||
| 481 | |||
| 482 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); | ||
| 483 | let original_enable_state = self.channel(cc_channel).is_enabled(); | ||
| 484 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; | ||
| 485 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | ||
| 486 | |||
| 487 | // redirect CC DMA request onto Update Event | ||
| 488 | if !original_cc_dma_on_update { | ||
| 489 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) | ||
| 490 | } | ||
| 491 | |||
| 492 | if !original_cc_dma_enabled { | ||
| 493 | self.inner.set_cc_dma_enable_state(cc_channel, true); | ||
| 494 | } | ||
| 495 | |||
| 496 | if !original_enable_state { | ||
| 497 | self.channel(cc_channel).enable(); | ||
| 498 | } | ||
| 499 | |||
| 500 | unsafe { | ||
| 501 | #[cfg(not(any(bdma, gpdma)))] | ||
| 502 | use crate::dma::{Burst, FifoThreshold}; | ||
| 503 | use crate::dma::{Transfer, TransferOptions}; | ||
| 504 | |||
| 505 | let dma_transfer_option = TransferOptions { | ||
| 506 | #[cfg(not(any(bdma, gpdma)))] | ||
| 507 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 508 | #[cfg(not(any(bdma, gpdma)))] | ||
| 509 | mburst: Burst::Incr8, | ||
| 510 | ..Default::default() | ||
| 511 | }; | ||
| 512 | |||
| 513 | match self.inner.bits() { | ||
| 514 | TimerBits::Bits16 => { | ||
| 515 | Transfer::new_write( | ||
| 516 | dma, | ||
| 517 | req, | ||
| 518 | duty, | ||
| 519 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, | ||
| 520 | dma_transfer_option, | ||
| 521 | ) | ||
| 522 | .await | ||
| 523 | } | ||
| 524 | #[cfg(not(any(stm32l0)))] | ||
| 525 | TimerBits::Bits32 => { | ||
| 526 | #[cfg(not(any(bdma, gpdma)))] | ||
| 527 | panic!("unsupported timer bits"); | ||
| 528 | |||
| 529 | #[cfg(any(bdma, gpdma))] | ||
| 530 | Transfer::new_write( | ||
| 531 | dma, | ||
| 532 | req, | ||
| 533 | duty, | ||
| 534 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, | ||
| 535 | dma_transfer_option, | ||
| 536 | ) | ||
| 537 | .await | ||
| 538 | } | ||
| 539 | }; | ||
| 540 | }; | ||
| 541 | |||
| 542 | // restore output compare state | ||
| 543 | if !original_enable_state { | ||
| 544 | self.channel(cc_channel).disable(); | ||
| 545 | } | ||
| 546 | |||
| 547 | self.channel(cc_channel).set_duty_cycle(original_duty_state); | ||
| 548 | |||
| 549 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | ||
| 550 | // this can almost always trigger a DMA FIFO error. | ||
| 551 | // | ||
| 552 | // optional TODO: | ||
| 553 | // clean FEIF after disable UDE | ||
| 554 | if !original_cc_dma_enabled { | ||
| 555 | self.inner.set_cc_dma_enable_state(cc_channel, false); | ||
| 556 | } | ||
| 557 | |||
| 558 | if !original_cc_dma_on_update { | ||
| 559 | self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) | ||
| 560 | } | ||
| 561 | } | 395 | } |
| 562 | } | 396 | } |
| 563 | 397 | ||
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 69c3a740f..26d2b8991 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -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/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index a80a2692b..466e1a9b4 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs | |||
| @@ -420,9 +420,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 420 | return Err(XspiError::InvalidCommand); | 420 | return Err(XspiError::InvalidCommand); |
| 421 | } | 421 | } |
| 422 | 422 | ||
| 423 | T::REGS.cr().modify(|w| { | 423 | T::REGS |
| 424 | w.set_fmode(0.into()); | 424 | .cr() |
| 425 | }); | 425 | .modify(|w| w.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 426 | 426 | ||
| 427 | // Configure alternate bytes | 427 | // Configure alternate bytes |
| 428 | if let Some(ab) = command.alternate_bytes { | 428 | if let Some(ab) = command.alternate_bytes { |
| @@ -538,8 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 538 | w.set_dmaen(false); | 538 | w.set_dmaen(false); |
| 539 | }); | 539 | }); |
| 540 | 540 | ||
| 541 | // self.configure_command(&transaction, Some(buf.len()))?; | 541 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 542 | self.configure_command(&transaction, Some(buf.len())).unwrap(); | 542 | self.configure_command(&transaction, Some(transfer_size_bytes))?; |
| 543 | 543 | ||
| 544 | let current_address = T::REGS.ar().read().address(); | 544 | let current_address = T::REGS.ar().read().address(); |
| 545 | let current_instruction = T::REGS.ir().read().instruction(); | 545 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -578,7 +578,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 578 | w.set_dmaen(false); | 578 | w.set_dmaen(false); |
| 579 | }); | 579 | }); |
| 580 | 580 | ||
| 581 | self.configure_command(&transaction, Some(buf.len()))?; | 581 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 582 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 582 | 583 | ||
| 583 | T::REGS | 584 | T::REGS |
| 584 | .cr() | 585 | .cr() |
| @@ -1145,7 +1146,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1145 | // Wait for peripheral to be free | 1146 | // Wait for peripheral to be free |
| 1146 | while T::REGS.sr().read().busy() {} | 1147 | while T::REGS.sr().read().busy() {} |
| 1147 | 1148 | ||
| 1148 | self.configure_command(&transaction, Some(buf.len()))?; | 1149 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1150 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1149 | 1151 | ||
| 1150 | let current_address = T::REGS.ar().read().address(); | 1152 | let current_address = T::REGS.ar().read().address(); |
| 1151 | let current_instruction = T::REGS.ir().read().instruction(); | 1153 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1160,16 +1162,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1160 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1162 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1161 | } | 1163 | } |
| 1162 | 1164 | ||
| 1163 | let transfer = unsafe { | 1165 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1164 | self.dma | 1166 | let transfer = unsafe { |
| 1165 | .as_mut() | 1167 | self.dma |
| 1166 | .unwrap() | 1168 | .as_mut() |
| 1167 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1169 | .unwrap() |
| 1168 | }; | 1170 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1171 | }; | ||
| 1169 | 1172 | ||
| 1170 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1173 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1171 | 1174 | ||
| 1172 | transfer.blocking_wait(); | 1175 | transfer.blocking_wait(); |
| 1176 | } | ||
| 1173 | 1177 | ||
| 1174 | finish_dma(T::REGS); | 1178 | finish_dma(T::REGS); |
| 1175 | 1179 | ||
| @@ -1185,21 +1189,24 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1185 | // Wait for peripheral to be free | 1189 | // Wait for peripheral to be free |
| 1186 | while T::REGS.sr().read().busy() {} | 1190 | while T::REGS.sr().read().busy() {} |
| 1187 | 1191 | ||
| 1188 | self.configure_command(&transaction, Some(buf.len()))?; | 1192 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1193 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1189 | T::REGS | 1194 | T::REGS |
| 1190 | .cr() | 1195 | .cr() |
| 1191 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | 1196 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 1192 | 1197 | ||
| 1193 | let transfer = unsafe { | 1198 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1194 | self.dma | 1199 | let transfer = unsafe { |
| 1195 | .as_mut() | 1200 | self.dma |
| 1196 | .unwrap() | 1201 | .as_mut() |
| 1197 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 1202 | .unwrap() |
| 1198 | }; | 1203 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) |
| 1204 | }; | ||
| 1199 | 1205 | ||
| 1200 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1206 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1201 | 1207 | ||
| 1202 | transfer.blocking_wait(); | 1208 | transfer.blocking_wait(); |
| 1209 | } | ||
| 1203 | 1210 | ||
| 1204 | finish_dma(T::REGS); | 1211 | finish_dma(T::REGS); |
| 1205 | 1212 | ||
| @@ -1215,7 +1222,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1215 | // Wait for peripheral to be free | 1222 | // Wait for peripheral to be free |
| 1216 | while T::REGS.sr().read().busy() {} | 1223 | while T::REGS.sr().read().busy() {} |
| 1217 | 1224 | ||
| 1218 | self.configure_command(&transaction, Some(buf.len()))?; | 1225 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1226 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1219 | 1227 | ||
| 1220 | let current_address = T::REGS.ar().read().address(); | 1228 | let current_address = T::REGS.ar().read().address(); |
| 1221 | let current_instruction = T::REGS.ir().read().instruction(); | 1229 | let current_instruction = T::REGS.ir().read().instruction(); |
| @@ -1230,16 +1238,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1230 | T::REGS.ar().write(|v| v.set_address(current_address)); | 1238 | T::REGS.ar().write(|v| v.set_address(current_address)); |
| 1231 | } | 1239 | } |
| 1232 | 1240 | ||
| 1233 | let transfer = unsafe { | 1241 | for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { |
| 1234 | self.dma | 1242 | let transfer = unsafe { |
| 1235 | .as_mut() | 1243 | self.dma |
| 1236 | .unwrap() | 1244 | .as_mut() |
| 1237 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | 1245 | .unwrap() |
| 1238 | }; | 1246 | .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) |
| 1247 | }; | ||
| 1239 | 1248 | ||
| 1240 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1249 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1241 | 1250 | ||
| 1242 | transfer.await; | 1251 | transfer.await; |
| 1252 | } | ||
| 1243 | 1253 | ||
| 1244 | finish_dma(T::REGS); | 1254 | finish_dma(T::REGS); |
| 1245 | 1255 | ||
| @@ -1255,21 +1265,25 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { | |||
| 1255 | // Wait for peripheral to be free | 1265 | // Wait for peripheral to be free |
| 1256 | while T::REGS.sr().read().busy() {} | 1266 | while T::REGS.sr().read().busy() {} |
| 1257 | 1267 | ||
| 1258 | self.configure_command(&transaction, Some(buf.len()))?; | 1268 | let transfer_size_bytes = buf.len() * W::size().bytes(); |
| 1269 | self.configure_command(&transaction, Some(transfer_size_bytes))?; | ||
| 1259 | T::REGS | 1270 | T::REGS |
| 1260 | .cr() | 1271 | .cr() |
| 1261 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | 1272 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); |
| 1262 | 1273 | ||
| 1263 | let transfer = unsafe { | 1274 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1264 | self.dma | 1275 | for chunk in buf.chunks(0xFFFF / W::size().bytes()) { |
| 1265 | .as_mut() | 1276 | let transfer = unsafe { |
| 1266 | .unwrap() | 1277 | self.dma |
| 1267 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 1278 | .as_mut() |
| 1268 | }; | 1279 | .unwrap() |
| 1280 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 1281 | }; | ||
| 1269 | 1282 | ||
| 1270 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1283 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1271 | 1284 | ||
| 1272 | transfer.await; | 1285 | transfer.await; |
| 1286 | } | ||
| 1273 | 1287 | ||
| 1274 | finish_dma(T::REGS); | 1288 | finish_dma(T::REGS); |
| 1275 | 1289 | ||
