M5StickCのバッテリー管理AXP192を調べる

※現時点の情報ですので、最新情報はM5StickC非公式日本語リファレンスを確認してください。

ちょっとAXP192のライブラリの動きが不明だったので、データシートをみて調べてみました。

データシート

英語のデータシートがなんとありません。。。

製造元サイトのは概要のみなので、GitHubにある中国語のデータシートをがんばって解読することが必要です。

I2C通信レジスター P29

グループアドレス説明R/Wデフォルト値
電力制御00電源ステータスレジスタR 
電力制御01電力モード/充電ステータスレジスタR 
電力制御04OTG VBUSステータスレジスタR 
電力制御06-0Bデータキャッシュレジスタ 0-5R/WF0/0F/00/FF/00/00
電力制御10EXTEN & DC-DC2 スイッチ制御レジスタR/WX5H
電力制御12DC-DC1/3 & LDO2/3 スイッチ制御レジスタR/WXFH
電力制御23DC-DC2 電圧設定レジスタR/W16H
電力制御25DC-DC2 電圧勾配パラメータ設定レジスタR/W00H
電力制御26DC-DC1 電圧設定レジスタR/W68H
電力制御27DC-DC3 電圧設定レジスタR/W48H
電力制御28LDO2/3 電圧設定レジスタR/WCFH
電力制御30VBUS-IPSOUT パス設定レジスタR/W60H
電力制御31VOFFシャットダウン電圧設定レジスタR/WX3H
電力制御32シャットダウン、電池検知、CHGLED制御レジスタR/W46H
電力制御33充電制御レジスタ 1R/WC8H
電力制御34充電制御レジスタ 2R/W41H
電力制御35バックアップ電池充電制御レジスタR/W22H
電力制御36PEKパラメータ設定レジスタR/W5DH
電力制御37DCDCコンバータ動作周波数設定レジスタR/W08H
電力制御38バッテリ充電低温警報設定レジスタR/WA5H
電力制御39バッテリ充電高温警報設定レジスタR/W1FH
電力制御3AAPS低電力レベル1設定レジスタR/W68H
電力制御3BAPS低電力レベル2設定レジスタR/W5FH
電力制御3Cバッテリ放電低温警報設定レジスタR/WFCH
電力制御3Dバッテリ放電高温警報設定レジスタR/W16H
電力制御80DCDC動作モード設定レジスタR/WE0H
電力制御82ADCイネーブル設定レジスタ1R/W83H
電力制御83ADCイネーブル設定レジスタ2R/W80H
電力制御84ADCサンプルレート設定、TS端子制御レジスタR/W32H
電力制御85GPIO [3:0]入力範囲設定レジスタR/WX0H
電力制御86GPIO1 ADC IRQ立ち上がりエッジしきい値設定R/WFFH
電力制御87GPIO1 ADC IRQ立ち下がりエッジしきい値設定R/W00H
電力制御8Aタイマ制御レジスタR/W00H
電力制御8BVBUS監視設定レジスタR/W00H
電力制御8F過熱シャットダウン制御レジスタR/W01H
GPIO制御90GPIO0制御レジスタR/W07H
GPIO制御91GPIO0 LDOモード出力電圧設定レジスタR/WA0H
GPIO制御92GPIO1制御レジスタR/W07H
GPIO制御93GPIO2制御レジスタR/W07H
GPIO制御94GPIO [2:0]信号ステータスレジスタR/W00H
GPIO制御95GPIO [4:3]機能制御レジスタR/W00H
GPIO制御96GPIO [4:3]信号ステータスレジスタR/W00H
GPIO制御97GPIO [2:0]プルダウン制御レジスタR/W00H
GPIO制御98PWM1周波数設定レジスタR/W00H
GPIO制御99PWM1デューティサイクル設定レジスタ1R/W16H
GPIO制御9APWM1デューティサイクル設定レジスタ2R/W0BH
GPIO制御9BPWM2周波数設定レジスタR/W00H
GPIO制御9CPWM2デューティサイクル設定レジスタ1R/W16H
GPIO制御9DPWM2デューティサイクル設定レジスタ2R/W0BH
GPIO制御9EN_RSTO (GPIO5) 制御レジスタR/W20H
割込制御40IRQイネーブル制御レジスタ1R/WD8H
割込制御41IRQイネーブル制御レジスタ2R/WFFH
割込制御42IRQイネーブル制御レジスタ3R/W3BH
割込制御43IRQイネーブル制御レジスタ4R/WC1H
割込制御4AIRQイネーブル制御レジスタ5R/W00H
割込制御44IRQステータスレジスタ1R/W00H
割込制御45IRQステータスレジスタ2R/W00H
割込制御46IRQステータスレジスタ3R/W00H
割込制御47IRQステータスレジスタ4R/W00H
割込制御4DIRQステータスレジスタ5R/W00H
ADCデータ56ACIN電圧ADCデータ上位8ビットR 
ADCデータ57ACIN電圧ADCデータ下位4ビットR 
ADCデータ58ACIN電流ADCデータ上位8ビットR 
ADCデータ59ACIN電流ADCデータ下位4ビットR 
ADCデータ5AVBUS電圧ADCデータ上位8ビットR 
ADCデータ5BVBUS電圧ADCデータ下位4ビットR 
ADCデータ5CVBUS電流ADCデータ上位8ビットR 
ADCデータ5DVBUS電流ADCデータ下位4ビットR 
ADCデータ5EAXP192内部温度監視ADCデータ上位8ビットR 
ADCデータ5FAXP192内部温度監視ADCデータ下位4ビットR 
ADCデータ62TS入力ADCデータ上位8ビット、デフォルトのバッテリ温度監視R 
ADCデータ63TS入力ADCデータは4ビット下位、デフォルトモニタバッテリ温度R 
ADCデータ64GPIO0電圧ADCデータ上位8ビットR 
ADCデータ65GPIO0電圧ADCデータ下位4ビットR 
ADCデータ66GPIO1電圧ADCデータ上位8ビットR 
ADCデータ67GPIO1電圧ADCデータ下位4ビットR 
ADCデータ68GPIO2電圧ADCデータ上位8ビットR 
ADCデータ69GPIO2電圧ADCデータ下位4ビットR 
ADCデータ6AGPIO3電圧ADCデータ上位8ビットR 
ADCデータ6BGPIO3電圧ADCデータ下位4ビットR 
ADCデータ70バッテリー瞬時電力上位8ビットR 
ADCデータ71バッテリー瞬時電力中位8ビットR 
ADCデータ72バッテリー瞬時電力下位8ビットR 
ADCデータ78電池電圧上位8ビットR 
ADCデータ79電池電圧下位4ビットR 
ADCデータ7Aバッテリー充電電流上位8ビットR 
ADCデータ7Bバッテリー充電電流下位5ビットR 
ADCデータ7Cバッテリー放電電流上位8ビットR 
ADCデータ7Dバッテリー放電電流下位4ビットR 
ADCデータ7EAPS電圧上位8ビットR 
ADCデータ7FAPS電圧下位4ビットR 
バッテリB0バッテリ充電クーロンカウントデータレジスタ[31:24]R/W00H
バッテリB1バッテリ充電クーロンカウントデータレジスタ[23:16]R/W00H
バッテリB2バッテリ充電クーロンカウントデータレジスタ[15:8]R/W00H
バッテリB3バッテリ充電クーロンカウントデータレジスタ[7:0]R/W00H
バッテリB4バッテリ放電クーロンカウントデータレジスタ[31:24]R/W00H
バッテリB5バッテリ放電クーロンカウントデータレジスタ[23:16]R/W00H
バッテリB6バッテリ放電クーロンカウントデータレジスタ[15:8]R/W00H
バッテリB7バッテリ放電クーロンカウントデータレジスタ[7:0]R/W00H
バッテリB8クーロンカウンタ制御レジスタR/W00H

