GreenPAK(SLG46826)をI2C経由で触ってみる その2 回路の読み書き実験

概要

前回は書き込み前のGreenPAKのGPIOの状態を取得してみました。今回は回路データの読み書きの実験をしたいと思います。

情報

日本GreenPAKの父であるAoiSayaさんのリポジトリと、本家データシートを見比べながら動作実験していきたいと思います。

I2Cアドレス

前回は0x08から4アドレス見えました。データシートを実機を確認してどんな構造になっているのかを確認してみます。

I2CアドレスGreenPAKアドレス用途
00レジスタ
10レジスタ
20NVM(回路)
30EEPROM
40未使用
50未使用
60未使用
70未使用
81レジスタ
91レジスタ
101NVM(回路)
111EEPROM

冒頭部分のみですが、上記のような関係になっていました。GreenPAKではI2Cのアドレスを16種類選択できます。そして各設定で8個のアドレスが利用されます。とはいっても、最後の4個は未使用になります。

GreenPAKのアドレスを1にすると、I2Cアドレス8から8個分、2にすると16から8個分という風に使っていき、16を選択すると120から8個分と全I2Cアドレスを使ってしまいます。

I2Cアドレス用途
設定×8+0レジスタ
設定×8+1レジスタ
設定×8+2NVM(回路)
設定×8+3EEPROM
設定×8+4未使用
設定×8+5未使用
設定×8+6未使用
設定×8+7未使用

こんな感じの使われ方です。スケッチでは<<3という書き方されていました。

レジスタ

前回読みだした回路の状態を取得したりする場合にアクセスします。カウンタの内部値なども取り出すことができます。

個人的にはI2Cアドレスの0はたしか使っちゃいけないアドレスなので、レジスタにアクセスする場合には設定×8+1を使ったほうが好みかもしれません。

NVM

回路のデータを書き込むときに利用する領域です。

EEPROM

GreenPAK内部に保存できるメモリ領域です。残念なことに回路からは読み出すことはできません。I2Cから書き込んだり、読み出したりは可能です。

回路の読み出し

設定×8+2のI2Cアドレスで読み出せるようです。

#include <M5StickC.h>

void setup() {
  M5.begin();
  Wire.begin(32, 33);
  Wire.setClock(100000);
}

void loop() {
  uint8_t address = (0x01 << 3) + 2;
  for (int i = 0; i < 256; i += 16) {
    Wire.beginTransmission(address);
    Wire.write(i);
    Wire.endTransmission();
    Wire.requestFrom(address, 16);
    for (int j = 0; j < 16; j++) {
      uint8_t val = Wire.read();
      Serial.printf("%02X", val);
    }
    Serial.println();
  }

  Serial.println();

  delay(5000);
}

上記でざっくり実験してみました。

  uint8_t address = (0x01 << 3) + 2;

上記でI2Cアドレスを指定しています。0x01<<3は8なので8+2で10(0x0A)を指定しています。

00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000010000000E00
00000000000000000000000000000000
00000000000000000000000000000000
3E120F28232410101000B800000000A5

こんな感じの出力になります。一番下の行はチップ固有のデータなので、チップにより異なると思います。下から4行目の1がアドレス指定になります。

また、スケッチでは16バイト単位で読み取りをしていますが、1バイト単位でも大丈夫でした。128バイトとかまでだったら取得できそうでしたが、16バイト単位で処理するのが全体になっているので、なるべく16バイト単位でアクセスしたほうが良さそうです。

初期化

どうやら初期化しないと書き込めないようです。

#include <M5StickC.h>

void setup() {
  M5.begin();
  Wire.begin(32, 33);
  Wire.setClock(100000);
}

void loop() {
  // Erase
  uint8_t address = (0x01 << 3) + 1;
  for (int i = 0; i < 16; i ++) {
    Wire.beginTransmission(address);
    Serial.printf("Wire.beginTransmission=%02X\n", address);

    Wire.write(0xE3);
    Serial.printf("Wire.write=%02X\n", 0xE3);
    Wire.write(0x80 + i);
    Serial.printf("Wire.write=%02X\n", 0x80 + i);
    Wire.endTransmission();
    Serial.println();
    delay(100);
  }

  // Read
  address = (0x01 << 3) + 2;
  for (int i = 0; i < 256; i += 16) {
    Wire.beginTransmission(address);
    Wire.write(i);
    Wire.endTransmission();
    Wire.requestFrom(address, 16);
    for (int j = 0; j < 16; j++) {
      uint8_t val = Wire.read();
      Serial.printf("%02X", val);
    }
    Serial.println();
  }

  Serial.println();

  delay(10000);
}

削除は16バイト単位で行われるようです。レジスタのアドレスに対して0xE3と0x80+iの2バイトを送信することで消せるとのことでした。

00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
3E120F28232410101000B800000000A5

16バイト単位で16回初期化しました。全データが初期化されているようです。下から4行目にあったアドレスが1から0に変化しています。どうやらアドレス情報も初期化すると消えて0になってしまうようです。

