Skip to content

Commit

Permalink
Merge pull request #5 from david-sawatzke/timers
Browse files Browse the repository at this point in the history
Add timer implementation
  • Loading branch information
therealprof authored Dec 18, 2018
2 parents 2814d5b + 200543b commit 7f7a7d6
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ matrix:
fast_finish: true
script:
- rustup target add thumbv6m-none-eabi
- cargo build --features=$MCU
- cargo build --features=$MCU --examples --release
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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", "rt"]

[[example]]
name = "i2c_hal_ina260reader"
required-features = ["stm32f042"]

[[example]]
name = "spi_hal_apa102c"
required-features = ["stm32f042"]
40 changes: 40 additions & 0 deletions examples/blinky_timer.rs
Original file line number Diff line number Diff line change
@@ -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;
}
}
4 changes: 2 additions & 2 deletions memory.x
Original file line number Diff line number Diff line change
@@ -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. */
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ pub mod rcc;
pub mod serial;
pub mod spi;
pub mod time;
pub mod timers;
238 changes: 238 additions & 0 deletions src/timers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
//! 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")]
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<TIM> {
clocks: Clocks,
tim: TIM,
}

/// Interrupt events
pub enum Event {
/// Timer timed out / count down ended
TimeOut,
}

impl Timer<SYST> {
/// Configures the SYST clock as a periodic count down timer
pub fn syst<T>(mut syst: SYST, timeout: T, clocks: Clocks) -> Self
where
T: Into<Hertz>,
{
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<SYST> {
type Time = Hertz;

/// Start the timer with a `timeout`
fn start<T>(&mut self, timeout: T)
where
T: Into<Hertz>,
{
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();
}

/// 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(())
} else {
Err(nb::Error::WouldBlock)
}
}
}

impl Periodic for Timer<SYST> {}

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<T>(tim: $TIM, timeout: T, clocks: Clocks) -> Self
where
T: Into<Hertz>,
{
// 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;

/// Start the timer with a `timeout`
fn start<T>(&mut self, timeout: T)
where
T: Into<Hertz>,
{
// 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());
}

/// 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)
} 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),
}

0 comments on commit 7f7a7d6

Please sign in to comment.