Merge pull request #5 from robamu-org/mueller/timer-implementation
Timer and Clock modules added
This commit is contained in:
commit
8e53678d11
@ -8,4 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
- First version of the HAL which adds the GPIO implementation
|
||||
## [0.1.0]
|
||||
|
||||
- First version of the HAL which adds the GPIO implementation and timer implementation.
|
||||
- Also adds some examples and helper files to set up new binary crates
|
||||
|
11
Cargo.toml
11
Cargo.toml
@ -15,6 +15,8 @@ cortex-m = "0.7"
|
||||
cortex-m-rt = "0.7"
|
||||
nb = "1"
|
||||
embedded-hal = { features = ["unproven"], version = "0.2.6" }
|
||||
void = { version = "1.0", default-features = false }
|
||||
once_cell = { version = "1.8.0", default-features = false }
|
||||
|
||||
[dependencies.va108xx]
|
||||
version = "0.1"
|
||||
@ -22,6 +24,11 @@ version = "0.1"
|
||||
[features]
|
||||
rt = ["va108xx/rt"]
|
||||
|
||||
[dev-dependencies]
|
||||
panic-rtt-target = { version = "0.1", features = ["cortex-m"] }
|
||||
rtt-target = { version = "0.3", features = ["cortex-m"] }
|
||||
panic-halt = "0.2"
|
||||
|
||||
[profile.dev]
|
||||
debug = true
|
||||
lto = false
|
||||
@ -30,3 +37,7 @@ lto = false
|
||||
lto = true
|
||||
debug = true
|
||||
opt-level = "s"
|
||||
|
||||
[[example]]
|
||||
name = "timer-ticks"
|
||||
required-features = ["rt"]
|
||||
|
124
examples/timer-ticks.rs
Normal file
124
examples/timer-ticks.rs
Normal file
@ -0,0 +1,124 @@
|
||||
//! MS and Second counter implemented using the TIM0 and TIM1 peripheral
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::cell::Cell;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{
|
||||
clock::{get_sys_clock, set_sys_clock},
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
timer::{CountDownTimer, Event},
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum LibType {
|
||||
Pac,
|
||||
Hal,
|
||||
}
|
||||
|
||||
static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||
static SEC_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let mut last_ms = 0;
|
||||
rprintln!("-- Vorago system ticks using timers --");
|
||||
set_sys_clock(50.mhz().into());
|
||||
let lib_type = LibType::Hal;
|
||||
match lib_type {
|
||||
LibType::Pac => {
|
||||
unsafe {
|
||||
dp.SYSCONFIG
|
||||
.peripheral_clk_enable
|
||||
.modify(|_, w| w.irqsel().set_bit());
|
||||
dp.SYSCONFIG
|
||||
.tim_clk_enable
|
||||
.modify(|r, w| w.bits(r.bits() | (1 << 0) | (1 << 1)));
|
||||
dp.IRQSEL.tim[0].write(|w| w.bits(0x00));
|
||||
dp.IRQSEL.tim[1].write(|w| w.bits(0x01));
|
||||
}
|
||||
|
||||
let sys_clk: Hertz = 50.mhz().into();
|
||||
let cnt_ms = sys_clk.0 / 1000 - 1;
|
||||
let cnt_sec = sys_clk.0 - 1;
|
||||
unsafe {
|
||||
dp.TIM0.cnt_value.write(|w| w.bits(cnt_ms));
|
||||
dp.TIM0.rst_value.write(|w| w.bits(cnt_ms));
|
||||
dp.TIM0.ctrl.write(|w| {
|
||||
w.enable().set_bit();
|
||||
w.irq_enb().set_bit()
|
||||
});
|
||||
dp.TIM1.cnt_value.write(|w| w.bits(cnt_sec));
|
||||
dp.TIM1.rst_value.write(|w| w.bits(cnt_sec));
|
||||
dp.TIM1.ctrl.write(|w| {
|
||||
w.enable().set_bit();
|
||||
w.irq_enb().set_bit()
|
||||
});
|
||||
unmask_irqs();
|
||||
}
|
||||
}
|
||||
LibType::Hal => {
|
||||
let mut ms_timer =
|
||||
CountDownTimer::tim0(&mut dp.SYSCONFIG, get_sys_clock().unwrap(), dp.TIM0);
|
||||
let mut second_timer =
|
||||
CountDownTimer::tim1(&mut dp.SYSCONFIG, get_sys_clock().unwrap(), dp.TIM1);
|
||||
ms_timer.listen(
|
||||
Event::TimeOut,
|
||||
&mut dp.SYSCONFIG,
|
||||
&mut dp.IRQSEL,
|
||||
interrupt::OC0,
|
||||
);
|
||||
second_timer.listen(
|
||||
Event::TimeOut,
|
||||
&mut dp.SYSCONFIG,
|
||||
&mut dp.IRQSEL,
|
||||
interrupt::OC1,
|
||||
);
|
||||
ms_timer.start(1000.hz());
|
||||
second_timer.start(1.hz());
|
||||
unmask_irqs();
|
||||
}
|
||||
}
|
||||
loop {
|
||||
let current_ms = cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get());
|
||||
if current_ms - last_ms >= 1000 {
|
||||
last_ms = current_ms;
|
||||
rprintln!("MS counter: {}", current_ms);
|
||||
let second = cortex_m::interrupt::free(|cs| SEC_COUNTER.borrow(cs).get());
|
||||
rprintln!("Second counter: {}", second);
|
||||
}
|
||||
cortex_m::asm::delay(10000);
|
||||
}
|
||||
}
|
||||
|
||||
fn unmask_irqs() {
|
||||
unsafe {
|
||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC1);
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn OC0() {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||
ms += 1;
|
||||
MS_COUNTER.borrow(cs).set(ms);
|
||||
});
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn OC1() {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||
sec += 1;
|
||||
SEC_COUNTER.borrow(cs).set(sec);
|
||||
});
|
||||
}
|
48
src/clock.rs
Normal file
48
src/clock.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use crate::time::Hertz;
|
||||
use cortex_m::interrupt::{self, Mutex};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use va108xx::SYSCONFIG;
|
||||
|
||||
static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
|
||||
|
||||
pub enum PeripheralClocks {
|
||||
PortA = 0,
|
||||
PortB = 1,
|
||||
Spi0 = 4,
|
||||
Spi1 = 5,
|
||||
Spi2 = 6,
|
||||
UArt0 = 8,
|
||||
Uart1 = 9,
|
||||
I2c0 = 16,
|
||||
I2c1 = 17,
|
||||
Irqsel = 21,
|
||||
Ioconfig = 22,
|
||||
Utility = 23,
|
||||
Gpio = 24,
|
||||
}
|
||||
|
||||
/// The Vorago in powered by an external clock which might have different frequencies.
|
||||
/// The clock can be set here so it can be used by other software components as well.
|
||||
/// The clock can be set exactly once
|
||||
pub fn set_sys_clock(freq: Hertz) {
|
||||
interrupt::free(|cs| {
|
||||
SYS_CLOCK.borrow(cs).set(freq).ok();
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the configured system clock
|
||||
pub fn get_sys_clock() -> Option<Hertz> {
|
||||
interrupt::free(|cs| SYS_CLOCK.borrow(cs).get().copied())
|
||||
}
|
||||
|
||||
pub fn enable_peripheral_clock(syscfg: &mut SYSCONFIG, clock: PeripheralClocks) {
|
||||
syscfg
|
||||
.peripheral_clk_enable
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
|
||||
}
|
||||
|
||||
pub fn disable_peripheral_clock(syscfg: &mut SYSCONFIG, clock: PeripheralClocks) {
|
||||
syscfg
|
||||
.peripheral_clk_enable
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
|
||||
}
|
@ -2,6 +2,10 @@
|
||||
|
||||
pub use va108xx;
|
||||
|
||||
pub mod clock;
|
||||
pub mod gpio;
|
||||
pub mod prelude;
|
||||
pub mod time;
|
||||
pub mod timer;
|
||||
|
||||
pub use va108xx as pac;
|
||||
|
@ -2,3 +2,10 @@
|
||||
pub use embedded_hal::prelude::*;
|
||||
|
||||
pub use crate::gpio::GpioExt as _va108xx_hal_gpio_GpioExt;
|
||||
|
||||
pub use embedded_hal::digital::v2::InputPin as _embedded_hal_gpio_InputPin;
|
||||
pub use embedded_hal::digital::v2::OutputPin as _embedded_hal_gpio_OutputPin;
|
||||
pub use embedded_hal::digital::v2::StatefulOutputPin as _embedded_hal_gpio_StatefulOutputPin;
|
||||
pub use embedded_hal::digital::v2::ToggleableOutputPin as _embedded_hal_gpio_ToggleableOutputPin;
|
||||
|
||||
pub use crate::time::U32Ext as _va108xx_hal_time_U32Ext;
|
||||
|
156
src/time.rs
Normal file
156
src/time.rs
Normal file
@ -0,0 +1,156 @@
|
||||
//! Time units
|
||||
//!
|
||||
//! See [`Hertz`], [`KiloHertz`] and [`MegaHertz`] for creating increasingly higher frequencies.
|
||||
//!
|
||||
//! The [`U32Ext`] trait adds various methods like `.hz()`, `.mhz()`, etc to the `u32` primitive type,
|
||||
//! allowing it to be converted into frequencies.
|
||||
|
||||
/// Bits per second
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
||||
pub struct Bps(pub u32);
|
||||
|
||||
/// Hertz
|
||||
///
|
||||
/// Create a frequency specified in [Hertz](https://en.wikipedia.org/wiki/Hertz).
|
||||
///
|
||||
/// See also [`KiloHertz`] and [`MegaHertz`] for semantically correct ways of creating higher
|
||||
/// frequencies.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Create an 60 Hz frequency
|
||||
///
|
||||
/// ```rust
|
||||
/// use stm32f1xx_hal::time::Hertz;
|
||||
///
|
||||
/// let freq = 60.hz();
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
||||
pub struct Hertz(pub u32);
|
||||
|
||||
/// Kilohertz
|
||||
///
|
||||
/// Create a frequency specified in kilohertz.
|
||||
///
|
||||
/// See also [`Hertz`] and [`MegaHertz`] for semantically correct ways of creating lower or higher
|
||||
/// frequencies.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Create a 100 Khz frequency
|
||||
///
|
||||
/// This example creates a 100 KHz frequency. This could be used to set an I2C data rate or PWM
|
||||
/// frequency, etc.
|
||||
///
|
||||
/// ```rust
|
||||
/// use stm32f1xx_hal::time::Hertz;
|
||||
///
|
||||
/// let freq = 100.khz();
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
||||
pub struct KiloHertz(pub u32);
|
||||
|
||||
/// Megahertz
|
||||
///
|
||||
/// Create a frequency specified in megahertz.
|
||||
///
|
||||
/// See also [`Hertz`] and [`KiloHertz`] for semantically correct ways of creating lower
|
||||
/// frequencies.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Create a an 8 MHz frequency
|
||||
///
|
||||
/// This example creates an 8 MHz frequency that could be used to configure an SPI peripheral, etc.
|
||||
///
|
||||
/// ```rust
|
||||
/// use stm32f1xx_hal::time::Hertz;
|
||||
///
|
||||
/// let freq = 8.mhz();
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
||||
pub struct MegaHertz(pub u32);
|
||||
|
||||
/// Time unit
|
||||
#[derive(PartialEq, PartialOrd, Clone, Copy)]
|
||||
pub struct MilliSeconds(pub u32);
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Clone, Copy)]
|
||||
pub struct MicroSeconds(pub u32);
|
||||
|
||||
/// Extension trait that adds convenience methods to the `u32` type
|
||||
pub trait U32Ext {
|
||||
/// Wrap in `Bps`
|
||||
fn bps(self) -> Bps;
|
||||
|
||||
/// Wrap in `Hertz`
|
||||
fn hz(self) -> Hertz;
|
||||
|
||||
/// Wrap in `KiloHertz`
|
||||
fn khz(self) -> KiloHertz;
|
||||
|
||||
/// Wrap in `MegaHertz`
|
||||
fn mhz(self) -> MegaHertz;
|
||||
|
||||
/// Wrap in `MilliSeconds`
|
||||
fn ms(self) -> MilliSeconds;
|
||||
|
||||
/// Wrap in `MicroSeconds`
|
||||
fn us(self) -> MicroSeconds;
|
||||
}
|
||||
|
||||
impl U32Ext for u32 {
|
||||
fn bps(self) -> Bps {
|
||||
Bps(self)
|
||||
}
|
||||
|
||||
fn hz(self) -> Hertz {
|
||||
Hertz(self)
|
||||
}
|
||||
|
||||
fn khz(self) -> KiloHertz {
|
||||
KiloHertz(self)
|
||||
}
|
||||
|
||||
fn mhz(self) -> MegaHertz {
|
||||
MegaHertz(self)
|
||||
}
|
||||
|
||||
fn ms(self) -> MilliSeconds {
|
||||
MilliSeconds(self)
|
||||
}
|
||||
|
||||
fn us(self) -> MicroSeconds {
|
||||
MicroSeconds(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KiloHertz> for Hertz {
|
||||
fn from(val: KiloHertz) -> Self {
|
||||
Self(val.0 * 1_000)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MegaHertz> for Hertz {
|
||||
fn from(val: MegaHertz) -> Self {
|
||||
Self(val.0 * 1_000_000)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MegaHertz> for KiloHertz {
|
||||
fn from(val: MegaHertz) -> Self {
|
||||
Self(val.0 * 1_000)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MilliSeconds> for Hertz {
|
||||
fn from(val: MilliSeconds) -> Self {
|
||||
Self(1_000 / val.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MicroSeconds> for Hertz {
|
||||
fn from(val: MicroSeconds) -> Self {
|
||||
Self(1_000_000 / val.0)
|
||||
}
|
||||
}
|
183
src/timer.rs
Normal file
183
src/timer.rs
Normal file
@ -0,0 +1,183 @@
|
||||
use crate::{
|
||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||
time::Hertz,
|
||||
};
|
||||
use embedded_hal::timer::{Cancel, CountDown, Periodic};
|
||||
use va108xx::{Interrupt, IRQSEL, SYSCONFIG};
|
||||
use void::Void;
|
||||
|
||||
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||
|
||||
/// Hardware timers
|
||||
pub struct CountDownTimer<TIM> {
|
||||
tim: TIM,
|
||||
sys_clk: Hertz,
|
||||
last_cnt: u32,
|
||||
}
|
||||
|
||||
/// Interrupt events
|
||||
pub enum Event {
|
||||
/// Timer timed out / count down ended
|
||||
TimeOut,
|
||||
}
|
||||
|
||||
pub enum TimerErrors {
|
||||
Canceled,
|
||||
}
|
||||
|
||||
fn enable_tim_clk(syscfg: &mut SYSCONFIG, idx: u8) {
|
||||
syscfg
|
||||
.tim_clk_enable
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
||||
}
|
||||
|
||||
macro_rules! timers {
|
||||
($($TIM:ident: ($tim:ident, $i:expr),)+) => {
|
||||
$(
|
||||
use crate::pac::$TIM;
|
||||
|
||||
impl CountDownTimer<$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(
|
||||
syscfg: &mut SYSCONFIG, sys_clk: Hertz, tim: $TIM
|
||||
) -> Self {
|
||||
enable_tim_clk(syscfg, $i);
|
||||
tim.ctrl.modify(|_, w| w.enable().set_bit());
|
||||
CountDownTimer {
|
||||
tim,
|
||||
sys_clk,
|
||||
last_cnt: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Listen for events. This also actives the IRQ in the IRQSEL register
|
||||
/// for the provided interrupt. It also actives the peripheral clock for
|
||||
/// IRQSEL
|
||||
pub fn listen(
|
||||
&mut self,
|
||||
event: Event,
|
||||
syscfg: &mut SYSCONFIG,
|
||||
irqsel: &mut IRQSEL,
|
||||
interrupt: Interrupt,
|
||||
) {
|
||||
match event {
|
||||
Event::TimeOut => {
|
||||
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||
irqsel.tim[$i].write(|w| unsafe { w.bits(interrupt as u32) });
|
||||
self.tim.ctrl.modify(|_, w| w.irq_enb().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlisten(
|
||||
&mut self, event: Event, syscfg: &mut SYSCONFIG, irqsel: &mut IRQSEL
|
||||
) {
|
||||
match event {
|
||||
Event::TimeOut => {
|
||||
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||
irqsel.tim[$i].write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
||||
self.tim.ctrl.modify(|_, w| w.irq_enb().clear_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release(self, syscfg: &mut SYSCONFIG) -> $TIM {
|
||||
self.tim.ctrl.write(|w| w.enable().clear_bit());
|
||||
syscfg
|
||||
.tim_clk_enable
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
|
||||
self.tim
|
||||
}
|
||||
|
||||
pub fn auto_disable(self, enable: bool) -> Self {
|
||||
if enable {
|
||||
self.tim.ctrl.modify(|_, w| w.auto_disable().set_bit());
|
||||
} else {
|
||||
self.tim.ctrl.modify(|_, w| w.auto_disable().clear_bit());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn auto_deactivate(self, enable: bool) -> Self {
|
||||
if enable {
|
||||
self.tim.ctrl.modify(|_, w| w.auto_deactivate().set_bit());
|
||||
} else {
|
||||
self.tim.ctrl.modify(|_, w| w.auto_deactivate().clear_bit());
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// CountDown implementation for TIMx
|
||||
impl CountDown for CountDownTimer<$TIM> {
|
||||
type Time = Hertz;
|
||||
|
||||
fn start<T>(&mut self, timeout: T)
|
||||
where
|
||||
T: Into<Hertz>,
|
||||
{
|
||||
self.last_cnt = self.sys_clk.0 / timeout.into().0 - 1;
|
||||
unsafe {
|
||||
self.tim.rst_value.write(|w| w.bits(self.last_cnt));
|
||||
self.tim.cnt_value.write(|w| w.bits(self.last_cnt));
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `Ok` if the timer has wrapped
|
||||
/// Automatically clears the flag and restarts the time
|
||||
fn wait(&mut self) -> nb::Result<(), Void> {
|
||||
let cnt = self.tim.cnt_value.read().bits();
|
||||
if cnt == 0 || cnt < self.last_cnt {
|
||||
self.last_cnt = cnt;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Periodic for CountDownTimer<$TIM> {}
|
||||
|
||||
impl Cancel for CountDownTimer<$TIM> {
|
||||
type Error = TimerErrors;
|
||||
fn cancel(&mut self) -> Result<(), Self::Error> {
|
||||
if !self.tim.ctrl.read().enable().bit_is_set() {
|
||||
return Err(TimerErrors::Canceled);
|
||||
}
|
||||
self.tim.ctrl.write(|w| w.enable().clear_bit());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
timers! {
|
||||
TIM0: (tim0, 0),
|
||||
TIM1: (tim1, 1),
|
||||
TIM2: (tim2, 2),
|
||||
TIM3: (tim3, 3),
|
||||
TIM4: (tim4, 4),
|
||||
TIM5: (tim5, 5),
|
||||
TIM6: (tim6, 6),
|
||||
TIM7: (tim7, 7),
|
||||
TIM8: (tim8, 8),
|
||||
TIM9: (tim9, 9),
|
||||
TIM10: (tim10, 10),
|
||||
TIM11: (tim11, 11),
|
||||
TIM12: (tim12, 12),
|
||||
TIM13: (tim13, 13),
|
||||
TIM14: (tim14, 14),
|
||||
TIM15: (tim15, 15),
|
||||
TIM16: (tim16, 16),
|
||||
TIM17: (tim17, 17),
|
||||
TIM18: (tim18, 18),
|
||||
TIM19: (tim19, 19),
|
||||
TIM20: (tim20, 20),
|
||||
TIM21: (tim21, 21),
|
||||
TIM22: (tim22, 22),
|
||||
TIM23: (tim23, 23),
|
||||
}
|
Reference in New Issue
Block a user