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/ShowRegs.cpp
#include "PowerSaveHelper.hpp"

void cShowClkRegs::ShowAll( Print * aPrint )
{
    ShowClockFrequency( aPrint );
    ShowPllRegs( aPrint ) ;
    ShowClkRegs( aPrint ) ;
    ShowXoscRegs( aPrint ) ;
    ShowRoscRegs( aPrint ) ;
}

void cShowClkRegs::ShowClockFrequency( Print * aPrint )
{
    /*
    RP2040
    #define CLOCKS_FC0_SRC_VALUE_NULL _u(0x00)
    #define CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY _u(0x01)
    #define CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY _u(0x02)
    #define CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC _u(0x03)
    #define CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC_PH _u(0x04)
    #define CLOCKS_FC0_SRC_VALUE_XOSC_CLKSRC _u(0x05)
    #define CLOCKS_FC0_SRC_VALUE_CLKSRC_GPIN0 _u(0x06)
    #define CLOCKS_FC0_SRC_VALUE_CLKSRC_GPIN1 _u(0x07)
    #define CLOCKS_FC0_SRC_VALUE_CLK_REF _u(0x08)
    #define CLOCKS_FC0_SRC_VALUE_CLK_SYS _u(0x09)
    #define CLOCKS_FC0_SRC_VALUE_CLK_PERI _u(0x0a)
    #define CLOCKS_FC0_SRC_VALUE_CLK_USB _u(0x0b)
    #define CLOCKS_FC0_SRC_VALUE_CLK_ADC _u(0x0c)
    #define CLOCKS_FC0_SRC_VALUE_CLK_RTC _u(0x0d)
    RP2350
    0x01 → PLL_SYS_CLKSRC_PRIMARY
    0x02 → PLL_USB_CLKSRC_PRIMARY
    0x03 → ROSC_CLKSRC
    0x04 → ROSC_CLKSRC_PH
    0x05 → XOSC_CLKSRC
    0x06 → CLKSRC_GPIN0
    0x07 → CLKSRC_GPIN1
    0x08 → CLK_REF
    0x09 → CLK_SYS
    0x0a → CLK_PERI
    0x0b → CLK_USB
    0x0c → CLK_ADC
    0x0d → CLK_HSTX
    0x0e → LPOSC_CLKSRC
    0x0f → OTP_CLK2FC
    0x10 → PLL_USB_CLKSRC_PRIMARY_DFT
    */
    uint fc0_src[]={ 1,2,3,5,  8,9,  10,11,12,13} ;
    const char * src_name[]={"PLL_SYS","PLL_USB","ROSC","XOSC",
                             "CLK_REF","CLK_SYS",
                             "CLK_PERI","CLK_USB","CLK_ADC","CLK_RTC"     } ;
    // scr_nameにあるPLL_SYSからCLK_ADCまでを表示します
    // RP2350で追加されたclkはここでは表示していません
    for ( int n=0;n<9;n++ ){
        uint32_t Frq = frequency_count_khz( fc0_src[n] ) ;
        aPrint->printf("%-8s:%6dKHz\n",src_name[n],Frq);
    }
#if defined( PICO_RP2040 )
    // RP2350にRTCは無い
    uint32_t Frq = frequency_count_hz( fc0_src[9] ) ;
    aPrint->printf("%-8s:%6dHz\n",src_name[9],Frq);
#endif
}