ここは肝になりますが、基本的にAPI経由だと知らなくてもなんとかなります。

APIでの呼び出し確認

bigin : 利用開始

アドレスアドレス説明
0x10EXTEN & DC-DC2 スイッチ制御レジスタ0xff
0x28LDO2/3 電圧設定レジスタ0xff
0x82ADCイネーブル設定レジスタ10xff
0x33充電制御レジスタ 10xC0
0xB8クーロンカウンタ制御レジスタ0x80
0x12DC-DC1/3 & LDO2/3 スイッチ制御レジスタ0x4d
0x36PEKパラメータ設定レジスタ0x5c
0x90GPIO0制御レジスタ0x02
0x31VOFFシャットダウン電圧設定レジスタ0x04

初期設定をしています。バッテリーは4.2Vまで100mAで充電して、3.0V以下になったらシャットダウンします。

ScreenBreath : 画面の明るさ設定

0x28LDO2/3 電圧設定レジスタ Val

画面の明るさを指定します。0x00(1.8V:暗)から0x0f(3.3V:明)までの値を設定できます。初期化時にはLDO2/3の両方を設定していますが、この関数だと実際にScreenが接続されていると思われるLDO2だけに設定しています。

EnableCoulombcounter : バッテリ残量ゲージ有効

0xB8クーロンカウンタ制御レジスタ0x80

