Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
84 lines
2.6 KiB
Rust
84 lines
2.6 KiB
Rust
//! # Time
|
||
//!
|
||
//! The `time` module offers a way to get the system now.
|
||
|
||
/// Represents a duration of time.
|
||
///
|
||
/// The resolution is 1 microsecond, represented as a 64-bit unsigned integer.
|
||
pub type Duration = fugit::Duration<u64, 1, 1_000_000>;
|
||
|
||
/// Represents an instant in time.
|
||
///
|
||
/// The resolution is 1 microsecond, represented as a 64-bit unsigned integer.
|
||
pub type Instant = fugit::Instant<u64, 1, 1_000_000>;
|
||
|
||
/// Provides time since system start in microseconds precision.
|
||
///
|
||
/// The counter won’t measure time in sleep-mode.
|
||
///
|
||
/// The timer will wrap after
|
||
#[cfg_attr(esp32, doc = "36_558 years")]
|
||
#[cfg_attr(esp32s2, doc = "7_311 years")]
|
||
#[cfg_attr(not(any(esp32, esp32s2)), doc = "more than 7 years")]
|
||
pub fn now() -> Instant {
|
||
#[cfg(esp32)]
|
||
let (ticks, div) = {
|
||
// on ESP32 use LACT
|
||
let tg0 = unsafe { crate::peripherals::TIMG0::steal() };
|
||
tg0.lactupdate().write(|w| unsafe { w.update().bits(1) });
|
||
|
||
// The peripheral doesn't have a bit to indicate that the update is done, so we
|
||
// poll the lower 32 bit part of the counter until it changes, or a timeout
|
||
// expires.
|
||
let lo_initial = tg0.lactlo().read().bits();
|
||
let mut div = tg0.lactconfig().read().divider().bits();
|
||
let lo = loop {
|
||
let lo = tg0.lactlo().read().bits();
|
||
if lo != lo_initial || div == 0 {
|
||
break lo;
|
||
}
|
||
div -= 1;
|
||
};
|
||
let hi = tg0.lacthi().read().bits();
|
||
|
||
let ticks = (hi as u64) << 32u64 | lo as u64;
|
||
(ticks, 16)
|
||
};
|
||
|
||
#[cfg(not(esp32))]
|
||
let (ticks, div) = {
|
||
// otherwise use SYSTIMER
|
||
let ticks = crate::timer::systimer::SystemTimer::now();
|
||
(
|
||
ticks,
|
||
(crate::timer::systimer::SystemTimer::ticks_per_second() / 1_000_000),
|
||
)
|
||
};
|
||
|
||
Instant::from_ticks(ticks / div)
|
||
}
|
||
|
||
#[cfg(esp32)]
|
||
pub(crate) fn time_init() {
|
||
let apb = crate::Clocks::get().apb_clock.to_Hz();
|
||
// we assume 80MHz APB clock source - there is no way to configure it in a
|
||
// different way currently
|
||
assert_eq!(apb, 80_000_000u32);
|
||
|
||
let tg0 = unsafe { crate::peripherals::TIMG0::steal() };
|
||
|
||
tg0.lactconfig().write(|w| unsafe { w.bits(0) });
|
||
tg0.lactalarmhi().write(|w| unsafe { w.bits(u32::MAX) });
|
||
tg0.lactalarmlo().write(|w| unsafe { w.bits(u32::MAX) });
|
||
tg0.lactload().write(|w| unsafe { w.load().bits(1) });
|
||
|
||
// 16 MHz counter
|
||
tg0.lactconfig()
|
||
.modify(|_, w| unsafe { w.divider().bits((apb / 16_000_000u32) as u16) });
|
||
tg0.lactconfig().modify(|_, w| {
|
||
w.increase().bit(true);
|
||
w.autoreload().bit(true);
|
||
w.en().bit(true)
|
||
});
|
||
}
|