void cShowClkRegs::ShowPllRegs( Print * aPrint)
{
    const char * PllName[]={"PLL_SYS","PLL_USB"};
    pll_hw_t * pll_hw[]={pll_sys,pll_usb};
    int PreDiv;
    int FBDiv;
    int PostDiv1;
    int PostDiv2;
    double CLK = 12.0 ; /* MHz*/

    for(int rr=0;rr<2;rr++)
    {
        uint32_t cs  = pll_hw[rr]->cs;
        uint32_t pwr = pll_hw[rr]->pwr;
        uint32_t fdiv= pll_hw[rr]->fbdiv_int;
        uint32_t prim= pll_hw[rr]->prim;
        aPrint->printf("%-10s cs=%8X pwr=%8X fdiv=%8X prim=%8X \n",PllName[rr], cs, pwr, fdiv, prim );
        PreDiv = cs & PLL_CS_REFDIV_BITS;
        FBDiv  = fdiv & PLL_FBDIV_INT_BITS ;
        PostDiv2 = (prim>>PLL_PRIM_POSTDIV2_LSB) & PLL_PRIM_POSTDIV2_RESET ;
        PostDiv1 = (prim>>PLL_PRIM_POSTDIV1_LSB) & PLL_PRIM_POSTDIV1_RESET ;
        aPrint->printf("  F = 12Mhz / %2d * %4d / %d /%d = %5.1f Mhz\n",PreDiv,FBDiv,PostDiv1,PostDiv2,
                        CLK/PreDiv*FBDiv/PostDiv1/PostDiv2  );
    }
}

