Raspberry Pi Picoのバッテリー動作環境 その2

2021年8月27日金曜日

Raspberry Pi Pico

t f B! P L

Raspberry Pi Picoのバッテリー動作環境 その1の続編です。

外部追加回路の説明

ブレッドボードテスト用回路

SMD部品を使用しないブレッドボードテスト用回路をベースに外部追加回路について説明します。

バッテリーチャージャーとの接続部 P-ch MOS-FET (T1)

バッテリーチャージャーとの接続部 P-ch MOS-FET

その1で触れたように、USB電源未接続時はバッテリー電源を使用しUSB電源接続時はバッテリーを充電しながらDC/DCコンバータ側にはUSB電源が供給されるようにするするためにpico-datasheet.pdfでの推奨に基づいてP-ch MOS-FET (T1) を配置しています。通常のハイサイドスイッチの使用法としては、ソース(S)を供給源側、ドレイン(D)を供給先側に接続するのですが(この回路図ではT5の使い方)、この部分ではそのように接続してしまうとMOS-FETのボディダイオードの働きにより、USB電源接続時にUSB側からバッテリーチャージャー側に電流が逆流してしまうため問題となります。このP-ch MOS-FETはON時にドレイン(D)側からソース(S)側へと電流が流れるように動作します。ここをダイオード一本でなくP-ch MOS-FETにするメリットは、FETがONとなったときにダイオードの順方向降下電圧が発生しない点にあります。ゲート(G)電圧の基準となるソース(S)側電圧はドレイン(D)側からボディダイオードを通じてソース(S)側に与えられる点も面白いです。本当にドレイン(D)側からソース(S)側へ電流が流れているかどうかを確認するには、バッテリー電源動作時のVSYSの電圧と、ゲート(G)をVBUSから浮かしてソース(S)側とショートした状態でのVSYSの電圧を比べてみればわかります。(仮にバッテリー電圧が3.7Vとすると、後者のボディダイオードを介してのみの供給の場合はVSYSが3.2V程度に下がってしまいます。)

DC/DCコンバータの3V3_EN制御 (T2)

DC/DCコンバータの3V3_EN制御 (T2)

T2のドレイン(D)はRaspberry Pi Picoボードの3V3_ENに接続されているため、T2がONでDC/DCコンバーターがOFF、T2がOFFでDC/DCコンバーターがONとなります。つまり上図の青色で囲んだ部分のみに注目すると、電源が供給されている状態ではT2がONでDC/DCコンバーターがOFFとなります。この状態でPOWER_SW (SW2)が押されるとT2がOFFでDC/DCコンバーターがONとなります。したがってPOWER_SW (SW2)はStand-by状態からDC/DCコンバーターをONにしてRP2040を起動させる役割をします。またPOWER_SW (SW2)はGP21に接続されていますので、RP2040が起動した後はGPIO入力としての役割もします。R1 100KohmはGP21への入力電圧が3.3Vを超えないように配置されています。R6 100Kohmはバッテリー動作時にGP21に対するpull-up電圧が低くならないように配置してあります。このようにGP21周辺は微妙なバランスで成り立っていますので、GP21自体のRP2040によるプルアップ、プルダウンはOFFにしておく必要があります。

DC/DCコンバータのON維持 (T3, T4)

DC/DCコンバータのON維持 (T3, T4)