ただし、この時点ではまだ0x01<<3で読み取りができていますね。電源を落としてリセットすることで0x00<<3に変更しないと読み込めなくなりました。

#include <M5StickC.h>

void setup() {
  M5.begin();
  Wire.begin(32, 33);
  Wire.setClock(100000);
}

void loop() {
  // Erase
  uint8_t address = (0x01 << 3) + 1;
  for (int i = 0; i < 16; i ++) {
    Wire.beginTransmission(address);
    Serial.printf("Wire.beginTransmission=%02X\n", address);

    Wire.write(0xE3);
    Serial.printf("Wire.write=%02X\n", 0xE3);
    Wire.write(0x80 + i);
    Serial.printf("Wire.write=%02X\n", 0x80 + i);
    Wire.endTransmission();
    Serial.println();
    delay(100);
  }

  // Read
  address = (0x01 << 3) + 2;
  for (int i = 0; i < 256; i += 16) {
    Wire.beginTransmission(address);
    Wire.write(i);
    Wire.endTransmission();
    Wire.requestFrom(address, 16);
    for (int j = 0; j < 16; j++) {
      uint8_t val = Wire.read();
      Serial.printf("%02X", val);
    }
    Serial.println();
  }

  Serial.println();

  // Reset
  address = (0x01 << 3) + 1;
  Wire.beginTransmission(address);
  Wire.write(0xC8);
  Wire.write(0x02);
  Wire.endTransmission();
  delay(1000);

  // Read
  address = (0x00 << 3) + 2;
  for (int i = 0; i < 256; i += 16) {
    Wire.beginTransmission(address);
    Wire.write(i);
    Wire.endTransmission();
    Wire.requestFrom(address, 16);
    for (int j = 0; j < 16; j++) {
      uint8_t val = Wire.read();
      Serial.printf("%02X", val);
    }
    Serial.println();
  }

  delay(10000);
}

ソフトウエアリセットを追加してみました。レジスタ宛に0xC8と0x02を投げるとリセットされて、I2Cアドレスが反映されるようでした。

書き込み

#include <M5StickC.h>

void setup() {
  M5.begin();
  Wire.begin(32, 33);
  Wire.setClock(100000);
}

void loop() {
  // Erase
  uint8_t address = (0x00 << 3) + 1;
  for (int i = 0; i < 16; i ++) {
    Wire.beginTransmission(address);
    Serial.printf("Wire.beginTransmission=%02X\n", address);

    Wire.write(0xE3);
    Serial.printf("Wire.write=%02X\n", 0xE3);
    Wire.write(0x80 + i);
    Serial.printf("Wire.write=%02X\n", 0x80 + i);
    Wire.endTransmission();
    Serial.println();
    delay(100);
  }

  // Write
  address = (0x00 << 3) + 2;
  for (int i = 0; i < 256; i += 16) {
    Wire.beginTransmission(address);
    Serial.printf("Wire.beginTransmission=%02X\n", address);

    Wire.write(i);
    Serial.printf("Wire.write=%02X\n", i);

    for (int j = 0; j < 16; j++) {
      Wire.write(i + j);
      Serial.printf("Wire.write=%02X\n", j);
    }
    Wire.endTransmission();
    Serial.println();
    delay(100);
  }

  // Read
  address = (0x00 << 3) + 2;
  for (int i = 0; i < 256; i += 16) {
    Wire.beginTransmission(address);
    Wire.write(i);
    Wire.endTransmission();
    Wire.requestFrom(address, 16);
    for (int j = 0; j < 16; j++) {
      uint8_t val = Wire.read();
      Serial.printf("%02X", val);
    }
    Serial.println();
  }

  Serial.println();

  delay(10000);
}

書き込みは16バイト単位になります。とりあえず0x00からインクリメントした値を書き込んでみたいと思います。

000102030405060708090A0B0C0D0E0F
101112131415161718191A1B1C1D1E1F
202122232425262728292A2B2C2D2E2F
303132333435363738393A3B3C3D3E3F
404142434445464748494A4B4C4D4E4F
505152535455565758595A5B5C5D5E5F
606162636465666768696A6B6C6D6E6F
707172737475767778797A7B7C7D7E7F
808182838485868788898A8B8C8D8E8F
909192939495969798999A9B9C9D9E9F
A0A1A2A3A4A5A6A7A8A9AAABACADAEAF
B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF
C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF
D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF
E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF
3E120F28232410101000B800000000A5

上記のように書き込めました。一番下の行はやっぱり書き込めませんね。

さて、この処理ですが真似しないでください。基本的にどんな回路を書き込んでも壊れないようにできているはずですが、ここまで適当な回路データを書き込むとどんな回路になっているかわかりません。実際に動かすと問題が出る可能性があるのでおすすめしません!

まとめ

さて、回路データの読み書きができるようになりました。次回は実際の回路を作成して、書き込みをしてみたいと思っています。

続編

コメント