Timer and Clock modules added

- Clock module to set and retrieve system clock which can have
  varying frequencies. Also allows enabling peripheral clocks
- Prelude updated
- Common time types added, based on stm32f1xx HAL implementation
- Basic timer implementation added
This commit is contained in:
Robin Müller 2021-11-08 01:40:01 +01:00
parent 0c235358e4
commit f3d71cf0f9
No known key found for this signature in database
GPG Key ID: 71B58F8A3CDFA9AC
6 changed files with 400 additions and 0 deletions

View File

@ -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"

48
src/clock.rs Normal file
View 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)) });
}

View File

@ -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;

View File

@ -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
View 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
View 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),
}