1.はじめに
M5StickCPlusにはRTCが搭載されています。このRTCを使って、M5StickCPlusをDeep Sleepモードから復帰させたりできるはずなのですが、いまいち上手く使えませんでした。
なので、データシートを確認した上で手順を追って、ちゃんと使えるようにしたいと思います。
ちゃんと使えるようにできませんでした。
以降、当初の目論見と、いろいろやった結果を書いていきたいと思います。
対象は、M5StickC Plusになります。
新しいM5StickC Plus2では未検証です。
2.使い方
データシートは002 BM8563 データシート要約で確認したので完全に 理解しました。
現在進行形で理解できていません。
この記事では、BM8563のタイマー機能を使って、タイマーを設定してから所定時間後にINTピンからの割込み信号を 生成させたいと思います。
結果として、INTピンからの割込みを生成できませんでした。
3.基本設計
BM8563を使って以下の動作をするプログラムを作っていきたいと思います。
3-1.動作フロー
以下フローで動作するプログラムを作りたいと思います。
なお、デバッグのため以下の動作を入れます。
- 各状態に遷移したら、状態名をシリアル出力させる
- 状態4から状態2に遷移するときにLEDを反転させる(正常に動いている場合は、タイマー設定間隔でLEDが点滅します。)
3-2.タイマー設定値
タイマーは8bitのダウンカウンタとなっており、TD0/TD1
でカウントソースを1/60Hz
、1Hz
、64Hz
、4096Hz
から選択できるので、設定できる最大値はクロックソースを1/60Hz
、カウント値を255
に設定した場合で、255分 = 4時間15分
になります。
今回はテストのため、クロックソース周波数=1Hz
、タイマー値=5
で設定したいと思います。
3-3.プログラム
以下が作成したプログラムです。
結果として期待の動作をさせることはできませんでした。BM8563のレジスタをダンプして表示してみたり、いろいろ頑張ったんですが、これ以上の対策を思いつかなくなったのでいったんここまでとします。
M5StickCPlus2での検証に備えて、備忘録として残しておきます。
#include <M5StickCPlus.h>
#include <Wire.h>
#define LED 10
//#define SYS_INT GPIO_NUM_35
#define BM8563 0x51
#define AXP192 0x34
#define MPU6886 0x68
#define DELAY 1000
static uint8_t rtc_sq;
#define RTCSQ_INIT 0
#define RTCSQ_SET 1
#define RTCSQ_START 2
#define RTCSQ_WAIT 3
void i2c_byteWrite(uint8_t dev, uint8_t adr, uint8_t val);
uint8_t i2c_byteRead(uint8_t dev, uint8_t adr);
void dumpBM8563(void);
void zprintf(const char *format, ...);
void dumpAXP192(void);
void dumpMPU6886(void);
void IRAM_ATTR sysint_isr(void);
void IRAM_ATTR sysint_isr(void)
{
rtc_sq = RTCSQ_SET;
digitalWrite(LED, digitalRead(LED)^1);
}
void setup()
{
M5.begin();
/* USB Serial */
Serial.begin(115200);
/* LED */
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
/* SYSINT */
pinMode(GPIO_NUM_35, INPUT);
attachInterrupt(GPIO_NUM_35, sysint_isr, FALLING);
/* Internal I2C : IMU(MPU6886)/PMIC(AXP192)/RTC(BM8563) */
Wire1.begin(21, 22);
Wire1.setClock(100000);
Wire1.write(0);
// disable AXP192 IRQ
i2c_byteWrite(AXP192, 0x40, 0x00);
i2c_byteWrite(AXP192, 0x41, 0x00);
i2c_byteWrite(AXP192, 0x42, 0x00);
i2c_byteWrite(AXP192, 0x43, 0x00);
i2c_byteWrite(AXP192, 0x4A, 0x00);
// clear AXP192 All IRQstatus
i2c_byteWrite(AXP192, 0x44, 0xFF);
i2c_byteWrite(AXP192, 0x45, 0xFF);
i2c_byteWrite(AXP192, 0x46, 0xFF);
i2c_byteWrite(AXP192, 0x47, 0xFF);
i2c_byteWrite(AXP192, 0x4D, 0xFF);
// disable MPU6886 IRQ
i2c_byteWrite(MPU6886, 0x37, 0b11011000);
i2c_byteWrite(MPU6886, 0x38, 0x00);
// clear MPU6886 All IRQstatus
uint8_t ret;
ret = i2c_byteRead(MPU6886, 0x36);
ret = i2c_byteRead(MPU6886, 0x39);
ret = i2c_byteRead(MPU6886, 0x3A);
}
void loop()
{
switch(rtc_sq)
{
case RTCSQ_INIT:
// debug
Serial.println("RTCSQ_INIT");
// RTC動作開始
i2c_byteWrite(BM8563, 0x00, 0x00);
// 各アラーム無効化
i2c_byteWrite(BM8563, 0x09, 0x80);
i2c_byteWrite(BM8563, 0x0A, 0x80);
i2c_byteWrite(BM8563, 0x0B, 0x80);
i2c_byteWrite(BM8563, 0x0C, 0x80);
i2c_byteWrite(BM8563, 0x0D, 0x83);
// タイマー無効化
i2c_byteWrite(BM8563, 0x0E, 0x03);
// INTピン設定、アラーム・タイマー割込みフラグ削除・無効化
i2c_byteWrite(BM8563, 0x01, 0x00);
// wait
dumpBM8563();
dumpAXP192();
dumpMPU6886();
delay(DELAY);
// 状態遷移
rtc_sq = RTCSQ_SET;
break;
case RTCSQ_SET:
// debug
Serial.println("RTCSQ_SET");
// タイマー割込み有効化
i2c_byteWrite(BM8563, 0x01, 0x00);
// タイマークロックソース選択
i2c_byteWrite(BM8563, 0x0E, 0x02);
// タイマー設定値書き込み
i2c_byteWrite(BM8563, 0x0F, 0x05);
// wait
dumpBM8563();
delay(DELAY);
// 状態遷移
rtc_sq = RTCSQ_START;
break;
case RTCSQ_START:
// debug
Serial.println("RTCSQ_START");
// タイマー有効化
i2c_byteWrite(BM8563, 0x0E, 0x82);
// アラーム・タイマーフラグクリア
i2c_byteWrite(BM8563, 0x01, 0x03);
// wait
dumpBM8563();
delay(DELAY);
// 状態遷移
if(digitalRead(GPIO_NUM_35) == HIGH)
{
rtc_sq = RTCSQ_WAIT;
}
else
{
rtc_sq = RTCSQ_INIT;
}
break;
case RTCSQ_WAIT:
// debug
Serial.println("RTCSQ_WAIT");
// wait
dumpBM8563();
//dumpAXP192();
//dumpMPU6886();
delay(DELAY);
// 状態遷移
if(digitalRead(GPIO_NUM_35) == LOW)
{
rtc_sq = RTCSQ_SET;
digitalWrite(LED, digitalRead(LED)^1);
}
break;
default:
rtc_sq = RTCSQ_INIT;
break;
}
}
void i2c_byteWrite(uint8_t dev, uint8_t adr, uint8_t val)
{
Wire1.beginTransmission(dev);
Wire1.write(adr);
Wire1.write(val);
Wire1.endTransmission();
}
uint8_t i2c_byteRead(uint8_t dev, uint8_t adr)
{
uint8_t ret;
Wire1.beginTransmission(dev);
Wire1.write(adr);
Wire1.endTransmission();
Wire1.requestFrom( (int)dev, 1, 1 );
while(Wire1.available() < 1);
ret = Wire1.read();
return ret;
}
void dumpBM8563(void)
{
uint8_t dump[16];
uint8_t i;
for(i=0; i<16; i++)
{
dump[i] = i2c_byteRead(BM8563, i);
}
Serial.println("DUMP BM8563 00h-0Fh");
Serial.println("00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
for(i=0; i<16; i++)
{
zprintf("%02x ", dump[i]);
}
Serial.println("");
}
#define MAX_ZPRINTF_LENGTH 80 // 一度に変換する最大文字数
void zprintf(const char *format, ...) {
char s[MAX_ZPRINTF_LENGTH];
va_list args;
va_start(args, format);
vsnprintf(s, MAX_ZPRINTF_LENGTH, format, args);
va_end(args);
Serial.print(s);
}
void dumpAXP192(void)
{
uint8_t dump[16];
uint8_t i;
for(i=0; i<16; i++)
{
dump[i] = i2c_byteRead(AXP192, i+0x40);
}
Serial.println("DUMP AXP192 40h-4Fh");
Serial.println("00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
for(i=0; i<16; i++)
{
zprintf("%02x ", dump[i]);
}
Serial.println("");
}
void dumpMPU6886(void)
{
uint8_t dump[16];
uint8_t i;
for(i=0; i<16; i++)
{
dump[i] = i2c_byteRead(MPU6886, i+0x30);
}
Serial.println("DUMP MPU6886 30h-3Fh");
Serial.println("00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
for(i=0; i<16; i++)
{
zprintf("%02x ", dump[i]);
}
Serial.println("");
for(i=0; i<16; i++)
{
dump[i] = i2c_byteRead(MPU6886, i+0x60);
}
Serial.println("DUMP MPU6886 60h-6Fh");
Serial.println("00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
for(i=0; i<16; i++)
{
zprintf("%02x ", dump[i]);
}
Serial.println("");
}
4. 発生した問題と実施した対策
4-1. 問題1:SYS_INTピンがLOのまま。
プログラムを書いてまず最初に躓いたのは、SYS_INT(GPIO35)
がローレベルのままとなっている。つまり、割込み発生状態が続いてしまっているという点でした。
BM8563のINTピンは、M5StickCPlusの内部回路としてはSYS_INT
ラインに接続されています。SYS_INT
ラインは抵抗R2(2.2kΩ)でプルアップされているため、オープンドレイン端子のINTがローレベルに落ちることで、割込みが発生するはずです。
しかし、まだタイマーがタイムアップしていない筈なのに、SYS_INT
ラインがローに落ちている・・・。
この問題の原因として考えられるのは、以下3点でした。
- AXP192が
SYS_INT
をローに引っ張っている。 - MPU6886が
SYS_INT
をローに引っ張っている。 - BM8563が
SYS_INT
をローに引っ張っている。
原因1/2を取り除くためAXP192とMPU6886のIRQ無効化、割込みフラグクリアを行うことにしました。対策を行うにあたり、追加したコードが以下になります。
#define BM8563 0x51
#define AXP192 0x34
#define MPU6886 0x68
...
// disable AXP192 IRQ
i2c_byteWrite(AXP192, 0x40, 0x00); // IRQ1
i2c_byteWrite(AXP192, 0x41, 0x00); // IRQ2
i2c_byteWrite(AXP192, 0x42, 0x00); // IRQ3
i2c_byteWrite(AXP192, 0x43, 0x00); // IRQ4
i2c_byteWrite(AXP192, 0x4A, 0x00); // IRQ5
// clear AXP192 All IRQstatus
i2c_byteWrite(AXP192, 0x44, 0xFF); // IRQ1 status
i2c_byteWrite(AXP192, 0x45, 0xFF); // IRQ2 status
i2c_byteWrite(AXP192, 0x46, 0xFF); // IRQ3 status
i2c_byteWrite(AXP192, 0x47, 0xFF); // IRQ4 status
i2c_byteWrite(AXP192, 0x4D, 0xFF); // IRQ5 status
// disable MPU6886 IRQ
i2c_byteWrite(MPU6886, 0x37, 0xD8);
i2c_byteWrite(MPU6886, 0x38, 0x00);
// clear MPU6886 All IRQstatus
uint8_t ret;
ret = i2c_byteRead(MPU6886, 0x36);
ret = i2c_byteRead(MPU6886, 0x39);
ret = i2c_byteRead(MPU6886, 0x3A);
この対策を施したことでSYS_INT(GPIO35)
がハイレベルになり、プログラムもRTCSQ_WAIT
まで進むようになりました。
DUMP表示していたBM8563のタイマカウントレジスタ(0Fh)も1秒間隔でカウントダウンしているので、これで完成かと思ったのですが・・・
4-2. 問題2:今度はSYS_INTピンがHIGHのまま。
DUMP表示を見てタイマーがタイムアップしTFフラグもセットされているのに、今度はSYS_INTピンがローにならずにずっとハイレベルのまま。つまり、BM8563からタイマー割込みが発生していません。
シリアルモニタのデバッグ表示を以下に貼り付けます。
アドレス01h:Control/Status2レジスタのbit3:TF(Timer Flag?)が1
になっているのですが、SYS_INTがローに落ちていないようです。TIEbitも1:enableにしてあります。結論から言うと、ここで詰みました。 以下、いろいろやったけどダメだった対策を箇条書きにします。
- TIEbitの設定論理を逆にしてみる。(TIE=1:enableと書きましたが、データシートに誤記がありどちらがenableなのか、はっきりしたことが実は分かりません。)
- タイマー割込みは諦めて、アラーム割込みで割込みを発生させようとしてみる。
- TI/TPビットを1にしてINTピンからの出力をパルス出力設定にしてみる。
- GPIO35の立下りエッジ割込みを追加して、割込みで
SYS_INT = ロー
をとらえようとしてみる。
いずれの対策を行ってもダメでした。
5. おわりに
もともとはM5StickCPlusのdeep sleepモードからの復帰にRTCを使えればと思い、チャレンジしてみたのですがうまくいきませんでした。検索も活用したのですが、M5StickCPlusをRTCを用いて復帰させたという記事は見つからず。 できていないの自分だけでしょうか・・・?
MPU6886の割込みピンは、初期設定がアクティブハイでプッシュ-プルの設定となっていたので、もしかすると貫通電流でRTCのINTピンが破損しているのかもしれません。(もしこれが原因だとすると、設計が悪い気がします。)M5StickCPlus2の回路図を見ると、INTピンはRTCだけが接続されるようになっておりMPU6886のINTピンは接続されないように修正が加えられているので、ダウトな気もしますが。
もしM5StickCPlus2を入手する機会があれば、再度チャレンジしてみたいと思います。