Skip to content

Commit 2fb7418

Browse files
authored
Merge pull request #45 from nickray/pwm
Intial PWM implementation, currently supporting tim2.
2 parents 1360c0a + 54cd77c commit 2fb7418

File tree

6 files changed

+477
-3
lines changed

6 files changed

+477
-3
lines changed

examples/pwm.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//! Testing PWM output
2+
3+
#![deny(unsafe_code)]
4+
#![deny(warnings)]
5+
#![no_main]
6+
#![no_std]
7+
8+
extern crate panic_halt;
9+
10+
// use cortex_m::asm;
11+
use stm32l4xx_hal::{
12+
prelude::*,
13+
stm32,
14+
delay,
15+
};
16+
use cortex_m_rt::entry;
17+
18+
#[entry]
19+
fn main() -> ! {
20+
let c = cortex_m::Peripherals::take().unwrap();
21+
let p = stm32::Peripherals::take().unwrap();
22+
23+
let mut flash = p.FLASH.constrain();
24+
let mut rcc = p.RCC.constrain();
25+
26+
let clocks = rcc.cfgr.freeze(&mut flash.acr);
27+
28+
let mut gpioa = p.GPIOA.split(&mut rcc.ahb2);
29+
30+
// TIM2
31+
let c1 = gpioa.pa0
32+
.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper)
33+
.into_af1(&mut gpioa.moder, &mut gpioa.afrl);
34+
let c2 = gpioa.pa1
35+
.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper)
36+
.into_af1(&mut gpioa.moder, &mut gpioa.afrl);
37+
let c3 = gpioa.pa2
38+
.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper)
39+
.into_af1(&mut gpioa.moder, &mut gpioa.afrl);
40+
let c4 = gpioa.pa3
41+
.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper)
42+
.into_af1(&mut gpioa.moder, &mut gpioa.afrl);
43+
44+
let mut pwm = p
45+
.TIM2
46+
.pwm(
47+
(c1, c2, c3, c4),
48+
1.khz(),
49+
clocks,
50+
&mut rcc.apb1r1,
51+
)
52+
.3;
53+
54+
let max = pwm.get_max_duty();
55+
56+
pwm.enable();
57+
58+
let mut timer = delay::Delay::new(c.SYST, clocks);
59+
let second: u32 = 100;
60+
61+
// NB: if the pins are LEDs, brightness is not
62+
// linear in duty value.
63+
loop {
64+
pwm.set_duty(max);
65+
timer.delay_ms(second);
66+
// asm::bkpt();
67+
68+
pwm.set_duty(max/11*10);
69+
timer.delay_ms(second);
70+
// asm::bkpt();
71+
72+
pwm.set_duty(3*max/4);
73+
timer.delay_ms(second);
74+
// asm::bkpt();
75+
76+
pwm.set_duty(max/2);
77+
timer.delay_ms(second);
78+
// asm::bkpt();
79+
80+
pwm.set_duty(max/4);
81+
timer.delay_ms(second);
82+
// asm::bkpt();
83+
84+
}
85+
}

src/bb.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//! Bit banding
2+
3+
// seems this is not a HAL trait (yet?)
4+
// TODO: why is it used
5+
//
6+
// "The processor memory map includes two bit-band regions.
7+
// These occupy the lowest 1MB of the SRAM and Peripheral memory regions respectively.
8+
// These bit-band regions map each word in an alias region of memory to a bit
9+
// in a bit-band region of memory."
10+
//
11+
// This module only handles peripheral, not SRAM bit-banding
12+
13+
use core::ptr;
14+
15+
pub fn clear<T>(register: *const T, bit: u8) {
16+
write(register, bit, false);
17+
}
18+
19+
pub fn set<T>(register: *const T, bit: u8) {
20+
write(register, bit, true);
21+
}
22+
23+
pub fn write<T>(register: *const T, bit: u8, set: bool) {
24+
let addr = register as usize;
25+
26+
// Peripheral memory starts at 0x4000_0000, the first megabyte is aliased.
27+
//
28+
// Bit-band region is first megabyte
29+
assert!(addr >= 0x4000_0000 && addr <= 0x4010_0000);
30+
assert!(bit < 32);
31+
32+
let bit = bit as usize;
33+
// bit_word_addr = bit_band_base + (byte_offset x 32) + (bit_number × 4)
34+
let bb_addr = (0x4200_0000 + (addr - 0x4000_0000) * 32) + 4 * bit;
35+
unsafe { ptr::write_volatile(bb_addr as *mut u32, if set { 1 } else { 0 }) }
36+
}

src/gpio.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ pub struct Alternate<AF, MODE>
4545
_mode: PhantomData<MODE>,
4646
}
4747

