From c099f7d871e0ce5dd1838a7142403a3cdb69c2ea Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 30 Apr 2019 23:37:15 +0300 Subject: [PATCH 1/4] pwm: custom selection of channels Provide documentaion and example for custom channel configurations using newtype pattern to implement Pins trait. Signed-off-by: Sergey Matyukevich --- CHANGELOG.md | 3 ++ examples/pwm_custom.rs | 80 ++++++++++++++++++++++++++++++++++++++++++ src/pwm.rs | 79 +++++++++++++++++++++++++++++------------ 3 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 examples/pwm_custom.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 67302187..d2f5004d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add methods `stop`, `release` and `clear_update_interrupt_flag` to `Timer` (`clear_update_interrupt_flag` does not apply to `Timer`) - Add timer interrupt example using RTFM - Implement IndependentWatchdog for the IWDG peripheral +- Remove all PWM channel configurations except 'all the channels for default remapping' configuratons +- Update PWM documentation: clarify custom selection of channels +- Add PWM example for custom selection of channels ### Changed diff --git a/examples/pwm_custom.rs b/examples/pwm_custom.rs new file mode 100644 index 00000000..2699d6d5 --- /dev/null +++ b/examples/pwm_custom.rs @@ -0,0 +1,80 @@ +//! Testing PWM output + +#![deny(unsafe_code)] +#![no_main] +#![no_std] + +use panic_halt as _; + +use cortex_m::asm; +use stm32f1xx_hal::{ + gpio::gpiob::{PB4, PB5}, + gpio::{Alternate, PushPull}, + pac, + prelude::*, + pwm::{Pins, Pwm, C1, C2}, + stm32::TIM3, +}; + +use cortex_m_rt::entry; + +struct MyChannels(PB4>, PB5>); + +impl Pins for MyChannels { + const REMAP: u8 = 0b10; + const C1: bool = true; + const C2: bool = true; + const C3: bool = false; + const C4: bool = false; + type Channels = (Pwm, Pwm); +} + +#[entry] +fn main() -> ! { + let p = pac::Peripherals::take().unwrap(); + + let mut flash = p.FLASH.constrain(); + let mut rcc = p.RCC.constrain(); + + let clocks = rcc.cfgr.freeze(&mut flash.acr); + + let mut afio = p.AFIO.constrain(&mut rcc.apb2); + + let mut gpiob = p.GPIOB.split(&mut rcc.apb2); + + // TIM3 + let p0 = gpiob.pb4.into_alternate_push_pull(&mut gpiob.crl); + let p1 = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl); + + let mut pwm = p.TIM3.pwm( + MyChannels(p0, p1), + &mut afio.mapr, + 1.khz(), + clocks, + &mut rcc.apb1, + ); + + let max = pwm.0.get_max_duty(); + + pwm.0.enable(); + pwm.1.enable(); + + // full + pwm.0.set_duty(max); + pwm.1.set_duty(max); + + asm::bkpt(); + + // dim + pwm.1.set_duty(max / 4); + + asm::bkpt(); + + // zero + pwm.0.set_duty(0); + pwm.1.set_duty(0); + + asm::bkpt(); + + loop {} +} diff --git a/src/pwm.rs b/src/pwm.rs index 3772a5f7..6407db45 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -5,7 +5,7 @@ pulse width modulated signals on some pins. The timers support up to 4 simultaneous pwm outputs in separate `Channels` - ## Usage + ## Usage for pre-defined channel combinations Start by setting all the pins for the timer you want to use to alternate push pull pins. The `Pins` trait shows the mapping between timers, output pins @@ -31,9 +31,9 @@ let (c0, c1, c2, c3) = device.TIM2.pwm( pins, &mut afio.mapr, - 100.hz() + 100.hz(), clocks, - &mut rcc.apb + &mut rcc.apb1 ); // Set the duty cycle of channel 0 to 50% @@ -41,6 +41,59 @@ // PWM outputs are disabled by default c0.enable() ``` + + ## Usage for custom channel combinations + + Note that crate itself defines only basic channel combinations for default AFIO remapppings, + where all the channels are enabled. Meanwhile it is possible to configure PWM for any custom + selection of channels. For this purpose the 'Pins' trait should be implemented for the chosen + combination of channels and AFIO remappings. However minor additional efforts are needed since + it is not possible to implement a foreign trait for a foreign type. The trick is to use + the newtype pattern. Here is an example: + + ``` + use stm32f1xx_hal::stm32::TIM3; + use stm32f1xx_hal::gpio::gpiob::{PB4, PB5}; + use stm32f1xx_hal::gpio::{Alternate, PushPull}; + use stm32f1xx_hal::pwm::{Pins, Pwm, C1, C2, C3, C4}; + + struct MyChannels(PB4>, PB5>); + + impl Pins for MyChannels + { + const REMAP: u8 = 0b10; // use TIM3 AFIO remapping for PB4, PB5, PB0, PB1 pins + const C1: bool = true; + const C2: bool = true; + const C3: bool = false; + const C4: bool = false; + type Channels = (Pwm, Pwm) + } + + ... + + let gpiob = ..; // Set up and split GPIOB + + let p1 = gpiob.pb4.into_alternate_push_pull(&mut gpiob.crl); + let p2 = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl); + + ... + + let device: pac::Peripherals = ..; + + let (c1, c2) = device.TIM3.pwm( + MyChannels(p1, p2), + &mut afio.mapr, + 100.hz(), + clocks, + &mut rcc.apb1 + ); + + // Set the duty cycle of channel 0 to 50% + c1.set_duty(c1.get_max_duty() / 2); + // PWM outputs are disabled by default + c1.enable() + + ``` */ use core::marker::PhantomData; @@ -84,15 +137,6 @@ impl Pins type Channels = (Pwm, Pwm, Pwm, Pwm); } -impl Pins for PA0> { - const REMAP: u8 = 0b00; - const C1: bool = true; - const C2: bool = false; - const C3: bool = false; - const C4: bool = false; - type Channels = Pwm; -} - impl Pins for ( PA6>, @@ -109,15 +153,6 @@ impl Pins type Channels = (Pwm, Pwm, Pwm, Pwm); } -impl Pins for (PB0>, PB1>) { - const REMAP: u8 = 0b00; - const C1: bool = false; - const C2: bool = false; - const C3: bool = true; - const C4: bool = true; - type Channels = (Pwm, Pwm); -} - impl Pins for ( PB6>, @@ -126,7 +161,7 @@ impl Pins PB9>, ) { - const REMAP: u8 = 0b0; + const REMAP: u8 = 0b00; const C1: bool = true; const C2: bool = true; const C3: bool = true; From 27f36a7c2770e5cd2ecdf6ca200ba09ec693d851 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Tue, 7 May 2019 22:13:16 +0300 Subject: [PATCH 2/4] pwm: custom selection of channels Add more details and examples for custom channel selection. Clarify where to get REMAP and Cx values. Signed-off-by: Sergey Matyukevich --- examples/pwm.rs | 2 +- examples/pwm_custom.rs | 15 +++++++++-- src/pwm.rs | 59 +++++++++++++++++++++++++++++++++--------- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/examples/pwm.rs b/examples/pwm.rs index 1b9fcc45..d032ac73 100644 --- a/examples/pwm.rs +++ b/examples/pwm.rs @@ -1,4 +1,4 @@ -//! Testing PWM output +//! Testing PWM output for pre-defined pin combination: all pins for default mapping #![deny(unsafe_code)] #![no_main] diff --git a/examples/pwm_custom.rs b/examples/pwm_custom.rs index 2699d6d5..673d5b20 100644 --- a/examples/pwm_custom.rs +++ b/examples/pwm_custom.rs @@ -1,4 +1,4 @@ -//! Testing PWM output +//! Testing PWM output for custom pin combinations #![deny(unsafe_code)] #![no_main] @@ -18,8 +18,19 @@ use stm32f1xx_hal::{ use cortex_m_rt::entry; +// Using PB5 channel for TIM3 PWM output +// struct MyChannels(PB5>); +// impl Pins for MyChannels { +// const REMAP: u8 = 0b10; +// const C1: bool = false; +// const C2: bool = true; +// const C3: bool = false; +// const C4: bool = false; +// type Channels = Pwm; +// } + +// Using PB4 and PB5 channels for TIM3 PWM output struct MyChannels(PB4>, PB5>); - impl Pins for MyChannels { const REMAP: u8 = 0b10; const C1: bool = true; diff --git a/src/pwm.rs b/src/pwm.rs index 6407db45..bc64a78f 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -2,14 +2,14 @@ # Pulse width modulation The general purpose timers (`TIM2`, `TIM3`, and `TIM4`) can be used to output - pulse width modulated signals on some pins. The timers support up to 4 simultaneous - pwm outputs in separate `Channels` + pulse width modulated signals on some pins. The timers support up to 4 + simultaneous pwm outputs in separate `Channels` ## Usage for pre-defined channel combinations - Start by setting all the pins for the timer you want to use to alternate - push pull pins. The `Pins` trait shows the mapping between timers, output pins - and channels. + Crate defines only basic channel combinations for default AFIO remappings, + where all the channels are enabled. Start by setting all the pins for the + timer you want to use to alternate push pull pins: ```rust let gpioa = ..; // Set up and split GPIOA @@ -21,7 +21,7 @@ ); ``` - Then call the `pwm` function on the corresponding timer + Then call the `pwm` function on the corresponding timer: ``` let device: pac::Peripherals = ..; @@ -44,12 +44,47 @@ ## Usage for custom channel combinations - Note that crate itself defines only basic channel combinations for default AFIO remapppings, + Note that crate itself defines only basic channel combinations for default AFIO remappings, where all the channels are enabled. Meanwhile it is possible to configure PWM for any custom - selection of channels. For this purpose the 'Pins' trait should be implemented for the chosen - combination of channels and AFIO remappings. However minor additional efforts are needed since - it is not possible to implement a foreign trait for a foreign type. The trick is to use - the newtype pattern. Here is an example: + selection of channels. The `Pins` trait shows the mapping between timers, output pins and + channels. So this trait needs to be implemented for the custom combination of channels and + AFIO remappings. However minor additional efforts are needed since it is not possible to + implement a foreign trait for a foreign type. The trick is to use the newtype pattern. + + The first example selects PB5 channel for TIM3 PWM output: + + ``` + struct MyChannels(PB5>); + + impl Pins for MyChannels { + const REMAP: u8 = 0b10; // use TIM3 AFIO remapping for PB4, PB5, PB0, PB1 pins + const C1: bool = false; + const C2: bool = true; // use channel C2 + const C3: bool = false; + const C4: bool = false; + type Channels = Pwm; + } + ``` + + The second example selects PC8 and PC9 channels for TIM3 PWM output: + + ``` + struct MyChannels(PB5>); + + impl Pins for MyChannels { + const REMAP: u8 = 0b11; // use TIM3 AFIO remapping for PC6, PC7, PC8, PC9 pins + const C1: bool = false; + const C2: bool = false; + const C3: bool = true; // use channel C3 + const C4: bool = true; // use channel C4 + type Channels = (Pwm, Pwm); + } + ``` + + REMAP value and channel pins should specified according to the stm32f1xx specification, + namely according to the chapter about AFIO and timer alternate function remappings. + + Finally, here is a complete example for two channels: ``` use stm32f1xx_hal::stm32::TIM3; @@ -61,7 +96,7 @@ impl Pins for MyChannels { - const REMAP: u8 = 0b10; // use TIM3 AFIO remapping for PB4, PB5, PB0, PB1 pins + const REMAP: u8 = 0b10; const C1: bool = true; const C2: bool = true; const C3: bool = false; From d2c81264f3ad399175f99f8b1c6fab7fe2e42e17 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Sat, 11 May 2019 17:42:58 +0300 Subject: [PATCH 3/4] fixup: pwm: revert change for TIM4_REMAP TIM4_REMAP is only one bit: 0b0 -> PB6, PB7, PB8, PB9 0b1 -> PD12, PD13, PD14, PD15 Signed-off-by: Sergey Matyukevich --- src/pwm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pwm.rs b/src/pwm.rs index bc64a78f..fb5de298 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -196,7 +196,7 @@ impl Pins PB9>, ) { - const REMAP: u8 = 0b00; + const REMAP: u8 = 0b0; const C1: bool = true; const C2: bool = true; const C3: bool = true; From 5b8c41da1fbb430bc7cf77d69386369e55eb601e Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Fri, 24 May 2019 17:42:41 +0300 Subject: [PATCH 4/4] fixup: update docs according to review comments - phrasing - specify AFIO/TIM section from TRM Signed-off-by: Sergey Matyukevich --- src/pwm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pwm.rs b/src/pwm.rs index fb5de298..103b331b 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -7,7 +7,7 @@ ## Usage for pre-defined channel combinations - Crate defines only basic channel combinations for default AFIO remappings, + This crate only defines basic channel combinations for default AFIO remappings, where all the channels are enabled. Start by setting all the pins for the timer you want to use to alternate push pull pins: @@ -81,8 +81,8 @@ } ``` - REMAP value and channel pins should specified according to the stm32f1xx specification, - namely according to the chapter about AFIO and timer alternate function remappings. + REMAP value and channel pins should be specified according to the stm32f1xx specification, + e.g. the section 9.3.7 "Timer alternate function remapping" in RM0008 Rev 20. Finally, here is a complete example for two channels: