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
+170
@@ -0,0 +1,170 @@
|
||||
//! Interrupts
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
pub use bare_metal::CriticalSection;
|
||||
|
||||
/// Trait for enums of external interrupt numbers.
|
||||
///
|
||||
/// This trait should be implemented by a peripheral access crate (PAC)
|
||||
/// on its enum of available external interrupts for a specific device.
|
||||
/// Each variant must convert to a u16 of its interrupt number,
|
||||
/// which is its exception number - 16.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented on enums of device interrupts. Each
|
||||
/// enum variant must represent a distinct value (no duplicates are permitted),
|
||||
/// and must always return the same value (do not change at runtime).
|
||||
///
|
||||
/// These requirements ensure safe nesting of critical sections.
|
||||
pub unsafe trait InterruptNumber: Copy {
|
||||
/// Return the interrupt number associated with this variant.
|
||||
///
|
||||
/// See trait documentation for safety requirements.
|
||||
fn number(self) -> u16;
|
||||
}
|
||||
|
||||
/// Disables all interrupts and return the previous settings
|
||||
#[inline]
|
||||
pub fn disable() -> u32 {
|
||||
unsafe { set_mask(0) }
|
||||
}
|
||||
|
||||
/// Enables all the interrupts
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Do not call this function inside an `interrupt::free` critical section
|
||||
#[inline]
|
||||
pub unsafe fn enable() -> u32 {
|
||||
set_mask(!0)
|
||||
}
|
||||
|
||||
/// Enables specific interrupts and returns the previous setting
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Do not call this function inside an `interrupt::free` critical section
|
||||
#[inline]
|
||||
pub unsafe fn set_mask(mut mask: u32) -> u32 {
|
||||
asm!("
|
||||
xsr {0}, intenable
|
||||
rsync
|
||||
",
|
||||
inout(reg) mask, options(nostack)
|
||||
);
|
||||
mask
|
||||
}
|
||||
|
||||
/// Disables specific interrupts and returns the previous settings
|
||||
#[inline]
|
||||
pub fn disable_mask(mask: u32) -> u32 {
|
||||
let mut prev: u32 = 0;
|
||||
let _dummy: u32;
|
||||
unsafe {
|
||||
asm!("
|
||||
xsr.intenable {0} // get mask and temporarily disable interrupts
|
||||
and {1}, {1}, {0}
|
||||
rsync
|
||||
wsr.intenable {1}
|
||||
rsync
|
||||
", inout(reg) prev, inout(reg) !mask => _dummy, options(nostack)
|
||||
);
|
||||
}
|
||||
prev
|
||||
}
|
||||
|
||||
/// Enables specific interrupts and returns the previous setting
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Do not call this function inside an `interrupt::free` critical section
|
||||
#[inline]
|
||||
pub unsafe fn enable_mask(mask: u32) -> u32 {
|
||||
let mut prev: u32 = 0;
|
||||
let _dummy: u32;
|
||||
asm!("
|
||||
xsr.intenable {0} // get mask and temporarily disable interrupts
|
||||
or {1}, {1}, {0}
|
||||
rsync
|
||||
wsr.intenable {1}
|
||||
rsync
|
||||
", inout(reg) prev, inout(reg) mask => _dummy, options(nostack));
|
||||
prev
|
||||
}
|
||||
|
||||
/// Get current interrupt mask
|
||||
#[inline]
|
||||
pub fn get_mask() -> u32 {
|
||||
let mask: u32;
|
||||
unsafe { asm!("rsr.intenable {0}", out(reg) mask) };
|
||||
mask
|
||||
}
|
||||
|
||||
/// Get currently active interrupts
|
||||
#[inline]
|
||||
pub fn get() -> u32 {
|
||||
let mask: u32;
|
||||
unsafe {
|
||||
asm!("rsr.interrupt {0}", out(reg) mask, options(nostack));
|
||||
}
|
||||
mask
|
||||
}
|
||||
|
||||
/// Set interrupt
|
||||
///
|
||||
/// Only valid for software interrupts
|
||||
#[inline]
|
||||
pub unsafe fn set(mask: u32) {
|
||||
asm!("
|
||||
wsr.intset {0}
|
||||
rsync
|
||||
",
|
||||
in(reg) mask, options(nostack)
|
||||
);
|
||||
}
|
||||
|
||||
/// Clear interrupt
|
||||
///
|
||||
/// Only valid for software and edge-triggered interrupts
|
||||
#[inline]
|
||||
pub unsafe fn clear(mask: u32) {
|
||||
asm!("
|
||||
wsr.intclear {0}
|
||||
rsync
|
||||
",
|
||||
in(reg) mask, options(nostack)
|
||||
);
|
||||
}
|
||||
|
||||
/// Get current interrupt level
|
||||
#[inline]
|
||||
pub fn get_level() -> u32 {
|
||||
let ps: u32;
|
||||
unsafe {
|
||||
asm!("rsr.ps {0}", out(reg) ps, options(nostack));
|
||||
};
|
||||
ps & 0xf
|
||||
}
|
||||
|
||||
/// Execute closure `f` in an interrupt-free context.
|
||||
///
|
||||
/// This as also known as a "critical section".
|
||||
#[inline]
|
||||
pub fn free<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&CriticalSection) -> R,
|
||||
{
|
||||
// disable interrupts and store old mask
|
||||
let old_mask = disable();
|
||||
|
||||
let r = f(unsafe { &CriticalSection::new() });
|
||||
|
||||
// enable previously disable interrupts
|
||||
unsafe {
|
||||
enable_mask(old_mask);
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
Reference in new issue
Block a user