Skip to content

Commit

Permalink
implement embedded_hal's Timer trait for each channel of k210's hardw…
Browse files Browse the repository at this point in the history
…are timer.
  • Loading branch information
longfangsong committed Feb 20, 2021
1 parent 98fb515 commit d893e11
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 7 deletions.
1 change: 0 additions & 1 deletion src/clock.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Clock configuration
//use crate::pac::PRCI;
use crate::sysctl::ACLK;
use crate::time::Hertz;

/// Frozen clock frequencies
Expand Down
13 changes: 7 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
74 changes: 74 additions & 0 deletions src/sysctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ impl SysctlExt for SYSCTL {
aclk: ACLK { _ownership: () },
apb0: APB0 { _ownership: () },
pll0: PLL0 { _ownership: () },
timer0: Timer0 { _ownership: () },
}
}
}
Expand All @@ -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
}

Expand Down Expand Up @@ -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>) -> 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
75 changes: 75 additions & 0 deletions src/timer.rs
Original file line number Diff line number Diff line change
@@ -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<T>(&mut self, count: T) -> Result<(), Self::Error>
where
T: Into<Self::Time>,
{
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);

0 comments on commit d893e11

Please sign in to comment.