| パソコン制御をもっと気軽に 電子制御をもっと気楽に |
低速CPUクロックによる省電力化
Arduino RP2040 2026/01/10
#include "PowerSaveHelper.hpp"
void cPowerSave::WithXOSC()
{
#if 1
sleep_run_from_dormant_source(DORMANT_SOURCE_XOSC);
#else
// 省電力設定
// 133MHz通常実行時は24mA
// CLK_SYSのSRCをCLK_REFに変更する
clocks_hw->clk[clk_sys].ctrl = clocks_hw->clk[clk_ref].ctrl & 0xFFFFFFE | 0x00 ;
// CLK_SYS 切り替え完了を待つ
while( clocks_hw->clk[clk_sys].selected==0 )
{ ; }
// ここまでの変更で消費電流は約8.8mA
// PLL_SYS停止
pll_sys->pwr = 0x2D ;
ShowClockRegs() ;
delay(100);
//
// これ以降はUSBが停止するのでSerialは使えない
//
// PLL_USB停止
pll_usb->pwr = 0x2D ;
// ここまでの変更で消費電流は約3.3mA
// PLL_USBが停止したので clk_peri,clk_usb,clk_adc,clk_rtcも使えない
rosc_hw->ctrl = 0xD1EFA4;
#endif
// ここまでの変更で消費電流は約3.1mA
// clc_periのextsrcをXOSCに切り替える
// sleep_run_from_dormant_sourceではclk_sysになっている
#if 1
cClkPeri::Setup( cClkPeri::XOSC , XOSC_HZ ) ;
#else
clocks_hw->clk[clk_peri].ctrl = 4 << 5 | 1<<11 ;
clock_set_reported_hz(clk_peri,12000000); // 控え
#endif
Serial2.end();
Serial1.end();
Serial.end();
// 2. USBコントローラの割り込みを無効化
irq_set_enabled(USBCTRL_IRQ, false);
// 3. USB PHY の電源をオフにする (省電力の核心)
// USBコントローラをリセット状態に保つことで電力を抑えます
reset_block(RESETS_RESET_USBCTRL_BITS);
// ここまでの変更で消費電流は約2.9mA
}
//
// RefDiv:1..3 SysDiv 1..2
void cPowerSave::CpuSpeedAdjust( u_int ClkRefDiv , u_int ClkSysDiv )
{
if ( clock_get_hz(clk_sys)> 12*MHZ ){
// clk_sysのソースをclk_refに変更する
clocks_hw->clk[clk_sys].ctrl = clocks_hw->clk[clk_sys].ctrl & ~CLOCKS_CLK_SYS_CTRL_SRC_BITS |
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF ;
// CLK_SYS 切り替え完了を待つ
while( clocks_hw->clk[clk_sys].selected==0 )
{ ; }
// PLL_SYSを停止する
pll_sys->pwr = PLL_PWR_RESET ;
}
// 前提条件 : clk_refのソースはXOSC , clk_sysのソースはclk_ref
// ClkRefDivは 1~3
// ClkSysDivは 1~2
clocks_hw->clk[clk_ref].div = ClkRefDiv << CLOCKS_CLK_REF_DIV_INT_LSB ;
clock_set_reported_hz(clk_ref, XOSC_HZ / ClkRefDiv ); // 控え
// ここまでの変更で消費電流は約1.85mA
// clockは 6MHz delay(250)が1秒位に遅くなっている
clocks_hw->clk[clk_sys].div = ClkSysDiv << CLOCKS_CLK_SYS_DIV_INT_LSB ; // clk_sys 1/3分周
clock_set_reported_hz(clk_sys,XOSC_HZ / ClkRefDiv / ClkSysDiv ); // 控え
// ここまでの変更で消費電流は約1.45mA
// clockは 2MHz clk_sysのdevを使っても省電力効果は少ない
#if defined( PICO_RP2040 )
// delayなどはTIMER( 24bit systick system timer ) を使っていますが
// sys_refを分周して1MHzを作る必要があります。
// この為 sys_refを6MHz変更するとtickの分周を6にすることでdelayは正常にります
watchdog_hw->tick = watchdog_hw->tick & 0xFFFFFFE0 | (XOSC_MHZ/ClkRefDiv) ;
#else
// RP2350は複数のtick generatorを持っています。今回は関係ありそうな3つを変更します
{
tick_gen_num_t Num[] = {tick_gen_num_t::TICK_TIMER0,tick_gen_num_t::TICK_TIMER1,tick_gen_num_t::TICK_WATCHDOG };
for( int n=0;n<3;n++){
ticks_slice_hw_t * slice = & ticks_hw->ticks[ Num[n] ] ;
slice->cycles = slice->cycles & 0xFFFFFFE0 | (XOSC_MHZ/ClkRefDiv) ;
}
}
#endif
}
void cPowerSave::CpuSpeedAdjustPLL( uint FBDIV , uint PDIV1, uint PDIV2 )
{
//
/* Datasheet 2.18.2. Calculating PLL parameters
• Oscillator frequency (FOUTVCO) must be in the range 750MHz → 1600MHz
• Feedback divider (FBDIV) must be in the range 16 → 320
• The post dividers POSTDIV1 and POSTDIV2 must be in the range 1 → 7
750MHz/12MHz = 62.5 1600MHz/12MHz =133.3 FDIV must in 63..133
*/
if ( clock_get_hz(clk_sys) > 12*MHZ ){
// clk_sys src を clk_refに変更
clocks_hw->clk[clk_sys].ctrl = clocks_hw->clk[clk_sys].ctrl & ~CLOCKS_CLK_SYS_CTRL_SRC_BITS |
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF ;
// CLK_SYS 切り替え完了を待つ
while( clocks_hw->clk[clk_sys].selected==0 )
{ ; }
// pll_sys の 停止
pll_sys->pwr = PLL_PWR_RESET ;
} else if ( clock_get_hz(clk_sys) != 12*MHZ) {
// clk_ref,clk_sysの div を1に戻す
CpuSpeedAdjust( 1 , 1 ) ;
}
pll_sys->fbdiv_int = FBDIV ;
pll_sys->prim = ( PDIV2 << PLL_PRIM_POSTDIV2_LSB ) | ( PDIV1 << PLL_PRIM_POSTDIV1_LSB ) ;
// pll_sys の 開始
pll_sys->pwr = PLL_PWR_DSMPD_BITS ;
// wait PLL locked
while( ( pll_sys->cs & PLL_CS_LOCK_BITS ) == 0)
{;}
// clk_sys src を ext_srcに変更
clocks_hw->clk[clk_sys].ctrl = clocks_hw->clk[clk_sys].ctrl & ~CLOCKS_CLK_SYS_CTRL_SRC_BITS |
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX ;
// CLK_SYS 切り替え完了を待つ
while( clocks_hw->clk[clk_sys].selected==0 )
{ ; }
clock_set_reported_hz(clk_sys, 12 * MHZ * FBDIV / PDIV1 / PDIV2 ); // 控え
}
// Setup for clk_gpout0..3
void cClkGpout::Setup(eOutPin PinNo, eSrc Src, double div)
{
// 2040 datasheet の 2.15.6.3. Configuring a GPIO output clock を
// 参考に作りました
uint gpclk = 0;
switch ( PinNo)
{
#if defined( PICO_RP2350 )
case eOutPin::GPOUT13 :
#endif
case eOutPin::GPOUT21 :
gpclk = clk_gpout0;
break;
#if defined( PICO_RP2350 )
case eOutPin::GPOUT15 :
#endif
case eOutPin::GPOUT23 :
gpclk = clk_gpout1;
break;
case eOutPin::GPOUT24 :
gpclk = clk_gpout2;
break;
case eOutPin::GPOUT25 :
gpclk = clk_gpout3;
break;
}
div = div + 0.5 / ( 1<< (CLOCKS_CLK_GPOUT0_DIV_FRAC_MSB+1) ) ;
u_int32_t DivInt = (u_int32_t) div ;
u_int32_t DivFrac = (u_int32_t)
( div * ( 1<< (CLOCKS_CLK_GPOUT0_DIV_FRAC_MSB+1) ) ) & CLOCKS_CLK_GPOUT0_DIV_FRAC_BITS ;
// Serial2.printf( "cClkGpout::Setup pin=%d src=%d DInt=%d DFrac=%d \n",PinNo,Src,DivInt,DivFrac ) ;
// Set up the gpclk generator
clocks_hw->clk[gpclk].ctrl = ( Src << CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_LSB) |
CLOCKS_CLK_GPOUT0_CTRL_ENABLE_BITS;
clocks_hw->clk[gpclk].div = ( DivInt << CLOCKS_CLK_GPOUT0_DIV_INT_LSB) | DivFrac ;
// Set gpio pin to gpclock function
gpio_set_function( PinNo , GPIO_FUNC_GPCK );
}
// Setup for clk_peri
void cClkPeri::Setup( eSrc Src ,u_int32_t Hz )
{
if ( Hz != 0 ){
// Set up the gpclk generator
clocks_hw->clk[clk_peri].ctrl = ( Src << CLOCKS_CLK_PERI_CTRL_AUXSRC_LSB) |
CLOCKS_CLK_PERI_CTRL_ENABLE_BITS;
} else {
clocks_hw->clk[clk_peri].ctrl &= ~CLOCKS_CLK_PERI_CTRL_ENABLE_BITS;
}
clock_set_reported_hz(clk_peri , Hz );
}
// Setup for clk_adc
// Div[2040:1..3],[2350:1..15]
void cClkAdc::Setup( eSrc Src ,u_int32_t Div,u_int32_t Hz )
{
if ( Hz != 0 ){
// Set up the gpclk generator
clocks_hw->clk[clk_adc].ctrl = ( Src << CLOCKS_CLK_ADC_CTRL_AUXSRC_LSB) |
CLOCKS_CLK_ADC_CTRL_ENABLE_BITS;
clocks_hw->clk[clk_adc].div = (Div << CLOCKS_CLK_ADC_DIV_INT_LSB ) & CLOCKS_CLK_ADC_DIV_INT_BITS ;
} else {
clocks_hw->clk[clk_adc].ctrl &= ~CLOCKS_CLK_ADC_CTRL_ENABLE_BITS;
}
clock_set_reported_hz(clk_adc , Hz );
}
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 |