Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add timer implementation #5

Merged
merged 7 commits into from
Dec 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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),
}