LOGO パソコン制御をもっと気軽に  
電子制御をもっと気楽に

低速CPUクロックによる省電力化

Arduino RP2040 2026/01/10

CPUクロックを 2MHz,4MHz,12MHz,40MHz,80MHz,120MHz に設定できます。
PowerSaveHelper.hppに書いてある電流値は Waveshare RP2040Zero でのtypeCの実測値です
download ZIP( 14Kbyte )

Viewer

PowerSaveHelper/src/Pico_sleep/sleep.c
/*
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#if defined(ARDUINO_ARCH_RP2040)

#include "pico.h"

#include "pico/stdlib.h"
#include "pico_sleep.h"

#include "hardware/rtc.h"
#include "hardware/pll.h"
#include "hardware/clocks.h"
#include "hardware/xosc.h"
#include "pico_rosc.h"
#include "hardware/regs/io_bank0.h"
// For __wfi
#include "hardware/sync.h"
// For scb_hw so we can enable deep sleep
#include "hardware/structs/scb.h"

// The difference between sleep and dormant is that ALL clocks are stopped in dormant mode,
// until the source (either xosc or rosc) is started again by an external event.
// In sleep mode some clocks can be left running controlled by the SLEEP_EN registers in the clocks
// block. For example you could keep clk_rtc running. Some destinations (proc0 and proc1 wakeup logic)
// can't be stopped in sleep mode otherwise there wouldn't be enough logic to wake up again.


// TODO: Optionally, memories can also be powered down.

static dormant_source_t _dormant_source;

bool dormant_source_valid(dormant_source_t dormant_source) {
    return (dormant_source == DORMANT_SOURCE_XOSC) || (dormant_source == DORMANT_SOURCE_ROSC);
}

// In order to go into dormant mode we need to be running from a stoppable clock source:
// either the xosc or rosc with no PLLs running. This means we disable the USB and ADC clocks
// and all PLLs

void sleep_run_from_dormant_source(dormant_source_t dormant_source) {
    assert(dormant_source_valid(dormant_source));
    _dormant_source = dormant_source;

    // FIXME: Just defining average rosc freq here.
    uint src_hz = (dormant_source == DORMANT_SOURCE_XOSC) ? XOSC_MHZ * MHZ : 6.5 * MHZ;
    uint clk_ref_src = (dormant_source == DORMANT_SOURCE_XOSC) ?
                       CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC :
                       CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH;

    // CLK_REF = XOSC or ROSC
    clock_configure(clk_ref,
                    clk_ref_src,
                    0, // No aux mux
                    src_hz,
                    src_hz);

    // CLK SYS = CLK_REF
    clock_configure(clk_sys,
                    CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF,
                    0, // Using glitchless mux
                    src_hz,
                    src_hz);

    // CLK USB = 0MHz
    clock_stop(clk_usb);

    // CLK ADC = 0MHz
    clock_stop(clk_adc);
#if ARDUINO_PICO_REVISION == 1
    // clk_rtcは RP2350 には無い
    // CLK RTC = ideally XOSC (12MHz) / 256 = 46875Hz but could be rosc
    uint clk_rtc_src = (dormant_source == DORMANT_SOURCE_XOSC) ? 
                       CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC : 
                       CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH;

    clock_configure(clk_rtc,
                    0, // No GLMUX
                    clk_rtc_src,
                    src_hz,
                    46875);
#endif
    // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
    clock_configure(clk_peri,
                    0,
                    CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
                    src_hz,
                    src_hz);

    pll_deinit(pll_sys);
    pll_deinit(pll_usb);

    // Assuming both xosc and rosc are running at the moment
    if (dormant_source == DORMANT_SOURCE_XOSC) {
        // Can disable rosc
        rosc_disable();
    } else {
        // Can disable xosc
        xosc_disable();
    }
#if 0
    // Reconfigure uart with new clocks
    setup_default_uart();
#endif
}


// Go to sleep until woken up by the RTC
#if 0 // Naka
void sleep_goto_sleep_until(datetime_t *t, rtc_callback_t callback) {
    // We should have already called the sleep_run_from_dormant_source function
    assert(dormant_source_valid(_dormant_source));

    // Turn off all clocks when in sleep mode except for RTC
    clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS;
    clocks_hw->sleep_en1 = 0x0;

    rtc_set_alarm(t, callback);

    uint save = scb_hw->scr;
    // Enable deep sleep at the proc
    scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS;

    // Go to sleep
    __wfi();
}
#endif

static void _go_dormant(void) {
    assert(dormant_source_valid(_dormant_source));

    if (_dormant_source == DORMANT_SOURCE_XOSC) {
        xosc_dormant();
    } else {
        rosc_set_dormant();
    }
}

void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high) {
    bool low = !high;
    bool level = !edge;

    // Configure the appropriate IRQ at IO bank 0
    assert(gpio_pin < NUM_BANK0_GPIOS);

    uint32_t event = 0;

    if (level && low) event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_LOW_BITS;
    if (level && high) event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_HIGH_BITS;
    if (edge && high) event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_HIGH_BITS;
    if (edge && low) event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_LOW_BITS;

    gpio_set_dormant_irq_enabled(gpio_pin, event, true);

    _go_dormant();
    // Execution stops here until woken up

    // Clear the irq so we can go back to dormant mode again if we want
    gpio_acknowledge_irq(gpio_pin, event);
}
#endif /* ARDUINO_ARCH_RP2040 */
CPU is RP2040 group
CPU is RP2040
Setup done

CPU 12MHz Cu=  1
PLL_SYS :     0KHz
PLL_USB :     0KHz
ROSC    :     0KHz
XOSC    : 12000KHz
CLK_REF : 12002KHz
CLK_SYS : 12000KHz  <-- 12MHz
CLK_PERI: 12002KHz
CLK_USB :     0KHz
CLK_ADC :     0KHz
CLK_RTC :     0Hz

CPU 40MHz Cu=  2
PLL_SYS : 40000KHz  <-- PLL_SYSが動作している
PLL_USB :     0KHz
ROSC    :     0KHz
XOSC    : 12000KHz
CLK_REF : 12002KHz
CLK_SYS : 40000KHz  <-- 40MHz
CLK_PERI: 12002KHz
CLK_USB :     0KHz
CLK_ADC :     0KHz
CLK_RTC :     0Hz

CPU 2MHz Cu=  3
PLL_SYS :     0KHz
PLL_USB :     0KHz
ROSC    :     0KHz
XOSC    : 12000KHz
CLK_REF :  4002KHz
CLK_SYS :  2000KHz  <-- 2MHz
CLK_PERI: 12000KHz
CLK_USB :     0KHz
CLK_ADC :     0KHz
CLK_RTC :     0Hz

シーブイデブ e-mail:mnakatani@cvdev-jp.com