mirror of
https://github.com/infiniteCable2/panda.git
synced 2026-04-06 14:13:59 +08:00
Reduce panda power usage (#2340)
* add bootkick for ship mode * boot standby * comment * stop mode * fix som status * exti wakeup * all standby * clean * analog mode * adc * clean * HSI * stop only cuatro * comments * UM2331 * rm * comment * enforce SAFETY_SILENT * clean * misra * rm * disable pulls * assert * Revert "disable pulls" This reverts commit 3b954b929a5f864279d52e28a01768ca22962810. * pull note * add stop mode USB cmd * add stop mode to HITL * fix * async stop mode request * test all harness/ign configs * more time * use uptime * print debug * unstuck * show prints * more * logger * loglevel * log * uptime test * tighter timing * print wakeout source * rm debug * clean * robust * add CAN2 * test more cans * clean * more * longer * multiple * more * can1 * reorder * normal * clean * partial * clean * test * time * delay * reset * setup * reset * revert * silent * fix ordering * no random * warnings * err * timings * heartbeat * time * faster * can1 * simpler * test 20x * put back random * clean * comment * jenkinsfile * final
This commit is contained in:
10
board/main.c
10
board/main.c
@@ -341,6 +341,11 @@ int main(void) {
|
||||
|
||||
// LED should keep on blinking all the time
|
||||
while (true) {
|
||||
#ifdef ALLOW_DEBUG
|
||||
if (stop_mode_requested) {
|
||||
enter_stop_mode();
|
||||
}
|
||||
#endif
|
||||
if (!power_save_enabled) {
|
||||
#ifdef DEBUG_FAULTS
|
||||
if (fault_status == FAULT_STATUS_NONE) {
|
||||
@@ -369,6 +374,11 @@ int main(void) {
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if ((hw_type == HW_TYPE_CUATRO) && !current_board->read_som_gpio()) {
|
||||
assert_fatal(current_safety_mode == SAFETY_SILENT, "Error: Entering low power mode while not in SAFETY_SILENT. Hanging\n");
|
||||
enter_stop_mode(); // deep sleep, wakes on CAN or SBU activity
|
||||
assert_fatal(false, "Error: enter_stop_mode returned after system reset. Hanging\n");
|
||||
}
|
||||
__WFI();
|
||||
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,14 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
|
||||
resp[1] = ((fan_state.rpm & 0xFF00U) >> 8U);
|
||||
resp_len = 2;
|
||||
break;
|
||||
// **** 0xb5: request deep sleep, wakes on CAN or SBU
|
||||
#ifdef ALLOW_DEBUG
|
||||
case 0xb5:
|
||||
set_safety_mode(SAFETY_SILENT, 0U);
|
||||
set_power_save_state(true);
|
||||
stop_mode_requested = true;
|
||||
break;
|
||||
#endif
|
||||
// **** 0xc0: reset communications state
|
||||
case 0xc0:
|
||||
comms_can_reset();
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#include "board/sys/sys.h"
|
||||
|
||||
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM1840, we should never implement any of the available hardware low power modes.
|
||||
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM2331, we should never use any of the available hardware low power modes during safety function execution.
|
||||
// See rule: CoU_3
|
||||
|
||||
// Low power state "stop mode" is only entered from SAFETY_SILENT when no safety function is active and exited via reset which is a safe state.
|
||||
|
||||
bool power_save_enabled = false;
|
||||
#ifdef ALLOW_DEBUG
|
||||
volatile bool stop_mode_requested = false;
|
||||
#endif
|
||||
|
||||
void enable_can_transceivers(bool enabled) {
|
||||
// Leave main CAN always on for CAN-based ignition detection
|
||||
@@ -46,3 +51,98 @@ void set_power_save_state(bool enable) {
|
||||
power_save_enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
static void enter_stop_mode(void) {
|
||||
// set all GPIO to analog mode to reduce power, analog mode also disables pull resistors
|
||||
register_set(&(GPIOA->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOB->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOC->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOD->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOE->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOF->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOG->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
|
||||
// init GPIO to lowest power state
|
||||
current_board->set_bootkick(BOOT_STANDBY);
|
||||
current_board->set_amp_enabled(false);
|
||||
for (uint8_t i = 1U; i <= 4U; i++) {
|
||||
current_board->enable_can_transceiver(i, false);
|
||||
}
|
||||
|
||||
// disable ADCs
|
||||
ADC1->CR &= ~(ADC_CR_ADEN);
|
||||
ADC1->CR |= ADC_CR_DEEPPWD;
|
||||
ADC2->CR &= ~(ADC_CR_ADEN);
|
||||
ADC2->CR |= ADC_CR_DEEPPWD;
|
||||
|
||||
// disable HSI48: 48 MHz USB clock
|
||||
register_clear_bits(&(RCC->CR), RCC_CR_HSI48ON);
|
||||
// disable SRAM retention in stop mode
|
||||
register_clear_bits(&(RCC->AHB2LPENR), RCC_AHB2LPENR_SRAM1LPEN | RCC_AHB2LPENR_SRAM2LPEN);
|
||||
register_clear_bits(&(RCC->AHB4LPENR), RCC_AHB4LPENR_SRAM4LPEN);
|
||||
register_clear_bits(&(RCC->AHB3LPENR), RCC_AHB3LPENR_AXISRAMLPEN);
|
||||
|
||||
// SBU pins to input for EXTI wakeup
|
||||
set_gpio_mode(current_board->harness_config->GPIO_SBU1,
|
||||
current_board->harness_config->pin_SBU1, MODE_INPUT);
|
||||
set_gpio_mode(current_board->harness_config->GPIO_SBU2,
|
||||
current_board->harness_config->pin_SBU2, MODE_INPUT);
|
||||
|
||||
// EXTI1: SBU2 (PA1)
|
||||
// EXTI4: SBU1 (PC4)
|
||||
register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI1_PA, 0xF0U);
|
||||
register_set(&(SYSCFG->EXTICR[1]), SYSCFG_EXTICR2_EXTI4_PC, 0xFU);
|
||||
register_set_bits(&(EXTI->IMR1), (1U << 1) | (1U << 4));
|
||||
register_set_bits(&(EXTI->RTSR1), (1U << 1) | (1U << 4));
|
||||
register_set_bits(&(EXTI->FTSR1), (1U << 1) | (1U << 4));
|
||||
|
||||
// EXTI for CAN wakeup
|
||||
// EXTI8: FDCAN1 RX (PB8)
|
||||
// EXTI5: FDCAN2 RX (PB5)
|
||||
// EXTI12: FDCAN3 RX (PD12)
|
||||
set_gpio_mode(GPIOB, 8, MODE_INPUT);
|
||||
register_set(&(SYSCFG->EXTICR[2]), SYSCFG_EXTICR3_EXTI8_PB, 0xFU);
|
||||
set_gpio_mode(GPIOB, 5, MODE_INPUT);
|
||||
register_set(&(SYSCFG->EXTICR[1]), SYSCFG_EXTICR2_EXTI5_PB, 0xF0U);
|
||||
set_gpio_mode(GPIOD, 12, MODE_INPUT);
|
||||
register_set(&(SYSCFG->EXTICR[3]), SYSCFG_EXTICR4_EXTI12_PD, 0xFU);
|
||||
uint32_t can_exti_line = (1UL << 8) | (1UL << 5) | (1UL << 12);
|
||||
register_set_bits(&(EXTI->IMR1), can_exti_line);
|
||||
register_set_bits(&(EXTI->FTSR1), can_exti_line);
|
||||
|
||||
// clear pending EXTI
|
||||
EXTI->PR1 = (1U << 1) | (1U << 4) | can_exti_line;
|
||||
|
||||
// reset if ignition just came on before going to sleep
|
||||
if (harness_check_ignition()) {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
// stop mode
|
||||
register_clear_bits(&(PWR->CPUCR), PWR_CPUCR_PDDS_D1 | PWR_CPUCR_PDDS_D2 | PWR_CPUCR_PDDS_D3);
|
||||
|
||||
// set SVOS5 voltage scaling, flash low-power
|
||||
register_set(&(PWR->CR1), PWR_CR1_SVOS_0 | PWR_CR1_FLPS, PWR_CR1_SVOS | PWR_CR1_FLPS);
|
||||
|
||||
// enter stop mode on WFI
|
||||
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
||||
|
||||
__disable_irq();
|
||||
|
||||
// disable all NVIC interrupts and clear pending
|
||||
for (uint32_t i = 0U; i < 8U; i++) {
|
||||
NVIC->ICER[i] = 0xFFFFFFFFU;
|
||||
NVIC->ICPR[i] = 0xFFFFFFFFU;
|
||||
}
|
||||
// enable only wakeup EXTI interrupts
|
||||
NVIC_EnableIRQ(EXTI1_IRQn); // SBU2 (PA1)
|
||||
NVIC_EnableIRQ(EXTI4_IRQn); // SBU1 (PC4)
|
||||
NVIC_EnableIRQ(EXTI9_5_IRQn); // FDCAN1 RX (PB8), FDCAN2 RX (PB5)
|
||||
NVIC_EnableIRQ(EXTI15_10_IRQn); // FDCAN3 RX (PD12)
|
||||
|
||||
__DSB();
|
||||
__ISB();
|
||||
__WFI();
|
||||
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
@@ -58,9 +58,6 @@ void fault_recovered(uint32_t fault);
|
||||
|
||||
// ******************** power_saving ********************
|
||||
|
||||
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM1840, we should never implement any of the available hardware low power modes.
|
||||
// See rule: CoU_3
|
||||
|
||||
extern bool power_save_enabled;
|
||||
|
||||
void set_power_save_state(bool enable);
|
||||
|
||||
@@ -641,6 +641,9 @@ class Panda:
|
||||
def set_power_save(self, power_save_enabled=0):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe7, int(power_save_enabled), 0, b'')
|
||||
|
||||
def enter_stop_mode(self):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xb5, 0, 0, b'', expect_disconnect=True)
|
||||
|
||||
def set_safety_mode(self, mode=CarParams.SafetyModel.silent, param=0):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, param, b'')
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import time
|
||||
import pytest
|
||||
|
||||
from panda import Panda
|
||||
|
||||
@@ -11,3 +12,33 @@ def test_boot_time(p):
|
||||
# USB enumeration is slow, so SPI is faster
|
||||
assert time.monotonic() - st < (1.0 if p.spi else 5.0)
|
||||
|
||||
@pytest.mark.panda_expect_can_error
|
||||
@pytest.mark.test_panda_types((Panda.HW_TYPE_CUATRO, ))
|
||||
def test_stop_mode(p, panda_jungle):
|
||||
serial = p.get_usb_serial()
|
||||
panda_jungle.set_obd(True)
|
||||
|
||||
for orientation in (Panda.HARNESS_STATUS_FLIPPED, Panda.HARNESS_STATUS_NORMAL):
|
||||
panda_jungle.set_harness_orientation(orientation)
|
||||
time.sleep(0.25) # wait for orientation detection
|
||||
|
||||
for wakeup in ("ign", "0", "1", "2"):
|
||||
panda_jungle.set_ignition(False)
|
||||
print(f"orientation={orientation} wakeup={wakeup}")
|
||||
|
||||
p.enter_stop_mode()
|
||||
p.close()
|
||||
# wait for panda to enter stop mode
|
||||
time.sleep(1.5)
|
||||
|
||||
# wake via ignition or CAN activity
|
||||
if wakeup == "ign":
|
||||
panda_jungle.set_ignition(True)
|
||||
else:
|
||||
panda_jungle.can_send(0x123, b'\x01\x02', int(wakeup))
|
||||
|
||||
# panda should reset and come back
|
||||
assert Panda.wait_for_panda(serial, timeout=10)
|
||||
|
||||
p.reconnect()
|
||||
assert p.health()['uptime'] < 3
|
||||
|
||||
Reference in New Issue
Block a user