Change charge display off to sleep (#3153)

This commit is contained in:
Pezsma
2026-04-29 19:57:17 +02:00
committed by GitHub
parent c9f310f548
commit ab986a0378
18 changed files with 1195 additions and 826 deletions
+202 -3
View File
@@ -37,8 +37,20 @@
#include "ch.h"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "irq_rtc.hpp"
#include "i2c_lld.h"
#include "i2cdevmanager.hpp"
#include "i2cdev_ppmod.hpp"
#include "lpc43xx.inc"
#include "nvic.h"
#include "lpc43xx_m0.h"
#include "rffc507x_spi.hpp"
#include <array>
@@ -134,7 +146,194 @@ void EventDispatcher::set_display_sleep(const bool sleep) {
// Let frame sync handler turn on backlight after repaint.
}
EventDispatcher::display_sleep = sleep;
};
}
void EventDispatcher::charge_deep_sleep(const bool sleep) {
bool detect = false;
uint8_t valid_mask = 0;
uint8_t percent = 0;
uint16_t voltage = 0;
int32_t current = 0;
constexpr I2CConfig i2c_config_12mhz{
.high_count = 15,
.low_count = 15,
};
if (sleep) {
auto dev = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
if (dev) dev->send_poweroff_command();
rffc507x::spi::SPI().power_down();
portapack::shutdown(false, true);
// Unmount SD card and stop driver
f_mount(nullptr, reinterpret_cast<const TCHAR*>(_T("")), 0);
sdcDisconnect(&SDCD1);
sdcStop(&SDCD1);
// Signal application shutdown
ShutdownMessage shutdown_message;
shared_memory.application_queue.push(shutdown_message);
shared_memory.baseband_message = nullptr;
// Disable core interrupts and system tick
nvicDisableVector(DMA_IRQn);
nvicDisableVector(M4CORE_IRQn);
chSysDisable();
systick_stop();
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;
#ifdef PRALINE
// Power management and GPIO configuration for Praline hardware
gpio_vaa_disable.set();
gpio_1v2_enable.clear();
LPC_GPIO->DIR[0] &= ~0xFFFF4000;
LPC_GPIO->DIR[1] &= ~0xFFFF1000;
LPC_GPIO->DIR[2] &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) | (1 << 6) | (1 << 0));
LPC_GPIO->DIR[3] &= ~((1 << 7) | (1 << 5));
LPC_GPIO->DIR[5] &= ~(1 << 16);
#else
// Power management and GPIO configuration for legacy hardware
gpio_og_vaa_disable.set();
gpio_r9_vaa_disable.clear();
LPC_GPIO->DIR[0] &= ~0xFFFF4000;
LPC_GPIO->DIR[1] &= ~0xFFFF1000;
LPC_GPIO->DIR[2] &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) | (1 << 6) | (1 << 0));
LPC_GPIO->DIR[3] &= ~((1 << 7) | (1 << 5));
LPC_GPIO->DIR[5] &= ~(1 << 16);
#endif
// Power down peripherals (CGU cleanup)
LPC_RGU->RESET_CTRL[0] = (1 << 5); // USB0 Reset
LPC_CGU->PLL0USB_CTRL.PD = 1;
LPC_CGU->BASE_USB0_CLK.PD = 1;
LPC_CREG->CREG0 |= (1 << 5);
LPC_CGU->BASE_USB1_CLK.PD = 1;
LPC_CGU->BASE_UART0_CLK.PD = 1;
LPC_CGU->BASE_UART1_CLK.PD = 1;
LPC_CGU->BASE_UART2_CLK.PD = 1;
LPC_CGU->BASE_UART3_CLK.PD = 1;
LPC_CGU->BASE_SPI_CLK.PD = 1;
LPC_CGU->BASE_PERIPH_CLK.PD = 1;
LPC_CGU->BASE_SDIO_CLK.PD = 1;
LPC_CGU->BASE_SSP0_CLK.PD = 1;
LPC_CGU->BASE_SSP1_CLK.PD = 1;
LPC_CGU->BASE_LCD_CLK.PD = 1;
LPC_CGU->BASE_OUT_CLK.PD = 1;
(*(volatile uint32_t*)(&LPC_CGU->PLL0AUDIO_CTRL)) |= (1 << 0);
LPC_ADC0->CR &= ~(1 << 21);
LPC_ADC1->CR &= ~(1 << 21);
led_rx.off();
led_usb.off();
rtc_wakeup_init();
NVIC_EnableIRQ(I2C0_OR_I2C1_IRQn);
while (1) {
// --- Battery Status Check (I2C) ---
detect = battery::BatteryManagement::isDetected();
if (detect) {
battery::BatteryManagement::getBatteryInfo(valid_mask, percent, voltage, current);
bool is_full = (valid_mask == 31 && percent == 100 && current <= 10) ||
(valid_mask == 1 && percent == 100);
if (is_full) {
// Case 1: Battery full (All LEDs off)
led_rx.off();
led_tx.off();
} else if ((voltage < 4150 && current < 10) || valid_mask == 0) {
// Case 2: Not full but low current draw (<10mA) -> Charging error
led_tx.on(); // LED indicates error/idle
led_rx.off();
} else {
// Case 3: Actively charging
led_rx.on(); // LED indicates charging
led_tx.off();
}
} else {
// Case 4: Battery IC not detected -> Error or H2 or older, so don't show that as an error.
led_tx.on();
led_rx.on();
}
// Shut down I2C and power down the APB bus for sleep
portapack::i2c0.stop();
LPC_CGU->BASE_APB1_CLK.PD = 1;
// Save interrupt states before mass disable
uint32_t saved_iser0 = NVIC->ISER[0];
uint32_t saved_iser1 = NVIC->ISER[1];
uint32_t saved_iser2 = NVIC->ISER[2];
// Disable and clear all pending interrupts
NVIC->ICER[0] = 0xFFFFFFFF;
NVIC->ICER[1] = 0xFFFFFFFF;
NVIC->ICER[2] = 0xFFFFFFFF;
NVIC->ICPR[0] = 0xFFFFFFFF;
NVIC->ICPR[1] = 0xFFFFFFFF;
NVIC->ICPR[2] = 0xFFFFFFFF;
// Re-enable only necessary wakeup sources
NVIC_EnableIRQ(RTC_IRQn);
NVIC_EnableIRQ(EVENTROUTER_IRQn);
// Configure RTC wakeup interval
if (valid_mask != 0 || detect) {
rtc_wakeup(60);
} else {
rtc_wakeup(3);
}
LPC_RTC->ILR = 3;
LPC_EVENTROUTER->CLR_STAT = 0xFFFFFFFF;
NVIC_ClearPendingIRQ(RTC_IRQn);
NVIC_ClearPendingIRQ(EVENTROUTER_IRQn);
__disable_irq();
// Configure and enter Deep Sleep
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB();
__ISB();
// CPU enters sleep here
__WFI();
// --- WAKEUP SEQUENCE ---
__enable_irq();
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
// Cleanup RTC and restore peripheral clocks
LPC_RTC->AMR = 0xFF;
LPC_RTC->ILR = 3;
LPC_CGU->BASE_APB1_CLK.PD = 0;
LPC_RGU->RESET_CTRL[1] = (1 << 16);
// Short delay for power stability (3V3 rail)
for (volatile int d = 0; d < 10000; d++);
// Restart I2C controller
LPC_CGU->BASE_APB1_CLK.PD = 0;
portapack::i2c0.start(i2c_config_12mhz);
// Restore original interrupt enable states
NVIC->ISER[0] = saved_iser0;
NVIC->ISER[1] = saved_iser1;
NVIC->ISER[2] = saved_iser2;
} // End of while(1) deep sleep loop
} else {
portapack::display.wake(true);
}
}
eventmask_t EventDispatcher::wait() {
return chEvtWaitAny(ALL_EVENTS);
+2
View File
@@ -63,6 +63,8 @@ class EventDispatcher {
static void set_display_sleep(const bool sleep);
static void charge_deep_sleep(const bool sleep);
static inline void check_fifo_isr() {
if (!shared_memory.application_queue.is_empty()) {
events_flag_isr(EVT_MASK_APPLICATION);
+1 -1
View File
@@ -45,7 +45,7 @@ SdOverUsbView::SdOverUsbView(NavigationView& nav)
sdcDisconnect(&SDCD1);
sdcStop(&SDCD1);
portapack::shutdown(true);
portapack::shutdown(true, false);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
m0_halt();
/* will not return*/
+8
View File
@@ -103,5 +103,13 @@ data_t SPI::transfer_word(const Direction direction, const address_t address, co
return data_in;
}
void SPI::power_down() {
// A 0x01 address the MIX_CTRL register. A 0x0000
transfer_word(Direction::Write, 0x01, 0x0000);
// a chip 300 µA-es Power Down
transfer_word(Direction::Write, 0x00, 0x0000);
}
} // namespace spi
} // namespace rffc507x
+1
View File
@@ -41,6 +41,7 @@ class SPI {
};
void init();
void power_down();
reg_t read(const address_t address) {
return transfer_word(Direction::Read, address, 0);
+80
View File
@@ -27,6 +27,8 @@
using namespace lpc43xx;
#include "event_m0.hpp"
#include "nvic.h"
#include "lpc43xx.inc"
static Thread* thread_rtc_event = NULL;
@@ -36,6 +38,84 @@ void rtc_interrupt_enable() {
nvicEnableVector(RTC_IRQn, CORTEX_PRIORITY_MASK(LPC_RTC_IRQ_PRIORITY));
}
void rtc_reset_default() {
// 1. FULL RTC CLEANUP/RESET
LPC_RTC->CIIR = 0;
LPC_RTC->AMR = 0xFF; // Disable all alarms
LPC_RTC->ILR = 3; // Clear stuck interrupt flags
LPC_RTC->ASEC = 0;
LPC_RTC->AMIN = 0;
LPC_RTC->AHRS = 0;
// 2. RESET EVENT ROUTER
// Reset to edge-triggered (Garantálja, hogy nem ragad be az ébresztés!)
LPC_EVENTROUTER->EDGE |= (1 << 5);
// DISABLE RTC channel routing
LPC_EVENTROUTER->CLR_EN = (1 << 5);
// Clear pending events
LPC_EVENTROUTER->CLR_STAT = 0xFFFFFFFF;
}
void rtc_wakeup_init() {
rtc_reset_default();
// 1. Force RTC Clock (In case it was modified by the app)
// Bit 0: Enable, Bit 4: Calibration OFF
LPC_RTC->CCR = (1 << 0);
// 2. EVENT ROUTER CONFIGURATION
LPC_EVENTROUTER->HILO |= (1 << 5);
LPC_EVENTROUTER->EDGE |= (1 << 5); // <-- JAVÍTVA! (Itt volt a hiba a te kódodban)
LPC_EVENTROUTER->CLR_STAT = 0xFFFFFFFF; // Clear pending events
LPC_EVENTROUTER->SET_EN = (1 << 5); // Enable RTC channel routing
// 3. NVIC Cleanup
NVIC_ClearPendingIRQ(RTC_IRQn);
NVIC_ClearPendingIRQ(EVENTROUTER_IRQn);
NVIC_EnableIRQ(RTC_IRQn);
NVIC_EnableIRQ(EVENTROUTER_IRQn);
}
void rtc_wakeup(uint32_t sleep_seconds) {
// 1. Safety cleanup after application tasks
LPC_RTC->CCR &= ~(1 << 4); // Disable calibration (Important!)
uint32_t sec = LPC_RTC->SEC;
uint32_t min = LPC_RTC->MIN;
uint32_t hrs = LPC_RTC->HRS;
sec += sleep_seconds;
while (sec >= 60) {
sec -= 60;
min++;
}
while (min >= 60) {
min -= 60;
hrs++;
}
while (hrs >= 24) {
hrs -= 24;
}
// 2. Write values to alarm registers
LPC_RTC->ASEC = sec;
LPC_RTC->AMIN = min;
LPC_RTC->AHRS = hrs;
// Mask for SEC, MIN, HRS (Disable all other alarm triggers)
uint32_t mask = 0xFF ^ ((1 << 0) | (1 << 1) | (1 << 2));
LPC_RTC->AMR = mask;
// Verify write (Sync with RTC domain)
while (LPC_RTC->ASEC != sec);
while (LPC_RTC->AMR != mask);
// Brief extra delay to allow internal logic to settle/latch
for (volatile int i = 0; i < 5000; i++) __asm__("nop");
}
extern "C" {
CH_IRQ_HANDLER(RTC_IRQHandler) {
+5
View File
@@ -22,6 +22,11 @@
#ifndef __IPC_RTC_H__
#define __IPC_RTC_H__
#include <cstdint>
void rtc_interrupt_enable();
void rtc_reset_default();
void rtc_wakeup_init();
void rtc_wakeup(uint32_t sleep_seconds);
#endif /*__IPC_RTC_H__*/
+4
View File
@@ -141,6 +141,8 @@ Continuous (Fox-oring)
#include <string.h>
#include "i2cdevmanager.hpp"
#include "lpc43xx.inc"
#include "rffc507x.hpp" /* c/m, avoiding initial short ON Ant_DC_Bias pulse, from cold reset */
rffc507x::RFFC507x first_if;
ui::SystemView* system_view_ptr;
@@ -181,6 +183,8 @@ static void event_loop() {
}
int main(void) {
rtc_reset_default();
#ifndef PRALINE // Do not perform quick set up of GP01_RFF507X = 1 for PRALINE
first_if.init(); /* To avoid initial short Ant_DC_Bias pulse ,we need quick set up GP01_RFF507X =1 */
#endif
+20 -2
View File
@@ -339,6 +339,20 @@ static void shutdown_base() {
clock_manager.shutdown();
}
static void shutdown_base_12mhz() {
i2c0.stop();
set_clock_config(clock_config_irc);
cgu::pll1::disable();
set_idivc_base_clocks(cgu::CLK_SEL::IRC);
i2c0.start(i2c_config_boot_clock);
clock_manager.shutdown();
}
static void set_cpu_clock_speed() {
/* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4
* core clock speed to the 110 - 204MHz range.
@@ -698,7 +712,7 @@ init_status_t init() {
return return_code;
}
void shutdown(const bool leave_screen_on) {
void shutdown(const bool leave_screen_on, const bool slow_clock) {
gpdma::controller.disable();
if (!leave_screen_on) {
@@ -712,7 +726,11 @@ void shutdown(const bool leave_screen_on) {
hackrf::cpld::init_from_eeprom();
shutdown_base();
if (slow_clock) {
shutdown_base_12mhz();
} else {
shutdown_base();
}
}
void setEventDispatcherToUSBSerial(EventDispatcher* evt) {
+1 -1
View File
@@ -78,7 +78,7 @@ void set_antenna_bias(const bool v);
bool get_antenna_bias();
init_status_t init();
void shutdown(const bool leave_screen_on = false);
void shutdown(const bool leave_screen_on = false, const bool slow_clock = false);
void setEventDispatcherToUSBSerial(EventDispatcher* evt);
+1 -1
View File
@@ -606,4 +606,4 @@ uint32_t register_read(const size_t register_number) {
} /* namespace debug */
} /* namespace radio */
} /* namespace radio */
+1 -1
View File
@@ -136,4 +136,4 @@ uint32_t register_read(const size_t register_number);
} /* namespace radio */
#endif /*__RADIO_H__*/
#endif /*__RADIO_H__*/
+4 -4
View File
@@ -347,11 +347,11 @@ void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
// Only show charging modal when transitioning to charging state
nav_.display_modal(
"CHARGING",
"Screen on while charging?",
"Enter deep sleep? \n \nExit: Press reset button. \n \nRX LED: Charging. \nTX LED: Charging error. \nLEDs OFF: Charge complete.",
YESNO,
[this](bool keep_screen_on) {
if (!keep_screen_on) {
EventDispatcher::set_display_sleep(true);
[this](bool deepsleep) {
if (deepsleep) {
EventDispatcher::charge_deep_sleep(true);
}
});
}