ArduinoとI2Cプロトコル(Wire)ガイド

デバイスとセンサー間を、2線シリアルインターフェイスバスで通信します。


AUTHOR: Arduino, Nicholas Zambetti、LAST REVISION: 2022/12/08 18:58


この記事は、2021/11/18にKarl Söderby氏により改定されました。

I2Cプロトコルは、2本の信号線を使ってデータを送受信するものです。1本は、Arduinoコントローラボードが一定間隔でパルスを送信するシリアルクロックピン(SCL)、もう1本は、2つのデバイス間でデータを送信するシリアルデータピン(SDA)です。クロック線がLOWからHIGHに変化すると(クロックパルスの立ち上がりエッジとして知られています)、連続することで特定のデバイスのアドレスやコマンドあるいはデータを構成する1ビットの情報が、ボードからI2Cデバイスへ、データ線上を通じて送信されます。この情報がビットごとに送信されると、呼び出されたデバイスは要求を実行し、必要に応じ、コントローラによってSCL上に継続して生成されているクロックを使い、同じデータ線を通じて、データをボードに返します。

I2Cプロトコルは、各デバイスが固有のアドレスを持ち、コントローラと周辺デバイスが1本の信号線上で順番に通信するので、マイクロコントローラの2本のピンを使うだけで、Arduinoボードは多くのデバイスや他のボードと順番に通信できます。

必要なハードウェアとソフトウェア

i
利用しているボードにより、I2Cバスで利用するピンは異なります。例えば、MKR WiFi 1010で使われるピンは、D11とD12です。一方、UNOでは、D18と19です。ボード上の正しいピンの見つけ方は、以下を参照してください。
UNOのI2Cピン

UNOのI2Cピン

MKR WiFi 1010のI2Cピン

MKR WiFi 1010のI2Cピン

チュートリアル

ArduinoボードでのI2Cの使い方に関する、詳細なステップごとのチュートリアルは、以下を参照してください。

i
上記のチュートリアルは、Nanoファミリーボード向けに書かれていますが、全てのArduinoボードに適用することができます。

コントローラリーダー

状況によっては、2つ(以上)のArduinoボードが、互いに情報を共有するように設定することが有効なことがあります。この例では、I2Cシリアル同期プロトコルを通じ、コントローラリーダー/ペリフェラルセンダー構成で、互いに通信するようにプログラムしています。これを実現するため、ArduinoのWireライブラリのいくつかの関数を使います。Arduino1(コントローラ)は、要求を出した後、固有のアドレス持つペリフェラルArduinoから、6バイトのデータを受信するようにプログラムされています。そのメッセージを受信すると、Arduinoソフトウェア(IDE)のシリアルモニタウインドウで、見ることができます。

コントローラリーダーのスケッチ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Wire Controller Reader
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Reads data from an I2C/TWI peripheral device
// Refer to the "Wire Peripheral Sender" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  Wire.requestFrom(8, 6);    // request 6 bytes from peripheral device #8

  while (Wire.available()) { // peripheral may send less than requested
    char c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
  }

  delay(500);
}

ペリフェラルセンダーのスケッチ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Wire Peripheral Sender
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Sends data as an I2C/TWI peripheral device
// Refer to the "Wire Master Reader" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
}

void loop() {
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Wire.write("hello "); // respond with message of 6 bytes
  // as expected by master
}

コントローラライター

状況によっては、2つ(以上)のArduinoボードが、互いに情報を共有するように設定することが有効なことがあります。この例では、I2Cシリアル同期プロトコルを通じ、コントローラライター/ペリフェラルレシーバー構成で、互いに通信するようにプログラムしています。これを実現するため、ArduinoのWireライブラリのいくつかの関数を使います。Arduino1(コントローラ)は、0.5秒ごとに、固有のアドレス持つペリフェラルArduinoに、6バイトのデータを送信するようにプログラムされています。そのメッセージを受信すると、Arduinoソフトウェア(IDE)を実行しているUSB接続したPCで開いた、ペリフェラルボードのシリアルモニタウインドウで見ることができます。

コントローラーライターのスケッチ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Wire Master Writer
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Writes data to an I2C/TWI Peripheral device
// Refer to the "Wire Peripheral Receiver" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
}

byte x = 0;

void loop()
{
  Wire.beginTransmission(4); // transmit to device #4
  Wire.write("x is ");        // sends five bytes
  Wire.write(x);              // sends one byte  
  Wire.endTransmission();    // stop transmitting

  x++;
  delay(500);
}

