diff options
| -rw-r--r-- | embassy-stm32/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/word.rs | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/fmt.rs | 10 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/complementary_pwm.rs | 41 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/input_capture.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/low_level.rs | 209 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/mod.rs | 30 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/one_pulse.rs | 12 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/pwm_input.rs | 14 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/ringbuffered.rs | 29 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 62 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/pwm_complementary.rs | 2 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/ws2812_pwm.rs | 2 |
13 files changed, 210 insertions, 208 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 867f83065..70c46b025 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | ## Unreleased - ReleaseDate | 8 | ## Unreleased - ReleaseDate |
| 9 | 9 | ||
| 10 | - change: stm32: use typelevel timer type to allow dma for 32 bit timers | ||
| 10 | - fix: fix incorrect handling of split interrupts in timer driver | 11 | - fix: fix incorrect handling of split interrupts in timer driver |
| 11 | - feat: allow granular stop for regular usart | 12 | - feat: allow granular stop for regular usart |
| 12 | - feat: Add continuous waveform method to SimplePWM | 13 | - feat: Add continuous waveform method to SimplePWM |
diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs index fb1bde860..5c3bb8f7f 100644 --- a/embassy-stm32/src/dma/word.rs +++ b/embassy-stm32/src/dma/word.rs | |||
| @@ -31,6 +31,10 @@ pub trait Word: SealedWord + Default + Copy + 'static { | |||
| 31 | fn size() -> WordSize; | 31 | fn size() -> WordSize; |
| 32 | /// Amount of bits of this word size. | 32 | /// Amount of bits of this word size. |
| 33 | fn bits() -> usize; | 33 | fn bits() -> usize; |
| 34 | /// Maximum value of this type. | ||
| 35 | fn max() -> usize { | ||
| 36 | (1 << Self::bits()) - 1 | ||
| 37 | } | ||
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | macro_rules! impl_word { | 40 | macro_rules! impl_word { |
diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs index b6ae24ee8..b731796f0 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs | |||
| @@ -207,6 +207,16 @@ macro_rules! error { | |||
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | #[cfg(feature = "defmt")] | 209 | #[cfg(feature = "defmt")] |
| 210 | trait_set::trait_set! { | ||
| 211 | pub trait Debuggable = Debug + defmt::Format; | ||
| 212 | } | ||
| 213 | |||
| 214 | #[cfg(not(feature = "defmt"))] | ||
| 215 | trait_set::trait_set! { | ||
| 216 | pub trait Debuggable = Debug; | ||
| 217 | } | ||
| 218 | |||
| 219 | #[cfg(feature = "defmt")] | ||
| 210 | #[collapse_debuginfo(yes)] | 220 | #[collapse_debuginfo(yes)] |
| 211 | macro_rules! unwrap { | 221 | macro_rules! unwrap { |
| 212 | ($($x:tt)*) => { | 222 | ($($x:tt)*) => { |
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 77f19a37b..6ca13820a 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -8,6 +8,7 @@ use super::low_level::{CountingMode, OutputPolarity, Timer}; | |||
| 8 | use super::simple_pwm::PwmPin; | 8 | use super::simple_pwm::PwmPin; |
| 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; | 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; |
| 10 | use crate::Peri; | 10 | use crate::Peri; |
| 11 | use crate::dma::word::Word; | ||
| 11 | use crate::gpio::{AnyPin, OutputType}; | 12 | use crate::gpio::{AnyPin, OutputType}; |
| 12 | use crate::time::Hertz; | 13 | use crate::time::Hertz; |
| 13 | use crate::timer::TimerChannel; | 14 | use crate::timer::TimerChannel; |
| @@ -176,20 +177,20 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 176 | /// Get max duty value. | 177 | /// Get max duty value. |
| 177 | /// | 178 | /// |
| 178 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 179 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 179 | pub fn get_max_duty(&self) -> u16 { | 180 | pub fn get_max_duty(&self) -> u32 { |
| 180 | if self.inner.get_counting_mode().is_center_aligned() { | 181 | if self.inner.get_counting_mode().is_center_aligned() { |
| 181 | self.inner.get_max_compare_value() as u16 | 182 | self.inner.get_max_compare_value().into() |
| 182 | } else { | 183 | } else { |
| 183 | self.inner.get_max_compare_value() as u16 + 1 | 184 | self.inner.get_max_compare_value().into() + 1 |
| 184 | } | 185 | } |
| 185 | } | 186 | } |
| 186 | 187 | ||
| 187 | /// Set the duty for a given channel. | 188 | /// Set the duty for a given channel. |
| 188 | /// | 189 | /// |
| 189 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | 190 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. |
| 190 | pub fn set_duty(&mut self, channel: Channel, duty: u16) { | 191 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { |
| 191 | assert!(duty <= self.get_max_duty()); | 192 | assert!(duty <= self.get_max_duty()); |
| 192 | self.inner.set_compare_value(channel, duty as _) | 193 | self.inner.set_compare_value(channel, unwrap!(duty.try_into())) |
| 193 | } | 194 | } |
| 194 | 195 | ||
| 195 | /// Set the output polarity for a given channel. | 196 | /// Set the output polarity for a given channel. |
| @@ -220,8 +221,14 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 220 | /// | 221 | /// |
| 221 | /// Note: | 222 | /// Note: |
| 222 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 223 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. |
| 223 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 224 | pub async fn waveform_up<W: Word + Into<T::Word>>( |
| 225 | &mut self, | ||
| 226 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 227 | channel: Channel, | ||
| 228 | duty: &[W], | ||
| 229 | ) { | ||
| 224 | self.inner.enable_channel(channel, true); | 230 | self.inner.enable_channel(channel, true); |
| 231 | self.inner.clamp_compare_value::<W>(channel); | ||
| 225 | self.inner.enable_update_dma(true); | 232 | self.inner.enable_update_dma(true); |
| 226 | self.inner.setup_update_dma(dma, channel, duty).await; | 233 | self.inner.setup_update_dma(dma, channel, duty).await; |
| 227 | self.inner.enable_update_dma(false); | 234 | self.inner.enable_update_dma(false); |
| @@ -256,13 +263,21 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 256 | /// Also be aware that embassy timers use one of timers internally. It is possible to | 263 | /// 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. | 264 | /// switch this timer by using `time-driver-timX` feature. |
| 258 | /// | 265 | /// |
| 259 | pub async fn waveform_up_multi_channel( | 266 | pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>( |
| 260 | &mut self, | 267 | &mut self, |
| 261 | dma: Peri<'_, impl super::UpDma<T>>, | 268 | dma: Peri<'_, impl super::UpDma<T>>, |
| 262 | starting_channel: Channel, | 269 | starting_channel: Channel, |
| 263 | ending_channel: Channel, | 270 | ending_channel: Channel, |
| 264 | duty: &[u16], | 271 | duty: &[W], |
| 265 | ) { | 272 | ) { |
| 273 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | ||
| 274 | .iter() | ||
| 275 | .filter(|ch| ch.index() >= starting_channel.index()) | ||
| 276 | .filter(|ch| ch.index() <= ending_channel.index()) | ||
| 277 | .for_each(|ch| { | ||
| 278 | self.inner.enable_channel(*ch, true); | ||
| 279 | self.inner.clamp_compare_value::<W>(*ch); | ||
| 280 | }); | ||
| 266 | self.inner.enable_update_dma(true); | 281 | self.inner.enable_update_dma(true); |
| 267 | self.inner | 282 | self.inner |
| 268 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) | 283 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) |
| @@ -291,20 +306,20 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< | |||
| 291 | } | 306 | } |
| 292 | 307 | ||
| 293 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { | 308 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { |
| 294 | self.inner.get_compare_value(channel) as u16 | 309 | unwrap!(self.inner.get_compare_value(channel).try_into()) |
| 295 | } | 310 | } |
| 296 | 311 | ||
| 297 | fn get_max_duty(&self) -> Self::Duty { | 312 | fn get_max_duty(&self) -> Self::Duty { |
| 298 | if self.inner.get_counting_mode().is_center_aligned() { | 313 | if self.inner.get_counting_mode().is_center_aligned() { |
| 299 | self.inner.get_max_compare_value() as u16 | 314 | unwrap!(self.inner.get_max_compare_value().try_into()) |
| 300 | } else { | 315 | } else { |
| 301 | self.inner.get_max_compare_value() as u16 + 1 | 316 | unwrap!(self.inner.get_max_compare_value().try_into()) + 1 |
| 302 | } | 317 | } |
| 303 | } | 318 | } |
| 304 | 319 | ||
| 305 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { | 320 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { |
| 306 | assert!(duty <= self.get_max_duty()); | 321 | assert!(duty <= unwrap!(self.get_max_duty().try_into())); |
| 307 | self.inner.set_compare_value(channel, duty as u32) | 322 | self.inner.set_compare_value(channel, unwrap!(duty.try_into())) |
| 308 | } | 323 | } |
| 309 | 324 | ||
| 310 | fn set_period<P>(&mut self, period: P) | 325 | fn set_period<P>(&mut self, period: P) |
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 9cf0f8c34..3e1482b67 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs | |||
| @@ -97,7 +97,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 97 | 97 | ||
| 98 | /// Get capture value for a channel. | 98 | /// Get capture value for a channel. |
| 99 | pub fn get_capture_value(&self, channel: Channel) -> u32 { | 99 | pub fn get_capture_value(&self, channel: Channel) -> u32 { |
| 100 | self.inner.get_capture_value(channel) | 100 | self.inner.get_capture_value(channel).into() |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | /// Get input interrupt. | 103 | /// Get input interrupt. |
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index aba08081f..6a70d2a40 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -268,6 +268,11 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 268 | unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } | 268 | unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | #[cfg(stm32l0)] | ||
| 272 | fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp16 { | ||
| 273 | unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } | ||
| 274 | } | ||
| 275 | |||
| 271 | /// Start the timer. | 276 | /// Start the timer. |
| 272 | pub fn start(&self) { | 277 | pub fn start(&self) { |
| 273 | self.regs_core().cr1().modify(|r| r.set_cen(true)); | 278 | self.regs_core().cr1().modify(|r| r.set_cen(true)); |
| @@ -296,7 +301,12 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 296 | 301 | ||
| 297 | /// get the capability of the timer | 302 | /// get the capability of the timer |
| 298 | pub fn bits(&self) -> TimerBits { | 303 | pub fn bits(&self) -> TimerBits { |
| 299 | T::BITS | 304 | match T::Word::bits() { |
| 305 | 16 => TimerBits::Bits16, | ||
| 306 | #[cfg(not(stm32l0))] | ||
| 307 | 32 => TimerBits::Bits32, | ||
| 308 | _ => unreachable!(), | ||
| 309 | } | ||
| 300 | } | 310 | } |
| 301 | 311 | ||
| 302 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. | 312 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. |
| @@ -306,18 +316,10 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 306 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved | 316 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved |
| 307 | /// because it needs to count up and down. | 317 | /// because it needs to count up and down. |
| 308 | pub fn set_frequency(&self, frequency: Hertz) { | 318 | pub fn set_frequency(&self, frequency: Hertz) { |
| 309 | match T::BITS { | 319 | self.set_frequency_internal(frequency, T::Word::bits()); |
| 310 | TimerBits::Bits16 => { | ||
| 311 | self.set_frequency_internal(frequency, 16); | ||
| 312 | } | ||
| 313 | #[cfg(not(stm32l0))] | ||
| 314 | TimerBits::Bits32 => { | ||
| 315 | self.set_frequency_internal(frequency, 32); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | 320 | } |
| 319 | 321 | ||
| 320 | pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { | 322 | pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: usize) { |
| 321 | let f = frequency.0; | 323 | let f = frequency.0; |
| 322 | assert!(f > 0); | 324 | assert!(f > 0); |
| 323 | let timer_f = T::frequency().0; | 325 | let timer_f = T::frequency().0; |
| @@ -326,25 +328,15 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 326 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); | 328 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); |
| 327 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); | 329 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); |
| 328 | 330 | ||
| 329 | match T::BITS { | 331 | // the timer counts `0..=arr`, we want it to count `0..divide_by` |
| 330 | TimerBits::Bits16 => { | 332 | let arr: T::Word = unwrap!(T::Word::try_from(divide_by - 1)); |
| 331 | // the timer counts `0..=arr`, we want it to count `0..divide_by` | ||
| 332 | let arr = unwrap!(u16::try_from(divide_by - 1)); | ||
| 333 | |||
| 334 | let regs = self.regs_core(); | ||
| 335 | regs.psc().write_value(psc); | ||
| 336 | regs.arr().write(|r| r.set_arr(arr)); | ||
| 337 | } | ||
| 338 | #[cfg(not(stm32l0))] | ||
| 339 | TimerBits::Bits32 => { | ||
| 340 | // the timer counts `0..=arr`, we want it to count `0..divide_by` | ||
| 341 | let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); | ||
| 342 | 333 | ||
| 343 | let regs = self.regs_gp32_unchecked(); | 334 | let regs = self.regs_gp32_unchecked(); |
| 344 | regs.psc().write_value(psc); | 335 | regs.psc().write_value(psc); |
| 345 | regs.arr().write_value(arr); | 336 | #[cfg(stm32l0)] |
| 346 | } | 337 | regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); |
| 347 | } | 338 | #[cfg(not(stm32l0))] |
| 339 | regs.arr().write_value(arr.into()); | ||
| 348 | } | 340 | } |
| 349 | 341 | ||
| 350 | /// Set tick frequency. | 342 | /// Set tick frequency. |
| @@ -393,23 +385,14 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 393 | pub fn get_frequency(&self) -> Hertz { | 385 | pub fn get_frequency(&self) -> Hertz { |
| 394 | let timer_f = T::frequency(); | 386 | let timer_f = T::frequency(); |
| 395 | 387 | ||
| 396 | match T::BITS { | 388 | let regs = self.regs_gp32_unchecked(); |
| 397 | TimerBits::Bits16 => { | 389 | #[cfg(not(stm32l0))] |
| 398 | let regs = self.regs_core(); | 390 | let arr = regs.arr().read(); |
| 399 | let arr = regs.arr().read().arr(); | 391 | #[cfg(stm32l0)] |
| 400 | let psc = regs.psc().read(); | 392 | let arr = regs.arr().read().arr(); |
| 401 | 393 | let psc = regs.psc().read(); | |
| 402 | timer_f / arr / (psc + 1) | ||
| 403 | } | ||
| 404 | #[cfg(not(stm32l0))] | ||
| 405 | TimerBits::Bits32 => { | ||
| 406 | let regs = self.regs_gp32_unchecked(); | ||
| 407 | let arr = regs.arr().read(); | ||
| 408 | let psc = regs.psc().read(); | ||
| 409 | 394 | ||
| 410 | timer_f / arr / (psc + 1) | 395 | timer_f / arr / (psc + 1) |
| 411 | } | ||
| 412 | } | ||
| 413 | } | 396 | } |
| 414 | 397 | ||
| 415 | /// Get the clock frequency of the timer (before prescaler is applied). | 398 | /// Get the clock frequency of the timer (before prescaler is applied). |
| @@ -469,42 +452,29 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { | |||
| 469 | } | 452 | } |
| 470 | 453 | ||
| 471 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. | 454 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. |
| 472 | pub fn get_max_compare_value(&self) -> u32 { | 455 | pub fn get_max_compare_value(&self) -> T::Word { |
| 473 | match T::BITS { | 456 | #[cfg(not(stm32l0))] |
| 474 | TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, | 457 | return unwrap!(self.regs_gp32_unchecked().arr().read().try_into()); |
| 475 | #[cfg(not(stm32l0))] | 458 | #[cfg(stm32l0)] |
| 476 | TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), | 459 | return unwrap!(self.regs_gp32_unchecked().arr().read().arr().try_into()); |
| 477 | } | ||
| 478 | } | 460 | } |
| 479 | 461 | ||
| 480 | /// Set the max compare value. | 462 | /// Set the max compare value. |
| 481 | /// | 463 | /// |
| 482 | /// An update event is generated to load the new value. The update event is | 464 | /// An update event is generated to load the new value. The update event is |
| 483 | /// generated such that it will not cause an interrupt or DMA request. | 465 | /// generated such that it will not cause an interrupt or DMA request. |
| 484 | pub fn set_max_compare_value(&self, ticks: u32) { | 466 | pub fn set_max_compare_value(&self, ticks: T::Word) { |
| 485 | match T::BITS { | 467 | let arr = ticks; |
| 486 | TimerBits::Bits16 => { | ||
| 487 | let arr = unwrap!(u16::try_from(ticks)); | ||
| 488 | 468 | ||
| 489 | let regs = self.regs_1ch(); | 469 | let regs = self.regs_gp32_unchecked(); |
| 490 | regs.arr().write(|r| r.set_arr(arr)); | 470 | #[cfg(not(stm32l0))] |
| 471 | regs.arr().write_value(arr.into()); | ||
| 472 | #[cfg(stm32l0)] | ||
| 473 | regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); | ||
| 491 | 474 | ||
| 492 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | 475 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); |
| 493 | regs.egr().write(|r| r.set_ug(true)); | 476 | regs.egr().write(|r| r.set_ug(true)); |
| 494 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | 477 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); |
| 495 | } | ||
| 496 | #[cfg(not(stm32l0))] | ||
| 497 | TimerBits::Bits32 => { | ||
| 498 | let arr = ticks; | ||
| 499 | |||
| 500 | let regs = self.regs_gp32_unchecked(); | ||
| 501 | regs.arr().write_value(arr); | ||
| 502 | |||
| 503 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 504 | regs.egr().write(|r| r.set_ug(true)); | ||
| 505 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 506 | } | ||
| 507 | } | ||
| 508 | } | 478 | } |
| 509 | } | 479 | } |
| 510 | 480 | ||
| @@ -638,35 +608,44 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 638 | } | 608 | } |
| 639 | 609 | ||
| 640 | /// Set compare value for a channel. | 610 | /// Set compare value for a channel. |
| 641 | pub fn set_compare_value(&self, channel: Channel, value: u32) { | 611 | pub fn set_compare_value(&self, channel: Channel, value: T::Word) { |
| 642 | match T::BITS { | 612 | #[cfg(not(stm32l0))] |
| 643 | TimerBits::Bits16 => { | 613 | self.regs_gp32_unchecked() |
| 644 | let value = unwrap!(u16::try_from(value)); | 614 | .ccr(channel.index()) |
| 645 | self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); | 615 | .write_value(value.into()); |
| 646 | } | 616 | #[cfg(stm32l0)] |
| 647 | #[cfg(not(stm32l0))] | 617 | self.regs_gp16() |
| 648 | TimerBits::Bits32 => { | 618 | .ccr(channel.index()) |
| 649 | self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); | 619 | .modify(|w| w.set_ccr(unwrap!(value.try_into()))); |
| 650 | } | ||
| 651 | } | ||
| 652 | } | 620 | } |
| 653 | 621 | ||
| 654 | /// Get compare value for a channel. | 622 | /// Get compare value for a channel. |
| 655 | pub fn get_compare_value(&self, channel: Channel) -> u32 { | 623 | pub fn get_compare_value(&self, channel: Channel) -> T::Word { |
| 656 | match T::BITS { | 624 | #[cfg(not(stm32l0))] |
| 657 | TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, | 625 | return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().try_into()); |
| 658 | #[cfg(not(stm32l0))] | 626 | #[cfg(stm32l0)] |
| 659 | TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), | 627 | return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into()); |
| 660 | } | 628 | } |
| 629 | |||
| 630 | pub(crate) fn clamp_compare_value<W: Word>(&mut self, channel: Channel) { | ||
| 631 | self.set_compare_value( | ||
| 632 | channel, | ||
| 633 | unwrap!( | ||
| 634 | self.get_compare_value(channel) | ||
| 635 | .into() | ||
| 636 | .clamp(0, W::max() as u32) | ||
| 637 | .try_into() | ||
| 638 | ), | ||
| 639 | ); | ||
| 661 | } | 640 | } |
| 662 | 641 | ||
| 663 | /// Setup a ring buffer for the channel | 642 | /// Setup a ring buffer for the channel |
| 664 | pub fn setup_ring_buffer<'a>( | 643 | pub fn setup_ring_buffer<'a, W: Word + Into<T::Word>>( |
| 665 | &mut self, | 644 | &mut self, |
| 666 | dma: Peri<'a, impl super::UpDma<T>>, | 645 | dma: Peri<'a, impl super::UpDma<T>>, |
| 667 | channel: Channel, | 646 | channel: Channel, |
| 668 | dma_buf: &'a mut [u16], | 647 | dma_buf: &'a mut [W], |
| 669 | ) -> WritableRingBuffer<'a, u16> { | 648 | ) -> WritableRingBuffer<'a, W> { |
| 670 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 649 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
| 671 | let req = dma.request(); | 650 | let req = dma.request(); |
| 672 | 651 | ||
| @@ -686,7 +665,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 686 | WritableRingBuffer::new( | 665 | WritableRingBuffer::new( |
| 687 | dma, | 666 | dma, |
| 688 | req, | 667 | req, |
| 689 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | 668 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut W, |
| 690 | dma_buf, | 669 | dma_buf, |
| 691 | dma_transfer_option, | 670 | dma_transfer_option, |
| 692 | ) | 671 | ) |
| @@ -697,11 +676,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 697 | /// | 676 | /// |
| 698 | /// Note: | 677 | /// Note: |
| 699 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 678 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. |
| 700 | pub fn setup_update_dma<'a>( | 679 | pub fn setup_update_dma<'a, W: Word + Into<T::Word>>( |
| 701 | &mut self, | 680 | &mut self, |
| 702 | dma: Peri<'a, impl super::UpDma<T>>, | 681 | dma: Peri<'a, impl super::UpDma<T>>, |
| 703 | channel: Channel, | 682 | channel: Channel, |
| 704 | duty: &'a [u16], | 683 | duty: &'a [W], |
| 705 | ) -> Transfer<'a> { | 684 | ) -> Transfer<'a> { |
| 706 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 685 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
| 707 | let req = dma.request(); | 686 | let req = dma.request(); |
| @@ -719,29 +698,13 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 719 | ..Default::default() | 698 | ..Default::default() |
| 720 | }; | 699 | }; |
| 721 | 700 | ||
| 722 | match self.bits() { | 701 | Transfer::new_write( |
| 723 | TimerBits::Bits16 => Transfer::new_write( | 702 | dma, |
| 724 | dma, | 703 | req, |
| 725 | req, | 704 | duty, |
| 726 | duty, | 705 | self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W, |
| 727 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, | 706 | dma_transfer_option, |
| 728 | dma_transfer_option, | 707 | ) |
| 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 | } | 708 | } |
| 746 | } | 709 | } |
| 747 | 710 | ||
| @@ -774,12 +737,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 774 | /// Also be aware that embassy timers use one of timers internally. It is possible to | 737 | /// 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. | 738 | /// switch this timer by using `time-driver-timX` feature. |
| 776 | /// | 739 | /// |
| 777 | pub fn setup_update_dma_burst<'a>( | 740 | pub fn setup_update_dma_burst<'a, W: Word + Into<T::Word>>( |
| 778 | &mut self, | 741 | &mut self, |
| 779 | dma: Peri<'a, impl super::UpDma<T>>, | 742 | dma: Peri<'a, impl super::UpDma<T>>, |
| 780 | starting_channel: Channel, | 743 | starting_channel: Channel, |
| 781 | ending_channel: Channel, | 744 | ending_channel: Channel, |
| 782 | duty: &'a [u16], | 745 | duty: &'a [W], |
| 783 | ) -> Transfer<'a> { | 746 | ) -> Transfer<'a> { |
| 784 | let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; | 747 | let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; |
| 785 | let start_ch_index = starting_channel.index(); | 748 | let start_ch_index = starting_channel.index(); |
| @@ -815,14 +778,14 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 815 | dma, | 778 | dma, |
| 816 | req, | 779 | req, |
| 817 | duty, | 780 | duty, |
| 818 | self.regs_gp16().dmar().as_ptr() as *mut u16, | 781 | self.regs_gp16().dmar().as_ptr() as *mut W, |
| 819 | dma_transfer_option, | 782 | dma_transfer_option, |
| 820 | ) | 783 | ) |
| 821 | } | 784 | } |
| 822 | } | 785 | } |
| 823 | 786 | ||
| 824 | /// Get capture value for a channel. | 787 | /// Get capture value for a channel. |
| 825 | pub fn get_capture_value(&self, channel: Channel) -> u32 { | 788 | pub fn get_capture_value(&self, channel: Channel) -> T::Word { |
| 826 | self.get_compare_value(channel) | 789 | self.get_compare_value(channel) |
| 827 | } | 790 | } |
| 828 | 791 | ||
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 3fa363881..74d1c9e77 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -15,6 +15,8 @@ pub mod qei; | |||
| 15 | pub mod ringbuffered; | 15 | pub mod ringbuffered; |
| 16 | pub mod simple_pwm; | 16 | pub mod simple_pwm; |
| 17 | 17 | ||
| 18 | use crate::dma::word::Word; | ||
| 19 | use crate::fmt::Debuggable; | ||
| 18 | use crate::interrupt; | 20 | use crate::interrupt; |
| 19 | use crate::rcc::RccPeripheral; | 21 | use crate::rcc::RccPeripheral; |
| 20 | 22 | ||
| @@ -163,7 +165,12 @@ pub trait CoreInstance: SealedInstance + 'static { | |||
| 163 | type UpdateInterrupt: interrupt::typelevel::Interrupt; | 165 | type UpdateInterrupt: interrupt::typelevel::Interrupt; |
| 164 | 166 | ||
| 165 | /// Amount of bits this timer has. | 167 | /// Amount of bits this timer has. |
| 166 | const BITS: TimerBits; | 168 | type Word: Word |
| 169 | + TryInto<u16, Error: Debuggable> | ||
| 170 | + From<u16> | ||
| 171 | + TryFrom<u32, Error: Debuggable> | ||
| 172 | + Into<u32> | ||
| 173 | + TryFrom<u64, Error: Debuggable>; | ||
| 167 | 174 | ||
| 168 | /// Registers for this timer. | 175 | /// Registers for this timer. |
| 169 | /// | 176 | /// |
| @@ -241,7 +248,7 @@ dma_trait!(Dma, GeneralInstance4Channel, TimerChannel); | |||
| 241 | 248 | ||
| 242 | #[allow(unused)] | 249 | #[allow(unused)] |
| 243 | macro_rules! impl_core_timer { | 250 | macro_rules! impl_core_timer { |
| 244 | ($inst:ident, $bits:expr) => { | 251 | ($inst:ident, $bits:ident) => { |
| 245 | impl SealedInstance for crate::peripherals::$inst { | 252 | impl SealedInstance for crate::peripherals::$inst { |
| 246 | fn state() -> &'static State { | 253 | fn state() -> &'static State { |
| 247 | static STATE: State = State::new(); | 254 | static STATE: State = State::new(); |
| @@ -251,8 +258,7 @@ macro_rules! impl_core_timer { | |||
| 251 | 258 | ||
| 252 | impl CoreInstance for crate::peripherals::$inst { | 259 | impl CoreInstance for crate::peripherals::$inst { |
| 253 | type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; | 260 | type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; |
| 254 | 261 | type Word = $bits; | |
| 255 | const BITS: TimerBits = $bits; | ||
| 256 | 262 | ||
| 257 | fn regs() -> *mut () { | 263 | fn regs() -> *mut () { |
| 258 | crate::pac::$inst.as_ptr() | 264 | crate::pac::$inst.as_ptr() |
| @@ -306,13 +312,13 @@ macro_rules! impl_general_4ch_blank_sealed { | |||
| 306 | 312 | ||
| 307 | foreach_interrupt! { | 313 | foreach_interrupt! { |
| 308 | ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { | 314 | ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { |
| 309 | impl_core_timer!($inst, TimerBits::Bits16); | 315 | impl_core_timer!($inst, u16); |
| 310 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 316 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 311 | impl BasicInstance for crate::peripherals::$inst {} | 317 | impl BasicInstance for crate::peripherals::$inst {} |
| 312 | }; | 318 | }; |
| 313 | 319 | ||
| 314 | ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { | 320 | ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { |
| 315 | impl_core_timer!($inst, TimerBits::Bits16); | 321 | impl_core_timer!($inst, u16); |
| 316 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 322 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 317 | impl BasicInstance for crate::peripherals::$inst {} | 323 | impl BasicInstance for crate::peripherals::$inst {} |
| 318 | impl_general_1ch!($inst); | 324 | impl_general_1ch!($inst); |
| @@ -322,7 +328,7 @@ foreach_interrupt! { | |||
| 322 | }; | 328 | }; |
| 323 | 329 | ||
| 324 | ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { | 330 | ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { |
| 325 | impl_core_timer!($inst, TimerBits::Bits16); | 331 | impl_core_timer!($inst, u16); |
| 326 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 332 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 327 | impl BasicInstance for crate::peripherals::$inst {} | 333 | impl BasicInstance for crate::peripherals::$inst {} |
| 328 | impl_general_1ch!($inst); | 334 | impl_general_1ch!($inst); |
| @@ -332,7 +338,7 @@ foreach_interrupt! { | |||
| 332 | }; | 338 | }; |
| 333 | 339 | ||
| 334 | ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { | 340 | ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { |
| 335 | impl_core_timer!($inst, TimerBits::Bits16); | 341 | impl_core_timer!($inst, u16); |
| 336 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 342 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 337 | impl BasicInstance for crate::peripherals::$inst {} | 343 | impl BasicInstance for crate::peripherals::$inst {} |
| 338 | impl_general_1ch!($inst); | 344 | impl_general_1ch!($inst); |
| @@ -342,7 +348,7 @@ foreach_interrupt! { | |||
| 342 | }; | 348 | }; |
| 343 | 349 | ||
| 344 | ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { | 350 | ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { |
| 345 | impl_core_timer!($inst, TimerBits::Bits32); | 351 | impl_core_timer!($inst, u32); |
| 346 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 352 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 347 | impl BasicInstance for crate::peripherals::$inst {} | 353 | impl BasicInstance for crate::peripherals::$inst {} |
| 348 | impl_general_1ch!($inst); | 354 | impl_general_1ch!($inst); |
| @@ -353,7 +359,7 @@ foreach_interrupt! { | |||
| 353 | }; | 359 | }; |
| 354 | 360 | ||
| 355 | ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { | 361 | ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { |
| 356 | impl_core_timer!($inst, TimerBits::Bits16); | 362 | impl_core_timer!($inst, u16); |
| 357 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 363 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 358 | impl BasicInstance for crate::peripherals::$inst {} | 364 | impl BasicInstance for crate::peripherals::$inst {} |
| 359 | impl_general_1ch!($inst); | 365 | impl_general_1ch!($inst); |
| @@ -366,7 +372,7 @@ foreach_interrupt! { | |||
| 366 | }; | 372 | }; |
| 367 | 373 | ||
| 368 | ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { | 374 | ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { |
| 369 | impl_core_timer!($inst, TimerBits::Bits16); | 375 | impl_core_timer!($inst, u16); |
| 370 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 376 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 371 | impl BasicInstance for crate::peripherals::$inst {} | 377 | impl BasicInstance for crate::peripherals::$inst {} |
| 372 | impl_general_1ch!($inst); | 378 | impl_general_1ch!($inst); |
| @@ -379,7 +385,7 @@ foreach_interrupt! { | |||
| 379 | }; | 385 | }; |
| 380 | 386 | ||
| 381 | ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { | 387 | ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { |
| 382 | impl_core_timer!($inst, TimerBits::Bits16); | 388 | impl_core_timer!($inst, u16); |
| 383 | impl BasicNoCr2Instance for crate::peripherals::$inst {} | 389 | impl BasicNoCr2Instance for crate::peripherals::$inst {} |
| 384 | impl BasicInstance for crate::peripherals::$inst {} | 390 | impl BasicInstance for crate::peripherals::$inst {} |
| 385 | impl_general_1ch!($inst); | 391 | impl_general_1ch!($inst); |
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index fe8681356..989e1d630 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs | |||
| @@ -199,7 +199,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 199 | fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { | 199 | fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { |
| 200 | self.inner.set_counting_mode(counting_mode); | 200 | self.inner.set_counting_mode(counting_mode); |
| 201 | self.inner.set_tick_freq(freq); | 201 | self.inner.set_tick_freq(freq); |
| 202 | self.inner.set_max_compare_value(pulse_end); | 202 | self.inner.set_max_compare_value(unwrap!(pulse_end.try_into())); |
| 203 | self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); | 203 | self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); |
| 204 | // Required for advanced timers, see GeneralInstance4Channel for details | 204 | // Required for advanced timers, see GeneralInstance4Channel for details |
| 205 | self.inner.enable_outputs(); | 205 | self.inner.enable_outputs(); |
| @@ -211,14 +211,14 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 211 | 211 | ||
| 212 | /// Get the end of the pulse in ticks from the trigger. | 212 | /// Get the end of the pulse in ticks from the trigger. |
| 213 | pub fn pulse_end(&self) -> u32 { | 213 | pub fn pulse_end(&self) -> u32 { |
| 214 | let max = self.inner.get_max_compare_value(); | 214 | let max: u32 = self.inner.get_max_compare_value().into(); |
| 215 | assert!(max < u32::MAX); | 215 | assert!(max < u32::MAX); |
| 216 | max + 1 | 216 | max + 1 |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | /// Set the end of the pulse in ticks from the trigger. | 219 | /// Set the end of the pulse in ticks from the trigger. |
| 220 | pub fn set_pulse_end(&mut self, ticks: u32) { | 220 | pub fn set_pulse_end(&mut self, ticks: u32) { |
| 221 | self.inner.set_max_compare_value(ticks) | 221 | self.inner.set_max_compare_value(unwrap!(ticks.try_into())) |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | /// Reset the timer on each trigger | 224 | /// Reset the timer on each trigger |
| @@ -327,7 +327,7 @@ pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> { | |||
| 327 | impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { | 327 | impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { |
| 328 | /// Get the end of the pulse in ticks from the trigger. | 328 | /// Get the end of the pulse in ticks from the trigger. |
| 329 | pub fn pulse_end(&self) -> u32 { | 329 | pub fn pulse_end(&self) -> u32 { |
| 330 | let max = self.inner.get_max_compare_value(); | 330 | let max: u32 = self.inner.get_max_compare_value().into(); |
| 331 | assert!(max < u32::MAX); | 331 | assert!(max < u32::MAX); |
| 332 | max + 1 | 332 | max + 1 |
| 333 | } | 333 | } |
| @@ -339,13 +339,13 @@ impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { | |||
| 339 | 339 | ||
| 340 | /// Get the start of the pulse in ticks from the trigger. | 340 | /// Get the start of the pulse in ticks from the trigger. |
| 341 | pub fn pulse_delay(&mut self) -> u32 { | 341 | pub fn pulse_delay(&mut self) -> u32 { |
| 342 | self.inner.get_compare_value(self.channel) | 342 | self.inner.get_compare_value(self.channel).into() |
| 343 | } | 343 | } |
| 344 | 344 | ||
| 345 | /// Set the start of the pulse in ticks from the trigger. | 345 | /// Set the start of the pulse in ticks from the trigger. |
| 346 | pub fn set_pulse_delay(&mut self, delay: u32) { | 346 | pub fn set_pulse_delay(&mut self, delay: u32) { |
| 347 | assert!(delay <= self.pulse_end()); | 347 | assert!(delay <= self.pulse_end()); |
| 348 | self.inner.set_compare_value(self.channel, delay); | 348 | self.inner.set_compare_value(self.channel, unwrap!(delay.try_into())); |
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | /// Set the pulse width in ticks. | 351 | /// Set the pulse width in ticks. |
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 057ab011a..f2f00927d 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs | |||
| @@ -91,16 +91,18 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | |||
| 91 | 91 | ||
| 92 | /// Get the period tick count | 92 | /// Get the period tick count |
| 93 | pub fn get_period_ticks(&self) -> u32 { | 93 | pub fn get_period_ticks(&self) -> u32 { |
| 94 | self.inner.get_capture_value(self.channel) | 94 | self.inner.get_capture_value(self.channel).into() |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | /// Get the pulse width tick count | 97 | /// Get the pulse width tick count |
| 98 | pub fn get_width_ticks(&self) -> u32 { | 98 | pub fn get_width_ticks(&self) -> u32 { |
| 99 | self.inner.get_capture_value(match self.channel { | 99 | self.inner |
| 100 | Channel::Ch1 => Channel::Ch2, | 100 | .get_capture_value(match self.channel { |
| 101 | Channel::Ch2 => Channel::Ch1, | 101 | Channel::Ch1 => Channel::Ch2, |
| 102 | _ => panic!("Invalid channel for PWM input"), | 102 | Channel::Ch2 => Channel::Ch1, |
| 103 | }) | 103 | _ => panic!("Invalid channel for PWM input"), |
| 104 | }) | ||
| 105 | .into() | ||
| 104 | } | 106 | } |
| 105 | 107 | ||
| 106 | /// Get the duty cycle in 100% | 108 | /// Get the duty cycle in 100% |
diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs index e8f97bf59..fbb6b19ea 100644 --- a/embassy-stm32/src/timer/ringbuffered.rs +++ b/embassy-stm32/src/timer/ringbuffered.rs | |||
| @@ -7,6 +7,7 @@ use super::low_level::Timer; | |||
| 7 | use super::{Channel, GeneralInstance4Channel}; | 7 | use super::{Channel, GeneralInstance4Channel}; |
| 8 | use crate::dma::WritableRingBuffer; | 8 | use crate::dma::WritableRingBuffer; |
| 9 | use crate::dma::ringbuffer::Error; | 9 | use crate::dma::ringbuffer::Error; |
| 10 | use crate::dma::word::Word; | ||
| 10 | 11 | ||
| 11 | /// A PWM channel that uses a DMA ring buffer for continuous waveform generation. | 12 | /// A PWM channel that uses a DMA ring buffer for continuous waveform generation. |
| 12 | /// | 13 | /// |
| @@ -23,17 +24,17 @@ use crate::dma::ringbuffer::Error; | |||
| 23 | /// channel.start(); // Start DMA transfer | 24 | /// channel.start(); // Start DMA transfer |
| 24 | /// channel.write(&[100, 200, 300]).ok(); // Update duty cycles | 25 | /// channel.write(&[100, 200, 300]).ok(); // Update duty cycles |
| 25 | /// ``` | 26 | /// ``` |
| 26 | pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { | 27 | pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> { |
| 27 | timer: ManuallyDrop<Timer<'d, T>>, | 28 | timer: ManuallyDrop<Timer<'d, T>>, |
| 28 | ring_buf: WritableRingBuffer<'d, u16>, | 29 | ring_buf: WritableRingBuffer<'d, W>, |
| 29 | channel: Channel, | 30 | channel: Channel, |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { | 33 | impl<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> RingBufferedPwmChannel<'d, T, W> { |
| 33 | pub(crate) fn new( | 34 | pub(crate) fn new( |
| 34 | timer: ManuallyDrop<Timer<'d, T>>, | 35 | timer: ManuallyDrop<Timer<'d, T>>, |
| 35 | channel: Channel, | 36 | channel: Channel, |
| 36 | ring_buf: WritableRingBuffer<'d, u16>, | 37 | ring_buf: WritableRingBuffer<'d, W>, |
| 37 | ) -> Self { | 38 | ) -> Self { |
| 38 | Self { | 39 | Self { |
| 39 | timer, | 40 | timer, |
| @@ -55,18 +56,18 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { | |||
| 55 | } | 56 | } |
| 56 | 57 | ||
| 57 | /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. | 58 | /// 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 | pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { |
| 59 | self.ring_buf.write_immediate(buf) | 60 | self.ring_buf.write_immediate(buf) |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | /// Write elements from the ring buffer | 63 | /// Write elements from the ring buffer |
| 63 | /// Return a tuple of the length written and the length remaining in the buffer | 64 | /// 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 | pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { |
| 65 | self.ring_buf.write(buf) | 66 | self.ring_buf.write(buf) |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | /// Write an exact number of elements to the ringbuffer. | 69 | /// Write an exact number of elements to the ringbuffer. |
| 69 | pub async fn write_exact(&mut self, buffer: &[u16]) -> Result<usize, Error> { | 70 | pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> { |
| 70 | self.ring_buf.write_exact(buffer).await | 71 | self.ring_buf.write_exact(buffer).await |
| 71 | } | 72 | } |
| 72 | 73 | ||
| @@ -140,7 +141,7 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { | |||
| 140 | /// | 141 | /// |
| 141 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 142 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 142 | pub fn max_duty_cycle(&self) -> u16 { | 143 | pub fn max_duty_cycle(&self) -> u16 { |
| 143 | let max = self.timer.get_max_compare_value(); | 144 | let max: u32 = self.timer.get_max_compare_value().into(); |
| 144 | assert!(max < u16::MAX as u32); | 145 | assert!(max < u16::MAX as u32); |
| 145 | max as u16 + 1 | 146 | max as u16 + 1 |
| 146 | } | 147 | } |
| @@ -155,15 +156,3 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { | |||
| 155 | self.timer.set_output_compare_mode(self.channel, mode); | 156 | self.timer.set_output_compare_mode(self.channel, mode); |
| 156 | } | 157 | } |
| 157 | } | 158 | } |
| 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 484e9fd81..b79ed364b 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -7,6 +7,7 @@ use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; | |||
| 7 | use super::ringbuffered::RingBufferedPwmChannel; | 7 | use super::ringbuffered::RingBufferedPwmChannel; |
| 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; | 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; |
| 9 | use crate::Peri; | 9 | use crate::Peri; |
| 10 | use crate::dma::word::Word; | ||
| 10 | #[cfg(gpio_v2)] | 11 | #[cfg(gpio_v2)] |
| 11 | use crate::gpio::Pull; | 12 | use crate::gpio::Pull; |
| 12 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 13 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| @@ -98,18 +99,16 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | |||
| 98 | /// Get max duty value. | 99 | /// Get max duty value. |
| 99 | /// | 100 | /// |
| 100 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 101 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 101 | pub fn max_duty_cycle(&self) -> u16 { | 102 | pub fn max_duty_cycle(&self) -> u32 { |
| 102 | let max = self.timer.get_max_compare_value(); | 103 | self.timer.get_max_compare_value().into() + 1 |
| 103 | assert!(max < u16::MAX as u32); | ||
| 104 | max as u16 + 1 | ||
| 105 | } | 104 | } |
| 106 | 105 | ||
| 107 | /// Set the duty for a given channel. | 106 | /// Set the duty for a given channel. |
| 108 | /// | 107 | /// |
| 109 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. | 108 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. |
| 110 | pub fn set_duty_cycle(&mut self, duty: u16) { | 109 | pub fn set_duty_cycle(&mut self, duty: u32) { |
| 111 | assert!(duty <= (*self).max_duty_cycle()); | 110 | assert!(duty <= (*self).max_duty_cycle()); |
| 112 | self.timer.set_compare_value(self.channel, duty.into()) | 111 | self.timer.set_compare_value(self.channel, unwrap!(duty.try_into())) |
| 113 | } | 112 | } |
| 114 | 113 | ||
| 115 | /// Set the duty cycle to 0%, or always inactive. | 114 | /// Set the duty cycle to 0%, or always inactive. |
| @@ -126,21 +125,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | |||
| 126 | /// | 125 | /// |
| 127 | /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, | 126 | /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, |
| 128 | /// and that `denom` is not zero. | 127 | /// and that `denom` is not zero. |
| 129 | pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { | 128 | pub fn set_duty_cycle_fraction(&mut self, num: u32, denom: u32) { |
| 130 | assert!(denom != 0); | 129 | assert!(denom != 0); |
| 131 | assert!(num <= denom); | 130 | assert!(num <= denom); |
| 132 | let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); | 131 | let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); |
| 133 | 132 | ||
| 134 | // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) | 133 | // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) |
| 135 | #[allow(clippy::cast_possible_truncation)] | 134 | #[allow(clippy::cast_possible_truncation)] |
| 136 | self.set_duty_cycle(duty as u16); | 135 | self.set_duty_cycle(unwrap!(duty.try_into())); |
| 137 | } | 136 | } |
| 138 | 137 | ||
| 139 | /// Set the duty cycle to `percent / 100` | 138 | /// Set the duty cycle to `percent / 100` |
| 140 | /// | 139 | /// |
| 141 | /// The caller is responsible for ensuring that `percent` is less than or equal to 100. | 140 | /// The caller is responsible for ensuring that `percent` is less than or equal to 100. |
| 142 | pub fn set_duty_cycle_percent(&mut self, percent: u8) { | 141 | pub fn set_duty_cycle_percent(&mut self, percent: u8) { |
| 143 | self.set_duty_cycle_fraction(u16::from(percent), 100) | 142 | self.set_duty_cycle_fraction(percent as u32, 100) |
| 144 | } | 143 | } |
| 145 | 144 | ||
| 146 | /// Get the duty for a given channel. | 145 | /// Get the duty for a given channel. |
| @@ -171,13 +170,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | |||
| 171 | /// | 170 | /// |
| 172 | /// # Panics | 171 | /// # Panics |
| 173 | /// Panics if `dma_buf` is empty or longer than 65535 elements. | 172 | /// Panics if `dma_buf` is empty or longer than 65535 elements. |
| 174 | pub fn into_ring_buffered_channel( | 173 | pub fn into_ring_buffered_channel<W: Word + Into<T::Word>>( |
| 175 | mut self, | 174 | mut self, |
| 176 | tx_dma: Peri<'d, impl super::UpDma<T>>, | 175 | tx_dma: Peri<'d, impl super::UpDma<T>>, |
| 177 | dma_buf: &'d mut [u16], | 176 | dma_buf: &'d mut [W], |
| 178 | ) -> RingBufferedPwmChannel<'d, T> { | 177 | ) -> RingBufferedPwmChannel<'d, T, W> { |
| 179 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | 178 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); |
| 180 | 179 | ||
| 180 | self.timer.clamp_compare_value::<W>(self.channel); | ||
| 181 | self.timer.enable_update_dma(true); | 181 | self.timer.enable_update_dma(true); |
| 182 | 182 | ||
| 183 | RingBufferedPwmChannel::new( | 183 | RingBufferedPwmChannel::new( |
| @@ -332,10 +332,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 332 | /// Get max duty value. | 332 | /// Get max duty value. |
| 333 | /// | 333 | /// |
| 334 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 334 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 335 | pub fn max_duty_cycle(&self) -> u16 { | 335 | pub fn max_duty_cycle(&self) -> u32 { |
| 336 | let max = self.inner.get_max_compare_value(); | 336 | self.inner.get_max_compare_value().into() + 1 |
| 337 | assert!(max < u16::MAX as u32); | ||
| 338 | max as u16 + 1 | ||
| 339 | } | 337 | } |
| 340 | 338 | ||
| 341 | /// Generate a sequence of PWM waveform | 339 | /// Generate a sequence of PWM waveform |
| @@ -344,8 +342,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 344 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | 342 | /// 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 | 343 | /// 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. | 344 | /// switch this timer by using `time-driver-timX` feature. |
| 347 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 345 | pub async fn waveform_up<W: Word + Into<T::Word>>( |
| 346 | &mut self, | ||
| 347 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 348 | channel: Channel, | ||
| 349 | duty: &[W], | ||
| 350 | ) { | ||
| 348 | self.inner.enable_channel(channel, true); | 351 | self.inner.enable_channel(channel, true); |
| 352 | self.inner.clamp_compare_value::<W>(channel); | ||
| 349 | self.inner.enable_update_dma(true); | 353 | self.inner.enable_update_dma(true); |
| 350 | self.inner.setup_update_dma(dma, channel, duty).await; | 354 | self.inner.setup_update_dma(dma, channel, duty).await; |
| 351 | self.inner.enable_update_dma(false); | 355 | self.inner.enable_update_dma(false); |
| @@ -380,13 +384,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 380 | /// Also be aware that embassy timers use one of timers internally. It is possible to | 384 | /// 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. | 385 | /// switch this timer by using `time-driver-timX` feature. |
| 382 | /// | 386 | /// |
| 383 | pub async fn waveform_up_multi_channel( | 387 | pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>( |
| 384 | &mut self, | 388 | &mut self, |
| 385 | dma: Peri<'_, impl super::UpDma<T>>, | 389 | dma: Peri<'_, impl super::UpDma<T>>, |
| 386 | starting_channel: Channel, | 390 | starting_channel: Channel, |
| 387 | ending_channel: Channel, | 391 | ending_channel: Channel, |
| 388 | duty: &[u16], | 392 | duty: &[W], |
| 389 | ) { | 393 | ) { |
| 394 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | ||
| 395 | .iter() | ||
| 396 | .filter(|ch| ch.index() >= starting_channel.index()) | ||
| 397 | .filter(|ch| ch.index() <= ending_channel.index()) | ||
| 398 | .for_each(|ch| { | ||
| 399 | self.inner.enable_channel(*ch, true); | ||
| 400 | self.inner.clamp_compare_value::<W>(*ch); | ||
| 401 | }); | ||
| 390 | self.inner.enable_update_dma(true); | 402 | self.inner.enable_update_dma(true); |
| 391 | self.inner | 403 | self.inner |
| 392 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) | 404 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) |
| @@ -401,11 +413,11 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePw | |||
| 401 | 413 | ||
| 402 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { | 414 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { |
| 403 | fn max_duty_cycle(&self) -> u16 { | 415 | fn max_duty_cycle(&self) -> u16 { |
| 404 | self.max_duty_cycle() | 416 | unwrap!(self.max_duty_cycle().try_into()) |
| 405 | } | 417 | } |
| 406 | 418 | ||
| 407 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { | 419 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { |
| 408 | self.set_duty_cycle(duty); | 420 | self.set_duty_cycle(duty.into()); |
| 409 | Ok(()) | 421 | Ok(()) |
| 410 | } | 422 | } |
| 411 | 423 | ||
| @@ -420,7 +432,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl | |||
| 420 | } | 432 | } |
| 421 | 433 | ||
| 422 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { | 434 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { |
| 423 | self.set_duty_cycle_fraction(num, denom); | 435 | self.set_duty_cycle_fraction(num.into(), denom.into()); |
| 424 | Ok(()) | 436 | Ok(()) |
| 425 | } | 437 | } |
| 426 | 438 | ||
| @@ -448,16 +460,16 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { | |||
| 448 | } | 460 | } |
| 449 | 461 | ||
| 450 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { | 462 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { |
| 451 | self.inner.get_compare_value(channel) | 463 | self.inner.get_compare_value(channel).into() |
| 452 | } | 464 | } |
| 453 | 465 | ||
| 454 | fn get_max_duty(&self) -> Self::Duty { | 466 | fn get_max_duty(&self) -> Self::Duty { |
| 455 | self.inner.get_max_compare_value() + 1 | 467 | self.inner.get_max_compare_value().into() + 1 |
| 456 | } | 468 | } |
| 457 | 469 | ||
| 458 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { | 470 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { |
| 459 | assert!(duty <= self.max_duty_cycle() as u32); | 471 | assert!(duty <= self.max_duty_cycle() as u32); |
| 460 | self.inner.set_compare_value(channel, duty) | 472 | self.inner.set_compare_value(channel, unwrap!(duty.try_into())) |
| 461 | } | 473 | } |
| 462 | 474 | ||
| 463 | fn set_period<P>(&mut self, period: P) | 475 | fn set_period<P>(&mut self, period: P) |
diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 50008a37b..5e39a06f5 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs | |||
| @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner) { | |||
| 33 | ); | 33 | ); |
| 34 | 34 | ||
| 35 | let max = pwm.get_max_duty(); | 35 | let max = pwm.get_max_duty(); |
| 36 | pwm.set_dead_time(max / 1024); | 36 | pwm.set_dead_time((max / 1024) as u16); |
| 37 | 37 | ||
| 38 | pwm.enable(Channel::Ch1); | 38 | pwm.enable(Channel::Ch1); |
| 39 | 39 | ||
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index ccfd0661e..4e556f0d4 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs | |||
| @@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) { | |||
| 61 | // construct ws2812 non-return-to-zero (NRZ) code bit by bit | 61 | // construct ws2812 non-return-to-zero (NRZ) code bit by bit |
| 62 | // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low | 62 | // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low |
| 63 | 63 | ||
| 64 | let max_duty = ws2812_pwm.max_duty_cycle(); | 64 | let max_duty = ws2812_pwm.max_duty_cycle() as u16; |
| 65 | let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing | 65 | let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing |
| 66 | let n1 = 2 * n0; // ws2812 Bit 1 high level timing | 66 | let n1 = 2 * n0; // ws2812 Bit 1 high level timing |
| 67 | 67 | ||
