diff --git a/CHANGELOG.md b/CHANGELOG.md index f1ce502..0381e9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Cargo.toml b/Cargo.toml index a1dee8b..e673b71 100644 --- a/Cargo.toml +++ b/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"] diff --git a/examples/timer-ticks.rs b/examples/timer-ticks.rs new file mode 100644 index 0000000..5e20f0b --- /dev/null +++ b/examples/timer-ticks.rs @@ -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> = Mutex::new(Cell::new(0)); +static SEC_COUNTER: Mutex> = 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); + }); +} diff --git a/src/clock.rs b/src/clock.rs new file mode 100644 index 0000000..d5a21dc --- /dev/null +++ b/src/clock.rs @@ -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> = 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 { + 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)) }); +} diff --git a/src/lib.rs b/src/lib.rs index ccc7032..5c4fb8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/prelude.rs b/src/prelude.rs index bbdbc4f..ed67fbe 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -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; diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..6b0200e --- /dev/null +++ b/src/time.rs @@ -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 for Hertz { + fn from(val: KiloHertz) -> Self { + Self(val.0 * 1_000) + } +} + +impl From for Hertz { + fn from(val: MegaHertz) -> Self { + Self(val.0 * 1_000_000) + } +} + +impl From for KiloHertz { + fn from(val: MegaHertz) -> Self { + Self(val.0 * 1_000) + } +} + +impl From for Hertz { + fn from(val: MilliSeconds) -> Self { + Self(1_000 / val.0) + } +} + +impl From for Hertz { + fn from(val: MicroSeconds) -> Self { + Self(1_000_000 / val.0) + } +} diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..c34e3ee --- /dev/null +++ b/src/timer.rs @@ -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, + 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(&mut self, timeout: T) + where + T: Into, + { + 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), +}