同じことをしたくてもマイコンごとに言語や書き方が違って,せっかく学んでも使うマイコンが変わったらもう一度やり直しということがあります.
特にI2Cがマイコンごとの個性が出てるイメージがあったので,比較してみました.
比較したのは下3つです
初期化(master)
i2cのmasterとしての初期設定です.
クロックの設定や,ピンの設定を行います
obniz
obniz.i2c0.start({mode:"master", sda:2, scl:3, clock:400000});
arduino
Wire.begin();
Wire.setClock(400000);
//対応ピンはボードごとに固定
esp32
int i2c_master_port = I2C_EXAMPLE_MASTER_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_EXAMPLE_MASTER_SDA_IO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = I2C_EXAMPLE_MASTER_SCL_IO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = I2C_EXAMPLE_MASTER_FREQ_HZ;
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, conf.mode,
I2C_EXAMPLE_MASTER_RX_BUF_DISABLE,
I2C_EXAMPLE_MASTER_TX_BUF_DISABLE, 0);
espだけ設定項目が多いですね.
クロックの設定以外にもバッファとかプルアップの設定とかをしています
いろいろカスタマイズしたいときに融通がききそうです.
arduinoのi2cライブラリがWire
となっているのも混乱しやすい点かもしれません
書き込み(レスポンスなし書き込み)
次のようなコマンドを送る場合で比較します
- start
- SLASVE_ADDR + wr_bit
- write 2 byte (SEND_DATA1,SEND_DATA2)
- stop
obniz
obniz.i2c0.write(slave_addr, [SEND_DATA1, SEND_DATA2]);
arduino
Wire.beginTransmission(slave_addr);
Wire.write(SEND_DATA1);
Wire.write(SEND_DATA2);
Wire.endTransmission();
esp32
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, SLASVE_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, SEND_DATA1, ACK_CHECK_EN);
i2c_master_write_byte(cmd, SEND_DATA2, ACK_CHECK_EN);
i2c_master_stop(cmd);
int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
esp32はSLASVE_ADDRにwriteかreadかのフラグを自分で付加してbyte単位で書いていきます.
obnizとarduinoはそこは意識せずにSLASVE_ADDRを指定するだけですね
esp32だけ送信するコマンドを作る→送信するとなっているところも違いますね
読み込み(書き込みなしの読み込み)
受信は次のようなコマンドを送る場合で比較します
- start
- SLASVE_ADDR + read_bit
- receive 3 byte
- stop
あんまりこれに対応してるデバイスは無いかもしれません
obniz
var ret = await obniz.i2c0.readWait(SLASVE_ADDR, 3); //3byte読み込み
//ret = [data1,data2,data3]
arduino
Wire.requestFrom(SLASVE_ADDR, 3); //3byte読み込み命令
byte val1 = Wire.read(); //データ受信
byte val2 = Wire.read(); //データ受信
byte val3 = Wire.read(); //データ受信
esp32
uint8_t data_rd[3];
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( SLASVE_ADDR << 1 ) | READ_BIT, ACK_CHECK_EN);
i2c_master_read(cmd, data_rd, 2, ACK_VAL); //2byteはackありで受信
i2c_master_read_byte(cmd, &data_rd[2], NACK_VAL); //3byteで終了なので最後はnackで受信
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
やはりespだけ設定が細かいですね.
obnizとarduinoは受信したいバイト数だけ書けばあとは内部でやってくれてますが,
espは自分でackとnackを考えながらコマンドを作る必要があるようです
アドレス指定読み込み(レスポンスあり書き込み)
これが一番使うんじゃないでしょうか,
- start
- SLASVE_ADDR
- write 1 byte (WRITE_DATA)
- receive 3 byte
- stop
obniz
obniz.i2c0.write(SLASVE_ADDR, [WRITE_DATA]);
let val = await obniz.i2c0.readWait(SLASVE_ADDR, 3);
arduino
Wire.beginTransmission(SLASVE_ADDR);
Wire.write(WRITE_DATA);
Wire.endTransmission(false);
Wire.requestFrom(0x1E, 3, true);
byte val1 = Wire.read();
byte val2 = Wire.read();
byte val3 = Wire.read();
esp32
uint8_t data_rd[3];
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (SLASVE_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, WRITE_DATA, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (SLASVE_ADDR << 1) | I2C_MASTER_READ, true);
i2c_master_read(cmd, data_rd, cnt-1, ACK_VAL);
i2c_master_read_byte(cmd, &data_rd[2], NACK_VAL);
i2c_master_stop(cmd);
i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
どれもwriteとreadをつなぎ合わせた形になっています
接続部分がそれぞれ違ってますね
obnizはそのままつなげていて,arduinoはWire.endTransmission(false);
と引数が入っています.
espはi2c_master_stop(cmd)
をスキップしてつなげているようですね
まとめ
それぞれ違いがありますが,espがダントツで難しいですね
その分,使いこなせばいろいろ細かくできるのでしょうが,規格が決まってるのでカスタマイズ項目って少ないような...
obnizもarduinoもi2cの簡単さという点では似たり寄ったりのレベルですね
arduinoだとWireって言葉になれるのが大変・・・ぐらいでしょうか,