残量ゲージのカウンタを有効にします。通常使うことはないでしょう。

DisableCoulombcounter : バッテリ残量ゲージ無効

0xB8クーロンカウンタ制御レジスタ0x00

残量ゲージのカウンタを無効にします。通常使うことはないでしょう。

StopCoulombcounter : バッテリ残量ゲージ停止

0xB8クーロンカウンタ制御レジスタ0xC0

残量ゲージのカウンタを停止にします。通常使うことはないでしょう。

ClearCoulombcounter : バッテリ残量ゲージクリア

0xB8クーロンカウンタ制御レジスタ0xA0

残量ゲージのカウンタをクリアします。通常使うことはないでしょう。

GetCoulombchargeData : バッテリ充電状態取得

0xB0バッテリ充電クーロンカウントデータレジスタ[31:24] 
0xB1バッテリ充電クーロンカウントデータレジスタ[23:16] 
0xB2バッテリ充電クーロンカウントデータレジスタ[15:8] 
0xB3バッテリ充電クーロンカウントデータレジスタ[7:0] 

この値も直接使うことはないでしょう。

GetCoulombdischargeData : バッテリ放電状態取得

0xB4バッテリ放電クーロンカウントデータレジスタ[31:24] 
0xB5バッテリ放電クーロンカウントデータレジスタ[23:16] 
0xB6バッテリ放電クーロンカウントデータレジスタ[15:8] 
0xB7バッテリ放電クーロンカウントデータレジスタ[7:0] 

この値も直接使うことはないでしょう。

GetCoulombData : バッテリ状態取得

内部で上記の2つの関数を呼び出して計算をしています。戻り値はmAhのfloat型です。

GetVinData : ACIN(外部電源)電圧取得

0x56ACIN電圧ADCデータ上位8ビット 
0x57ACIN電圧ADCデータ下位4ビット 

お尻の端子からの5V外部電源の電圧取得です。取得するのはADCのステップ数なので、ステップ数 × 1.7mVで実際の電圧に変換する必要があります。

GetIinData : ACIN(外部電源)電流取得

0x58ACIN電流ADCデータ上位8ビット 
0x59ACIN電流ADCデータ下位4ビット 

お尻の端子からの5V外部電源の電流取得です。取得するのはADCのステップ数なので、ステップ数 × 0.625mAで実際の電流に変換する必要があります。

GetIchargeData : バッテリー充電電流取得

0x7aバッテリ充電電流上位8ビット 
0x7bバッテリ充電電流下位5ビット 

バッテリーに充電する電流取得です。取得するのはADCのステップ数なので、ステップ数 × 0.5mAで実際の電流に変換する必要があります。(API上では2で割っています)

GetIdischargeData : バッテリー放電電流取得

0x7cバッテリ放電電流上位8ビット 
0x7dバッテリ放電電流下位4ビット 

バッテリーから放電する電流取得です。取得するのはADCのステップ数なので、ステップ数 × 0.5mAで実際の電流に変換する必要があります。(API上では2で割っています)

GetTempData : 内部温度取得

0x5eAXP192内部温度監視ADCデータ上位8ビット 
0x5fAXP192内部温度監視ADCデータ下位4ビット 

内部の温度を取得します。室温などには使えないので、実際のところあまり利用しないと思いますが、高熱になるとシャットダウンするためについているセンサーです。取得するのはADCのステップ数なので、-144.7度 + ステップ数 × 0.1度で実際の温度に変換する必要があります。

