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:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
#[doc = r"Register block"]
#[repr(C)]
pub struct XOSC32KI {
#[doc = "0x00 - Enable or disable bypass of LFCLK crystal oscillator with external clock source"]
pub bypass: BYPASS,
_reserved1: [u8; 0x0c],
#[doc = "0x10 - Control usage of internal load capacitors"]
pub intcap: INTCAP,
}
#[doc = "BYPASS (rw) register accessor: an alias for `Reg<BYPASS_SPEC>`"]
pub type BYPASS = crate::Reg<bypass::BYPASS_SPEC>;
#[doc = "Enable or disable bypass of LFCLK crystal oscillator with external clock source"]
pub mod bypass;
#[doc = "INTCAP (rw) register accessor: an alias for `Reg<INTCAP_SPEC>`"]
pub type INTCAP = crate::Reg<intcap::INTCAP_SPEC>;
#[doc = "Control usage of internal load capacitors"]
pub mod intcap;
@@ -0,0 +1,126 @@
#[doc = "Register `BYPASS` reader"]
pub struct R(crate::R<BYPASS_SPEC>);
impl core::ops::Deref for R {
type Target = crate::R<BYPASS_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<crate::R<BYPASS_SPEC>> for R {
#[inline(always)]
fn from(reader: crate::R<BYPASS_SPEC>) -> Self {
R(reader)
}
}
#[doc = "Register `BYPASS` writer"]
pub struct W(crate::W<BYPASS_SPEC>);
impl core::ops::Deref for W {
type Target = crate::W<BYPASS_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for W {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<crate::W<BYPASS_SPEC>> for W {
#[inline(always)]
fn from(writer: crate::W<BYPASS_SPEC>) -> Self {
W(writer)
}
}
#[doc = "Field `BYPASS` reader - Enable or disable bypass of LFCLK crystal oscillator with external clock source"]
pub type BYPASS_R = crate::BitReader<BYPASS_A>;
#[doc = "Enable or disable bypass of LFCLK crystal oscillator with external clock source\n\nValue on reset: 0"]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BYPASS_A {
#[doc = "0: Disable (use with crystal or low-swing external source)"]
DISABLED = 0,
#[doc = "1: Enable (use with rail-to-rail external source)"]
ENABLED = 1,
}
impl From<BYPASS_A> for bool {
#[inline(always)]
fn from(variant: BYPASS_A) -> Self {
variant as u8 != 0
}
}
impl BYPASS_R {
#[doc = "Get enumerated values variant"]
#[inline(always)]
pub fn variant(&self) -> BYPASS_A {
match self.bits {
false => BYPASS_A::DISABLED,
true => BYPASS_A::ENABLED,
}
}
#[doc = "Checks if the value of the field is `DISABLED`"]
#[inline(always)]
pub fn is_disabled(&self) -> bool {
*self == BYPASS_A::DISABLED
}
#[doc = "Checks if the value of the field is `ENABLED`"]
#[inline(always)]
pub fn is_enabled(&self) -> bool {
*self == BYPASS_A::ENABLED
}
}
#[doc = "Field `BYPASS` writer - Enable or disable bypass of LFCLK crystal oscillator with external clock source"]
pub type BYPASS_W<'a, const O: u8> = crate::BitWriter<'a, u32, BYPASS_SPEC, BYPASS_A, O>;
impl<'a, const O: u8> BYPASS_W<'a, O> {
#[doc = "Disable (use with crystal or low-swing external source)"]
#[inline(always)]
pub fn disabled(self) -> &'a mut W {
self.variant(BYPASS_A::DISABLED)
}
#[doc = "Enable (use with rail-to-rail external source)"]
#[inline(always)]
pub fn enabled(self) -> &'a mut W {
self.variant(BYPASS_A::ENABLED)
}
}
impl R {
#[doc = "Bit 0 - Enable or disable bypass of LFCLK crystal oscillator with external clock source"]
#[inline(always)]
pub fn bypass(&self) -> BYPASS_R {
BYPASS_R::new((self.bits & 1) != 0)
}
}
impl W {
#[doc = "Bit 0 - Enable or disable bypass of LFCLK crystal oscillator with external clock source"]
#[inline(always)]
pub fn bypass(&mut self) -> BYPASS_W<0> {
BYPASS_W::new(self)
}
#[doc = "Writes raw bits to the register."]
#[inline(always)]
pub unsafe fn bits(&mut self, bits: u32) -> &mut Self {
self.0.bits(bits);
self
}
}
#[doc = "Enable or disable bypass of LFCLK crystal oscillator with external clock source\n\nThis register you can [`read`](crate::generic::Reg::read), [`write_with_zero`](crate::generic::Reg::write_with_zero), [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`modify`](crate::generic::Reg::modify). See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [bypass](index.html) module"]
pub struct BYPASS_SPEC;
impl crate::RegisterSpec for BYPASS_SPEC {
type Ux = u32;
}
#[doc = "`read()` method returns [bypass::R](R) reader structure"]
impl crate::Readable for BYPASS_SPEC {
type Reader = R;
}
#[doc = "`write(|w| ..)` method takes [bypass::W](W) writer structure"]
impl crate::Writable for BYPASS_SPEC {
type Writer = W;
}
#[doc = "`reset()` method sets BYPASS to value 0"]
impl crate::Resettable for BYPASS_SPEC {
#[inline(always)]
fn reset_value() -> Self::Ux {
0
}
}
@@ -0,0 +1,155 @@
#[doc = "Register `INTCAP` reader"]
pub struct R(crate::R<INTCAP_SPEC>);
impl core::ops::Deref for R {
type Target = crate::R<INTCAP_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<crate::R<INTCAP_SPEC>> for R {
#[inline(always)]
fn from(reader: crate::R<INTCAP_SPEC>) -> Self {
R(reader)
}
}
#[doc = "Register `INTCAP` writer"]
pub struct W(crate::W<INTCAP_SPEC>);
impl core::ops::Deref for W {
type Target = crate::W<INTCAP_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for W {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<crate::W<INTCAP_SPEC>> for W {
#[inline(always)]
fn from(writer: crate::W<INTCAP_SPEC>) -> Self {
W(writer)
}
}
#[doc = "Field `INTCAP` reader - Control usage of internal load capacitors"]
pub type INTCAP_R = crate::FieldReader<u8, INTCAP_A>;
#[doc = "Control usage of internal load capacitors\n\nValue on reset: 0"]
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)]
pub enum INTCAP_A {
#[doc = "0: Use external load capacitors"]
EXTERNAL = 0,
#[doc = "1: 6 pF internal load capacitance"]
C6PF = 1,
#[doc = "2: 7 pF internal load capacitance"]
C7PF = 2,
#[doc = "3: 9 pF internal load capacitance"]
C9PF = 3,
}
impl From<INTCAP_A> for u8 {
#[inline(always)]
fn from(variant: INTCAP_A) -> Self {
variant as _
}
}
impl INTCAP_R {
#[doc = "Get enumerated values variant"]
#[inline(always)]
pub fn variant(&self) -> INTCAP_A {
match self.bits {
0 => INTCAP_A::EXTERNAL,
1 => INTCAP_A::C6PF,
2 => INTCAP_A::C7PF,
3 => INTCAP_A::C9PF,
_ => unreachable!(),
}
}
#[doc = "Checks if the value of the field is `EXTERNAL`"]
#[inline(always)]
pub fn is_external(&self) -> bool {
*self == INTCAP_A::EXTERNAL
}
#[doc = "Checks if the value of the field is `C6PF`"]
#[inline(always)]
pub fn is_c6pf(&self) -> bool {
*self == INTCAP_A::C6PF
}
#[doc = "Checks if the value of the field is `C7PF`"]
#[inline(always)]
pub fn is_c7pf(&self) -> bool {
*self == INTCAP_A::C7PF
}
#[doc = "Checks if the value of the field is `C9PF`"]
#[inline(always)]
pub fn is_c9pf(&self) -> bool {
*self == INTCAP_A::C9PF
}
}
#[doc = "Field `INTCAP` writer - Control usage of internal load capacitors"]
pub type INTCAP_W<'a, const O: u8> =
crate::FieldWriterSafe<'a, u32, INTCAP_SPEC, u8, INTCAP_A, 2, O>;
impl<'a, const O: u8> INTCAP_W<'a, O> {
#[doc = "Use external load capacitors"]
#[inline(always)]
pub fn external(self) -> &'a mut W {
self.variant(INTCAP_A::EXTERNAL)
}
#[doc = "6 pF internal load capacitance"]
#[inline(always)]
pub fn c6pf(self) -> &'a mut W {
self.variant(INTCAP_A::C6PF)
}
#[doc = "7 pF internal load capacitance"]
#[inline(always)]
pub fn c7pf(self) -> &'a mut W {
self.variant(INTCAP_A::C7PF)
}
#[doc = "9 pF internal load capacitance"]
#[inline(always)]
pub fn c9pf(self) -> &'a mut W {
self.variant(INTCAP_A::C9PF)
}
}
impl R {
#[doc = "Bits 0:1 - Control usage of internal load capacitors"]
#[inline(always)]
pub fn intcap(&self) -> INTCAP_R {
INTCAP_R::new((self.bits & 3) as u8)
}
}
impl W {
#[doc = "Bits 0:1 - Control usage of internal load capacitors"]
#[inline(always)]
pub fn intcap(&mut self) -> INTCAP_W<0> {
INTCAP_W::new(self)
}
#[doc = "Writes raw bits to the register."]
#[inline(always)]
pub unsafe fn bits(&mut self, bits: u32) -> &mut Self {
self.0.bits(bits);
self
}
}
#[doc = "Control usage of internal load capacitors\n\nThis register you can [`read`](crate::generic::Reg::read), [`write_with_zero`](crate::generic::Reg::write_with_zero), [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`modify`](crate::generic::Reg::modify). See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [intcap](index.html) module"]
pub struct INTCAP_SPEC;
impl crate::RegisterSpec for INTCAP_SPEC {
type Ux = u32;
}
#[doc = "`read()` method returns [intcap::R](R) reader structure"]
impl crate::Readable for INTCAP_SPEC {
type Reader = R;
}
#[doc = "`write(|w| ..)` method takes [intcap::W](W) writer structure"]
impl crate::Writable for INTCAP_SPEC {
type Writer = W;
}
#[doc = "`reset()` method sets INTCAP to value 0"]
impl crate::Resettable for INTCAP_SPEC {
#[inline(always)]
fn reset_value() -> Self::Ux {
0
}
}
+140
View File
@@ -0,0 +1,140 @@
#[doc = "Register `XOSC32MCAPS` reader"]
pub struct R(crate::R<XOSC32MCAPS_SPEC>);
impl core::ops::Deref for R {
type Target = crate::R<XOSC32MCAPS_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<crate::R<XOSC32MCAPS_SPEC>> for R {
#[inline(always)]
fn from(reader: crate::R<XOSC32MCAPS_SPEC>) -> Self {
R(reader)
}
}
#[doc = "Register `XOSC32MCAPS` writer"]
pub struct W(crate::W<XOSC32MCAPS_SPEC>);
impl core::ops::Deref for W {
type Target = crate::W<XOSC32MCAPS_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for W {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<crate::W<XOSC32MCAPS_SPEC>> for W {
#[inline(always)]
fn from(writer: crate::W<XOSC32MCAPS_SPEC>) -> Self {
W(writer)
}
}
#[doc = "Field `CAPVALUE` reader - Value representing capacitance, calculated using provided equation"]
pub type CAPVALUE_R = crate::FieldReader<u8, u8>;
#[doc = "Field `CAPVALUE` writer - Value representing capacitance, calculated using provided equation"]
pub type CAPVALUE_W<'a, const O: u8> = crate::FieldWriter<'a, u32, XOSC32MCAPS_SPEC, u8, u8, 5, O>;
#[doc = "Field `ENABLE` reader - Enable on-chip capacitors on XC1 and XC2"]
pub type ENABLE_R = crate::BitReader<ENABLE_A>;
#[doc = "Enable on-chip capacitors on XC1 and XC2\n\nValue on reset: 0"]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ENABLE_A {
#[doc = "0: Capacitor disabled (use external caps)"]
DISABLED = 0,
#[doc = "1: Capacitor enabled"]
ENABLED = 1,
}
impl From<ENABLE_A> for bool {
#[inline(always)]
fn from(variant: ENABLE_A) -> Self {
variant as u8 != 0
}
}
impl ENABLE_R {
#[doc = "Get enumerated values variant"]
#[inline(always)]
pub fn variant(&self) -> ENABLE_A {
match self.bits {
false => ENABLE_A::DISABLED,
true => ENABLE_A::ENABLED,
}
}
#[doc = "Checks if the value of the field is `DISABLED`"]
#[inline(always)]
pub fn is_disabled(&self) -> bool {
*self == ENABLE_A::DISABLED
}
#[doc = "Checks if the value of the field is `ENABLED`"]
#[inline(always)]
pub fn is_enabled(&self) -> bool {
*self == ENABLE_A::ENABLED
}
}
#[doc = "Field `ENABLE` writer - Enable on-chip capacitors on XC1 and XC2"]
pub type ENABLE_W<'a, const O: u8> = crate::BitWriter<'a, u32, XOSC32MCAPS_SPEC, ENABLE_A, O>;
impl<'a, const O: u8> ENABLE_W<'a, O> {
#[doc = "Capacitor disabled (use external caps)"]
#[inline(always)]
pub fn disabled(self) -> &'a mut W {
self.variant(ENABLE_A::DISABLED)
}
#[doc = "Capacitor enabled"]
#[inline(always)]
pub fn enabled(self) -> &'a mut W {
self.variant(ENABLE_A::ENABLED)
}
}
impl R {
#[doc = "Bits 0:4 - Value representing capacitance, calculated using provided equation"]
#[inline(always)]
pub fn capvalue(&self) -> CAPVALUE_R {
CAPVALUE_R::new((self.bits & 0x1f) as u8)
}
#[doc = "Bit 8 - Enable on-chip capacitors on XC1 and XC2"]
#[inline(always)]
pub fn enable(&self) -> ENABLE_R {
ENABLE_R::new(((self.bits >> 8) & 1) != 0)
}
}
impl W {
#[doc = "Bits 0:4 - Value representing capacitance, calculated using provided equation"]
#[inline(always)]
pub fn capvalue(&mut self) -> CAPVALUE_W<0> {
CAPVALUE_W::new(self)
}
#[doc = "Bit 8 - Enable on-chip capacitors on XC1 and XC2"]
#[inline(always)]
pub fn enable(&mut self) -> ENABLE_W<8> {
ENABLE_W::new(self)
}
#[doc = "Writes raw bits to the register."]
#[inline(always)]
pub unsafe fn bits(&mut self, bits: u32) -> &mut Self {
self.0.bits(bits);
self
}
}
#[doc = "Programmable capacitance of XC1 and XC2\n\nThis register you can [`read`](crate::generic::Reg::read), [`write_with_zero`](crate::generic::Reg::write_with_zero), [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`modify`](crate::generic::Reg::modify). See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [xosc32mcaps](index.html) module"]
pub struct XOSC32MCAPS_SPEC;
impl crate::RegisterSpec for XOSC32MCAPS_SPEC {
type Ux = u32;
}
#[doc = "`read()` method returns [xosc32mcaps::R](R) reader structure"]
impl crate::Readable for XOSC32MCAPS_SPEC {
type Reader = R;
}
#[doc = "`write(|w| ..)` method takes [xosc32mcaps::W](W) writer structure"]
impl crate::Writable for XOSC32MCAPS_SPEC {
type Writer = W;
}
#[doc = "`reset()` method sets XOSC32MCAPS to value 0"]
impl crate::Resettable for XOSC32MCAPS_SPEC {
#[inline(always)]
fn reset_value() -> Self::Ux {
0
}
}