#![no_std]
#![no_main]
use core::cell::RefCell;
use cortex_m::peripheral::NVIC;
use cortex_m_rt::entry;
use critical_section::Mutex;
use defmt_rtt as _;
use panic_halt as _;
use gd32e2_hal::gpio::{Alternate, Pin};
use gd32e2_hal::pac::{self, interrupt};
use gd32e2_hal::prelude::*;
use gd32e2_hal::rcu::{ClockConfig, PllFreq, SysClk};
use gd32e2_hal::time::MillisDuration;
use gd32e2_hal::timer::{Capture, CaptureChannel, Edge, Event};
const CAPTURE_PSC: u16 = 47;
const COUNTER_SPAN: u64 = 1 << 16;
const COUNTER_HALF: u16 = 1 << 15;
const HALF_PERIODS_MS: [u32; 3] = [20, 60, 150];
const PERIODS: u8 = 3;
const TOLERANCE_PERCENT: u64 = 5;
type SensePin = Pin<'A', 3, Alternate<0>>;
type Shared = (
Capture<pac::Timer14>,
CaptureChannel<pac::Timer14, SensePin, 1>,
u32,
Option<u64>,
Option<u64>,
);
static SHARED: Mutex<RefCell<Option<Shared>>> = Mutex::new(RefCell::new(None));
fn take_interval() -> Option<u64> {
critical_section::with(|cs| {
let mut shared = SHARED.borrow(cs).borrow_mut();
let (_capture, _sense, _rollovers, _last, interval) = shared.as_mut().unwrap();
interval.take()
})
}
fn forget_last_edge() {
critical_section::with(|cs| {
let mut shared = SHARED.borrow(cs).borrow_mut();
let (_capture, _sense, _rollovers, last, interval) = shared.as_mut().unwrap();
*last = None;
*interval = None;
});
}
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
let mut fmc = dp.fmc.constrain();
let config = ClockConfig::default().sysclk(SysClk::Pll(PllFreq::Mhz48));
let mut rcu = dp.rcu.constrain().freeze(&mut fmc, config);
let gpioa = dp.gpioa.split(&mut rcu);
let mut source = gpioa.pa2.into_push_pull_output();
let sense_pin = gpioa.pa3.into_alternate::<0>();
let mut delay = dp.timer5.constrain(&mut rcu).into_delay();
let mut capture = dp.timer14.constrain(&mut rcu).into_capture(CAPTURE_PSC);
let mut sense = capture.channel(sense_pin, Edge::Rising);
sense.enable();
sense.listen();
capture.listen(Event::Update);
critical_section::with(|cs| {
SHARED
.borrow(cs)
.replace(Some((capture, sense, 0, None, None)));
});
unsafe { NVIC::unmask(pac::Interrupt::TIMER14) };
source.set_low();
defmt::info!("driving PA2, capturing PA3 on interrupt, 1 us per tick");
loop {
for half_ms in HALF_PERIODS_MS {
let expected_us = u64::from(half_ms) * 2 * 1_000;
let spans = expected_us / COUNTER_SPAN;
defmt::info!(
"--- period {} ms, {} counter rollovers per period ---",
half_ms * 2,
spans
);
forget_last_edge();
let half = MillisDuration::from_millis(half_ms);
for _ in 0..PERIODS {
source.set_high();
delay.delay(half);
source.set_low();
delay.delay(half);
if let Some(measured) = take_interval() {
let off_by = measured.abs_diff(expected_us) * 100 / expected_us;
if off_by <= TOLERANCE_PERCENT {
defmt::info!("period {} us, expected {} us : OK", measured, expected_us);
} else {
defmt::error!(
"period {} us, expected {} us : off by {} %",
measured,
expected_us,
off_by
);
}
}
}
}
}
}
#[interrupt]
fn TIMER14() {
critical_section::with(|cs| {
let mut shared = SHARED.borrow(cs).borrow_mut();
let (capture, sense, rollovers, last, interval) = shared.as_mut().unwrap();
if sense.is_listening() && sense.is_pending() {
let wrapped = capture.is_pending(Event::Update);
if let Ok(cv) = sense.read() {
let cycles = match wrapped && cv < COUNTER_HALF {
true => u64::from(*rollovers) + 1,
false => u64::from(*rollovers),
};
let now = cycles * COUNTER_SPAN + u64::from(cv);
*interval = last.map(|previous| now - previous);
*last = Some(now);
}
}
if capture.is_listening(Event::Update) && capture.is_pending(Event::Update) {
capture.clear_interrupt(Event::Update);
*rollovers += 1;
}
});
}