void cShowClkRegs::ShowClkRegs( Print * aPrint )
{
#if 0
	typedef enum clock_num_rp2040 {
	    clk_gpout0 = 0, ///< Select CLK_GPOUT0 as clock source
	    clk_gpout1 = 1, ///< Select CLK_GPOUT1 as clock source
	    clk_gpout2 = 2, ///< Select CLK_GPOUT2 as clock source
	    clk_gpout3 = 3, ///< Select CLK_GPOUT3 as clock source
	    clk_ref = 4, ///< Select CLK_REF as clock source
	    clk_sys = 5, ///< Select CLK_SYS as clock source
	    clk_peri = 6, ///< Select CLK_PERI as clock source
	    clk_usb = 7, ///< Select CLK_USB as clock source
	    clk_adc = 8, ///< Select CLK_ADC as clock source
	    clk_rtc = 9, ///< Select CLK_RTC as clock source
	    CLK_COUNT
	} clock_num_t;

    typedef enum clock_num_rp2350 {
	    clk_gpout0 = 0, ///< Select CLK_GPOUT0 as clock source
	    clk_gpout1 = 1, ///< Select CLK_GPOUT1 as clock source
	    clk_gpout2 = 2, ///< Select CLK_GPOUT2 as clock source
	    clk_gpout3 = 3, ///< Select CLK_GPOUT3 as clock source
	    clk_ref = 4, ///< Select CLK_REF as clock source
	    clk_sys = 5, ///< Select CLK_SYS as clock source
	    clk_peri = 6, ///< Select CLK_PERI as clock source
	    clk_hstx = 7, ///< Select CLK_HSTX as clock source
	    clk_usb = 8, ///< Select CLK_USB as clock source
	    clk_adc = 9, ///< Select CLK_ADC as clock source
	    CLK_COUNT
	} clock_num_t;
#endif

#if defined( PICO_RP2040 )
    int CLK_NO[10] = {0,1,2,3,4,5,6,7,8,9} ;
    const char * RegName[10]={"CLK_GPOUT0","CLK_GPOUT1","CLK_GPOUT2","CLK_GPOUT3",
                        "CLK_REF","CLK_SYS","CLK_PERI","CLK_USB","CLK_ADC","CLK_RTC"};
    const char * AuxSrcName0[16]={"PLL_SYS","GPIN0","GPIN1","PLL_USB","ROSC","XOSC",              // 0..5                  
                                  "CLK_SYS","CLK_USB","CLK_ADC","CLK_RTC","CLK_REF",              // 6..10  
                                  "SRC11","SRC12","SRC13","SRC14","SRC15"};                       // 11..15  
    const char * AuxSrcName4[4]={"PLL_USB","GPIN0","GPIN1","SRC3"};                               // for clk_ref  
    const char * SrcName4   [4]={"ROSC","AuxSrc","XOSC","SRC3"};
#else
    int CLK_NO[10] = {0,1,2,3,4,5,6,8,9,7} ;
    const char * RegName[10]={"CLK_GPOUT0","CLK_GPOUT1","CLK_GPOUT2","CLK_GPOUT3",
                        "CLK_REF","CLK_SYS","CLK_PERI","CLK_USB","CLK_ADC","CLK_HSTX"};

    const char * AuxSrcName0[16]={"PLL_SYS","GPIN0","GPIN1","PLL_USB","PLL_USB_PRIM","ROSC","XOSC", // 0..6
                                  "LPOSC","CLK_SYS","CLK_USB","CLK_ADC","CLK_REF","CLK_PERI",       // 7..12
                                  "CLK_HSTX","OPT_CLK2","SRC15" };                                  // 13..15
    const char * AuxSrcName4[4]={"PLL_USB","GPIN0","GPIN1","PLL_USB_PRIM"};                         // for clk_ref
    const char * SrcName4   [4]={"ROSC","AuxSrc","XOSC","LPOSC"};
#endif
    const char * AuxSrcName5[8]={"PLL_SYS","PLL_USB","ROSC","XOSC" ,"GPIN0","GPIN1","SRC6" ,"SRC7"};    // for clk_sys
    const char * SrcName5   [8]={"CLK_REF","AuxSrc"};
    const char * AuxSrcName6[8]={"CLK_SYS","PLL_SYS","PLL_USB","ROSC","XOSC" ,"GPIN0","GPIN1","SRC7"};  // for clk_peri
    const char * AuxSrcName7[8]={"PLL_USB","PLL_SYS","ROSC"   ,"XOSC","GPIN0","GPIN1","SRC6" ,"SRC7"};  // for clk_usb, clk_adc

    int Auxsrc ;
    int Src    ;
    int Int    ;
    int Frac   ;
    String Kill="" ;
    String Enable="" ;

    for(int rr=0;rr<10;rr++)
     {
      uint32_t ctrl=clocks_hw->clk[ CLK_NO[rr] ].ctrl;
      uint32_t div =clocks_hw->clk[ CLK_NO[rr] ].div ;
      aPrint->printf("%-10s ctrl=%8X div=%8X \n",RegName[rr],ctrl,div );
      switch(rr){
        case 0:
        case 1:
        case 2:
        case 3:
          Enable = (ctrl & 0x0800)==0 ? "Disable" : "Enable" ;
          Kill   = (ctrl & 0x0400)==0 ? "" : "Kill" ;
          Auxsrc = ( ctrl>>5 ) &      0xF;
          Int    = ( div >>8 ) & 0xFFFFFF ;
          Frac   =   div       &     0xFF ;
          aPrint->printf("    ExtSrc=%-10s int=%8d frac=%3d %s %s \n",AuxSrcName0[Auxsrc],Int,Frac,Enable.c_str(),Kill.c_str() );
          break;
        case 4: // ref
          Auxsrc = ( ctrl>>5 ) &      0x3;
          Src    =   ctrl      &      0x3;
          Int    = ( div >>8 ) & 0x3 ;
          aPrint->printf("    ExtSrc=%-10s Src=%-10s int=%8d \n",AuxSrcName4[Auxsrc],SrcName4[Src],Int );
          break;
        case 5: // sys
          Auxsrc = ( ctrl>>5 ) &      0x7 ;
          Src    =   ctrl      &      0x01;
          Int    = ( div >>8 ) & 0xFFFFFF ;
          Frac   =   div       &     0xFF ;
          aPrint->printf("    ExtSrc=%-10s Src=%-10s int=%8d frac=%3d\n",AuxSrcName5[Auxsrc],SrcName5[Src],Int,Frac );
          break;
        case 6: // Peri
          Enable = (ctrl & 0x0800)==0 ? "Disable" : "Enable" ;
          Kill   = (ctrl & 0x0400)==0 ? "" : "Kill" ;
          Auxsrc = ( ctrl>>5 ) & 0x7 ;
          aPrint->printf("    ExtSrc=%-10s %s %s \n",AuxSrcName6[Auxsrc],Enable.c_str(),Kill.c_str()  );
          break;
        case 7: // USB rp2040:[7] rp2350:[8]
        case 8: // ADC rp2040:[8] rp2350:[9]
          Enable = (ctrl & 0x0800)==0 ? "Disable" : "Enable" ;
          Kill   = (ctrl & 0x0400)==0 ? "" : "Kill" ;
          Auxsrc = ( ctrl>>5 ) & 0x7 ;
          Int    = ( div >>8 ) & 0x3 ;
          aPrint->printf("    ExtSrc=%-10s int=%8d  %s %s \n",AuxSrcName7[Auxsrc],Int,Enable.c_str(),Kill.c_str()  );
          break;
#if defined( PICO_RP2040 ) 
        // for RP2040 RP2350にRTCは無い
        case 9: // RTC
          Enable = (ctrl & 0x0800)==0 ? "Disable" : "Enable" ;
          Kill   = (ctrl & 0x0400)==0 ? "" : "Kill" ;
          Auxsrc = ( ctrl>>5 ) &      0x7 ;
          Int    = ( div >>8 ) & 0xFFFFFF ;
          Frac   =   div       &     0xFF ;
          aPrint->printf("    ExtSrc=%-10s int=%8d frac=%3d %s %s\n",AuxSrcName7[Auxsrc],Int,Frac,Enable.c_str(),Kill.c_str() );
          break;
#else
        // for RP2350 HSTX
#endif
      }
    }
}