POWER_SW (SW2)によりDC/DCコンバーターをONになりRP2040が起動されたらソフトウェア側でPOWER_KEEP (GP19)をONにすることによりT3がON → T2がOFFとなってPOWER_SW (SW2)が離されてもDC/DCコンバーターONを維持することができ、以降はソフトウェア側の制御下に入ります。
またはStand-by状態にあるときにUSB電源が接続された場合でもT4がON → T2がOFFとなってDC/DCコンバーターONが維持されます。バッテリー動作機器として考えた場合にUSB電源が接続されたとき(つまり充電するとき)に常に電源がONとなってしまうのは好ましい動作ではありませんが、このようにしておかないとプログラム書き換え時にRP2040のONが維持できないので困った事態となります。したがってUSB電源を接続して充電するときには別途Chargeステートを設けて充電中をユーザーに知らせてすぐにdormant状態に入ることで機器としては動作しない状態を保ちながら、RP2040のONを維持するようにしました。
T4のゲート(G)がR4, R5の100Kohmで分圧後に接続されているのは、Raspberry Pi Picoボードに搭載されているD1のショットキーバリアダイオードに理由があります。
上図のD1はVSYSからVBUSへ逆流を防ぐ役割をしていますが、一方でUSB電源使用時にはVBUSからVSYSへ電流を流す際にできるだけ順方向降下電圧を小さくするためにショットキーバリアダイオードが使用されています。しかしショットキーバリアダイオードは逆方向電流(リバースカレント)が通常のダイオードより一桁大きいために、バッテリー電源のみを使用した状態でVBUS側に約0.8Vが発生する状態となります。この電圧をT4のゲート(G)にそのまま接続するとFETがONする電圧となってしまうので所望の動作となりません。したがってVBUS = 0.8V程度の場合はT4のゲート(G)がスレッショルド電圧よりも充分に小さくなるようにR4, R5の100Kohmで分圧をおこなってから接続しています。

ペリフェラル電源制御 (T5)

ペリフェラル電源制御 (T5)

T5は通常のP-ch MOS-FETのハイサイドスイッチとしての使用方法です。PERI_POWER (GP20)がLowでペリフェラル電源が供給され、PERI_POWER (GP20)がHighでペリフェラル電源が遮断されます。

使用部品の説明

バッテリーチャージャー (TP4056 Project)

TP4056 Project

バッテリーチャージャーとしては、TP4056 Projectと称さる上記の写真のモジュールを使用しました。USBのタイプはmicroB, typeCのどちらでも構いません。Amazon, Aliexpress等で安価に入手が可能です。注意点としては充電対応のみのものではなく必ず過電流、過放電保護機能のあるタイプを使用してください。充電対応のみのものは上記の写真のモジュールとは異なりIC素子が1つだけ搭載されており、端子も5V入力とバッテリー接続端子の2系統しかありませんので簡単に判別可能です。

P-ch MOSFET

T1, T5のP-ch MOSFETに関しては電源ラインの制御に使用しますので、ON抵抗が充分に小さく(0.1ohm未満) かつ電流容量に余裕のあるもの (3A以上)のものを使用してください。またゲートスレッショルド電圧はバッテリーの電圧で完全にONにする必要があるため、スレッショルド電圧の低いもの (2.5V以下) を使用してください。こちらでの動作確認の際は、非SMD部品で適当なものが見つかりませんでしたので、SMD部品のIRLML6401を使用しています。
IRLML6401 (変換基板に実装)

N-ch MOSFET

T2, T3, T4のN-ch MOSFETに関しては信号制御にのみ使用しますので小信号用のものでOKです。こちらでの動作確認の際は、SMD部品ではIRLML2502、非SMD部品では2N7000を使用しました。

サンプルプログラムの説明

https://github.com/elehobica/pico_battery_op
上記のサンプルのプログラムについて説明します。

USB電源接続判断

USB電源に接続されているかどうか(このプロジェクトの場合は充電中かどうか)の判断はRaspberry Pi Picoボードにある接続を利用してGPIO24の値を取得するだけで判断できます。
bool pm_usb_power_detected()
{
    return gpio_get(PIN_USB_POWER_DETECT);
}
またStand-by状態からRP2040が起動された際に、Powerスイッチにより起動されたのか、USB電源が接続されたことにより起動されたのかを判別できます。 したがってUSB電源が接続されたことにより起動された場合は、そのままChargeステートに入る処理をします。
    if (pm_usb_power_detected()) {
        power_state = ChargeState;
    } else {
        power_state = NormalState;
    }

パワーオンの維持

RP2040が起動した後にpm_set_power_keep(true)をコールするとパワーオンの状態が維持されます。また、pm_set_power_keep(false)をコールすればUSB電源接続されていない場合はStand-by状態に移行します。
void pm_set_power_keep(bool value)
{
    gpio_put(PIN_POWER_KEEP, value);
}

dormant状態への移行と復帰

