From 79e17d8f2821743681f4a9365687d130391fa3bb Mon Sep 17 00:00:00 2001
From: Per Lindgren <per.lindgren@ltu.se>
Date: Tue, 12 Jan 2021 17:06:07 +0100
Subject: [PATCH] pwm wip take 2
---
examples/rtt-pwm-dma.rs | 26 -------
examples/rtt-pwm-saw.rs | 149 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 149 insertions(+), 26 deletions(-)
create mode 100644 examples/rtt-pwm-saw.rs
diff --git a/examples/rtt-pwm-dma.rs b/examples/rtt-pwm-dma.rs
index dc9cc7f..1c169a9 100644
--- a/examples/rtt-pwm-dma.rs
+++ b/examples/rtt-pwm-dma.rs
@@ -46,18 +46,6 @@ const APP: () = {
// At this point it has been contrained into SysConf and used to set clocks
let rcc = unsafe { &(*stm32::RCC::ptr()) };
- // // pwm_all_channels!(TIM1: (tim1, apb2enr, apb2rstr, 0u8, pclk2, ppre2));
-
- // // Enable and reset the timer peripheral,
- // // it's the same bit position for both registers (0 in this case)
- // // Notice the use of bit banding to set/clear bits individually
- // // It is unsafe, as the register address could be anything within range
- // // of the bitband region
- // unsafe {
- // bb::set(&rcc.apb2enr, 0u8);
- // bb::set(&rcc.apb2rstr, 0u8);
- // bb::clear(&rcc.apb2rstr, 0u8);
- // }
rcc.apb2enr.modify(|_, w| w.tim1en().set_bit());
rcc.apb2rstr.modify(|_, w| w.tim1rst().set_bit());
rcc.apb2rstr.modify(|_, w| w.tim1rst().clear_bit());
@@ -151,17 +139,3 @@ const APP: () = {
}
}
};
-
-mod dma_pwm {
- // Not sure we need a dma mode
- // DMA mode
- // #[derive(Debug, Clone, Copy)]
- // pub enum Dma {
- // /// No DMA, disabled
- // Disabled,
- // /// Single DMA, DMA will be disabled after each conversion sequence
- // Single,
- // /// Continuous DMA, DMA will remain enabled after conversion
- // Continuous,
- // }
-}
diff --git a/examples/rtt-pwm-saw.rs b/examples/rtt-pwm-saw.rs
new file mode 100644
index 0000000..3a58134
--- /dev/null
+++ b/examples/rtt-pwm-saw.rs
@@ -0,0 +1,149 @@
+//! examples/rtt-pwm-saw.rs
+//! cargo run --examples rtt-pwm-saw
+
+// #![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+// use cortex_m::{asm, peripheral::DWT};
+use panic_halt as _;
+use rtt_target::{rprint, rprintln, rtt_init_print};
+use stm32f4xx_hal::{bb, gpio::Speed, prelude::*, stm32};
+
+#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true)]
+const APP: () = {
+ #[init]
+ fn init(mut cx: init::Context) {
+ rtt_init_print!();
+ rprintln!("init");
+ let dp = cx.device;
+
+ // Initialize (enable) the monotonic timer (CYCCNT)
+ cx.core.DCB.enable_trace();
+ cx.core.DWT.enable_cycle_counter();
+
+ let rcc = dp.RCC.constrain();
+ // Set up the system clock. 48 MHz?
+ let clocks = rcc
+ .cfgr
+ // .use_hse(8.mhz())
+ .sysclk(48.mhz())
+ .pclk1(24.mhz())
+ .freeze();
+
+ let gpioa = dp.GPIOA.split();
+ // we set the pins to VeryHigh to get the sharpest waveform possible
+ // (rise and fall times should have similar characteristics)
+ let _channels = (
+ gpioa.pa8.into_alternate_af1().set_speed(Speed::VeryHigh),
+ gpioa.pa9.into_alternate_af1().set_speed(Speed::VeryHigh),
+ );
+
+ // Setup PWM RAW
+ let tim1 = dp.TIM1;
+ // Here we need unsafe as we are "stealing" the RCC peripheral
+ // (At this point it has been constrained into SysConf and used to set clocks.)
+ let rcc = unsafe { &(*stm32::RCC::ptr()) };
+
+ // unsafe {
+ // bb::set(&rcc.apb2enr, 0u8);
+ // bb::set(&rcc.apb2rstr, 0u8);
+ // bb::clear(&rcc.apb2rstr, 0u8);
+ // }
+ //
+ // For some reason bb:: hangs target in release mode,
+ // so I implemented it using modify instead
+ rcc.apb2enr.modify(|_, w| w.tim1en().set_bit());
+ rcc.apb2rstr.modify(|_, w| w.tim1rst().set_bit());
+ rcc.apb2rstr.modify(|_, w| w.tim1rst().clear_bit());
+
+ // Setup chanel 1 and 2 as pwm_mode1
+ tim1.ccmr1_output()
+ .modify(|_, w| w.oc1pe().set_bit().oc1m().pwm_mode1());
+
+ tim1.ccmr1_output()
+ .modify(|_, w| w.oc2pe().set_bit().oc2m().pwm_mode1());
+
+ // The reference manual is a bit ambiguous about when enabling this bit is really
+ // necessary, but since we MUST enable the preload for the output channels then we
+ // might as well enable for the auto-reload too
+ tim1.cr1.modify(|_, w| w.arpe().set_bit());
+
+ let clk = clocks.pclk2().0 * if clocks.ppre2() == 1 { 1 } else { 2 };
+ // check that its actually 48_000_000
+ rprintln!("clk {}", clk);
+
+ // we want maximum performance, thus we set the prescaler to 0
+ let pre = 0;
+ rprintln!("pre {}", pre);
+ tim1.psc.write(|w| w.psc().bits(pre));
+
+ // we want 8 bits of resolution
+ // so our ARR = 2^8 - 1 = 256 - 1 = 255
+ let arr = 255;
+ rprintln!("arr {}", arr);
+ tim1.arr.write(|w| unsafe { w.bits(arr) });
+
+ // Trigger update event to load the registers
+ tim1.cr1.modify(|_, w| w.urs().set_bit());
+ tim1.egr.write(|w| w.ug().set_bit());
+ tim1.cr1.modify(|_, w| w.urs().clear_bit());
+
+ // Set main output enable of all Output Compare (OC) registers
+ tim1.bdtr.modify(|_, w| w.moe().set_bit());
+
+ // Set output enable for channels 1 and 2
+ // Channel 1 (bit 0)
+ unsafe { bb::set(&tim1.ccer, 0) }
+ // Channel 4 (bit 0)
+ unsafe { bb::set(&tim1.ccer, 4) }
+
+ // Setup the timer
+ tim1.cr1.write(|w| {
+ w.cms()
+ .bits(0b00) // edge aligned mode
+ .dir() // counter used as upcounter
+ .clear_bit()
+ .opm() // one pulse mode
+ .clear_bit()
+ .cen() // enable counter
+ .set_bit()
+ });
+
+ // Set duty cycle of Channels
+ tim1.ccr1.write(|w| unsafe { w.ccr().bits(128) });
+ tim1.ccr2.write(|w| unsafe { w.ccr().bits(128) });
+
+ // Set preload for the CCx
+ tim1.cr2.write(|w| w.ccpc().set_bit());
+
+ tim1.dier.write(|w| w.uie().enabled());
+ tim1.sr.modify(|_, w| w.uif().clear());
+
+ while tim1.sr.read().uif().is_clear() {
+ rprint!("-");
+ }
+ rprintln!("here");
+ tim1.sr.modify(|_, w| w.uif().clear());
+
+ loop {
+ for i in 0..256 {
+ // wait until next update event
+ while tim1.sr.read().uif().is_clear() {}
+ tim1.sr.modify(|_, w| w.uif().clear());
+
+ tim1.ccr1.write(|w| unsafe { w.ccr().bits(i) });
+ tim1.ccr2.write(|w| unsafe { w.ccr().bits(i) });
+ }
+ }
+ }
+
+ #[idle]
+ fn idle(_cx: idle::Context) -> ! {
+ rprintln!("idle");
+ loop {
+ continue;
+ }
+ }
+};
--
GitLab