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
+257
@@ -0,0 +1,257 @@
|
||||
use crate::{
|
||||
clock::{Clock, PllClock, XtalClock},
|
||||
regi2c_write,
|
||||
};
|
||||
|
||||
const REF_CLK_FREQ: u32 = 1000000;
|
||||
|
||||
const MHZ: u32 = 1000000;
|
||||
const UINT16_MAX: u32 = 0xffff;
|
||||
|
||||
const RTC_CNTL_DBIAS_1V10: u32 = 4;
|
||||
const RTC_CNTL_DBIAS_1V25: u32 = 7;
|
||||
|
||||
const DIG_DBIAS_80M_160M: u32 = RTC_CNTL_DBIAS_1V10;
|
||||
const DIG_DBIAS_XTAL: u32 = RTC_CNTL_DBIAS_1V10;
|
||||
|
||||
const I2C_BBPLL: u32 = 0x66;
|
||||
const I2C_BBPLL_HOSTID: u32 = 4;
|
||||
|
||||
const I2C_BBPLL_IR_CAL_DELAY: u32 = 0;
|
||||
const I2C_BBPLL_IR_CAL_EXT_CAP: u32 = 1;
|
||||
const I2C_BBPLL_OC_ENB_FCAL: u32 = 4;
|
||||
const I2C_BBPLL_OC_ENB_VCON: u32 = 10;
|
||||
const I2C_BBPLL_BBADC_CAL_7_0: u32 = 12;
|
||||
|
||||
const BBPLL_IR_CAL_DELAY_VAL: u32 = 0x18;
|
||||
const BBPLL_IR_CAL_EXT_CAP_VAL: u32 = 0x20;
|
||||
const BBPLL_OC_ENB_FCAL_VAL: u32 = 0x9a;
|
||||
const BBPLL_OC_ENB_VCON_VAL: u32 = 0x00;
|
||||
const BBPLL_BBADC_CAL_7_0_VAL: u32 = 0x00;
|
||||
|
||||
const I2C_BBPLL_ENDIV5: u32 = 11;
|
||||
|
||||
const BBPLL_ENDIV5_VAL_320M: u32 = 0x43;
|
||||
const BBPLL_BBADC_DSMP_VAL_320M: u32 = 0x84;
|
||||
const BBPLL_ENDIV5_VAL_480M: u32 = 0xc3;
|
||||
const BBPLL_BBADC_DSMP_VAL_480M: u32 = 0x74;
|
||||
|
||||
const I2C_BBPLL_BBADC_DSMP: u32 = 9;
|
||||
const I2C_BBPLL_OC_LREF: u32 = 2;
|
||||
const I2C_BBPLL_OC_DIV_7_0: u32 = 3;
|
||||
const I2C_BBPLL_OC_DCUR: u32 = 5;
|
||||
|
||||
pub(crate) fn esp32_rtc_bbpll_configure(xtal_freq: XtalClock, pll_freq: PllClock) {
|
||||
let efuse = unsafe { &*crate::peripherals::EFUSE::ptr() };
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
|
||||
let rtc_cntl_dbias_hp_volt: u32 =
|
||||
RTC_CNTL_DBIAS_1V25 - efuse.blk0_rdata5().read().rd_vol_level_hp_inv().bits() as u32;
|
||||
let dig_dbias_240_m: u32 = rtc_cntl_dbias_hp_volt;
|
||||
|
||||
let div_ref: u32;
|
||||
let div7_0: u32;
|
||||
let div10_8: u32;
|
||||
let lref: u32;
|
||||
let dcur: u32;
|
||||
let bw: u32;
|
||||
|
||||
if matches!(pll_freq, PllClock::Pll320MHz) {
|
||||
// Raise the voltage, if needed
|
||||
rtc_cntl
|
||||
.reg()
|
||||
.modify(|_, w| unsafe { w.dig_dbias_wak().bits(DIG_DBIAS_80M_160M as u8) });
|
||||
|
||||
// Configure 320M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 32;
|
||||
div10_8 = 0;
|
||||
lref = 0;
|
||||
dcur = 6;
|
||||
bw = 3;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq26M => {
|
||||
div_ref = 12;
|
||||
div7_0 = 224;
|
||||
div10_8 = 4;
|
||||
lref = 1;
|
||||
dcur = 0;
|
||||
bw = 1;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 12;
|
||||
div7_0 = 224;
|
||||
div10_8 = 4;
|
||||
lref = 0;
|
||||
dcur = 0;
|
||||
bw = 0;
|
||||
}
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_ENDIV5, BBPLL_ENDIV5_VAL_320M);
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_BBADC_DSMP, BBPLL_BBADC_DSMP_VAL_320M);
|
||||
} else {
|
||||
// Raise the voltage
|
||||
rtc_cntl
|
||||
.reg()
|
||||
.modify(|_, w| unsafe { w.dig_dbias_wak().bits(dig_dbias_240_m as u8) });
|
||||
|
||||
// Configure 480M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 28;
|
||||
div10_8 = 0;
|
||||
lref = 0;
|
||||
dcur = 6;
|
||||
bw = 3;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq26M => {
|
||||
div_ref = 12;
|
||||
div7_0 = 144;
|
||||
div10_8 = 4;
|
||||
lref = 1;
|
||||
dcur = 0;
|
||||
bw = 1;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 12;
|
||||
div7_0 = 224;
|
||||
div10_8 = 4;
|
||||
lref = 0;
|
||||
dcur = 0;
|
||||
bw = 0;
|
||||
}
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_ENDIV5, BBPLL_ENDIV5_VAL_480M);
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_BBADC_DSMP, BBPLL_BBADC_DSMP_VAL_480M);
|
||||
}
|
||||
|
||||
let i2c_bbpll_lref = (lref << 7) | (div10_8 << 4) | (div_ref);
|
||||
let i2c_bbpll_div_7_0 = div7_0;
|
||||
let i2c_bbpll_dcur = (bw << 6) | dcur;
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_LREF, i2c_bbpll_lref);
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
|
||||
}
|
||||
|
||||
pub(crate) fn esp32_rtc_bbpll_enable() {
|
||||
unsafe { &*crate::peripherals::RTC_CNTL::ptr() }
|
||||
.options0()
|
||||
.modify(|_, w| {
|
||||
w.bias_i2c_force_pd()
|
||||
.clear_bit()
|
||||
.bb_i2c_force_pd()
|
||||
.clear_bit()
|
||||
.bbpll_force_pd()
|
||||
.clear_bit()
|
||||
.bbpll_i2c_force_pd()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// reset BBPLL configuration
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_IR_CAL_DELAY, BBPLL_IR_CAL_DELAY_VAL);
|
||||
regi2c_write!(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_IR_CAL_EXT_CAP,
|
||||
BBPLL_IR_CAL_EXT_CAP_VAL
|
||||
);
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_ENB_FCAL, BBPLL_OC_ENB_FCAL_VAL);
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_ENB_VCON, BBPLL_OC_ENB_VCON_VAL);
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_BBADC_CAL_7_0, BBPLL_BBADC_CAL_7_0_VAL);
|
||||
}
|
||||
|
||||
pub(crate) fn esp32_rtc_update_to_xtal(freq: XtalClock, _div: u32) {
|
||||
let apb_cntl = unsafe { &*crate::peripherals::APB_CTRL::ptr() };
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
|
||||
unsafe {
|
||||
let value = (((freq.hz()) >> 12) & UINT16_MAX) | ((((freq.hz()) >> 12) & UINT16_MAX) << 16);
|
||||
esp32_update_cpu_freq(freq.hz());
|
||||
|
||||
// set divider from XTAL to APB clock
|
||||
apb_cntl.sysclk_conf().modify(|_, w| {
|
||||
w.pre_div_cnt()
|
||||
.bits(((freq.hz()) / REF_CLK_FREQ - 1) as u16)
|
||||
});
|
||||
|
||||
// adjust ref_tick
|
||||
apb_cntl.xtal_tick_conf().modify(|_, w| {
|
||||
w.xtal_tick_num()
|
||||
.bits(((freq.hz()) / REF_CLK_FREQ - 1) as u8)
|
||||
});
|
||||
|
||||
// switch clock source
|
||||
rtc_cntl.clk_conf().modify(|_, w| w.soc_clk_sel().xtal());
|
||||
rtc_cntl.store5().modify(|_, w| w.scratch5().bits(value));
|
||||
|
||||
// lower the voltage
|
||||
rtc_cntl
|
||||
.reg()
|
||||
.modify(|_, w| w.dig_dbias_wak().bits(DIG_DBIAS_XTAL as u8));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_cpu_freq(cpu_freq_mhz: crate::clock::CpuClock) {
|
||||
let efuse = unsafe { &*crate::peripherals::EFUSE::ptr() };
|
||||
let dport = unsafe { &*crate::peripherals::DPORT::ptr() };
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
|
||||
unsafe {
|
||||
const RTC_CNTL_DBIAS_1V25: u32 = 7;
|
||||
|
||||
let rtc_cntl_dbias_hp_volt: u32 =
|
||||
RTC_CNTL_DBIAS_1V25 - efuse.blk0_rdata5().read().rd_vol_level_hp_inv().bits() as u32;
|
||||
let dig_dbias_240_m: u32 = rtc_cntl_dbias_hp_volt;
|
||||
|
||||
const CPU_80M: u32 = 0;
|
||||
const CPU_160M: u32 = 1;
|
||||
const CPU_240M: u32 = 2;
|
||||
|
||||
let mut dbias = DIG_DBIAS_80M_160M;
|
||||
let per_conf;
|
||||
|
||||
match cpu_freq_mhz {
|
||||
crate::clock::CpuClock::Clock160MHz => {
|
||||
per_conf = CPU_160M;
|
||||
}
|
||||
crate::clock::CpuClock::Clock240MHz => {
|
||||
dbias = dig_dbias_240_m;
|
||||
per_conf = CPU_240M;
|
||||
}
|
||||
crate::clock::CpuClock::Clock80MHz => {
|
||||
per_conf = CPU_80M;
|
||||
}
|
||||
}
|
||||
|
||||
let value = (((80 * MHZ) >> 12) & UINT16_MAX) | ((((80 * MHZ) >> 12) & UINT16_MAX) << 16);
|
||||
dport
|
||||
.cpu_per_conf()
|
||||
.write(|w| w.cpuperiod_sel().bits(per_conf as u8));
|
||||
rtc_cntl
|
||||
.reg()
|
||||
.modify(|_, w| w.dig_dbias_wak().bits(dbias as u8));
|
||||
rtc_cntl.clk_conf().modify(|_, w| w.soc_clk_sel().pll());
|
||||
rtc_cntl.store5().modify(|_, w| w.scratch5().bits(value));
|
||||
|
||||
esp32_update_cpu_freq(cpu_freq_mhz.mhz());
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass the CPU clock in MHz so that ets_delay_us
|
||||
/// will be accurate. Call this function when CPU frequency is changed.
|
||||
fn esp32_update_cpu_freq(mhz: u32) {
|
||||
const G_TICKS_PER_US_PRO: u32 = 0x3ffe01e0;
|
||||
unsafe {
|
||||
// Update scale factors used by esp_rom_delay_us
|
||||
(G_TICKS_PER_US_PRO as *mut u32).write_volatile(mhz);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user