rp2/modmachine: Selectively leave the USB clocks enabled in lightsleep.
Without this change going to lightsleep stops the USB peripheral clock, and can lead to either the device going into a weird state or the host deciding to issue a bus reset. This change only keeps the USB peripheral clocks enabled if the USB device is currently active and a host has configured the device. This means the USB device continues to respond to host transfers and (presumably) will even complete pending endpoint transfers. All other requests are NAKed while still asleep, but the interaction with the host seems to resume correctly on wake Otherwise, if USB is not active or configured by a host, USB clocks are disabled, the same as before. With the change, one can issue a `machine.lightsleep(...)` with USB CDC connected and the USB CDC remains connected during the sleep and resumes when the lightsleep finishes. Tested on a RPi Pico, the power consumption is: - During normal idle at the REPL, about 15.3mA. - During lightsleep, prior to this change, about 1.35mA. - During lightsleep, with this change and USB CDC connected, about 3.7mA. If power consumption should be as low as possible when USB is connected, one can use `machine.USBDevice` to disable the USB before entering lightsleep. As discussed at https://github.com/orgs/micropython/discussions/14401 This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit is contained in:
parent
93394da69c
commit
a84c7a0ed9
@ -28,6 +28,7 @@
|
||||
// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE.
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "mp_usbd.h"
|
||||
#include "modmachine.h"
|
||||
#include "uart.h"
|
||||
#include "hardware/clocks.h"
|
||||
@ -134,8 +135,17 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Disable USB and ADC clocks.
|
||||
clock_stop(clk_usb);
|
||||
|
||||
#if MICROPY_HW_ENABLE_USBDEV
|
||||
// Only disable the USB clock if a USB host has not configured the device
|
||||
bool disable_usb = !tud_mounted();
|
||||
#else
|
||||
bool disable_usb = true;
|
||||
#endif
|
||||
if (disable_usb) {
|
||||
clock_stop(clk_usb);
|
||||
}
|
||||
|
||||
clock_stop(clk_adc);
|
||||
|
||||
// CLK_REF = XOSC
|
||||
@ -152,7 +162,9 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
|
||||
|
||||
// Disable PLLs.
|
||||
pll_deinit(pll_sys);
|
||||
pll_deinit(pll_usb);
|
||||
if (disable_usb) {
|
||||
pll_deinit(pll_usb);
|
||||
}
|
||||
|
||||
// Disable ROSC.
|
||||
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB;
|
||||
@ -181,6 +193,12 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
|
||||
// TODO: Use RTC alarm to wake.
|
||||
clocks_hw->sleep_en1 = 0;
|
||||
}
|
||||
|
||||
if (!disable_usb) {
|
||||
clocks_hw->sleep_en0 |= CLOCKS_SLEEP_EN0_CLK_SYS_PLL_USB_BITS;
|
||||
clocks_hw->sleep_en1 |= CLOCKS_SLEEP_EN1_CLK_USB_USBCTRL_BITS;
|
||||
}
|
||||
|
||||
scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS;
|
||||
__wfi();
|
||||
scb_hw->scr &= ~M0PLUS_SCR_SLEEPDEEP_BITS;
|
||||
|
Loading…
Reference in New Issue
Block a user