Skip to content

Commit afcb5bf

Browse files
authored
Implement full embedded_hal::Pwm (#176)
1 parent 01f1390 commit afcb5bf

File tree

5 files changed

+217
-30
lines changed

5 files changed

+217
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
- Extend the Pwm implementation to cover the full embedded_hal::Pwm API
1011
- Replace default blocking spi Write implementation with an optimized one
1112
- Use `Deref` for SPI generic implementations instead of macros
1213
- Make traits `rcc::Enable` and `rcc::Reset` public, but `RccBus` sealed

examples/pwm.rs

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use stm32f1xx_hal::{
1111
prelude::*,
1212
pac,
1313
timer::{Tim2NoRemap, Timer},
14+
time::U32Ext,
15+
pwm::Channel
1416
};
1517
use cortex_m_rt::entry;
1618

@@ -49,25 +51,56 @@ fn main() -> ! {
4951
// let c4 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh);
5052

5153
let mut pwm = Timer::tim2(p.TIM2, &clocks, &mut rcc.apb1)
52-
.pwm::<Tim2NoRemap, _, _, _>(pins, &mut afio.mapr, 1.khz())
53-
.2;
54+
.pwm::<Tim2NoRemap, _, _, _>(pins, &mut afio.mapr, 1.khz());
55+
56+
//// Operations affecting all defined channels on the Timer
57+
58+
// Adjust period to 0.5 seconds
59+
pwm.set_period(500.ms());
60+
61+
asm::bkpt();
62+
63+
// Return to the original frequency
64+
pwm.set_period(1.khz());
65+
66+
asm::bkpt();
5467

5568
let max = pwm.get_max_duty();
5669

57-
pwm.enable();
70+
//// Operations affecting single channels can be accessed through
71+
//// the Pwm object or via dereferencing to the pin.
72+
73+
// Use the Pwm object to set C3 to full strength
74+
pwm.set_duty(Channel::C3, max);
75+
76+
asm::bkpt();
77+
78+
// Use the Pwm object to set C3 to be dim
79+
pwm.set_duty(Channel::C3, max / 4);
80+
81+
asm::bkpt();
82+
83+
// Use the Pwm object to set C3 to be zero
84+
pwm.set_duty(Channel::C3, 0);
85+
86+
asm::bkpt();
87+
88+
89+
// Extract the PwmChannel for C3
90+
let mut pwm_channel = pwm.split().2;
5891

59-
// full
60-
pwm.set_duty(max);
92+
// Use the PwmChannel object to set C3 to be full strength
93+
pwm_channel.set_duty(max);
6194

6295
asm::bkpt();
6396

64-
// dim
65-
pwm.set_duty(max / 4);
97+
// Use the PwmChannel object to set C3 to be dim
98+
pwm_channel.set_duty(max / 4);
6699

67100
asm::bkpt();
68101

69-
// zero
70-
pwm.set_duty(0);
102+
// Use the PwmChannel object to set C3 to be zero
103+
pwm_channel.set_duty(0);
71104

72105
asm::bkpt();
73106

examples/pwm_custom.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,31 @@ fn main() -> ! {
3333
let p0 = pb4.into_alternate_push_pull(&mut gpiob.crl);
3434
let p1 = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl);
3535

36-
let mut pwm = Timer::tim3(p.TIM3, &clocks, &mut rcc.apb1)
36+
let pwm = Timer::tim3(p.TIM3, &clocks, &mut rcc.apb1)
3737
.pwm((p0, p1), &mut afio.mapr, 1.khz());
3838

39-
let max = pwm.0.get_max_duty();
39+
let max = pwm.get_max_duty();
4040

41-
pwm.0.enable();
42-
pwm.1.enable();
41+
let mut pwm_channels = pwm.split();
42+
43+
// Enable the individual channels
44+
pwm_channels.0.enable();
45+
pwm_channels.1.enable();
4346

4447
// full
45-
pwm.0.set_duty(max);
46-
pwm.1.set_duty(max);
48+
pwm_channels.0.set_duty(max);
49+
pwm_channels.1.set_duty(max);
4750

4851
asm::bkpt();
4952

5053
// dim
51-
pwm.1.set_duty(max / 4);
54+
pwm_channels.1.set_duty(max / 4);
5255

5356
asm::bkpt();
5457

5558
// zero
56-
pwm.0.set_duty(0);
57-
pwm.1.set_duty(0);
59+
pwm_channels.0.set_duty(0);
60+
pwm_channels.1.set_duty(0);
5861

5962
asm::bkpt();
6063

src/pwm.rs

Lines changed: 140 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
*/
5555

5656
use core::marker::PhantomData;
57+
use core::marker::{Copy};
5758
use core::mem;
5859

5960
use crate::hal;
@@ -68,6 +69,7 @@ use crate::afio::MAPR;
6869
use crate::bb;
6970
use crate::gpio::{self, Alternate, PushPull};
7071
use crate::time::Hertz;
72+
use crate::time::U32Ext;
7173
use crate::timer::Timer;
7274

7375
pub trait Pins<REMAP, P> {
@@ -76,6 +78,26 @@ pub trait Pins<REMAP, P> {
7678
const C3: bool = false;
7779
const C4: bool = false;
7880
type Channels;
81+
82+
fn check_used(c: Channel) -> Channel {
83+
if (c == Channel::C1 && Self::C1)
84+
|| (c == Channel::C2 && Self::C2)
85+
|| (c == Channel::C3 && Self::C3)
86+
|| (c == Channel::C4 && Self::C4)
87+
{
88+
c
89+
} else {
90+
panic!("Unused channel")
91+
}
92+
}
93+
}
94+
95+
#[derive(Clone, Copy, PartialEq)]
96+
pub enum Channel {
97+
C1,
98+
C2,
99+
C3,
100+
C4,
79101
}
80102

81103
use crate::timer::sealed::{Ch1, Ch2, Ch3, Ch4, Remap};
@@ -89,7 +111,7 @@ macro_rules! pins_impl {
89111
$($PINX: $TRAIT<REMAP> + gpio::Mode<Alternate<PushPull>>,)+
90112
{
91113
$(const $ENCHX: bool = true;)+
92-
type Channels = ($(Pwm<TIM, $ENCHX>),+);
114+
type Channels = ($(PwmChannel<TIM, $ENCHX>),+);
93115
}
94116
)+
95117
};
@@ -115,7 +137,7 @@ pins_impl!(
115137

116138
#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "stm32f105",))]
117139
impl Timer<TIM1> {
118-
pub fn pwm<REMAP, P, PINS, T>(self, _pins: PINS, mapr: &mut MAPR, freq: T) -> PINS::Channels
140+
pub fn pwm<REMAP, P, PINS, T>(self, _pins: PINS, mapr: &mut MAPR, freq: T) -> Pwm<TIM1, REMAP, P, PINS>
119141
where
120142
REMAP: Remap<Periph = TIM1>,
121143
PINS: Pins<REMAP, P>,
@@ -133,7 +155,7 @@ impl Timer<TIM1> {
133155
}
134156

135157
impl Timer<TIM2> {
136-
pub fn pwm<REMAP, P, PINS, T>(self, _pins: PINS, mapr: &mut MAPR, freq: T) -> PINS::Channels
158+
pub fn pwm<REMAP, P, PINS, T>(self, _pins: PINS, mapr: &mut MAPR, freq: T) -> Pwm<TIM2, REMAP, P, PINS>
137159
where
138160
REMAP: Remap<Periph = TIM2>,
139161
PINS: Pins<REMAP, P>,
@@ -147,7 +169,7 @@ impl Timer<TIM2> {
147169
}
148170

149171
impl Timer<TIM3> {
150-
pub fn pwm<REMAP, P, PINS, T>(self, _pins: PINS, mapr: &mut MAPR, freq: T) -> PINS::Channels
172+
pub fn pwm<REMAP, P, PINS, T>(self, _pins: PINS, mapr: &mut MAPR, freq: T) -> Pwm<TIM3, REMAP, P, PINS>
151173
where
152174
REMAP: Remap<Periph = TIM3>,
153175
PINS: Pins<REMAP, P>,
@@ -162,7 +184,7 @@ impl Timer<TIM3> {
162184

163185
#[cfg(feature = "medium")]
164186
impl Timer<TIM4> {
165-
pub fn pwm<REMAP, P, PINS, T>(self, _pins: PINS, mapr: &mut MAPR, freq: T) -> PINS::Channels
187+
pub fn pwm<REMAP, P, PINS, T>(self, _pins: PINS, mapr: &mut MAPR, freq: T) -> Pwm<TIM4, REMAP, P, PINS>
166188
where
167189
REMAP: Remap<Periph = TIM4>,
168190
PINS: Pins<REMAP, P>,
@@ -175,7 +197,26 @@ impl Timer<TIM4> {
175197
}
176198
}
177199

178-
pub struct Pwm<TIM, CHANNEL> {
200+
pub struct Pwm<TIM, REMAP, P, PINS>
201+
where
202+
REMAP: Remap<Periph = TIM>,
203+
PINS: Pins<REMAP, P>
204+
{
205+
clk: Hertz,
206+
_pins: PhantomData<(TIM, REMAP, P, PINS)>,
207+
}
208+
209+
impl<TIM, REMAP, P, PINS> Pwm<TIM, REMAP, P, PINS>
210+
where
211+
REMAP: Remap<Periph = TIM>,
212+
PINS: Pins<REMAP, P>
213+
{
214+
pub fn split(self) -> PINS::Channels {
215+
unsafe { mem::MaybeUninit::uninit().assume_init() }
216+
}
217+
}
218+
219+
pub struct PwmChannel<TIM, CHANNEL> {
179220
_channel: PhantomData<CHANNEL>,
180221
_tim: PhantomData<TIM>,
181222
}
@@ -193,7 +234,7 @@ macro_rules! hal {
193234
_pins: PINS,
194235
freq: Hertz,
195236
clk: Hertz,
196-
) -> PINS::Channels
237+
) -> Pwm<$TIMX, REMAP, P, PINS>
197238
where
198239
REMAP: Remap<Periph = $TIMX>,
199240
PINS: Pins<REMAP, P>,
@@ -240,10 +281,97 @@ macro_rules! hal {
240281
.set_bit()
241282
);
242283

243-
unsafe { mem::MaybeUninit::uninit().assume_init() }
284+
Pwm {
285+
clk: clk,
286+
_pins: PhantomData
287+
}
288+
}
289+
290+
/*
291+
The following implemention of the embedded_hal::Pwm uses Hertz as a time type. This was choosen
292+
because of the timescales of operations being on the order of nanoseconds and not being able to
293+
efficently represent a float on the hardware. It might be possible to change the time type to
294+
a different time based using such as the nanosecond. The issue with doing so is that the max
295+
delay would then be at just a little over 2 seconds because of the 32 bit depth of the number.
296+
Using milliseconds is also an option, however, using this as a base unit means that only there
297+
could be resolution issues when trying to get a specific value, because of the integer nature.
298+
299+
To find a middle ground, the Hertz type is used as a base here and the Into trait has been
300+
defined for several base time units. This will allow for calling the set_period method with
301+
something that is natural to both the MCU and the end user.
302+
*/
303+
impl<REMAP, P, PINS> hal::Pwm for Pwm<$TIMX, REMAP, P, PINS> where
304+
REMAP: Remap<Periph = $TIMX>,
305+
PINS: Pins<REMAP, P>,
306+
{
307+
type Channel = Channel;
308+
type Duty = u16;
309+
type Time = Hertz;
310+
311+
fn enable(&mut self, channel: Self::Channel) {
312+
match PINS::check_used(channel) {
313+
Channel::C1 => unsafe { bb::set(&(*$TIMX::ptr()).ccer, 0) },
314+
Channel::C2 => unsafe { bb::set(&(*$TIMX::ptr()).ccer, 4) },
315+
Channel::C3 => unsafe { bb::set(&(*$TIMX::ptr()).ccer, 8) },
316+
Channel::C4 => unsafe { bb::set(&(*$TIMX::ptr()).ccer, 12) }
317+
}
318+
}
319+
320+
fn disable(&mut self, channel: Self::Channel) {
321+
match PINS::check_used(channel) {
322+
Channel::C1 => unsafe { bb::clear(&(*$TIMX::ptr()).ccer, 0) },
323+
Channel::C2 => unsafe { bb::clear(&(*$TIMX::ptr()).ccer, 4) },
324+
Channel::C3 => unsafe { bb::clear(&(*$TIMX::ptr()).ccer, 8) },
325+
Channel::C4 => unsafe { bb::clear(&(*$TIMX::ptr()).ccer, 12) },
326+
}
327+
}
328+
329+
fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
330+
match PINS::check_used(channel) {
331+
Channel::C1 => unsafe { (*$TIMX::ptr()).ccr1.read().ccr().bits() },
332+
Channel::C2 => unsafe { (*$TIMX::ptr()).ccr2.read().ccr().bits() },
333+
Channel::C3 => unsafe { (*$TIMX::ptr()).ccr3.read().ccr().bits() },
334+
Channel::C4 => unsafe { (*$TIMX::ptr()).ccr4.read().ccr().bits() },
335+
}
336+
}
337+
338+
fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
339+
match PINS::check_used(channel) {
340+
Channel::C1 => unsafe { (*$TIMX::ptr()).ccr1.write(|w| w.ccr().bits(duty)) },
341+
Channel::C2 => unsafe { (*$TIMX::ptr()).ccr2.write(|w| w.ccr().bits(duty)) },
342+
Channel::C3 => unsafe { (*$TIMX::ptr()).ccr3.write(|w| w.ccr().bits(duty)) },
343+
Channel::C4 => unsafe { (*$TIMX::ptr()).ccr4.write(|w| w.ccr().bits(duty)) },
344+
}
345+
}
346+
347+
fn get_max_duty(&self) -> Self::Duty {
348+
unsafe { (*$TIMX::ptr()).arr.read().arr().bits() }
349+
}
350+
351+
fn get_period(&self) -> Self::Time {
352+
let clk = self.clk;
353+
let psc: u16 = unsafe{(*$TIMX::ptr()).psc.read().psc().bits()};
354+
let arr: u16 = unsafe{(*$TIMX::ptr()).psc.read().psc().bits()};
355+
356+
// Length in ms of an internal clock pulse
357+
(clk.0 / u32(psc * arr)).hz()
358+
}
359+
360+
fn set_period<T>(&mut self, period: T) where
361+
T: Into<Self::Time> {
362+
let clk = self.clk;
363+
364+
let ticks = clk.0 / period.into().0;
365+
let psc = u16(ticks / (1 << 16)).unwrap();
366+
let arr = u16(ticks / u32(psc + 1)).unwrap();
367+
unsafe {
368+
(*$TIMX::ptr()).psc.write(|w| w.psc().bits(psc));
369+
(*$TIMX::ptr()).arr.write(|w| w.arr().bits(arr));
370+
}
371+
}
244372
}
245373

246-
impl hal::PwmPin for Pwm<$TIMX, C1> {
374+
impl hal::PwmPin for PwmChannel<$TIMX, C1> {
247375
type Duty = u16;
248376

249377
fn disable(&mut self) {
@@ -267,7 +395,7 @@ macro_rules! hal {
267395
}
268396
}
269397

270-
impl hal::PwmPin for Pwm<$TIMX, C2> {
398+
impl hal::PwmPin for PwmChannel<$TIMX, C2> {
271399
type Duty = u16;
272400

273401
fn disable(&mut self) {
@@ -291,7 +419,7 @@ macro_rules! hal {
291419
}
292420
}
293421

294-
impl hal::PwmPin for Pwm<$TIMX, C3> {
422+
impl hal::PwmPin for PwmChannel<$TIMX, C3> {
295423
type Duty = u16;
296424

297425
fn disable(&mut self) {
@@ -315,7 +443,7 @@ macro_rules! hal {
315443
}
316444
}
317445

318-
impl hal::PwmPin for Pwm<$TIMX, C4> {
446+
impl hal::PwmPin for PwmChannel<$TIMX, C4> {
319447
type Duty = u16;
320448

321449
fn disable(&mut self) {

0 commit comments

Comments
 (0)