Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Timer trait in embedded_hal for Channels in hardware timer #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Comment on lines +8 to +9
Copy link
Contributor Author

@longfangsong longfangsong Feb 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should take a CHANNEL instance and provide a free function to return it back. However, I did not find a way to move a CHANNEL out of timer0::RegisterBlock::channel

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