added blocking delay functions
- DelayUs and DelayMs trait implementations for CountDown timer peripherals - Bugfix for wait function
This commit is contained in:
parent
030b555a7f
commit
2a9225fda5
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- DelayUs and DelayMs trait implementations for timer
|
||||||
|
|
||||||
## [0.2.1]
|
## [0.2.1]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -43,3 +43,7 @@ opt-level = "s"
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "timer-ticks"
|
name = "timer-ticks"
|
||||||
required-features = ["rt"]
|
required-features = ["rt"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "tests"
|
||||||
|
required-features = ["rt"]
|
||||||
|
@ -9,7 +9,13 @@ use cortex_m_rt::entry;
|
|||||||
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
|
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::gpio::{PinState, PinsA, PinsB};
|
use va108xx_hal::{
|
||||||
|
gpio::{PinState, PinsA, PinsB},
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
timer::{default_ms_irq_handler, set_up_ms_timer, CountDownTimer, Delay},
|
||||||
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -25,7 +31,8 @@ enum TestCase {
|
|||||||
// Tie PA0 to an oscilloscope and configure pulse detection
|
// Tie PA0 to an oscilloscope and configure pulse detection
|
||||||
Pulse,
|
Pulse,
|
||||||
// Tie PA0, PA1 and PA3 to an oscilloscope
|
// Tie PA0, PA1 and PA3 to an oscilloscope
|
||||||
Delay,
|
DelayGpio,
|
||||||
|
DelayMs,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
@ -33,10 +40,11 @@ fn main() -> ! {
|
|||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- VA108xx Test Application --");
|
rprintln!("-- VA108xx Test Application --");
|
||||||
let mut dp = va108xx::Peripherals::take().unwrap();
|
let mut dp = va108xx::Peripherals::take().unwrap();
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
let pinsa = PinsA::new(&mut dp.SYSCONFIG, None, dp.PORTA);
|
let pinsa = PinsA::new(&mut dp.SYSCONFIG, None, dp.PORTA);
|
||||||
let pinsb = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB);
|
let pinsb = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB);
|
||||||
let mut led1 = pinsa.pa10.into_push_pull_output();
|
let mut led1 = pinsa.pa10.into_push_pull_output();
|
||||||
let test_case = TestCase::Delay;
|
let test_case = TestCase::DelayMs;
|
||||||
|
|
||||||
match test_case {
|
match test_case {
|
||||||
TestCase::TestBasic
|
TestCase::TestBasic
|
||||||
@ -125,7 +133,7 @@ fn main() -> ! {
|
|||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TestCase::Delay => {
|
TestCase::DelayGpio => {
|
||||||
let mut out_0 = pinsa.pa0.into_push_pull_output().delay(true, false);
|
let mut out_0 = pinsa.pa0.into_push_pull_output().delay(true, false);
|
||||||
let mut out_1 = pinsa.pa1.into_push_pull_output().delay(false, true);
|
let mut out_1 = pinsa.pa1.into_push_pull_output().delay(false, true);
|
||||||
let mut out_2 = pinsa.pa3.into_push_pull_output().delay(true, true);
|
let mut out_2 = pinsa.pa3.into_push_pull_output().delay(true, true);
|
||||||
@ -136,6 +144,47 @@ fn main() -> ! {
|
|||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TestCase::DelayMs => {
|
||||||
|
let ms_timer = set_up_ms_timer(
|
||||||
|
&mut dp.SYSCONFIG,
|
||||||
|
&mut dp.IRQSEL,
|
||||||
|
50.mhz().into(),
|
||||||
|
dp.TIM0,
|
||||||
|
pac::Interrupt::OC0,
|
||||||
|
);
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||||
|
}
|
||||||
|
let mut delay = Delay::new(ms_timer);
|
||||||
|
for _ in 0..5 {
|
||||||
|
led1.toggle().ok();
|
||||||
|
delay.delay_ms(500);
|
||||||
|
led1.toggle().ok();
|
||||||
|
delay.delay_ms(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut delay_timer = CountDownTimer::tim1(&mut dp.SYSCONFIG, 50.mhz().into(), dp.TIM1);
|
||||||
|
let mut pa0 = pinsa.pa0.into_push_pull_output();
|
||||||
|
for _ in 0..5 {
|
||||||
|
led1.toggle().ok();
|
||||||
|
delay_timer.delay_ms(200_u32);
|
||||||
|
led1.toggle().ok();
|
||||||
|
delay_timer.delay_ms(200_u32);
|
||||||
|
}
|
||||||
|
let ahb_freq: Hertz = 50.mhz().into();
|
||||||
|
let mut syst_delay = cortex_m::delay::Delay::new(cp.SYST, ahb_freq.0);
|
||||||
|
// Test usecond delay using both TIM peripheral and SYST
|
||||||
|
loop {
|
||||||
|
pa0.toggle().ok();
|
||||||
|
delay_timer.delay_us(50_u32);
|
||||||
|
pa0.toggle().ok();
|
||||||
|
delay_timer.delay_us(50_u32);
|
||||||
|
pa0.toggle().ok();
|
||||||
|
syst_delay.delay_us(50);
|
||||||
|
pa0.toggle().ok();
|
||||||
|
syst_delay.delay_us(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rprintln!("Test success");
|
rprintln!("Test success");
|
||||||
@ -144,3 +193,8 @@ fn main() -> ! {
|
|||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ use va108xx_hal::{
|
|||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer::{set_up_ms_timer, CountDownTimer, Event},
|
timer::{default_ms_irq_handler, set_up_ms_timer, CountDownTimer, Event, MS_COUNTER},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -21,7 +21,6 @@ enum LibType {
|
|||||||
Hal,
|
Hal,
|
||||||
}
|
}
|
||||||
|
|
||||||
static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
|
||||||
static SEC_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
static SEC_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
@ -105,11 +104,7 @@ fn unmask_irqs() {
|
|||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn OC0() {
|
fn OC0() {
|
||||||
cortex_m::interrupt::free(|cs| {
|
default_ms_irq_handler()
|
||||||
let mut ms = MS_COUNTER.borrow(cs).get();
|
|
||||||
ms += 1;
|
|
||||||
MS_COUNTER.borrow(cs).set(ms);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
|
144
src/timer.rs
144
src/timer.rs
@ -10,17 +10,26 @@ use crate::{
|
|||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer,
|
timer,
|
||||||
};
|
};
|
||||||
use embedded_hal::timer::{Cancel, CountDown, Periodic};
|
use core::cell::Cell;
|
||||||
|
use cortex_m::interrupt::Mutex;
|
||||||
|
use embedded_hal::{
|
||||||
|
blocking::delay,
|
||||||
|
timer::{Cancel, CountDown, Periodic},
|
||||||
|
};
|
||||||
use va108xx::{Interrupt, IRQSEL, SYSCONFIG};
|
use va108xx::{Interrupt, IRQSEL, SYSCONFIG};
|
||||||
use void::Void;
|
use void::Void;
|
||||||
|
|
||||||
const IRQ_DST_NONE: u32 = 0xffffffff;
|
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||||
|
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
/// Hardware timers
|
/// Hardware timers
|
||||||
pub struct CountDownTimer<TIM> {
|
pub struct CountDownTimer<TIM> {
|
||||||
tim: TIM,
|
tim: TIM,
|
||||||
|
curr_freq: Hertz,
|
||||||
sys_clk: Hertz,
|
sys_clk: Hertz,
|
||||||
|
rst_val: u32,
|
||||||
last_cnt: u32,
|
last_cnt: u32,
|
||||||
|
listening: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interrupt events
|
/// Interrupt events
|
||||||
@ -57,6 +66,9 @@ macro_rules! timers {
|
|||||||
CountDownTimer {
|
CountDownTimer {
|
||||||
tim,
|
tim,
|
||||||
sys_clk,
|
sys_clk,
|
||||||
|
rst_val: 0,
|
||||||
|
curr_freq: 0.hz(),
|
||||||
|
listening: false,
|
||||||
last_cnt: 0,
|
last_cnt: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,6 +88,7 @@ macro_rules! timers {
|
|||||||
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||||
irqsel.tim[$i].write(|w| unsafe { w.bits(interrupt as u32) });
|
irqsel.tim[$i].write(|w| unsafe { w.bits(interrupt as u32) });
|
||||||
self.tim.ctrl.modify(|_, w| w.irq_enb().set_bit());
|
self.tim.ctrl.modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
self.listening = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,6 +101,7 @@ macro_rules! timers {
|
|||||||
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||||
irqsel.tim[$i].write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
irqsel.tim[$i].write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
||||||
self.tim.ctrl.modify(|_, w| w.irq_enb().clear_bit());
|
self.tim.ctrl.modify(|_, w| w.irq_enb().clear_bit());
|
||||||
|
self.listening = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,6 +131,14 @@ macro_rules! timers {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn curr_freq(&self) -> Hertz {
|
||||||
|
self.curr_freq
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listening(&self) -> bool {
|
||||||
|
self.listening
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CountDown implementation for TIMx
|
/// CountDown implementation for TIMx
|
||||||
@ -127,21 +149,28 @@ macro_rules! timers {
|
|||||||
where
|
where
|
||||||
T: Into<Hertz>,
|
T: Into<Hertz>,
|
||||||
{
|
{
|
||||||
self.last_cnt = self.sys_clk.0 / timeout.into().0 - 1;
|
self.tim.ctrl.modify(|_, w| w.enable().clear_bit());
|
||||||
|
self.curr_freq = timeout.into();
|
||||||
|
self.rst_val = self.sys_clk.0 / self.curr_freq.0;
|
||||||
unsafe {
|
unsafe {
|
||||||
self.tim.rst_value.write(|w| w.bits(self.last_cnt));
|
self.tim.rst_value.write(|w| w.bits(self.rst_val));
|
||||||
self.tim.cnt_value.write(|w| w.bits(self.last_cnt));
|
self.tim.cnt_value.write(|w| w.bits(self.rst_val));
|
||||||
}
|
}
|
||||||
|
self.tim.ctrl.modify(|_, w| w.enable().set_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `Ok` if the timer has wrapped
|
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
||||||
/// Automatically clears the flag and restarts the time
|
/// flag and restart the time if configured correctly
|
||||||
fn wait(&mut self) -> nb::Result<(), Void> {
|
fn wait(&mut self) -> nb::Result<(), Void> {
|
||||||
let cnt = self.tim.cnt_value.read().bits();
|
let cnt = self.tim.cnt_value.read().bits();
|
||||||
if cnt == 0 || cnt < self.last_cnt {
|
if cnt > self.last_cnt {
|
||||||
self.last_cnt = cnt;
|
self.last_cnt = self.rst_val;
|
||||||
|
Ok(())
|
||||||
|
} else if cnt == 0 {
|
||||||
|
self.last_cnt = self.rst_val;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
self.last_cnt = cnt;
|
||||||
Err(nb::Error::WouldBlock)
|
Err(nb::Error::WouldBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,6 +188,60 @@ macro_rules! timers {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delay for microseconds.
|
||||||
|
///
|
||||||
|
/// For delays less than 100 us, an assembly delay will be used.
|
||||||
|
/// For larger delays, the timer peripheral will be used.
|
||||||
|
/// Please note that the delay using the peripheral might not
|
||||||
|
/// work properly in debug mode.
|
||||||
|
impl delay::DelayUs<u32> for CountDownTimer<$TIM> {
|
||||||
|
fn delay_us(&mut self, us: u32) {
|
||||||
|
if(us < 100) {
|
||||||
|
cortex_m::asm::delay(us * (self.sys_clk.0 / 2_000_000));
|
||||||
|
} else {
|
||||||
|
// Configuring the peripheral for higher frequencies is unstable
|
||||||
|
self.start(1000.khz());
|
||||||
|
// The subtracted value is an empirical value measures by using tests with
|
||||||
|
// an oscilloscope.
|
||||||
|
for _ in 0..us - 7 {
|
||||||
|
nb::block!(self.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Forwards call to u32 variant of delay
|
||||||
|
impl delay::DelayUs<u16> for CountDownTimer<$TIM> {
|
||||||
|
fn delay_us(&mut self, us: u16) {
|
||||||
|
self.delay_us(u32::from(us));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Forwards call to u32 variant of delay
|
||||||
|
impl delay::DelayUs<u8> for CountDownTimer<$TIM> {
|
||||||
|
fn delay_us(&mut self, us: u8) {
|
||||||
|
self.delay_us(u32::from(us));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl delay::DelayMs<u32> for CountDownTimer<$TIM> {
|
||||||
|
fn delay_ms(&mut self, ms: u32) {
|
||||||
|
self.start(1000.hz());
|
||||||
|
for _ in 0..ms {
|
||||||
|
nb::block!(self.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl delay::DelayMs<u16> for CountDownTimer<$TIM> {
|
||||||
|
fn delay_ms(&mut self, ms: u16) {
|
||||||
|
self.delay_ms(u32::from(ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl embedded_hal::blocking::delay::DelayMs<u8> for CountDownTimer<$TIM> {
|
||||||
|
fn delay_ms(&mut self, ms: u8) {
|
||||||
|
self.delay_ms(u32::from(ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
)+
|
)+
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,10 +254,26 @@ pub fn set_up_ms_timer(
|
|||||||
sys_clk: Hertz,
|
sys_clk: Hertz,
|
||||||
tim0: TIM0,
|
tim0: TIM0,
|
||||||
irq: pac::Interrupt,
|
irq: pac::Interrupt,
|
||||||
) {
|
) -> CountDownTimer<TIM0> {
|
||||||
let mut ms_timer = CountDownTimer::tim0(syscfg, sys_clk, tim0);
|
let mut ms_timer = CountDownTimer::tim0(syscfg, sys_clk, tim0);
|
||||||
ms_timer.listen(timer::Event::TimeOut, syscfg, irqsel, irq);
|
ms_timer.listen(timer::Event::TimeOut, syscfg, irqsel, irq);
|
||||||
ms_timer.start(1000.hz());
|
ms_timer.start(1000.hz());
|
||||||
|
ms_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function can be called in a specified interrupt handler to increment
|
||||||
|
/// the MS counter
|
||||||
|
pub fn default_ms_irq_handler() {
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||||
|
ms += 1;
|
||||||
|
MS_COUNTER.borrow(cs).set(ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current MS tick count
|
||||||
|
pub fn get_ms_ticks() -> u32 {
|
||||||
|
cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get())
|
||||||
}
|
}
|
||||||
|
|
||||||
timers! {
|
timers! {
|
||||||
@ -203,3 +302,30 @@ timers! {
|
|||||||
TIM22: (tim22, 22),
|
TIM22: (tim22, 22),
|
||||||
TIM23: (tim23, 23),
|
TIM23: (tim23, 23),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Delay implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct Delay {
|
||||||
|
cd_tim: CountDownTimer<TIM0>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Delay {
|
||||||
|
pub fn new(tim0: CountDownTimer<TIM0>) -> Self {
|
||||||
|
Delay { cd_tim: tim0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This assumes that the user has already set up a MS tick timer in TIM0 as a system tick
|
||||||
|
impl embedded_hal::blocking::delay::DelayMs<u32> for Delay {
|
||||||
|
fn delay_ms(&mut self, ms: u32) {
|
||||||
|
if self.cd_tim.curr_freq() != 1000.hz() || !self.cd_tim.listening() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let start_time = get_ms_ticks();
|
||||||
|
while get_ms_ticks() - start_time < ms {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user