From d893e111cb2713a1e9756275d86b637d59a61b99 Mon Sep 17 00:00:00 2001 From: longfangsong Date: Sat, 13 Feb 2021 16:29:57 +0800 Subject: [PATCH] implement embedded_hal's Timer trait for each channel of k210's hardware timer. --- src/clock.rs | 1 - src/lib.rs | 13 ++++----- src/sysctl.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/timer.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 src/timer.rs diff --git a/src/clock.rs b/src/clock.rs index d833527..f9fb788 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,6 +1,5 @@ //! Clock configuration //use crate::pac::PRCI; -use crate::sysctl::ACLK; use crate::time::Hertz; /// Frozen clock frequencies diff --git a/src/lib.rs b/src/lib.rs index 4309e86..e7f273e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,23 +19,24 @@ pub mod gpio; pub mod gpiohs; pub mod plic; pub mod serial; -pub mod spi; pub mod sha256; +pub mod spi; pub mod stdout; pub mod sysctl; pub mod time; +pub mod timer; /// Prelude pub mod prelude { - pub use embedded_hal::prelude::*; - pub use crate::serial::SerialExt as _k210_hal_serial_SerialExt; - pub use crate::stdout::Write as _k210_hal_stdout_Write; - pub use crate::time::U32Ext as _k210_hal_time_U32Ext; pub use crate::fpioa::FpioaExt as _k210_hal_fpioa_FpioaExt; - pub use crate::sysctl::SysctlExt as _k210_hal_sysctl_SysctlExt; pub use crate::gpio::GpioExt as _k210_hal_gpio_GpioExt; pub use crate::gpiohs::GpiohsExt as _k210_hal_gpiohs_GpiohsExt; pub use crate::plic::PlicExt as _k210_hal_plic_PlicExt; + pub use crate::serial::SerialExt as _k210_hal_serial_SerialExt; + pub use crate::stdout::Write as _k210_hal_stdout_Write; + pub use crate::sysctl::SysctlExt as _k210_hal_sysctl_SysctlExt; + pub use crate::time::U32Ext as _k210_hal_time_U32Ext; + pub use embedded_hal::prelude::*; } mod bit_utils { diff --git a/src/sysctl.rs b/src/sysctl.rs index 2440436..4c6e204 100644 --- a/src/sysctl.rs +++ b/src/sysctl.rs @@ -75,6 +75,7 @@ impl SysctlExt for SYSCTL { aclk: ACLK { _ownership: () }, apb0: APB0 { _ownership: () }, pll0: PLL0 { _ownership: () }, + timer0: Timer0 { _ownership: () }, } } } @@ -87,6 +88,8 @@ pub struct Parts { pub pll0: PLL0, /// entry for controlling the enable/disable/frequency of apb0 pub apb0: APB0, + /// entry for controlling the enable/disable/frequency of Timer + pub timer0: Timer0, // todo: SRAM, APB-bus, ROM, DMA, AI, PLL1, PLL2, APB1, APB2 } @@ -302,3 +305,74 @@ impl ACLK { } } } + +pub struct Timer0 { + _ownership: (), +} + +impl Timer0 { + pub fn enable(&mut self, apb0: &mut APB0) { + apb0.enable(); + clk_en_peri().modify(|_, w| w.timer0_clk_en().set_bit()); + } + + pub fn disable(&mut self) { + clk_en_peri().modify(|_, w| w.timer0_clk_en().clear_bit()); + } + + fn use_external(&mut self) { + sysctl() + .clk_sel0 + .modify(|_, w| w.timer0_clk_sel().clear_bit()); + } + + fn use_pll0(&mut self) { + sysctl() + .clk_sel0 + .modify(|_, w| w.timer0_clk_sel().set_bit()); + } + + pub fn set_frequency(&mut self, resolution: impl Into) -> Hertz { + let resolution = resolution.into().0; + // resolution = input / ((timer0_clk + 1) * 2), timer0_clk is 8 bits + + // find a nearest input + let in0_nearest_clk = (CLOCK_FREQ_IN0 / resolution / 2 - 1).min(255); + let in0_result = CLOCK_FREQ_IN0 / ((in0_nearest_clk + 1) * 2); + let in0_diff = (in0_result as i64 - resolution as i64).abs(); + + let pll0_freq = PLL0::steal().get_frequency().0; + let pll0_nearest_clk = (pll0_freq / resolution / 2 - 1).min(255); + let pll0_result = pll0_freq / ((pll0_nearest_clk + 1) * 2); + let pll0_diff = (pll0_result as i64 - resolution as i64).abs(); + + if in0_diff <= pll0_diff { + unsafe { + self.use_external(); + sysctl() + .clk_th2 + .modify(|_, w| w.timer0_clk().bits(in0_nearest_clk as u8)); + } + Hertz(in0_result) + } else { + self.use_pll0(); + unsafe { + sysctl() + .clk_th2 + .modify(|_, w| w.timer0_clk().bits(pll0_nearest_clk as u8)); + } + Hertz(pll0_result) + } + } + pub fn get_frequency(&self) -> Hertz { + let threshold = sysctl().clk_th2.read().timer0_clk().bits() as u32; + let input = if sysctl().clk_sel0.read().timer0_clk_sel().bit() { + PLL0::steal().get_frequency().0 + } else { + CLOCK_FREQ_IN0 + }; + Hertz(input / ((threshold + 1) * 2)) + } +} + +// todo: Timer1, Timer2, maybe with a macro diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..a91e0b3 --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,75 @@ +use crate::pac::TIMER0; +use crate::sysctl::{self, APB0}; +use crate::time::Hertz; + +macro_rules! timer { + ($Timer: ident, $timer: ident, $TIMER: ident) => { + pub struct $Timer<'a> { + timer: &'a $TIMER, + channel_id: usize, + resolution: u32, + } + + impl<'a> $Timer<'a> { + pub fn new( + timer: &'a $TIMER, + channel_id: usize, + $timer: &mut sysctl::$Timer, + apb0: &mut APB0, + ) -> Self { + $timer.enable(apb0); + timer.channel[channel_id] + .control + .modify(|_, w| w.enable().set_bit().interrupt().set_bit().mode().user()); + Self { + timer, + channel_id, + resolution: $timer.get_frequency().0, + } + } + } + + impl<'a> embedded_hal::timer::Periodic for $Timer<'a> {} + + impl<'a> embedded_hal::timer::CountDown for $Timer<'a> { + type Error = (); + type Time = Hertz; + + fn try_start(&mut self, count: T) -> Result<(), Self::Error> + where + T: Into, + { + self.timer.channel[self.channel_id] + .control + .modify(|_, w| w.enable().clear_bit()); + let count = count.into().0; + let ticks = self.resolution / count; + unsafe { + self.timer.channel[self.channel_id] + .current_value + .write(|w| w.bits(0)); + self.timer.channel[self.channel_id] + .load_count + .write(|w| w.bits(ticks)); + } + self.timer.channel[self.channel_id] + .control + .modify(|_, w| w.enable().set_bit()); + Ok(()) + } + + fn try_wait(&mut self) -> nb::Result<(), Self::Error> { + if self.timer.raw_intr_stat.read().bits() & (1 << self.channel_id) == 0 { + Err(nb::Error::WouldBlock) + } else { + let _ = self.timer.channel[self.channel_id].eoi.read().bits(); + Ok(()) + } + } + } + }; +} + +timer!(Timer0, timer0, TIMER0); +// todo: timer!(Timer1, timer1, TIMER1); +// todo: timer!(Timer2, timer2, TIMER2);