From 34ce33ccac2b5f5ec761a11077d47f5d0a1b31be Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Tue, 18 Dec 2018 11:33:09 +0100 Subject: [PATCH 1/7] Add timer implementation --- src/lib.rs | 1 + src/timers.rs | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 src/timers.rs diff --git a/src/lib.rs b/src/lib.rs index 8df2a9c..2795a48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,3 +17,4 @@ pub mod rcc; pub mod serial; pub mod spi; pub mod time; +pub mod timers; diff --git a/src/timers.rs b/src/timers.rs new file mode 100644 index 0000000..0d17eb2 --- /dev/null +++ b/src/timers.rs @@ -0,0 +1,206 @@ +#[cfg(feature = "stm32f030")] +use crate::stm32::{RCC, TIM1, TIM14, TIM15, TIM16, TIM17, TIM3, TIM6, TIM7}; +#[cfg(feature = "stm32f042")] +use crate::stm32::{RCC, TIM1, TIM14, TIM16, TIM17, TIM2, TIM3}; +use cortex_m::peripheral::syst::SystClkSource; +use cortex_m::peripheral::SYST; + +use crate::rcc::Clocks; +use cast::{u16, u32}; +use embedded_hal::timer::{CountDown, Periodic}; +use nb; +use void::Void; + +use crate::time::Hertz; + +/// Hardware timers +pub struct Timer { + clocks: Clocks, + tim: TIM, +} + +/// Interrupt events +pub enum Event { + /// Timer timed out / count down ended + TimeOut, +} + +impl Timer { + /// Configures the SYST clock as a periodic count down timer + pub fn syst(mut syst: SYST, timeout: T, clocks: Clocks) -> Self + where + T: Into, + { + syst.set_clock_source(SystClkSource::Core); + let mut timer = Timer { tim: syst, clocks }; + timer.start(timeout); + timer + } + + /// Starts listening for an `event` + pub fn listen(&mut self, event: Event) { + match event { + Event::TimeOut => self.tim.enable_interrupt(), + } + } + + /// Stops listening for an `event` + pub fn unlisten(&mut self, event: Event) { + match event { + Event::TimeOut => self.tim.disable_interrupt(), + } + } +} + +impl CountDown for Timer { + type Time = Hertz; + + fn start(&mut self, timeout: T) + where + T: Into, + { + let rvr = self.clocks.sysclk().0 / timeout.into().0 - 1; + + assert!(rvr < (1 << 24)); + + self.tim.set_reload(rvr); + self.tim.clear_current(); + self.tim.enable_counter(); + } + + fn wait(&mut self) -> nb::Result<(), Void> { + if self.tim.has_wrapped() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } +} + +impl Periodic for Timer {} + +macro_rules! timers { + ($($TIM:ident: ($tim:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => { + $( + impl Timer<$TIM> { + // XXX(why not name this `new`?) bummer: constructors need to have different names + // even if the `$TIM` are non overlapping (compare to the `free` function below + // which just works) + /// Configures a TIM peripheral as a periodic count down timer + pub fn $tim(tim: $TIM, timeout: T, clocks: Clocks) -> Self + where + T: Into, + { + // NOTE(unsafe) This executes only during initialisation + let rcc = unsafe { &(*RCC::ptr()) }; + + // enable and reset peripheral to a clean slate state + rcc.$apbenr.modify(|_, w| w.$timXen().set_bit()); + rcc.$apbrstr.modify(|_, w| w.$timXrst().set_bit()); + rcc.$apbrstr.modify(|_, w| w.$timXrst().clear_bit()); + + let mut timer = Timer { + clocks, + tim, + }; + timer.start(timeout); + + timer + } + + /// Starts listening for an `event` + pub fn listen(&mut self, event: Event) { + match event { + Event::TimeOut => { + // Enable update event interrupt + self.tim.dier.write(|w| w.uie().set_bit()); + } + } + } + + /// Stops listening for an `event` + pub fn unlisten(&mut self, event: Event) { + match event { + Event::TimeOut => { + // Enable update event interrupt + self.tim.dier.write(|w| w.uie().clear_bit()); + } + } + } + + /// Releases the TIM peripheral + pub fn release(self) -> $TIM { + use crate::stm32::RCC; + let rcc = unsafe { &(*RCC::ptr()) }; + // Pause counter + self.tim.cr1.modify(|_, w| w.cen().clear_bit()); + // Disable timer + rcc.$apbenr.modify(|_, w| w.$timXen().clear_bit()); + self.tim + } + } + + impl CountDown for Timer<$TIM> { + type Time = Hertz; + + fn start(&mut self, timeout: T) + where + T: Into, + { + // pause + self.tim.cr1.modify(|_, w| w.cen().clear_bit()); + // restart counter + self.tim.cnt.reset(); + + let frequency = timeout.into().0; + let ticks = self.clocks.pclk().0 / frequency; + + let psc = u16((ticks - 1) / (1 << 16)).unwrap(); + self.tim.psc.write(|w| unsafe { w.psc().bits(psc) }); + + let arr = u16(ticks / u32(psc + 1)).unwrap(); + self.tim.arr.write(|w| unsafe { w.bits(u32(arr)) }); + + // start counter + self.tim.cr1.modify(|_, w| w.cen().set_bit()); + } + + fn wait(&mut self) -> nb::Result<(), Void> { + if self.tim.sr.read().uif().bit_is_clear() { + Err(nb::Error::WouldBlock) + } else { + self.tim.sr.modify(|_, w| w.uif().clear_bit()); + Ok(()) + } + } + } + + impl Periodic for Timer<$TIM> {} + )+ + } +} + +#[cfg(any(feature = "stm32f030", feature = "stm32f042",))] +timers! { + TIM1: (tim1, tim1en, tim1rst, apb2enr, apb2rstr), + TIM3: (tim3, tim3en, tim3rst, apb1enr, apb1rstr), + TIM14: (tim14, tim14en, tim14rst, apb1enr, apb1rstr), + TIM16: (tim16, tim16en, tim16rst, apb2enr, apb2rstr), + TIM17: (tim17, tim17en, tim17rst, apb2enr, apb2rstr), +} + +#[cfg(any(feature = "stm32f030x8", feature = "stm32f030xc"))] +timers! { + TIM6: (tim6, tim6en, tim6rst, apb1enr, apb1rstr), + TIM15: (tim15, tim15en, tim15rst, apb2enr, apb2rstr), +} + +#[cfg(feature = "stm32f030xc")] +timers! { + TIM7: (tim7, tim7en, tim7rst, apb1enr, apb1rstr), +} + +#[cfg(feature = "stm32f042")] +timers! { + TIM2: (tim2, tim2en, tim2rst, apb1enr, apb1rstr), +} From 7af92c3294d78b60c177b38fc89b99dd8617af20 Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Tue, 18 Dec 2018 12:20:14 +0100 Subject: [PATCH 2/7] Add timer example --- examples/blinky_timer.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 examples/blinky_timer.rs diff --git a/examples/blinky_timer.rs b/examples/blinky_timer.rs new file mode 100644 index 0000000..6cf7655 --- /dev/null +++ b/examples/blinky_timer.rs @@ -0,0 +1,40 @@ +#![no_main] +#![no_std] + +use panic_halt; + +use stm32f0xx_hal as hal; + +use crate::hal::prelude::*; +use crate::hal::stm32; +use crate::hal::time::*; +use crate::hal::timers::*; + +use cortex_m_rt::entry; +use nb::block; + +#[entry] +fn main() -> ! { + if let Some(p) = stm32::Peripherals::take() { + let gpioa = p.GPIOA.split(); + /* (Re-)configure PA1 as output */ + let mut led = gpioa.pa1.into_push_pull_output(); + + /* Constrain clocking registers */ + let rcc = p.RCC.constrain(); + + /* Configure clock to 8 MHz (i.e. the default) and freeze it */ + let clocks = rcc.cfgr.sysclk(8.mhz()).freeze(); + + let mut timer = Timer::tim1(p.TIM1, Hertz(1), clocks); + + loop { + led.toggle(); + block!(timer.wait()).ok(); + } + } + + loop { + continue; + } +} From 0841ae8c32e6554db609427309b6b229fced0a4b Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Tue, 18 Dec 2018 12:37:56 +0100 Subject: [PATCH 3/7] Disable some examples on stm32f030 --- Cargo.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index c82ca01..22aea33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,22 @@ debug = true lto = true opt-level = "s" +[[example]] +name = "i2c_hal_ssd1306alphabeter" +required-features = ["stm32f042"] + +[[example]] +name = "i2c_hal_ina260serial" +required-features = ["stm32f042"] + [[example]] name = "led_hal_button_irq" required-features = ["stm32f042"] + +[[example]] +name = "i2c_hal_ina260reader" +required-features = ["stm32f042"] + +[[example]] +name = "spi_hal_apa102c" +required-features = ["stm32f042"] From 9c864d8c374de3a378e829076abed863db632c42 Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Tue, 18 Dec 2018 12:38:34 +0100 Subject: [PATCH 4/7] Make sure the examples build as well --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 749ab5b..e011bc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,4 @@ matrix: fast_finish: true script: - rustup target add thumbv6m-none-eabi - - cargo build --features=$MCU + - cargo build --features=$MCU --examples From 168fdd8aa1e91032328966c81a655053fcd6d4d2 Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Tue, 18 Dec 2018 12:46:04 +0100 Subject: [PATCH 5/7] Use the smallest available flash& ram size Build as release so we can be sure they fit --- .travis.yml | 2 +- memory.x | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e011bc3..143127f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,4 @@ matrix: fast_finish: true script: - rustup target add thumbv6m-none-eabi - - cargo build --features=$MCU --examples + - cargo build --features=$MCU --examples --release diff --git a/memory.x b/memory.x index 51b2492..da8756c 100644 --- a/memory.x +++ b/memory.x @@ -1,8 +1,8 @@ MEMORY { /* NOTE K = KiBi = 1024 bytes */ - FLASH : ORIGIN = 0x08000000, LENGTH = 32K - RAM : ORIGIN = 0x20000000, LENGTH = 6K + FLASH : ORIGIN = 0x08000000, LENGTH = 16K + RAM : ORIGIN = 0x20000000, LENGTH = 4K } /* This is where the call stack will be allocated. */ From d0b3ea7414f14858b845204347e480dbe1569dbd Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Tue, 18 Dec 2018 12:58:25 +0100 Subject: [PATCH 6/7] Require rt for interrupt example --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 22aea33..099dd8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ required-features = ["stm32f042"] [[example]] name = "led_hal_button_irq" -required-features = ["stm32f042"] +required-features = ["stm32f042", "rt"] [[example]] name = "i2c_hal_ina260reader" From 200543b80bedfe41b43114aca9414875fe757ffb Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Tue, 18 Dec 2018 15:02:20 +0100 Subject: [PATCH 7/7] Add documentation to timers --- src/timers.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/timers.rs b/src/timers.rs index 0d17eb2..45c4f3b 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -1,3 +1,29 @@ +//! API for the integrated timers +//! +//! This only implements basic functions, a lot of things are missing +//! +//! # Example +//! Blink the led with 1Hz +//! ``` no_run +//! use stm32f0xx_hal as hal; +//! +//! use crate::hal::stm32; +//! use crate::hal::prelude::*; +//! use crate::hal::time::*; +//! use crate::hal::timers::*; +//! use nb::block; +//! +//! let mut p = stm32::Peripherals::take().unwrap(); +//! +//! let mut led = gpioa.pa1.into_push_pull_pull_output(); +//! let rcc = p.RCC.constrain().cfgr.freeze(); +//! let mut timer = Timer::tim1(p.TIM1, Hertz(1), clocks); +//! loop { +//! led.toggle(); +//! block!(timer.wait()).ok(); +//! } +//! ``` + #[cfg(feature = "stm32f030")] use crate::stm32::{RCC, TIM1, TIM14, TIM15, TIM16, TIM17, TIM3, TIM6, TIM7}; #[cfg(feature = "stm32f042")] @@ -55,6 +81,7 @@ impl Timer { impl CountDown for Timer { type Time = Hertz; + /// Start the timer with a `timeout` fn start(&mut self, timeout: T) where T: Into, @@ -68,6 +95,8 @@ impl CountDown for Timer { self.tim.enable_counter(); } + /// Return `Ok` if the timer has wrapped + /// Automatically clears the flag and restarts the time fn wait(&mut self) -> nb::Result<(), Void> { if self.tim.has_wrapped() { Ok(()) @@ -143,6 +172,7 @@ macro_rules! timers { impl CountDown for Timer<$TIM> { type Time = Hertz; + /// Start the timer with a `timeout` fn start(&mut self, timeout: T) where T: Into, @@ -165,6 +195,8 @@ macro_rules! timers { self.tim.cr1.modify(|_, w| w.cen().set_bit()); } + /// Return `Ok` if the timer has wrapped + /// Automatically clears the flag and restarts the time fn wait(&mut self) -> nb::Result<(), Void> { if self.tim.sr.read().uif().bit_is_clear() { Err(nb::Error::WouldBlock)