48+
pub enum State {
49+
High,
50+
Low,
51+
}
52+
4853
/// Alternate function 0 (type state)
4954
pub struct AF0;
5055

@@ -106,8 +111,10 @@ macro_rules! gpio {
106111

107112
use crate::rcc::AHB2;
108113
use super::{
109-
Alternate, AF4, AF5, AF6, AF7, AF8, AF9, Floating, GpioExt, Input, OpenDrain, Output,
110-
PullDown, PullUp, PushPull,
114+
Alternate,
115+
AF1, AF4, AF5, AF6, AF7, AF8, AF9,
116+
Floating, GpioExt, Input, OpenDrain, Output,
117+
PullDown, PullUp, PushPull, State,
111118
};
112119

113120
/// GPIO parts
@@ -231,6 +238,29 @@ macro_rules! gpio {
231238
}
232239

233240
impl<MODE> $PXi<MODE> {
241+
/// Configures the pin to serve as alternate function 1 (AF1)
242+
pub fn into_af1(
243+
self,
244+
moder: &mut MODER,
245+
afr: &mut $AFR,
246+
) -> $PXi<Alternate<AF1, MODE>> {
247+
let offset = 2 * $i;
248+
249+
// alternate function mode
250+
let mode = 0b10;
251+
moder.moder().modify(|r, w| unsafe {
252+
w.bits((r.bits() & !(0b11 << offset)) | (mode << offset))
253+
});
254+
255+
let af = 1;
256+
let offset = 4 * ($i % 8);
257+
afr.afr().modify(|r, w| unsafe {
258+
w.bits((r.bits() & !(0b1111 << offset)) | (af << offset))
259+
});
260+
261+
$PXi { _mode: PhantomData }
262+
}
263+
234264
/// Configures the pin to serve as alternate function 4 (AF4)
235265
pub fn into_af4(
236266
self,
@@ -458,11 +488,33 @@ macro_rules! gpio {
458488
}
459489

460490
/// Configures the pin to operate as an push pull output pin
491+
/// Initial state will be low
461492
pub fn into_push_pull_output(
462493
self,
463494
moder: &mut MODER,
464495
otyper: &mut OTYPER,
465496
) -> $PXi<Output<PushPull>> {
497+
self.into_push_pull_output_with_state(moder, otyper, State::Low)
498+
}
499+
500+
/// Configures the pin to operate as an push pull output pin
501+
/// Initial state can be chosen to be high or low
502+
pub fn into_push_pull_output_with_state(
503+
self,
504+
moder: &mut MODER,
505+
otyper: &mut OTYPER,
506+
initial_state: State,
507+
) -> $PXi<Output<PushPull>> {
508+
let mut res = $PXi { _mode: PhantomData };
509+
510+
// set pin high/low before activating, to prevent
511+
// spurious signals (e.g. LED flash)
512+
// TODO: I still see a flash of LED using this order
513+
match initial_state {
514+
State::High => res.set_high(),
515+
State::Low => res.set_low(),
516+
}
517+
466518
let offset = 2 * $i;
467519

468520
// general purpose output mode
@@ -476,7 +528,7 @@ macro_rules! gpio {
476528
.otyper()
477529
.modify(|r, w| unsafe { w.bits(r.bits() & !(0b1 << $i)) });
478530

479-
$PXi { _mode: PhantomData }
531+
res
480532
}
481533

482534
/// Configures the pin to operate as an touch sample

src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,19 @@ pub mod timer;
165165
feature = "stm32l4x6"
166166
))]
167167
pub mod tsc;
168+
#[cfg(any(
169+
feature = "stm32l4x1",
170+
feature = "stm32l4x2",
171+
feature = "stm32l4x3",
172+
feature = "stm32l4x5",
173+
feature = "stm32l4x6"
174+
))]
175+
pub mod bb;
176+
#[cfg(any(
177+
feature = "stm32l4x1",
178+
feature = "stm32l4x2",
179+
feature = "stm32l4x3",
180+
feature = "stm32l4x5",
181+
feature = "stm32l4x6"
182+
))]
183+
pub mod pwm;

src/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ pub use crate::datetime::U32Ext as _stm32l4_hal_datetime_U32Ext;
1010
pub use crate::dma::DmaExt as _stm32l4_hal_DmaExt;
1111
pub use crate::pwr::PwrExt as _stm32l4_hal_PwrExt;
1212
pub use crate::rng::RngExt as _stm32l4_hal_RngExt;
13+
pub use crate::pwm::PwmExt as _stm32l4_hal_PwmExt;

0 commit comments

Comments
 (0)