void cShowClkRegs::ShowXoscRegs( Print * aPrint )
{
    uint32_t ctrl   = xosc_hw->ctrl;
    uint32_t status = xosc_hw->status;
    aPrint->printf("XOSC  ctrl=%8X status=%8X \n",ctrl,status );
}

void cShowClkRegs::ShowRoscRegs( Print * aPrint ) 
{
    uint32_t ctrl   = rosc_hw->ctrl;
    uint32_t freqa  = rosc_hw->freqa;
    uint32_t freqb  = rosc_hw->freqb;
    uint32_t div    = rosc_hw->div;
    uint32_t status = rosc_hw->status;
    aPrint->printf("ROSC  ctrl=%8X freqa=%8X freqb=%8X div=%8X status=%8X \n",ctrl,freqa,freqb,div,status );
}

#if defined( PICO_RP2350 )

void cShowClkRegs::ShowTickRegs(Print * aPrint )
{
/*
typedef enum tick_gen_num_rp2350 {
    TICK_PROC0 = 0,
    TICK_PROC1 = 1,
    TICK_TIMER0 = 2,
    TICK_TIMER1 = 3,
    TICK_WATCHDOG = 4,
    TICK_RISCV = 5,
    TICK_COUNT
} tick_gen_num_t;
*/
    const char * TickName[] = {"TIMER0","TIMER1","WATCHDOG"} ;
    tick_gen_num_t Num[] = {tick_gen_num_t::TICK_TIMER0,tick_gen_num_t::TICK_TIMER1,tick_gen_num_t::TICK_WATCHDOG };
    //
    aPrint->printf("Tick regs\n");
    for( int n=0;n<3;n++){
        ticks_slice_hw_t * slice = & ticks_hw->ticks[ Num[n] ] ;
        aPrint->printf("  %-10s ctrl=%8X cycle=%8X \n" ,TickName[n], slice->ctrl, slice->cycles );
    }
}
#endif

uint32_t cShowClkRegs::frequency_count_hz(uint src) {
  // frequency_count_khzをマイナーチェンジしました
    fc_hw_t *fc = &clocks_hw->fc0;

    // If frequency counter is running need to wait for it. It runs even if the source is NULL
    while(fc->status & CLOCKS_FC0_STATUS_RUNNING_BITS) {
        tight_loop_contents();
    }

    // Set reference freq
    fc->ref_khz = clock_get_hz(clk_ref) / 1000;

    // FIXME: Don't pick random interval. Use best interval
    fc->interval = 15;

    // No min or max
    fc->min_khz = 0;
    fc->max_khz = 0xffffffff;

    // Set SRC which automatically starts the measurement
    fc->src = src;

    while(!(fc->status & CLOCKS_FC0_STATUS_DONE_BITS)) {
        tight_loop_contents();
    }

    // Return the result
    return fc->result * 1000 / ( 1 << CLOCKS_FC0_RESULT_KHZ_LSB );
}





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