GetPowerbatData : バッテリ瞬時電力取得

0x70バッテリ瞬時電力上位8ビット 
0x71バッテリ瞬時電力中位8ビット 
0x72バッテリ瞬時電力下位8ビット 

バッテリー放電中の電力だと思いますが、データシートにも計算方法が書いてありません。サンプルスケッチ上では「M5.Axp.GetPowerbatData() * 1.1 * 0.5 / 1000」で計算しています。通常は使うことがないデータだと思います。

GetVapsData : APS(内部電源)電圧取得 (0.0.6から?)

0x7EAPS電圧上位8ビット 
0x7FAPS電圧下位4ビット 

マイコンに供給されている電圧だと思います。USB経由の場合には5Vぐらいで、バッテリー駆動の場合には4Vぐらいになります。取得するのはADCのステップ数なので、ステップ数 × 1.4mVで実際の電圧に変換する必要があります。

GitHubだと実装されていましたが、0.0.5だと実装されていません。

SetSleep : スリープ設定 (0.0.6から?)

0x31VOFFシャットダウン電圧設定レジスタ 
0x31VOFFシャットダウン電圧設定レジスタ(1<<3)|buf
0x12DC-DC1/3 & LDO2/3 スイッチ制御レジスタ0x41

スリープ状態に設定します。GitHubだと実装されていましたが、0.0.5だと実装されていません。

GetWarningLeve : 警告レベル取得 (0.0.6から?)

0x47IRQステータスレジスタ4 

設定する関数がなく、初期値の0を所得するだけの関数です。GitHubだと実装されていましたが、0.0.5だと実装されていません。

未実装の機能

VBUS(USB)電圧取得

0x5aVBUS電圧ADCデータ上位8ビット
0x5bVBUS電圧ADCデータ下位4ビット

USBからの供給電圧。取得するのはADCのステップ数なので、ステップ数 × 1.7mVで実際の電圧に変換する必要があります。GetVinData()関数の中身の送信先アドレスを変更することで実装可能です。

VBUS(USB)電流取得

0x5cVBUS電流ADCデータ上位8ビット
0x5dVBUS電流ADCデータ下位4ビット

USBからの供給電流。取得するのはADCのステップ数なので、ステップ数 × 0.375mAで実際の電流に変換する必要があります。GetIinData()関数の中身の送信先アドレスを変更することで実装可能です。 ただし、ステップの幅が0.625mAのGetIinData()とは違うので注意してください。

個人的にはこのVBUSから流れている電流って興味あるんですが、いまのAPIだと無いんですね。

ステップについて

Channel000HSTEPFFFH
Battery Voltage0mV1.1mV4.5045V
Bat discharge current0mA0.5mA4.095A
Bat charge current0mA0.5mA4.095A
ACIN volatge0mV1.7mV6.9615V
ACIN current0mA0.625mA2.5594A
VBUS voltage0mV1.7mV6.9615V
VBUS current0mA0.375mA1.5356A
Internal temperature-144.7℃0.1℃264.8℃
APS voltage0mV1.4mV5.733V
TS pin input0mV0.8mV3.276V
GPIO00/0.7V0.5mV2.0475/2.7475V
GPIO10/0.7V0.5mV2.0475/2.7475V
GPIO20/0.7V0.5mV2.0475/2.7475V
GPIO30/0.7V0.5mV2.0475/2.7475V

ステップについて、APIだと詳細が書かれてなく、サンプルスケッチではマジックナンバーで計算していますが、P24に上記の表があるので、ここを参考に計算をします。

実験で使ったスケッチ