ペリフェラルレシーバーのスケッチ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Wire Peripheral Receiver
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Receives data as an I2C/TWI Peripheral device
// Refer to the "Wire Master Writer" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop()
{
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // loop through all but the last
  {
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  int x = Wire.read();    // receive byte as an integer
  Serial.println(x);         // print the integer
}

例1: 超音波距測センサ―

この例では、Devantech SRFxxという、I2C同期シリアルプロトコル経由で通信する超音波距測センサーのデータの読み方を示します。

必要なハードウェア

  • Arduinoボード
  • Devantech SRFxx超音波距測センサ―(SRF02かSRF08、SRF10)
  • 100μFのコンデンサー
  • ジャンプワイヤー
  • ブレッドボード

回路

SRFxxのSDAピンをボードのアナログピンの4番に、SCLピンをアナログピンの5番に接続します。SRFxxに5Vを給電し、電源を平滑化するために、100μFのコンデンサーを距測センサーに並列につなぎます。

Fritzingで書いた図

Fritzingで書いた図

回路図

コード

i
2つのSRFxxsを同一の信号線で利用するときは、同じアドレスを共有しないように保証する必要があります。距測センサーのアドレスを振りなおすためのスケッチは、コードの下の方に(コメントとして)書いてあります。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// I2C SRF10 or SRF08 Devantech Ultrasonic Ranger Finder
// by Nicholas Zambetti <http://www.zambetti.com>
// and James Tichenor <http://www.jamestichenor.net>

// Demonstrates use of the Wire library reading data from the
// Devantech Utrasonic Rangers SFR08 and SFR10

// Created 29 April 2006

// This example code is in the public domain.

#include <Wire.h>

void setup() {

  Wire.begin();                // join i2c bus (address optional for master)

  Serial.begin(9600);          // start serial communication at 9600bps
}

int reading = 0;

void loop() {

  // step 1: instruct sensor to read echoes

  Wire.beginTransmission(112); // transmit to device #112 (0x70)

  // the address specified in the datasheet is 224 (0xE0)

  // but i2c addressing uses the high 7 bits so it's 112

  Wire.write(byte(0x00));      // sets register pointer to the command register (0x00)

  Wire.write(byte(0x50));      // command sensor to measure in "inches" (0x50)

  // use 0x51 for centimeters

  // use 0x52 for ping microseconds

  Wire.endTransmission();      // stop transmitting

  // step 2: wait for readings to happen

  delay(70);                   // datasheet suggests at least 65 milliseconds

  // step 3: instruct sensor to return a particular echo reading

  Wire.beginTransmission(112); // transmit to device #112

  Wire.write(byte(0x02));      // sets register pointer to echo #1 register (0x02)

  Wire.endTransmission();      // stop transmitting

  // step 4: request reading from sensor

  Wire.requestFrom(112, 2);    // request 2 bytes from peripheral device #112

  // step 5: receive reading from sensor

  if (2 <= Wire.available()) { // if two bytes were received

    reading = Wire.read();  // receive high byte (overwrites previous reading)

    reading = reading << 8;    // shift high byte to be high 8 bits

    reading |= Wire.read(); // receive low byte as lower 8 bits

    Serial.println(reading);   // print the reading

  }

  delay(250);                  // wait a bit since people have to read the output :)
}

/*

// The following code changes the address of a Devantech Ultrasonic Range Finder (SRF10 or SRF08)

// usage: changeAddress(0x70, 0xE6);

void changeAddress(byte oldAddress, byte newAddress)

{

  Wire.beginTransmission(oldAddress);

  Wire.write(byte(0x00));

  Wire.write(byte(0xA0));

  Wire.endTransmission();

  Wire.beginTransmission(oldAddress);

  Wire.write(byte(0x00));

  Wire.write(byte(0xAA));

  Wire.endTransmission();

  Wire.beginTransmission(oldAddress);

  Wire.write(byte(0x00));

  Wire.write(byte(0xA5));

  Wire.endTransmission();

  Wire.beginTransmission(oldAddress);

  Wire.write(byte(0x00));

  Wire.write(newAddress);

  Wire.endTransmission();

}

*/

例2: デジタルポテンショメーター

必要なハードウェア

  • Arduinoボード
  • AD5171デジタルポテンショメーター
  • LED
  • 680Ωの抵抗
  • 4.7kΩの抵抗2個
  • フックアップワイヤー
  • ブレッドボード

回路

AD5171の3番ピンと6番ピン、7番ピンをGNDに、2番ピンと8番ピンを5Vに接続します。

4番ピン(デジタルポテンショメーターのクロックピン(SCL))をArduinoのアナログの5番ピンに、5番ピン(データ線(SDA))を、アナログピンの4番に接続します。SCLとSDAの双方に、4.7kΩのプルアップ抵抗を接続し、どちらも5Vに接続します。

最後に、LEDを680Ωの抵抗を介してピン1(AD5171のワイパー)に接続します。

Fritzingで書いた図

Fritzingで書いた図

AD5171の6番ピン(ADO)がGNDに接続しているので、アドレスは44です。別のデジタルポテンショメーターを同じSDAバスに追加するには、2つ目のデジタルポテンショメーターのAD0ピンを+5Vに接続することで、アドレスは45に変わります。

同時には2つのポテンショメータだけが利用可能です。

回路図

コード

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// I2C Digital Potentiometer
// by Nicholas Zambetti <http://www.zambetti.com>
// and Shawn Bonkowski <http://people.interaction-ivrea.it/s.bonkowski/>

// Demonstrates use of the Wire library
// Controls AD5171 digital potentiometer via I2C/TWI

// Created 31 March 2006

// This example code is in the public domain.

// This example code is in the public domain.


#include <Wire.h>

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
}

byte val = 0;

void loop()
{
  Wire.beginTransmission(44); // transmit to device #44 (0x2c)
                              // device address is specified in datasheet
  Wire.write(byte(0x00));            // sends instruction byte  
  Wire.write(val);             // sends potentiometer value byte  
  Wire.endTransmission();     // stop transmitting

  val++;        // increment value
  if(val == 64) // if reached 64th position (max)
  {
    val = 0;    // start over from lowest value
  }
  delay(500);
}

オリジナルのページ

https://docs.arduino.cc/learn/communication/wire

最終更新日

December 10, 2022

inserted by FC2 system