feat: add display UI with splash screen, dashboard, and RSSI sparkline
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>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+101
@@ -0,0 +1,101 @@
|
||||
//! Builtin temperature sensor driver.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use fixed::types::I30F2;
|
||||
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::TEMP;
|
||||
use crate::{interrupt, pac, Peripheral};
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct InterruptHandler {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl interrupt::typelevel::Handler<interrupt::typelevel::TEMP> for InterruptHandler {
|
||||
unsafe fn on_interrupt() {
|
||||
let r = unsafe { &*pac::TEMP::PTR };
|
||||
r.intenclr.write(|w| w.datardy().clear());
|
||||
WAKER.wake();
|
||||
}
|
||||
}
|
||||
|
||||
/// Builtin temperature sensor driver.
|
||||
pub struct Temp<'d> {
|
||||
_peri: PeripheralRef<'d, TEMP>,
|
||||
}
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
impl<'d> Temp<'d> {
|
||||
/// Create a new temperature sensor driver.
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = TEMP> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<interrupt::typelevel::TEMP, InterruptHandler> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(_peri);
|
||||
|
||||
// Enable interrupt that signals temperature values
|
||||
interrupt::TEMP.unpend();
|
||||
unsafe { interrupt::TEMP.enable() };
|
||||
|
||||
Self { _peri }
|
||||
}
|
||||
|
||||
/// Perform an asynchronous temperature measurement. The returned future
|
||||
/// can be awaited to obtain the measurement.
|
||||
///
|
||||
/// If the future is dropped, the measurement is cancelled.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use embassy_nrf::{bind_interrupts, temp};
|
||||
/// use embassy_nrf::temp::Temp;
|
||||
///
|
||||
/// bind_interrupts!(struct Irqs {
|
||||
/// TEMP => temp::InterruptHandler;
|
||||
/// });
|
||||
///
|
||||
/// # async {
|
||||
/// # let p: embassy_nrf::Peripherals = todo!();
|
||||
/// let mut t = Temp::new(p.TEMP, Irqs);
|
||||
/// let v: u16 = t.read().await.to_num::<u16>();
|
||||
/// # };
|
||||
/// ```
|
||||
pub async fn read(&mut self) -> I30F2 {
|
||||
// In case the future is dropped, stop the task and reset events.
|
||||
let on_drop = OnDrop::new(|| {
|
||||
let t = Self::regs();
|
||||
t.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
t.events_datardy.reset();
|
||||
});
|
||||
|
||||
let t = Self::regs();
|
||||
t.intenset.write(|w| w.datardy().set());
|
||||
unsafe { t.tasks_start.write(|w| w.bits(1)) };
|
||||
|
||||
let value = poll_fn(|cx| {
|
||||
WAKER.register(cx.waker());
|
||||
if t.events_datardy.read().bits() == 0 {
|
||||
Poll::Pending
|
||||
} else {
|
||||
t.events_datardy.reset();
|
||||
let raw = t.temp.read().bits();
|
||||
Poll::Ready(I30F2::from_bits(raw as i32))
|
||||
}
|
||||
})
|
||||
.await;
|
||||
on_drop.defuse();
|
||||
value
|
||||
}
|
||||
|
||||
fn regs() -> &'static pac::temp::RegisterBlock {
|
||||
unsafe { &*pac::TEMP::ptr() }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user