dormant状態への移行と復帰部分のプログラムは以下の通りです。
void pm_enter_dormant_and_wake()
{
    // === [1] Preparation for dormant ===
    bool psm = gpio_get(PIN_DCDC_PSM_CTRL);
    gpio_put(PIN_DCDC_PSM_CTRL, 0); // PFM mode for better efficiency
    stdio_usb_deinit(); // terminate usb cdc

    // === [2] goto dormant then wake up ===
    uint32_t ints = save_and_disable_interrupts(); // (+a)
    _preserve_clock_before_sleep(); // (+c)
    //--
    sleep_run_from_xosc();
    sleep_goto_dormant_until_pin(PIN_POWER_SW, true, false); // dormant until fall edge detected
    //--
    _recover_clock_after_sleep(); // (-c)
    restore_interrupts(ints); // (-a)

    // === [3] treatments after wake up ===
    _start_serial();
    gpio_put(PIN_DCDC_PSM_CTRL, psm); // recover PWM mode
}
dormant状態への移行前の準備としては以下を行います。
  • DC/DCのPSM設定を保存してから効率重視のPFMモードに設定する
  • USB CDCを終了する
  • 割り込み許可の状態を保存してから割り込みを禁止する
  • クロック設定を保存する
 
DC/DCのPSMモードがリップル品質重視のPWMモードになっている場合は低負荷時に効率が悪化するようなのでPFMモードで動作させる必要があります。
USB CDCに関してはdormant復帰後に再度initをかけると割込みベクタを2重登録する形となり異常な処理負荷状態となってしまいます。USB CDCを終了するコードは標準のpico-sdkには含まれていませんので、自前で用意しました。(lib/my_pico_stdio_usb/stdio_usb.c)
void stdio_usb_deinit(void) {
    stdio_set_driver_enabled(&stdio_usb, false);
    if ((bool) alarm_id) {
        cancel_alarm(alarm_id);
    }
    irq_set_enabled(PICO_STDIO_USB_LOW_PRIORITY_IRQ, false);
    irq_remove_handler(PICO_STDIO_USB_LOW_PRIORITY_IRQ, low_priority_worker_irq);
}
dormant移行時に割り込み許可の状態になっているとPWM使用時にうまくwake upできない場合があるようです。
クロック設定の保存 → 復元に関しては、以下の記事を参考にしました。 https://ghubcoder.github.io/posts/awaking-the-pico/
wake up条件の指定はピン指定、レベル/エッジ指定、極性指定の順で行います。 今回の場合は以下のように指定しますので、エッジ指定、Fallingとなり、PIN_POWER_SWの立下りでwake upすることになります。
sleep_goto_dormant_until_pin(PIN_POWER_SW, true, false);
wake up後の復帰処理としては以下を行います。
  • クロック設定を復元する
  • 割り込み許可の状態を復元する
  • UARTシリアルとUSB CDCを再開する
  • DC/DCのPSM設定を元に戻す
 
全体としてはdormantへの移行は問題なく出来たのですが、wake up後に元の状態に戻す点に関してはいろいろと試行錯誤が必要でした。dormant以前のRP2040の各種リソースの使用状態に応じてテストする必要があり、今回のサンプルでも使用するアプリケーションによってはまだ不足の点があるかもしれません。

ペリフェラル電源の制御

ペリフェラル電源をONにするにはpm_set_peripheral_power(true)をコールします。GP20ピンはオープンドレインとして使用するのでLow出力の場合のみドライブするようにします。
void pm_set_peripheral_power(bool value)
{
    // Open Drain, Active Low
    if (value) {
        gpio_put(PIN_PERI_POWER_ENB, 0);
        gpio_set_dir(PIN_PERI_POWER_ENB, GPIO_OUT); 
    } else {
        gpio_put(PIN_PERI_POWER_ENB, 0);
        gpio_set_dir(PIN_PERI_POWER_ENB, GPIO_IN); 
    }
}
Raspberry Pi Picoのバッテリー動作環境の説明は以上で終わりです。

自己紹介

自分の写真
電子工作&プログラミング、オーディオ・音楽

注目の投稿

Raspberry Pi Pico Wで電波時計を合わせる (JJY標準電波エミュレータ)

Raspberry Pi Pico Wのアプリケーションとして 最少の周辺部品で電波時計むけJJYエミュレータ(時刻合わせ用)を製作しました。 ※2023年6月6日: ソースコード修正の内容を反映させました。 時刻合わせ風景 概要 電波時計は電波が届くところで使...

QooQ