diff options
| author | Matteo Meluzzi <[email protected]> | 2025-10-02 10:53:31 +0200 |
|---|---|---|
| committer | Matteo Meluzzi <[email protected]> | 2025-10-02 10:53:31 +0200 |
| commit | 828a8df18d04877df1f55f04354980b28ff2f2f8 (patch) | |
| tree | c4fa405f5eba7a14b6d435d6cc746c9e0dc52632 /embassy-stm32/src | |
| parent | 176649e71ad442ca9856af6c11989b0b2f228c4b (diff) | |
| parent | 194a721d0eab929a2af0a2a4e45ca8e70e0d3f0a (diff) | |
Merge branch 'main' into 17-add-support-for-boot-protocol
Diffstat (limited to 'embassy-stm32/src')
| -rw-r--r-- | embassy-stm32/src/rcc/bd.rs | 10 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/c0.rs | 14 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mco.rs | 23 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/low_power.rs | 36 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/mod.rs | 21 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/v2.rs | 8 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/qei.rs | 100 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/buffered.rs | 67 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/mod.rs | 71 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/ringbuffered.rs | 118 |
10 files changed, 321 insertions, 147 deletions
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index e2c704405..63fc195dd 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs | |||
| @@ -52,9 +52,9 @@ impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv { | |||
| 52 | } | 52 | } |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | #[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))] | 55 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] |
| 56 | type Bdcr = crate::pac::rcc::regs::Bdcr; | 56 | type Bdcr = crate::pac::rcc::regs::Bdcr; |
| 57 | #[cfg(any(rtc_v2l0, rtc_v2l1))] | 57 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 58 | type Bdcr = crate::pac::rcc::regs::Csr; | 58 | type Bdcr = crate::pac::rcc::regs::Csr; |
| 59 | #[cfg(any(stm32c0))] | 59 | #[cfg(any(stm32c0))] |
| 60 | type Bdcr = crate::pac::rcc::regs::Csr1; | 60 | type Bdcr = crate::pac::rcc::regs::Csr1; |
| @@ -76,9 +76,9 @@ fn unlock() { | |||
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | fn bdcr() -> Reg<Bdcr, RW> { | 78 | fn bdcr() -> Reg<Bdcr, RW> { |
| 79 | #[cfg(any(rtc_v2l0, rtc_v2l1))] | 79 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 80 | return crate::pac::RCC.csr(); | 80 | return crate::pac::RCC.csr(); |
| 81 | #[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))] | 81 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] |
| 82 | return crate::pac::RCC.bdcr(); | 82 | return crate::pac::RCC.bdcr(); |
| 83 | #[cfg(any(stm32c0))] | 83 | #[cfg(any(stm32c0))] |
| 84 | return crate::pac::RCC.csr1(); | 84 | return crate::pac::RCC.csr1(); |
| @@ -273,7 +273,7 @@ impl LsConfig { | |||
| 273 | 273 | ||
| 274 | if self.rtc != RtcClockSource::DISABLE { | 274 | if self.rtc != RtcClockSource::DISABLE { |
| 275 | bdcr().modify(|w| { | 275 | bdcr().modify(|w| { |
| 276 | #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] | 276 | #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] |
| 277 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | 277 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); |
| 278 | 278 | ||
| 279 | #[cfg(not(rcc_wba))] | 279 | #[cfg(not(rcc_wba))] |
diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index c2295bab6..99f22273d 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs | |||
| @@ -49,6 +49,10 @@ pub struct Config { | |||
| 49 | /// System Clock Configuration | 49 | /// System Clock Configuration |
| 50 | pub sys: Sysclk, | 50 | pub sys: Sysclk, |
| 51 | 51 | ||
| 52 | /// HSI48 Configuration | ||
| 53 | #[cfg(crs)] | ||
| 54 | pub hsi48: Option<super::Hsi48Config>, | ||
| 55 | |||
| 52 | pub ahb_pre: AHBPrescaler, | 56 | pub ahb_pre: AHBPrescaler, |
| 53 | pub apb1_pre: APBPrescaler, | 57 | pub apb1_pre: APBPrescaler, |
| 54 | 58 | ||
| @@ -68,6 +72,8 @@ impl Config { | |||
| 68 | }), | 72 | }), |
| 69 | hse: None, | 73 | hse: None, |
| 70 | sys: Sysclk::HSISYS, | 74 | sys: Sysclk::HSISYS, |
| 75 | #[cfg(crs)] | ||
| 76 | hsi48: Some(crate::rcc::Hsi48Config::new()), | ||
| 71 | ahb_pre: AHBPrescaler::DIV1, | 77 | ahb_pre: AHBPrescaler::DIV1, |
| 72 | apb1_pre: APBPrescaler::DIV1, | 78 | apb1_pre: APBPrescaler::DIV1, |
| 73 | ls: crate::rcc::LsConfig::new(), | 79 | ls: crate::rcc::LsConfig::new(), |
| @@ -127,6 +133,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 127 | } | 133 | } |
| 128 | }; | 134 | }; |
| 129 | 135 | ||
| 136 | // Configure HSI48 if required | ||
| 137 | #[cfg(crs)] | ||
| 138 | let hsi48 = config.hsi48.map(super::init_hsi48); | ||
| 139 | |||
| 130 | let rtc = config.ls.init(); | 140 | let rtc = config.ls.init(); |
| 131 | 141 | ||
| 132 | let sys = match config.sys { | 142 | let sys = match config.sys { |
| @@ -185,13 +195,13 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 185 | hsi: hsi, | 195 | hsi: hsi, |
| 186 | hsiker: hsiker, | 196 | hsiker: hsiker, |
| 187 | hse: hse, | 197 | hse: hse, |
| 198 | #[cfg(crs)] | ||
| 199 | hsi48: hsi48, | ||
| 188 | rtc: rtc, | 200 | rtc: rtc, |
| 189 | 201 | ||
| 190 | // TODO | 202 | // TODO |
| 191 | lsi: None, | 203 | lsi: None, |
| 192 | lse: None, | 204 | lse: None, |
| 193 | #[cfg(crs)] | ||
| 194 | hsi48: None, | ||
| 195 | ); | 205 | ); |
| 196 | 206 | ||
| 197 | RCC.ccipr() | 207 | RCC.ccipr() |
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 59ccc8cb5..fa4b45a20 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -91,12 +91,29 @@ pub struct Mco<'d, T: McoInstance> { | |||
| 91 | 91 | ||
| 92 | impl<'d, T: McoInstance> Mco<'d, T> { | 92 | impl<'d, T: McoInstance> Mco<'d, T> { |
| 93 | /// Create a new MCO instance. | 93 | /// Create a new MCO instance. |
| 94 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, prescaler: McoPrescaler) -> Self { | 94 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, config: McoConfig) -> Self { |
| 95 | critical_section::with(|_| unsafe { | 95 | critical_section::with(|_| unsafe { |
| 96 | T::_apply_clock_settings(source, prescaler); | 96 | T::_apply_clock_settings(source, config.prescaler); |
| 97 | set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 97 | set_as_af!(pin, AfType::output(OutputType::PushPull, config.speed)); |
| 98 | }); | 98 | }); |
| 99 | 99 | ||
| 100 | Self { phantom: PhantomData } | 100 | Self { phantom: PhantomData } |
| 101 | } | 101 | } |
| 102 | } | 102 | } |
| 103 | |||
| 104 | #[non_exhaustive] | ||
| 105 | pub struct McoConfig { | ||
| 106 | /// Master Clock Out prescaler | ||
| 107 | pub prescaler: McoPrescaler, | ||
| 108 | /// IO Drive Strength | ||
| 109 | pub speed: Speed, | ||
| 110 | } | ||
| 111 | |||
| 112 | impl Default for McoConfig { | ||
| 113 | fn default() -> Self { | ||
| 114 | Self { | ||
| 115 | prescaler: McoPrescaler::DIV1, | ||
| 116 | speed: Speed::VeryHigh, | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index 78ccd3e6c..a81ac6746 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -1,4 +1,8 @@ | |||
| 1 | #[cfg(feature = "time")] | ||
| 2 | use embassy_time::{Duration, TICK_HZ}; | ||
| 3 | |||
| 1 | use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; | 4 | use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; |
| 5 | use crate::interrupt::typelevel::Interrupt; | ||
| 2 | use crate::peripherals::RTC; | 6 | use crate::peripherals::RTC; |
| 3 | use crate::rtc::SealedInstance; | 7 | use crate::rtc::SealedInstance; |
| 4 | 8 | ||
| @@ -11,7 +15,7 @@ pub(super) struct RtcInstant { | |||
| 11 | } | 15 | } |
| 12 | 16 | ||
| 13 | impl RtcInstant { | 17 | impl RtcInstant { |
| 14 | #[cfg(not(rtc_v2f2))] | 18 | #[cfg(not(rtc_v2_f2))] |
| 15 | const fn from(second: u8, subsecond: u16) -> Result<Self, DateTimeError> { | 19 | const fn from(second: u8, subsecond: u16) -> Result<Self, DateTimeError> { |
| 16 | if second > 59 { | 20 | if second > 59 { |
| 17 | Err(DateTimeError::InvalidSecond) | 21 | Err(DateTimeError::InvalidSecond) |
| @@ -38,8 +42,6 @@ impl core::ops::Sub for RtcInstant { | |||
| 38 | type Output = embassy_time::Duration; | 42 | type Output = embassy_time::Duration; |
| 39 | 43 | ||
| 40 | fn sub(self, rhs: Self) -> Self::Output { | 44 | fn sub(self, rhs: Self) -> Self::Output { |
| 41 | use embassy_time::{Duration, TICK_HZ}; | ||
| 42 | |||
| 43 | let second = if self.second < rhs.second { | 45 | let second = if self.second < rhs.second { |
| 44 | self.second + 60 | 46 | self.second + 60 |
| 45 | } else { | 47 | } else { |
| @@ -129,11 +131,6 @@ impl Rtc { | |||
| 129 | requested_duration: embassy_time::Duration, | 131 | requested_duration: embassy_time::Duration, |
| 130 | cs: critical_section::CriticalSection, | 132 | cs: critical_section::CriticalSection, |
| 131 | ) { | 133 | ) { |
| 132 | use embassy_time::{Duration, TICK_HZ}; | ||
| 133 | |||
| 134 | #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] | ||
| 135 | use crate::pac::rtc::vals::Calrf; | ||
| 136 | |||
| 137 | // Panic if the rcc mod knows we're not using low-power rtc | 134 | // Panic if the rcc mod knows we're not using low-power rtc |
| 138 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | 135 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] |
| 139 | unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap(); | 136 | unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap(); |
| @@ -150,17 +147,15 @@ impl Rtc { | |||
| 150 | self.write(false, |regs| { | 147 | self.write(false, |regs| { |
| 151 | regs.cr().modify(|w| w.set_wute(false)); | 148 | regs.cr().modify(|w| w.set_wute(false)); |
| 152 | 149 | ||
| 153 | #[cfg(any( | 150 | #[cfg(rtc_v2)] |
| 154 | rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb | ||
| 155 | ))] | ||
| 156 | { | 151 | { |
| 157 | regs.isr().modify(|w| w.set_wutf(false)); | 152 | regs.isr().modify(|w| w.set_wutf(false)); |
| 158 | while !regs.isr().read().wutwf() {} | 153 | while !regs.isr().read().wutwf() {} |
| 159 | } | 154 | } |
| 160 | 155 | ||
| 161 | #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] | 156 | #[cfg(rtc_v3)] |
| 162 | { | 157 | { |
| 163 | regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); | 158 | regs.scr().write(|w| w.set_cwutf(crate::pac::rtc::vals::Calrf::CLEAR)); |
| 164 | while !regs.icsr().read().wutwf() {} | 159 | while !regs.icsr().read().wutwf() {} |
| 165 | } | 160 | } |
| 166 | 161 | ||
| @@ -185,10 +180,6 @@ impl Rtc { | |||
| 185 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` | 180 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` |
| 186 | /// was called, otherwise none | 181 | /// was called, otherwise none |
| 187 | pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { | 182 | pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { |
| 188 | use crate::interrupt::typelevel::Interrupt; | ||
| 189 | #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] | ||
| 190 | use crate::pac::rtc::vals::Calrf; | ||
| 191 | |||
| 192 | let instant = self.instant().unwrap(); | 183 | let instant = self.instant().unwrap(); |
| 193 | if RTC::regs().cr().read().wute() { | 184 | if RTC::regs().cr().read().wute() { |
| 194 | trace!("rtc: stop wakeup alarm at {}", instant); | 185 | trace!("rtc: stop wakeup alarm at {}", instant); |
| @@ -197,13 +188,10 @@ impl Rtc { | |||
| 197 | regs.cr().modify(|w| w.set_wutie(false)); | 188 | regs.cr().modify(|w| w.set_wutie(false)); |
| 198 | regs.cr().modify(|w| w.set_wute(false)); | 189 | regs.cr().modify(|w| w.set_wute(false)); |
| 199 | 190 | ||
| 200 | #[cfg(any( | 191 | #[cfg(rtc_v2)] |
| 201 | rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb | ||
| 202 | ))] | ||
| 203 | regs.isr().modify(|w| w.set_wutf(false)); | 192 | regs.isr().modify(|w| w.set_wutf(false)); |
| 204 | 193 | #[cfg(rtc_v3)] | |
| 205 | #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] | 194 | regs.scr().write(|w| w.set_cwutf(crate::pac::rtc::vals::Calrf::CLEAR)); |
| 206 | regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); | ||
| 207 | 195 | ||
| 208 | // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar, | 196 | // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar, |
| 209 | // there is a table for every "Event input" / "EXTI Line". | 197 | // there is a table for every "Event input" / "EXTI Line". |
| @@ -222,8 +210,6 @@ impl Rtc { | |||
| 222 | } | 210 | } |
| 223 | 211 | ||
| 224 | pub(crate) fn enable_wakeup_line(&self) { | 212 | pub(crate) fn enable_wakeup_line(&self) { |
| 225 | use crate::interrupt::typelevel::Interrupt; | ||
| 226 | |||
| 227 | <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); | 213 | <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); |
| 228 | unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; | 214 | unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; |
| 229 | 215 | ||
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 449f3008a..92dec0960 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -18,14 +18,9 @@ use crate::pac::rtc::regs::{Dr, Tr}; | |||
| 18 | use crate::time::Hertz; | 18 | use crate::time::Hertz; |
| 19 | 19 | ||
| 20 | /// refer to AN4759 to compare features of RTC2 and RTC3 | 20 | /// refer to AN4759 to compare features of RTC2 and RTC3 |
| 21 | #[cfg_attr(any(rtc_v1), path = "v1.rs")] | 21 | #[cfg_attr(rtc_v1, path = "v1.rs")] |
| 22 | #[cfg_attr( | 22 | #[cfg_attr(rtc_v2, path = "v2.rs")] |
| 23 | any( | 23 | #[cfg_attr(rtc_v3, path = "v3.rs")] |
| 24 | rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb | ||
| 25 | ), | ||
| 26 | path = "v2.rs" | ||
| 27 | )] | ||
| 28 | #[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs, rtc_v3c0), path = "v3.rs")] | ||
| 29 | mod _version; | 24 | mod _version; |
| 30 | #[allow(unused_imports)] | 25 | #[allow(unused_imports)] |
| 31 | pub use _version::*; | 26 | pub use _version::*; |
| @@ -72,12 +67,12 @@ impl RtcTimeProvider { | |||
| 72 | 67 | ||
| 73 | // Calculate second fraction and multiply to microseconds | 68 | // Calculate second fraction and multiply to microseconds |
| 74 | // Formula from RM0410 | 69 | // Formula from RM0410 |
| 75 | #[cfg(not(rtc_v2f2))] | 70 | #[cfg(not(rtc_v2_f2))] |
| 76 | let us = { | 71 | let us = { |
| 77 | let prediv = RTC::regs().prer().read().prediv_s() as f32; | 72 | let prediv = RTC::regs().prer().read().prediv_s() as f32; |
| 78 | (((prediv - _ss as f32) / (prediv + 1.0)) * 1e6).min(999_999.0) as u32 | 73 | (((prediv - _ss as f32) / (prediv + 1.0)) * 1e6).min(999_999.0) as u32 |
| 79 | }; | 74 | }; |
| 80 | #[cfg(rtc_v2f2)] | 75 | #[cfg(rtc_v2_f2)] |
| 81 | let us = 0; | 76 | let us = 0; |
| 82 | 77 | ||
| 83 | DateTime::from(year, month, day, weekday, hour, minute, second, us).map_err(RtcError::InvalidDateTime) | 78 | DateTime::from(year, month, day, weekday, hour, minute, second, us).map_err(RtcError::InvalidDateTime) |
| @@ -87,9 +82,9 @@ impl RtcTimeProvider { | |||
| 87 | fn read<R>(&self, mut f: impl FnMut(Dr, Tr, u16) -> Result<R, RtcError>) -> Result<R, RtcError> { | 82 | fn read<R>(&self, mut f: impl FnMut(Dr, Tr, u16) -> Result<R, RtcError>) -> Result<R, RtcError> { |
| 88 | let r = RTC::regs(); | 83 | let r = RTC::regs(); |
| 89 | 84 | ||
| 90 | #[cfg(not(rtc_v2f2))] | 85 | #[cfg(not(rtc_v2_f2))] |
| 91 | let read_ss = || r.ssr().read().ss(); | 86 | let read_ss = || r.ssr().read().ss(); |
| 92 | #[cfg(rtc_v2f2)] | 87 | #[cfg(rtc_v2_f2)] |
| 93 | let read_ss = || 0; | 88 | let read_ss = || 0; |
| 94 | 89 | ||
| 95 | let mut ss = read_ss(); | 90 | let mut ss = read_ss(); |
| @@ -168,7 +163,7 @@ impl Rtc { | |||
| 168 | this.configure(async_psc, sync_psc); | 163 | this.configure(async_psc, sync_psc); |
| 169 | 164 | ||
| 170 | // Wait for the clock to update after initialization | 165 | // Wait for the clock to update after initialization |
| 171 | #[cfg(not(rtc_v2f2))] | 166 | #[cfg(not(rtc_v2_f2))] |
| 172 | { | 167 | { |
| 173 | let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); | 168 | let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); |
| 174 | while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} | 169 | while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} |
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 28380a3c0..23f6ccb0c 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -11,11 +11,11 @@ impl super::Rtc { | |||
| 11 | pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { | 11 | pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { |
| 12 | self.write(true, |rtc| { | 12 | self.write(true, |rtc| { |
| 13 | rtc.cr().modify(|w| { | 13 | rtc.cr().modify(|w| { |
| 14 | #[cfg(not(rtc_v2f2))] | 14 | #[cfg(not(rtc_v2_f2))] |
| 15 | w.set_bypshad(true); | 15 | w.set_bypshad(true); |
| 16 | #[cfg(rtc_v2f2)] | 16 | #[cfg(rtc_v2_f2)] |
| 17 | w.set_fmt(false); | 17 | w.set_fmt(false); |
| 18 | #[cfg(not(rtc_v2f2))] | 18 | #[cfg(not(rtc_v2_f2))] |
| 19 | w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR); | 19 | w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR); |
| 20 | w.set_osel(Osel::DISABLED); | 20 | w.set_osel(Osel::DISABLED); |
| 21 | w.set_pol(Pol::HIGH); | 21 | w.set_pol(Pol::HIGH); |
| @@ -36,7 +36,7 @@ impl super::Rtc { | |||
| 36 | /// | 36 | /// |
| 37 | /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` | 37 | /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` |
| 38 | /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). | 38 | /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). |
| 39 | #[cfg(not(rtc_v2f2))] | 39 | #[cfg(not(rtc_v2_f2))] |
| 40 | pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) { | 40 | pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) { |
| 41 | const RTC_CALR_MIN_PPM: f32 = -487.1; | 41 | const RTC_CALR_MIN_PPM: f32 = -487.1; |
| 42 | const RTC_CALR_MAX_PPM: f32 = 488.5; | 42 | const RTC_CALR_MAX_PPM: f32 = 488.5; |
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 82b5968b0..bb152731c 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | //! Quadrature decoder using a timer. | 1 | //! Quadrature decoder using a timer. |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use stm32_metapac::timer::vals::{self, Sms}; |
| 4 | |||
| 5 | use stm32_metapac::timer::vals; | ||
| 6 | 4 | ||
| 7 | use super::low_level::Timer; | 5 | use super::low_level::Timer; |
| 8 | pub use super::{Ch1, Ch2}; | 6 | pub use super::{Ch1, Ch2}; |
| @@ -11,35 +9,59 @@ use crate::gpio::{AfType, AnyPin, Pull}; | |||
| 11 | use crate::timer::TimerChannel; | 9 | use crate::timer::TimerChannel; |
| 12 | use crate::Peri; | 10 | use crate::Peri; |
| 13 | 11 | ||
| 14 | /// Counting direction | 12 | /// Qei driver config. |
| 15 | pub enum Direction { | 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 16 | /// Counting up. | 14 | #[derive(Clone, Copy)] |
| 17 | Upcounting, | 15 | pub struct Config { |
| 18 | /// Counting down. | 16 | /// Configures the internal pull up/down resistor for Qei's channel 1 pin. |
| 19 | Downcounting, | 17 | pub ch1_pull: Pull, |
| 18 | /// Configures the internal pull up/down resistor for Qei's channel 2 pin. | ||
| 19 | pub ch2_pull: Pull, | ||
| 20 | /// Specifies the encoder mode to use for the Qei peripheral. | ||
| 21 | pub mode: QeiMode, | ||
| 20 | } | 22 | } |
| 21 | 23 | ||
| 22 | /// Wrapper for using a pin with QEI. | 24 | impl Default for Config { |
| 23 | pub struct QeiPin<'d, T, Channel, #[cfg(afio)] A> { | 25 | /// Arbitrary defaults to preserve backwards compatibility |
| 24 | #[allow(unused)] | 26 | fn default() -> Self { |
| 25 | pin: Peri<'d, AnyPin>, | 27 | Self { |
| 26 | phantom: PhantomData<if_afio!((T, Channel, A))>, | 28 | ch1_pull: Pull::None, |
| 29 | ch2_pull: Pull::None, | ||
| 30 | mode: QeiMode::Mode3, | ||
| 31 | } | ||
| 32 | } | ||
| 27 | } | 33 | } |
| 28 | 34 | ||
| 29 | impl<'d, T: GeneralInstance4Channel, C: QeiChannel, #[cfg(afio)] A> if_afio!(QeiPin<'d, T, C, A>) { | 35 | /// See STMicro AN4013 for §2.3 for more information |
| 30 | /// Create a new QEI pin instance. | 36 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 31 | pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>) -> Self { | 37 | #[derive(Clone, Copy)] |
| 32 | critical_section::with(|_| { | 38 | pub enum QeiMode { |
| 33 | pin.set_low(); | 39 | /// Direct alias for [`Sms::ENCODER_MODE_1`] |
| 34 | set_as_af!(pin, AfType::input(Pull::None)); | 40 | Mode1, |
| 35 | }); | 41 | /// Direct alias for [`Sms::ENCODER_MODE_2`] |
| 36 | QeiPin { | 42 | Mode2, |
| 37 | pin: pin.into(), | 43 | /// Direct alias for [`Sms::ENCODER_MODE_3`] |
| 38 | phantom: PhantomData, | 44 | Mode3, |
| 45 | } | ||
| 46 | |||
| 47 | impl From<QeiMode> for Sms { | ||
| 48 | fn from(mode: QeiMode) -> Self { | ||
| 49 | match mode { | ||
| 50 | QeiMode::Mode1 => Sms::ENCODER_MODE_1, | ||
| 51 | QeiMode::Mode2 => Sms::ENCODER_MODE_2, | ||
| 52 | QeiMode::Mode3 => Sms::ENCODER_MODE_3, | ||
| 39 | } | 53 | } |
| 40 | } | 54 | } |
| 41 | } | 55 | } |
| 42 | 56 | ||
| 57 | /// Counting direction | ||
| 58 | pub enum Direction { | ||
| 59 | /// Counting up. | ||
| 60 | Upcounting, | ||
| 61 | /// Counting down. | ||
| 62 | Downcounting, | ||
| 63 | } | ||
| 64 | |||
| 43 | trait SealedQeiChannel: TimerChannel {} | 65 | trait SealedQeiChannel: TimerChannel {} |
| 44 | 66 | ||
| 45 | /// Marker trait for a timer channel eligible for use with QEI. | 67 | /// Marker trait for a timer channel eligible for use with QEI. |
| @@ -55,20 +77,28 @@ impl SealedQeiChannel for Ch2 {} | |||
| 55 | /// Quadrature decoder driver. | 77 | /// Quadrature decoder driver. |
| 56 | pub struct Qei<'d, T: GeneralInstance4Channel> { | 78 | pub struct Qei<'d, T: GeneralInstance4Channel> { |
| 57 | inner: Timer<'d, T>, | 79 | inner: Timer<'d, T>, |
| 80 | _ch1: Peri<'d, AnyPin>, | ||
| 81 | _ch2: Peri<'d, AnyPin>, | ||
| 58 | } | 82 | } |
| 59 | 83 | ||
| 60 | impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { | 84 | impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { |
| 61 | /// Create a new quadrature decoder driver. | 85 | /// Create a new quadrature decoder driver, with a given [`Config`]. |
| 62 | #[allow(unused)] | 86 | #[allow(unused)] |
| 63 | pub fn new<#[cfg(afio)] A>( | 87 | pub fn new<CH1: QeiChannel, CH2: QeiChannel, #[cfg(afio)] A>( |
| 64 | tim: Peri<'d, T>, | 88 | tim: Peri<'d, T>, |
| 65 | ch1: if_afio!(QeiPin<'d, T, Ch1, A>), | 89 | ch1: Peri<'d, if_afio!(impl TimerPin<T, CH1, A>)>, |
| 66 | ch2: if_afio!(QeiPin<'d, T, Ch2, A>), | 90 | ch2: Peri<'d, if_afio!(impl TimerPin<T, CH2, A>)>, |
| 91 | config: Config, | ||
| 67 | ) -> Self { | 92 | ) -> Self { |
| 68 | Self::new_inner(tim) | 93 | // Configure the pins to be used for the QEI peripheral. |
| 69 | } | 94 | critical_section::with(|_| { |
| 95 | ch1.set_low(); | ||
| 96 | set_as_af!(ch1, AfType::input(config.ch1_pull)); | ||
| 97 | |||
| 98 | ch2.set_low(); | ||
| 99 | set_as_af!(ch2, AfType::input(config.ch2_pull)); | ||
| 100 | }); | ||
| 70 | 101 | ||
| 71 | fn new_inner(tim: Peri<'d, T>) -> Self { | ||
| 72 | let inner = Timer::new(tim); | 102 | let inner = Timer::new(tim); |
| 73 | let r = inner.regs_gp16(); | 103 | let r = inner.regs_gp16(); |
| 74 | 104 | ||
| @@ -88,13 +118,17 @@ impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { | |||
| 88 | }); | 118 | }); |
| 89 | 119 | ||
| 90 | r.smcr().modify(|w| { | 120 | r.smcr().modify(|w| { |
| 91 | w.set_sms(vals::Sms::ENCODER_MODE_3); | 121 | w.set_sms(config.mode.into()); |
| 92 | }); | 122 | }); |
| 93 | 123 | ||
| 94 | r.arr().modify(|w| w.set_arr(u16::MAX)); | 124 | r.arr().modify(|w| w.set_arr(u16::MAX)); |
| 95 | r.cr1().modify(|w| w.set_cen(true)); | 125 | r.cr1().modify(|w| w.set_cen(true)); |
| 96 | 126 | ||
| 97 | Self { inner } | 127 | Self { |
| 128 | inner, | ||
| 129 | _ch1: ch1.into(), | ||
| 130 | _ch2: ch2.into(), | ||
| 131 | } | ||
| 98 | } | 132 | } |
| 99 | 133 | ||
| 100 | /// Get direction. | 134 | /// Get direction. |
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index c734eed49..10dc02334 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::slice; | 3 | use core::slice; |
| 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}; |
| 5 | use core::task::Poll; | 5 | use core::task::Poll; |
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| @@ -68,8 +68,15 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { | |||
| 68 | // FIXME: Should we disable any further RX interrupts when the buffer becomes full. | 68 | // FIXME: Should we disable any further RX interrupts when the buffer becomes full. |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | if !state.rx_buf.is_empty() { | 71 | let eager = state.eager_reads.load(Ordering::Relaxed); |
| 72 | state.rx_waker.wake(); | 72 | if eager > 0 { |
| 73 | if state.rx_buf.available() >= eager { | ||
| 74 | state.rx_waker.wake(); | ||
| 75 | } | ||
| 76 | } else { | ||
| 77 | if state.rx_buf.is_half_full() { | ||
| 78 | state.rx_waker.wake(); | ||
| 79 | } | ||
| 73 | } | 80 | } |
| 74 | } | 81 | } |
| 75 | 82 | ||
| @@ -132,6 +139,7 @@ pub(super) struct State { | |||
| 132 | tx_done: AtomicBool, | 139 | tx_done: AtomicBool, |
| 133 | tx_rx_refcount: AtomicU8, | 140 | tx_rx_refcount: AtomicU8, |
| 134 | half_duplex_readback: AtomicBool, | 141 | half_duplex_readback: AtomicBool, |
| 142 | eager_reads: AtomicUsize, | ||
| 135 | } | 143 | } |
| 136 | 144 | ||
| 137 | impl State { | 145 | impl State { |
| @@ -144,6 +152,7 @@ impl State { | |||
| 144 | tx_done: AtomicBool::new(true), | 152 | tx_done: AtomicBool::new(true), |
| 145 | tx_rx_refcount: AtomicU8::new(0), | 153 | tx_rx_refcount: AtomicU8::new(0), |
| 146 | half_duplex_readback: AtomicBool::new(false), | 154 | half_duplex_readback: AtomicBool::new(false), |
| 155 | eager_reads: AtomicUsize::new(0), | ||
| 147 | } | 156 | } |
| 148 | } | 157 | } |
| 149 | } | 158 | } |
| @@ -419,6 +428,9 @@ impl<'d> BufferedUart<'d> { | |||
| 419 | let state = T::buffered_state(); | 428 | let state = T::buffered_state(); |
| 420 | let kernel_clock = T::frequency(); | 429 | let kernel_clock = T::frequency(); |
| 421 | 430 | ||
| 431 | state | ||
| 432 | .eager_reads | ||
| 433 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 422 | state.half_duplex_readback.store( | 434 | state.half_duplex_readback.store( |
| 423 | config.duplex == Duplex::Half(HalfDuplexReadback::Readback), | 435 | config.duplex == Duplex::Half(HalfDuplexReadback::Readback), |
| 424 | Ordering::Relaxed, | 436 | Ordering::Relaxed, |
| @@ -456,6 +468,9 @@ impl<'d> BufferedUart<'d> { | |||
| 456 | let info = self.rx.info; | 468 | let info = self.rx.info; |
| 457 | let state = self.rx.state; | 469 | let state = self.rx.state; |
| 458 | state.tx_rx_refcount.store(2, Ordering::Relaxed); | 470 | state.tx_rx_refcount.store(2, Ordering::Relaxed); |
| 471 | state | ||
| 472 | .eager_reads | ||
| 473 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 459 | 474 | ||
| 460 | info.rcc.enable_and_reset(); | 475 | info.rcc.enable_and_reset(); |
| 461 | 476 | ||
| @@ -527,6 +542,11 @@ impl<'d> BufferedUart<'d> { | |||
| 527 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 542 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 528 | reconfigure(self.rx.info, self.rx.kernel_clock, config)?; | 543 | reconfigure(self.rx.info, self.rx.kernel_clock, config)?; |
| 529 | 544 | ||
| 545 | self.rx | ||
| 546 | .state | ||
| 547 | .eager_reads | ||
| 548 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 549 | |||
| 530 | self.rx.info.regs.cr1().modify(|w| { | 550 | self.rx.info.regs.cr1().modify(|w| { |
| 531 | w.set_rxneie(true); | 551 | w.set_rxneie(true); |
| 532 | w.set_idleie(true); | 552 | w.set_idleie(true); |
| @@ -553,24 +573,30 @@ impl<'d> BufferedUartRx<'d> { | |||
| 553 | poll_fn(move |cx| { | 573 | poll_fn(move |cx| { |
| 554 | let state = self.state; | 574 | let state = self.state; |
| 555 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | 575 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| 556 | let data = rx_reader.pop_slice(); | 576 | let mut buf_len = 0; |
| 577 | let mut data = rx_reader.pop_slice(); | ||
| 557 | 578 | ||
| 558 | if !data.is_empty() { | 579 | while !data.is_empty() && buf_len < buf.len() { |
| 559 | let len = data.len().min(buf.len()); | 580 | let data_len = data.len().min(buf.len() - buf_len); |
| 560 | buf[..len].copy_from_slice(&data[..len]); | 581 | buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]); |
| 582 | buf_len += data_len; | ||
| 561 | 583 | ||
| 562 | let do_pend = state.rx_buf.is_full(); | 584 | let do_pend = state.rx_buf.is_full(); |
| 563 | rx_reader.pop_done(len); | 585 | rx_reader.pop_done(data_len); |
| 564 | 586 | ||
| 565 | if do_pend { | 587 | if do_pend { |
| 566 | self.info.interrupt.pend(); | 588 | self.info.interrupt.pend(); |
| 567 | } | 589 | } |
| 568 | 590 | ||
| 569 | return Poll::Ready(Ok(len)); | 591 | data = rx_reader.pop_slice(); |
| 570 | } | 592 | } |
| 571 | 593 | ||
| 572 | state.rx_waker.register(cx.waker()); | 594 | if buf_len != 0 { |
| 573 | Poll::Pending | 595 | Poll::Ready(Ok(buf_len)) |
| 596 | } else { | ||
| 597 | state.rx_waker.register(cx.waker()); | ||
| 598 | Poll::Pending | ||
| 599 | } | ||
| 574 | }) | 600 | }) |
| 575 | .await | 601 | .await |
| 576 | } | 602 | } |
| @@ -579,21 +605,24 @@ impl<'d> BufferedUartRx<'d> { | |||
| 579 | loop { | 605 | loop { |
| 580 | let state = self.state; | 606 | let state = self.state; |
| 581 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | 607 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| 582 | let data = rx_reader.pop_slice(); | 608 | let mut buf_len = 0; |
| 609 | let mut data = rx_reader.pop_slice(); | ||
| 583 | 610 | ||
| 584 | if !data.is_empty() { | 611 | while !data.is_empty() && buf_len < buf.len() { |
| 585 | let len = data.len().min(buf.len()); | 612 | let data_len = data.len().min(buf.len() - buf_len); |
| 586 | buf[..len].copy_from_slice(&data[..len]); | 613 | buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]); |
| 614 | buf_len += data_len; | ||
| 587 | 615 | ||
| 588 | let do_pend = state.rx_buf.is_full(); | 616 | let do_pend = state.rx_buf.is_full(); |
| 589 | rx_reader.pop_done(len); | 617 | rx_reader.pop_done(data_len); |
| 590 | 618 | ||
| 591 | if do_pend { | 619 | if do_pend { |
| 592 | self.info.interrupt.pend(); | 620 | self.info.interrupt.pend(); |
| 593 | } | 621 | } |
| 594 | 622 | ||
| 595 | return Ok(len); | 623 | data = rx_reader.pop_slice(); |
| 596 | } | 624 | } |
| 625 | return Ok(buf_len); | ||
| 597 | } | 626 | } |
| 598 | } | 627 | } |
| 599 | 628 | ||
| @@ -633,6 +662,10 @@ impl<'d> BufferedUartRx<'d> { | |||
| 633 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 662 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 634 | reconfigure(self.info, self.kernel_clock, config)?; | 663 | reconfigure(self.info, self.kernel_clock, config)?; |
| 635 | 664 | ||
| 665 | self.state | ||
| 666 | .eager_reads | ||
| 667 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 668 | |||
| 636 | self.info.regs.cr1().modify(|w| { | 669 | self.info.regs.cr1().modify(|w| { |
| 637 | w.set_rxneie(true); | 670 | w.set_rxneie(true); |
| 638 | w.set_idleie(true); | 671 | w.set_idleie(true); |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ff211e0c9..0d2d86aca 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; | 7 | use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; |
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| @@ -185,6 +185,12 @@ pub enum ConfigError { | |||
| 185 | RxOrTxNotEnabled, | 185 | RxOrTxNotEnabled, |
| 186 | /// Data bits and parity combination not supported | 186 | /// Data bits and parity combination not supported |
| 187 | DataParityNotSupported, | 187 | DataParityNotSupported, |
| 188 | /// DE assertion time too high | ||
| 189 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 190 | DeAssertionTimeTooHigh, | ||
| 191 | /// DE deassertion time too high | ||
| 192 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 193 | DeDeassertionTimeTooHigh, | ||
| 188 | } | 194 | } |
| 189 | 195 | ||
| 190 | #[non_exhaustive] | 196 | #[non_exhaustive] |
| @@ -206,6 +212,21 @@ pub struct Config { | |||
| 206 | /// If false: the error is ignored and cleared | 212 | /// If false: the error is ignored and cleared |
| 207 | pub detect_previous_overrun: bool, | 213 | pub detect_previous_overrun: bool, |
| 208 | 214 | ||
| 215 | /// If `None` (the default) then read-like calls on `BufferedUartRx` and `RingBufferedUartRx` | ||
| 216 | /// typically only wake/return after line idle or after the buffer is at least half full | ||
| 217 | /// (for `BufferedUartRx`) or the DMA buffer is written at the half or full positions | ||
| 218 | /// (for `RingBufferedUartRx`), though it may also wake/return earlier in some circumstances. | ||
| 219 | /// | ||
| 220 | /// If `Some(n)` then such reads are also woken/return as soon as at least `n` words are | ||
| 221 | /// available in the buffer, in addition to waking/returning when the conditions described | ||
| 222 | /// above are met. `Some(0)` is treated as `None`. Setting this for `RingBufferedUartRx` | ||
| 223 | /// will trigger an interrupt for every received word to check the buffer level, which may | ||
| 224 | /// impact performance at high data rates. | ||
| 225 | /// | ||
| 226 | /// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either | ||
| 227 | /// return a single word, a full buffer, or after line idle. | ||
| 228 | pub eager_reads: Option<usize>, | ||
| 229 | |||
| 209 | /// Set this to true if the line is considered noise free. | 230 | /// Set this to true if the line is considered noise free. |
| 210 | /// This will increase the receiver’s tolerance to clock deviations, | 231 | /// This will increase the receiver’s tolerance to clock deviations, |
| 211 | /// but will effectively disable noise detection. | 232 | /// but will effectively disable noise detection. |
| @@ -239,6 +260,14 @@ pub struct Config { | |||
| 239 | /// Set the pin configuration for the DE pin. | 260 | /// Set the pin configuration for the DE pin. |
| 240 | pub de_config: OutputConfig, | 261 | pub de_config: OutputConfig, |
| 241 | 262 | ||
| 263 | /// Set DE assertion time before the first start bit, 0-31 16ths of a bit period. | ||
| 264 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 265 | pub de_assertion_time: u8, | ||
| 266 | |||
| 267 | /// Set DE deassertion time after the last stop bit, 0-31 16ths of a bit period. | ||
| 268 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 269 | pub de_deassertion_time: u8, | ||
| 270 | |||
| 242 | // private: set by new_half_duplex, not by the user. | 271 | // private: set by new_half_duplex, not by the user. |
| 243 | duplex: Duplex, | 272 | duplex: Duplex, |
| 244 | } | 273 | } |
| @@ -270,6 +299,7 @@ impl Default for Config { | |||
| 270 | parity: Parity::ParityNone, | 299 | parity: Parity::ParityNone, |
| 271 | // historical behavior | 300 | // historical behavior |
| 272 | detect_previous_overrun: false, | 301 | detect_previous_overrun: false, |
| 302 | eager_reads: None, | ||
| 273 | #[cfg(not(usart_v1))] | 303 | #[cfg(not(usart_v1))] |
| 274 | assume_noise_free: false, | 304 | assume_noise_free: false, |
| 275 | #[cfg(any(usart_v3, usart_v4))] | 305 | #[cfg(any(usart_v3, usart_v4))] |
| @@ -283,6 +313,10 @@ impl Default for Config { | |||
| 283 | tx_config: OutputConfig::PushPull, | 313 | tx_config: OutputConfig::PushPull, |
| 284 | rts_config: OutputConfig::PushPull, | 314 | rts_config: OutputConfig::PushPull, |
| 285 | de_config: OutputConfig::PushPull, | 315 | de_config: OutputConfig::PushPull, |
| 316 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 317 | de_assertion_time: 0, | ||
| 318 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 319 | de_deassertion_time: 0, | ||
| 286 | duplex: Duplex::Full, | 320 | duplex: Duplex::Full, |
| 287 | } | 321 | } |
| 288 | } | 322 | } |
| @@ -966,6 +1000,9 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 966 | let info = self.info; | 1000 | let info = self.info; |
| 967 | let state = self.state; | 1001 | let state = self.state; |
| 968 | state.tx_rx_refcount.store(1, Ordering::Relaxed); | 1002 | state.tx_rx_refcount.store(1, Ordering::Relaxed); |
| 1003 | state | ||
| 1004 | .eager_reads | ||
| 1005 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 969 | 1006 | ||
| 970 | info.rcc.enable_and_reset(); | 1007 | info.rcc.enable_and_reset(); |
| 971 | 1008 | ||
| @@ -982,6 +1019,9 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 982 | 1019 | ||
| 983 | /// Reconfigure the driver | 1020 | /// Reconfigure the driver |
| 984 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 1021 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 1022 | self.state | ||
| 1023 | .eager_reads | ||
| 1024 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 985 | reconfigure(self.info, self.kernel_clock, config) | 1025 | reconfigure(self.info, self.kernel_clock, config) |
| 986 | } | 1026 | } |
| 987 | 1027 | ||
| @@ -1462,6 +1502,9 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1462 | let info = self.rx.info; | 1502 | let info = self.rx.info; |
| 1463 | let state = self.rx.state; | 1503 | let state = self.rx.state; |
| 1464 | state.tx_rx_refcount.store(2, Ordering::Relaxed); | 1504 | state.tx_rx_refcount.store(2, Ordering::Relaxed); |
| 1505 | state | ||
| 1506 | .eager_reads | ||
| 1507 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 1465 | 1508 | ||
| 1466 | info.rcc.enable_and_reset(); | 1509 | info.rcc.enable_and_reset(); |
| 1467 | 1510 | ||
| @@ -1690,6 +1733,16 @@ fn configure( | |||
| 1690 | return Err(ConfigError::RxOrTxNotEnabled); | 1733 | return Err(ConfigError::RxOrTxNotEnabled); |
| 1691 | } | 1734 | } |
| 1692 | 1735 | ||
| 1736 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1737 | let dem = r.cr3().read().dem(); | ||
| 1738 | |||
| 1739 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1740 | if config.de_assertion_time > 31 { | ||
| 1741 | return Err(ConfigError::DeAssertionTimeTooHigh); | ||
| 1742 | } else if config.de_deassertion_time > 31 { | ||
| 1743 | return Err(ConfigError::DeDeassertionTimeTooHigh); | ||
| 1744 | } | ||
| 1745 | |||
| 1693 | // UART must be disabled during configuration. | 1746 | // UART must be disabled during configuration. |
| 1694 | r.cr1().modify(|w| { | 1747 | r.cr1().modify(|w| { |
| 1695 | w.set_ue(false); | 1748 | w.set_ue(false); |
| @@ -1738,6 +1791,20 @@ fn configure( | |||
| 1738 | w.set_re(enable_rx); | 1791 | w.set_re(enable_rx); |
| 1739 | } | 1792 | } |
| 1740 | 1793 | ||
| 1794 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 1795 | if dem { | ||
| 1796 | w.set_deat(if over8 { | ||
| 1797 | config.de_assertion_time / 2 | ||
| 1798 | } else { | ||
| 1799 | config.de_assertion_time | ||
| 1800 | }); | ||
| 1801 | w.set_dedt(if over8 { | ||
| 1802 | config.de_assertion_time / 2 | ||
| 1803 | } else { | ||
| 1804 | config.de_assertion_time | ||
| 1805 | }); | ||
| 1806 | } | ||
| 1807 | |||
| 1741 | // configure word size and parity, since the parity bit is inserted into the MSB position, | 1808 | // configure word size and parity, since the parity bit is inserted into the MSB position, |
| 1742 | // it increases the effective word size | 1809 | // it increases the effective word size |
| 1743 | match (config.parity, config.data_bits) { | 1810 | match (config.parity, config.data_bits) { |
| @@ -2022,6 +2089,7 @@ struct State { | |||
| 2022 | rx_waker: AtomicWaker, | 2089 | rx_waker: AtomicWaker, |
| 2023 | tx_waker: AtomicWaker, | 2090 | tx_waker: AtomicWaker, |
| 2024 | tx_rx_refcount: AtomicU8, | 2091 | tx_rx_refcount: AtomicU8, |
| 2092 | eager_reads: AtomicUsize, | ||
| 2025 | } | 2093 | } |
| 2026 | 2094 | ||
| 2027 | impl State { | 2095 | impl State { |
| @@ -2030,6 +2098,7 @@ impl State { | |||
| 2030 | rx_waker: AtomicWaker::new(), | 2098 | rx_waker: AtomicWaker::new(), |
| 2031 | tx_waker: AtomicWaker::new(), | 2099 | tx_waker: AtomicWaker::new(), |
| 2032 | tx_rx_refcount: AtomicU8::new(0), | 2100 | tx_rx_refcount: AtomicU8::new(0), |
| 2101 | eager_reads: AtomicUsize::new(0), | ||
| 2033 | } | 2102 | } |
| 2034 | } | 2103 | } |
| 2035 | } | 2104 | } |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 5f4e87834..27071fb31 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -26,9 +26,9 @@ use crate::Peri; | |||
| 26 | /// contain enough bytes to fill the buffer passed by the caller of | 26 | /// contain enough bytes to fill the buffer passed by the caller of |
| 27 | /// the function, or is empty. | 27 | /// the function, or is empty. |
| 28 | /// | 28 | /// |
| 29 | /// Waiting for bytes operates in one of two modes, depending on | 29 | /// Waiting for bytes operates in one of three modes, depending on |
| 30 | /// the behavior of the sender and the size of the buffer passed | 30 | /// the behavior of the sender, the size of the buffer passed |
| 31 | /// to the function: | 31 | /// to the function, and the configuration: |
| 32 | /// | 32 | /// |
| 33 | /// - If the sender sends intermittently, the 'idle line' | 33 | /// - If the sender sends intermittently, the 'idle line' |
| 34 | /// condition will be detected when the sender stops, and any | 34 | /// condition will be detected when the sender stops, and any |
| @@ -47,7 +47,11 @@ use crate::Peri; | |||
| 47 | /// interrupt when those specific buffer addresses have been | 47 | /// interrupt when those specific buffer addresses have been |
| 48 | /// written. | 48 | /// written. |
| 49 | /// | 49 | /// |
| 50 | /// In both cases this will result in variable latency due to the | 50 | /// - If `eager_reads` is enabled in `config`, the UART interrupt |
| 51 | /// is enabled on all data reception and the call will only wait | ||
| 52 | /// for at least one byte to be available before returning. | ||
| 53 | /// | ||
| 54 | /// In the first two cases this will result in variable latency due to the | ||
| 51 | /// buffering effect. For example, if the baudrate is 2400 bps, and | 55 | /// buffering effect. For example, if the baudrate is 2400 bps, and |
| 52 | /// the configuration is 8 data bits, no parity bit, and one stop bit, | 56 | /// the configuration is 8 data bits, no parity bit, and one stop bit, |
| 53 | /// then a byte will be received every ~4.16ms. If the ring buffer is | 57 | /// then a byte will be received every ~4.16ms. If the ring buffer is |
| @@ -68,15 +72,10 @@ use crate::Peri; | |||
| 68 | /// sending, but would be falsely triggered in the worst-case | 72 | /// sending, but would be falsely triggered in the worst-case |
| 69 | /// buffer delay scenario. | 73 | /// buffer delay scenario. |
| 70 | /// | 74 | /// |
| 71 | /// Note: This latency is caused by the limited capabilities of the | 75 | /// Note: Enabling `eager_reads` with `RingBufferedUartRx` will enable |
| 72 | /// STM32 DMA controller; since it cannot generate an interrupt when | 76 | /// an UART RXNE interrupt, which will cause an interrupt to occur on |
| 73 | /// it stores a byte into an empty ring buffer, or in any other | 77 | /// every received data byte. The data is still copied using DMA, but |
| 74 | /// configurable conditions, it is not possible to take notice of the | 78 | /// there is nevertheless additional processing overhead for each byte. |
| 75 | /// contents of the ring buffer more quickly without introducing | ||
| 76 | /// polling. As a result the latency can be reduced by calling the | ||
| 77 | /// read functions repeatedly with smaller buffers to receive the | ||
| 78 | /// available bytes, as each call to a read function will explicitly | ||
| 79 | /// check the ring buffer for available bytes. | ||
| 80 | pub struct RingBufferedUartRx<'d> { | 79 | pub struct RingBufferedUartRx<'d> { |
| 81 | info: &'static Info, | 80 | info: &'static Info, |
| 82 | state: &'static State, | 81 | state: &'static State, |
| @@ -133,6 +132,9 @@ impl<'d> UartRx<'d, Async> { | |||
| 133 | impl<'d> RingBufferedUartRx<'d> { | 132 | impl<'d> RingBufferedUartRx<'d> { |
| 134 | /// Reconfigure the driver | 133 | /// Reconfigure the driver |
| 135 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 134 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 135 | self.state | ||
| 136 | .eager_reads | ||
| 137 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 136 | reconfigure(self.info, self.kernel_clock, config) | 138 | reconfigure(self.info, self.kernel_clock, config) |
| 137 | } | 139 | } |
| 138 | 140 | ||
| @@ -148,8 +150,8 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 148 | let r = self.info.regs; | 150 | let r = self.info.regs; |
| 149 | // clear all interrupts and DMA Rx Request | 151 | // clear all interrupts and DMA Rx Request |
| 150 | r.cr1().modify(|w| { | 152 | r.cr1().modify(|w| { |
| 151 | // disable RXNE interrupt | 153 | // use RXNE only when returning reads early |
| 152 | w.set_rxneie(false); | 154 | w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed) > 0); |
| 153 | // enable parity interrupt if not ParityNone | 155 | // enable parity interrupt if not ParityNone |
| 154 | w.set_peie(w.pce()); | 156 | w.set_peie(w.pce()); |
| 155 | // enable idle line interrupt | 157 | // enable idle line interrupt |
| @@ -248,39 +250,67 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 248 | async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { | 250 | async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { |
| 249 | compiler_fence(Ordering::SeqCst); | 251 | compiler_fence(Ordering::SeqCst); |
| 250 | 252 | ||
| 251 | // Future which completes when idle line is detected | 253 | loop { |
| 252 | let s = self.state; | 254 | // Future which completes when idle line is detected |
| 253 | let uart = poll_fn(|cx| { | 255 | let s = self.state; |
| 254 | s.rx_waker.register(cx.waker()); | 256 | let mut uart_init = false; |
| 255 | 257 | let uart = poll_fn(|cx| { | |
| 256 | compiler_fence(Ordering::SeqCst); | 258 | s.rx_waker.register(cx.waker()); |
| 257 | 259 | ||
| 258 | if check_idle_and_errors(self.info.regs)? { | 260 | compiler_fence(Ordering::SeqCst); |
| 259 | // Idle line is detected | 261 | |
| 260 | Poll::Ready(Ok(())) | 262 | // We may have been woken by IDLE or, if eager_reads is set, by RXNE. |
| 261 | } else { | 263 | // However, DMA will clear RXNE, so we can't check directly, and because |
| 262 | Poll::Pending | 264 | // the other future borrows `ring_buf`, we can't check `len()` here either. |
| 263 | } | 265 | // Instead, return from this future and we'll check the length afterwards. |
| 264 | }); | 266 | let eager = s.eager_reads.load(Ordering::Relaxed) > 0; |
| 267 | |||
| 268 | let idle = check_idle_and_errors(self.info.regs)?; | ||
| 269 | if idle || (eager && uart_init) { | ||
| 270 | // Idle line is detected, or eager reads is set and some data is available. | ||
| 271 | Poll::Ready(Ok(idle)) | ||
| 272 | } else { | ||
| 273 | uart_init = true; | ||
| 274 | Poll::Pending | ||
| 275 | } | ||
| 276 | }); | ||
| 265 | 277 | ||
| 266 | let mut dma_init = false; | 278 | let mut dma_init = false; |
| 267 | // Future which completes when the DMA controller indicates it | 279 | // Future which completes when the DMA controller indicates it |
| 268 | // has written to the ring buffer's middle byte, or last byte | 280 | // has written to the ring buffer's middle byte, or last byte |
| 269 | let dma = poll_fn(|cx| { | 281 | let dma = poll_fn(|cx| { |
| 270 | self.ring_buf.set_waker(cx.waker()); | 282 | self.ring_buf.set_waker(cx.waker()); |
| 271 | 283 | ||
| 272 | let status = match dma_init { | 284 | let status = match dma_init { |
| 273 | false => Poll::Pending, | 285 | false => Poll::Pending, |
| 274 | true => Poll::Ready(()), | 286 | true => Poll::Ready(()), |
| 275 | }; | 287 | }; |
| 276 | 288 | ||
| 277 | dma_init = true; | 289 | dma_init = true; |
| 278 | status | 290 | status |
| 279 | }); | 291 | }); |
| 280 | 292 | ||
| 281 | match select(uart, dma).await { | 293 | match select(uart, dma).await { |
| 282 | Either::Left((result, _)) => result, | 294 | // UART woke with line idle |
| 283 | Either::Right(((), _)) => Ok(()), | 295 | Either::Left((Ok(true), _)) => { |
| 296 | return Ok(()); | ||
| 297 | } | ||
| 298 | // UART woke without idle or error: word received | ||
| 299 | Either::Left((Ok(false), _)) => { | ||
| 300 | let eager = self.state.eager_reads.load(Ordering::Relaxed); | ||
| 301 | if eager > 0 && self.ring_buf.len().unwrap_or(0) >= eager { | ||
| 302 | return Ok(()); | ||
| 303 | } else { | ||
| 304 | continue; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | // UART woke with error | ||
| 308 | Either::Left((Err(e), _)) => { | ||
| 309 | return Err(e); | ||
| 310 | } | ||
| 311 | // DMA woke | ||
| 312 | Either::Right(((), _)) => return Ok(()), | ||
| 313 | } | ||
| 284 | } | 314 | } |
| 285 | } | 315 | } |
| 286 | 316 | ||
