ESP32-devkitのI2C Master側の動作を試しました。
本記事では下記3点を説明しています。
- I2Cスキャナによるアドレス確認方法
- 3.3V/5VのI2C機器との結線方法
- 例として0.56″ 7-Segment backpackというデバイスの使い方
I2Cとは
I2C(アイ・スクエアード・シー。通称アイツーシー)は一言で言えばシリアル通信の一種です。
I2Cの主な特徴(メリット)は下記2つです。
- 結線がクロック(SCL)とデータ(SDA)の2本で済む
- アドレス管理をすることで複数のデバイスを1つのI2Cバス上に配置できる
Wikipedia上でのI2Cの説明は下記の通りです。
I2C(アイ・スクエアド・シー、アイ・アイ・シー)はフィリップス社で開発されたシリアルバスである。低速な周辺機器をマザーボードへ接続したり、組み込みシステム、携帯電話などで使われている。
https://ja.wikipedia.org/wiki/I2C
Inter-Integrated Circuit の略で、I-squared-C(アイ・スクエアド・シー)が正式な読みとされている。ただし、一般的な文字コード環境のプレーンテキスト上では上付き文字が使えないため、I2CあるいはIICと表記されることも多く、これをもって「アイ・ツー・シー」と発声されたりカタカナ表記される[1]ことがある。
接続図の例は上記wikipedia参照の通りです。
VddへRp(プルアップ抵抗)をつけ忘れがちなので、注意が必要です。
(プルアップを忘れると、信号がなまってうまく通信できないことがあります:経験済み)
詳しく確認されたい場合は、NXPのサイトに「I2Cバス仕様およびユーザーマニュアル」がありますので、そちらの資料を参照ください。
https://www.nxp.com/docs/ja/user-guide/UM10204.pdf
ESP32でI2CScannerを試す
ここではESP32でI2CScannerというプログラムを使って、I2C機器のアドレスを調べる方法を共有します。
I2CScannerとは
I2C機器のアドレスを調べるプログラムのことです。
前述の通り、I2C機器はそれぞれにアドレスを割り当てて使用します。
I2CScannerは有効なアドレスを総当たりでI2Cのバスに問い合わせ、応答があったアドレスを表示します。
Slave機器のアドレスが不明な場合や、自分でアドレス変更結果を確認したい時に便利です。
ESP32用のI2CScannerサンプルコード
ESP32用のI2CScannerがEspressifから提供されています。
Arduinoのコードを改変するとしてもSerialのボーレートを変えるくらいですが、そもそもESP32用のコードがあるので素直にこちらを使いましょう。
サンプルコードの場所は下記です。
https://github.com/espressif/arduino-esp32/blob/master/libraries/Wire/examples/WireScan/WireScan.ino
(PlatformIOを利用の方はArduino.hのインクルードだけ忘れずに)
以降の項の説明で示すESP32-スレーブ機器の結線をしたのち、下記コードを実行すると、シリアルモニタ上にスレーブ機器のアドレスが表示されます。
#include <Arduino.h>
#include "Wire.h"
void setup() {
Serial.begin(115200);
Wire.begin();
}
void loop() {
byte error, address;
int nDevices = 0;
delay(5000);
Serial.println("Scanning for I2C devices ...");
for(address = 0x01; address < 0x7f; address++){
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0){
Serial.printf("I2C device found at address 0x%02X\n", address);
nDevices++;
} else if(error != 2){
Serial.printf("Error %d at address 0x%02X\n", error, address);
}
}
if (nDevices == 0){
Serial.println("No I2C devices found");
}
}
ESP32とSlave機器のI2C接続(SCL/SDAポート)
ESP32には2系統のI2Cが用意されています。
ESP32-devkitの場合、1系統はデフォルトで下記のI/Oに割り当てられています。
- SDA: GPIO21
- SCL: GPIO22
その他のESP32機器でも基本的に上記GPIO21/22にI2Cが割り当てられています(下記参照)。
ただし、利用にあたってはそれぞれのリファレンスで一応IOを確認することを推奨します。
The default pins may vary from board to board. On the Generic ESP32 the default I2C pins are:
https://docs.espressif.com/projects/arduino-esp32/en/latest/api/i2c.html
・sdaPin GPIO21
・sclPin GPIO22
ESP32は3.3V動作のマイコンのため、3.3Vで動作するI2C機器の場合は上記ポートにI2C機器を接続すればOKです(必要に応じて外部のプルアップ抵抗もつけます)。
5V動作のI2C機器をESP32と接続する場合は3.3V-5Vのレベルシフタを併用する必要があります。
なお、I2C機器はSCL/SDAを結線するだけで手軽に動作しますが、下記2点の注意が必要です。
- I2C機器の動作電圧を確認する必要がある。
- SCL/SDAともVddへプルアップしておかないと、正しく信号が送受信できないことがある
3.3Vで動くI2C(Slave)機器を扱う場合の結線
ESP32はI2Cのマスターモードで設定すると内部プルアップをしているようなので、基本的にプルアップ抵抗はなくてもI2C機器を接続できます。
ただし、内蔵のプルアップの抵抗値はかなり高いです。
波形の歪みを見るなどして確認し、必要に応じて10kΩ程度の外部プルアップを追加すると良いかと思います。
下の写真は結線の例で、SDL/SCLとVdd/GNDの4線を接続することで動作しています。(内蔵Pullupのみ使用)
ESP32のI2Cバスに対してデフォルトでプルアップされている根拠。
ESP32のI2Cバスのプルアップの根拠は下記です。
ドライバ層以下のコードが公開されておらず追いきれませんでしたが、これでPullupされてなかったらかなりお恥ずかしいコードなので、大丈夫でしょう。
参照したコードはhttps://github.com/espressif/arduino-esp32 v2.0.3です。
以下、ソースコード抜粋です。
// Master Begin
bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
の中で、下記が呼ばれています。
esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency)
さらにesp_err_t i2cInit()にて下記コードが下位のドライバ層に渡されています。
i2c_config_t conf = { };
conf.mode = I2C_MODE_MASTER;
conf.scl_io_num = (gpio_num_t)scl;
conf.sda_io_num = (gpio_num_t)sda;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = frequency;
conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; //Any one clock source that is available for the specified frequency may be choosen
5Vで動くI2C機器とつなぐ場合の結線
5Vで動作する機器をESP32につなぐ場合、3.3V-5Vのレベルシフタが必要です。
私はAmazonで下記のようなデバイスを購入しました。:アマゾンの商品リンク:I2Cロジックレベル変換
写真の通り、4線のレベル変換が可能です。I2Cなら2系統使えます。
High側/Low側とも10kでプルアップされているので、追加のプルアップが不要な点はありがたいです。
上記レベルシフタを利用した結線が下記になります。
左上の黒いモジュールは3.3V/5V MB102ブレッドボード電源モジュールです。:アマゾンリンク
写真では少々線が多く煩雑に見えますが、レベル変換器の上を5V、下を3.3Vとして、SDA/SCL GND/Vddをそれぞれ結線しているのみです。
ESP32で0.56″ 7-Segment Backpackを使う。
これまでの写真で登場している0.56″ 7-Segment Backpackを実際に使ってみます。
このモジュールはセカンドソース品が多数出ているようで、Amazonなどでも安価に購入できます。
使い方はこちらのadafruitのサイトに詳しいので、これを順に実施します。
ただし、adafruitのサイトはArduinoIDEでの説明となっていましたので、以下はPlatformIOでの説明を記載します。
0.56″ 7-Segment Backpackの結線。5V?3.3V?
先のサイトの記載によると、「電源は5Vがベストだけど、3Vでも動くだろうね」のように書かれています。実際に試すと5V、3.3Vどちらでも使えますのでお好みで結線してください。
Connect VCC+ to power – 5V is best but 3V also seems to work for 3V microcontrollers.
https://learn.adafruit.com/adafruit-led-backpack/0-dot-56-seven-segment-backpack-arduino-setup
0.56″ 7-Segment Backpackに必要なライブラリをインストールする
Adafruit LED Backpack と Adafruit GFX ライブラリをインストールすればOKです。
以下スクリーンショットをご参考ください。
Libraries画面で検索窓にAdafruit LED Backpackを入力し、下記のようにリストされた項目をクリックします。
続いて、Add to Project で利用するプロジェクトに追加すればOKです。
Adafruit GFXも同様に追加します。
サンプルプログラム sevenseg.ino が下記にありますので、これをmain.cppにコピーします。
下記はmain.cppにコピーした後、ESP32のPlatformIO環境で動作するようにしたコードです。
#include <Arduino.h>
/***************************************************
This is a library for our I2C LED Backpacks
Designed specifically to work with the Adafruit LED 7-Segment backpacks
----> http://www.adafruit.com/products/881
----> http://www.adafruit.com/products/880
----> http://www.adafruit.com/products/879
----> http://www.adafruit.com/products/878
These displays use I2C to communicate, 2 pins are required to
interface. There are multiple selectable I2C addresses. For backpacks
with 2 Address Select pins: 0x70, 0x71, 0x72 or 0x73. For backpacks
with 3 Address Select pins: 0x70 thru 0x77
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include <Wire.h> // Enable this line if using Arduino Uno, Mega, etc.
#include <SPI.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
Adafruit_7segment matrix = Adafruit_7segment();
void setup()
{
#ifndef __AVR_ATtiny85__
Serial.begin(9600);
Serial.println("7 Segment Backpack Test");
#endif
matrix.begin(0x70);
}
void loop()
{
// try to print a number thats too long
matrix.print(10000, DEC);
matrix.writeDisplay();
delay(500);
// print a hex number
matrix.print(0xBEEF, HEX);
matrix.writeDisplay();
delay(500);
// print a floating point
matrix.print(12.34);
matrix.writeDisplay();
delay(500);
// print a string message
matrix.print("7SEG");
matrix.writeDisplay();
delay(10000);
// print with print/println
for (uint16_t counter = 0; counter < 9999; counter++)
{
matrix.println(counter);
matrix.writeDisplay();
delay(10);
}
// method #2 - draw each digit
uint16_t blinkcounter = 0;
boolean drawDots = false;
for (uint16_t counter = 0; counter < 9999; counter++)
{
matrix.writeDigitNum(0, (counter / 1000), drawDots);
matrix.writeDigitNum(1, (counter / 100) % 10, drawDots);
matrix.drawColon(drawDots);
matrix.writeDigitNum(3, (counter / 10) % 10, drawDots);
matrix.writeDigitNum(4, counter % 10, drawDots);
blinkcounter += 50;
if (blinkcounter < 500)
{
drawDots = false;
}
else if (blinkcounter < 1000)
{
drawDots = true;
}
else
{
blinkcounter = 0;
}
matrix.writeDisplay();
delay(10);
}
}
改変箇所は下記3点になります。
1行目: arduino.hをインクルード
26行目:SPI.hをインクルード(使ってないはずですが・・・)
38行目:I2CScannerで確認したアドレスに変更
まとめ
以上、本記事では下記3点について解説しました。
- I2Cスキャナによるアドレス確認方法
- 3.3V/5VのI2C機器との結線方法
- 例として0.56″ 7-Segment backpackというデバイスの使い方
ESP32をI2CのMaster/Slaveモードで動かす際の詳細までは本記事では触れませんでしたので、下記も続けてご参照いただければと思います。
その他のESP32でのI2Cの利用方法について、下記にまとめました。合わせてご参照ください
以上、ご参考になれば幸いです。