#include <M5StickC.h>
void setup() {
  // put your setup code here, to run once:
  M5.begin();
  M5.Lcd.fillScreen(BLACK);
  
  M5.Axp.EnableCoulombcounter();
}
double vbat = 0.0;
double discharge,charge;
double temp = 0.0;
double bat_p = 0.0;
double bat_p2 = 0.0;
void loop() {
  vbat      = M5.Axp.GetVbatData() * 1.1 / 1000;
  charge    = M5.Axp.GetIchargeData() / 2.0 / 1000;
  discharge = M5.Axp.GetIdischargeData() / 2.0 / 1000;
  temp      =  -144.7 + M5.Axp.GetTempData() * 0.1;
  bat_p     =  M5.Axp.GetPowerbatData() * 1.1 * 0.5 /1000 /1000;
  
  M5.Lcd.setCursor(0, 0, 1);
  M5.Lcd.printf("vbat   :%.2fV\r\n",vbat);
  M5.Lcd.printf("icharge:%.2fA\r\n",charge);
  M5.Lcd.printf("idischg:%.2fA\r\n",discharge);
  M5.Lcd.printf("temp   :%3.1fC\r\n",temp);
  M5.Lcd.printf("pbat   :%.2fW\r\n",bat_p);
  M5.Lcd.printf("CoIn   :%5d\r\n",M5.Axp.GetCoulombchargeData());
  M5.Lcd.printf("CoOut  :%5d\r\n",M5.Axp.GetCoulombdischargeData());
  M5.Lcd.printf("CoD    :%.2f\r\n",M5.Axp.GetCoulombData());
  M5.Lcd.printf("          mAh\r\n");
  M5.Lcd.printf("Vin    :%.2fV\r\n",M5.Axp.GetVinData() * 1.7 /1000);
  M5.Lcd.printf("Iin    :%.2fA\r\n",M5.Axp.GetIinData() * 0.625 /1000);
  M5.Lcd.printf("Vusbin :%.2fV\r\n",GetVusbinData() * 1.7 /1000);
  M5.Lcd.printf("Iusbin :%.2fA\r\n",GetIusbinData() * 0.375 /1000);
  M5.Lcd.printf("VAPS   :%.2fV\r\n", GetVapsData()*1.4/1000);
  M5.Lcd.printf("WL     :%5d\r\n",GetWarningLeve());
  delay(1000);
}
uint16_t GetVusbinData(void){
    uint16_t vin = 0;
    Wire1.beginTransmission(0x34);
    Wire1.write(0x5a);
    Wire1.endTransmission();
    Wire1.requestFrom(0x34, 1);
    uint8_t buf = Wire1.read();
    Wire1.beginTransmission(0x34);
    Wire1.write(0x5b);
    Wire1.endTransmission();
    Wire1.requestFrom(0x34, 1);
    uint8_t buf2 = Wire1.read();
    vin = ((buf << 4) + buf2); // V
    return vin;
}
uint16_t GetIusbinData(void){
    uint16_t iin = 0;
    Wire1.beginTransmission(0x34);
    Wire1.write(0x5c);
    Wire1.endTransmission();
    Wire1.requestFrom(0x34, 1);
    uint8_t buf = Wire1.read();
    Wire1.beginTransmission(0x34);
    Wire1.write(0x5d);
    Wire1.endTransmission();
    Wire1.requestFrom(0x34, 1);
    uint8_t buf2 = Wire1.read();
    iin = ((buf << 4) + buf2); // V
    return iin;
}
uint16_t GetVapsData(void){
    uint16_t vaps = 0;
    Wire1.beginTransmission(0x34);
    Wire1.write(0x7E);
    Wire1.endTransmission();
    Wire1.requestFrom(0x34, 1);
    uint8_t buf = Wire1.read();
    Wire1.beginTransmission(0x34);
    Wire1.write(0x7F);
    Wire1.endTransmission();
    Wire1.requestFrom(0x34, 1);
    uint8_t buf2 = Wire1.read();
    
    vaps = ((uint16_t)(buf << 4) + buf2);
    return vaps;
}
uint8_t GetWarningLeve(void){
    uint16_t vaps = 0;
    Wire1.beginTransmission(0x34);
    Wire1.write(0x47);
    Wire1.endTransmission();
    Wire1.requestFrom(0x34, 1);
    uint8_t buf = Wire1.read();
    return (buf & 0x01);
}

0.0.5で実装されていない関数は、ローカル関数として定義して実験をしました。

まとめ

個人的にはAPIの内部で計算してくれているAPIと、自分で計算しないとだめなAPIが混在しているので、ちょっと使いにくい気がします。

電流とかの精度は未検証ですが、バッテリー充電が6mAとかかなり小さいのが気になりますが、電源つけている状態だとほぼ充電されないので、そんなのものなのかもしれません。

充電するときには左側ボタンを6秒以上おして、電源オフにした状態で充電するのがよさそうでした。

コメント