公開日:2020/09/26
title.png

12th Season


#12-9 DUEのI2C通信

 DUEのI2C通信を試してみたいと思います。
 DUEのボードコンポーネントを確認した時、librariesディレクトリの中にWireライブラリの姿が見えましたので、UNOと同じ様に使用できるものと推定できます。また、DUEにはI2Cユニットが2ユニット搭載されていて、Wire、Wire1の2つのインスタンスが使用できるはずですので、その辺りの検証もしてみます。

DUEのI2C調査

 まず、DUEの回路図を確認しておきましょう。
 DUEの回路図は、Arduino HPの「RESOURCES ⇒ PRODUCTS ⇒ ARDUINO DUE ⇒ DOCUMENTATION ⇒ SCHEMATIC」からダウンロードすることが出来ます。

 DUEのMCU、SAM3X8EのI2C端子は、以下の様に記載されています。

回路図表記   Arduino   Wireライブラリ
ピン番号 ポート 名称 ピン番号 ポートに割り当てられた機能 インスタンス
9 PA17 SDA1   D70 PA17(TW0_SDA/SCK0)   Wire1
70 PA18 SCL1 D71 PA18(TW0_SCK/A20/WKUP9)
86 PB12 SDA0-3 D20 PB12(TW1_SDA/PWMH0/ADC8) Wire
87 PB13 SCL0-3 D21 PB13(TW1_SCK/PWMH1/ADC9)

 ちょっと混乱しますが、SAM3X8EのI2Cチャンネル0が回路図ではSCL1,SDA1と、表記されていて、SAM3X8EのI2Cチャンネル1が回路図ではSCL0,SDA0となっています。てんこしゃんこ、です。尚、回路図の表記0/1が、そのままWireライブラリのインスタンス(Wire/Wire1)に対応しています。

 DUEでのI2C端子の配置ですが、以下図の様になっています。

a129_i2cpin.jpg

 DUEの回路図に戻って、I2C端子を追いかけて行くと、SCL0-3,SDA0-3の端子には、ボード上にプルアップ抵抗が付いていました(下図)。

a129_i2cpu.png
※Arduino DUE回路図から抜粋して引用

 SCL1,SDA1には付いていませんでした。従って:

 本来I2Cバスライン上には、必ずプルアップ抵抗が必要なのですが、I2Cマスタのボード上にプルアップ抵抗が付いていなかった場合、外付けにプルアップする必要があって面倒です。と言って、複数のモジュールが同じことを考えて皆プルアップしていると、合成抵抗がどんどん小さくなって、ちょっと不安です。消費電流も大きくなります。
 そのため、プルアップ付きのWireとプルアップ無しのWire1をユーザが使い分けられるような仕様なのか?いずれにせよ、上の通りとなっています。Wire1を使う場合には、外付けにプルアップ抵抗が必要ですので、ご注意ください。

※UNOのWireライブラリでは、Wire.begin()した時に、I2C端子のI/OポートにINPUT_PULLUP属性を付けて、ATmega328の内蔵プルアップ抵抗を使っていました。UNOではソフトウェアでINPUT_PULLUPを無効化できたのですが、DUEのWire側プルアップ抵抗はボード上に実装されているので無効化できません。

試行に使うプログラム

 「#3-2 Arduino間I2C通信を試す」にて、UNOとMicro間でI2C通信を試した時のプログラム(test_i2c.ino)を使います。まず、単純にターゲットをDUEに変更してコンパイルしたところ、ノーエラーでコンパイルできてしまいました。素晴らしい\(1^o^)/。
 UNOと同じ作法で、UNOと同じソースコードが使えるようです。ちゃんと期待通りに動作すれば、今までの経験で培ってきたノウハウでDUEのI2Cを使うことが出来ます。次節の実験で確かめてみましょう。

 その前に、DUEのWire1インスタンスを選択可能な様に以下の様な#defineを追加してソースを改定しておきました。

  #define WIRE Wire  // DUE: select Wire or Wire1

 デフォルトでは、Wireインスタンスを使用するようになっていますので、DUEでWire1インスタンスを使って通信したい場合には、上の#defineにWire1を指定してコンパイル&書き込みしてください。

 修正版のプログラムをリンクしておきます。

  実験用プログラム:test_i2c.ino

 いつもの様にオリジナルが差し替えてあります。

 コンパイル&書き込みすると、シリアルモニタからコマンドを受け付けるようになっています。コマンドについてはこちらで説明してあります。

※蛇足ですが、このプログラム(test_i2c.ino)は、STM32ではコンパイルが通りません。STM32の場合、I2Cをスレーブで使う場合には、"Wire_slave.h"を#includeする必要があるためです(作法が異なる)。

実験1:DUE(I2Cマスタ)とUNO(I2Cスレーブ)通信

 実験してみましょう。
 まずは、DUEをI2Cマスタに設定し、I2Cスレーブに設定したUNOとの通信を試してみます。DUE、UNO共に同じプログラム(test_i2c.ino)をコンパイル&書き込みしておいてください。共に「#define WIRE Wire」としておきます。
 以下の様に接続します。

Arduino DUE 接続 L/S 接続 Arduino UNO
D4 GND    
3.3V LV
D20(SDA) LV1
D21(SCL) LV2
GND GND GND
  HV 5V
HV1 A4(SDA)
HV2 A5(SCL)
  × D4

 DUEのD4=GNDは、DUEをI2Cマスタとして使うための設定です。D4はINPUT_PULLUPに設定されているので、普通にdigitalRead()すると、HIGHが読めます。以下コードの様にHIGHが読めたArduinoはI2Cスレーブ、LOWが読めたArduinoはI2Cマスタとして機能するようにプログラムされています。

  const int pin_MTSV=4;  // master / slave select
  // initialize
  pinMode (pin_MTSV, INPUT_PULLUP);
  mst0_slv1=digitalRead (pin_MTSV);


 DUEのD4端子をGNDに落とすことでLOWが読み出され、DUEがI2Cマスタとなります。以下実験では、I2Cマスタに設定した側のシリアルモニタを使います。UNOのD4は未接続(開放)としておきます。

 なお、DUE(3.3V定格)とUNO(5V定格)でレベルが合いませんので、以下の様な双方向タイプのL/Sを間に挟む必要があります。

ar47_lvshft.jpg


 以下、実験中の様子です。

a129_expr.jpg

  実験サンプル:【 】内はDUEシリアルモニタからの送信データ
  【snd,hello, uno. i am due】
  OK: 2030[us] sent: hello, uno. i am due

 【snd】コマンドは、I2Cマスタ(DUE)からI2Cスレーブ(UNO)へデータ(文字列)を送信するコマンドです。I2Cスレーブは、データを受け取ると一旦内部バッファに蓄えています。

  【rcv】
  OK: 3264[us] recv: hello, uno. i am due・・・・・・・・・・・・・・・・

 【rcv】コマンドは、I2Cスレーブ(UNO)からデータ(文字列)を受信するコマンドです。I2Cスレーブ(UNO)は、直前の【snd】コマンドで受け取ったデータをオウム返ししてきます。
 受信データの後ろに付いたごみについては、こちらで解説してあります。想定通りの動作で問題ありません。

DUEのWire1も使っておこう

 以上で、DUEをI2Cマスタとして使い、Wireインスタンスを使った通信が確認できました。続いてWire1インスタンスの動作も確認しておきます。
 DUEの方だけ、「#define WIRE Wire1」に変更して、プログラム(test_i2c.ino)をコンパイル&書き込みします。更に、DUEの接続を変更します(↓)。

Arduino DUE 接続 L/S 接続 Arduino UNO
D4 GND    
3.3V LV
D70(SDA) LV1
D71(SCL) LV2
GND GND GND
  HV 5V
HV1 A4(SDA)
HV2 A5(SCL)
  × D4

 変更箇所をピンク色に着色しておきました。その他の接続は同じです。Wire1側にはオンボードのプルアップ抵抗が付いていないので、外付けにプルアップ抵抗が必要なのですが、今回は、双方向タイプのL/Sを接続しているため、自動的にプルアップされています。

  実験サンプル:【 】内はDUEシリアルモニタからの送信データ
  【snd,hello, uno. i am due-Wire1】
  OK: 2606[us] sent: hello, uno. i am due-Wire1

  【rcv】
  OK: 3374[us] recv: hello, uno. i am due-Wire1・・・・・・・・・・・・・・・・

 正しく通信できています。動作確認終了、正常動作を確認しました。

 手持ちのモジュールだと、LCDサブシステムI/Oエクスパンダ(PCF8574)、DA変換器(MCP4725)、9軸加速度・ジャイロ・磁気センサ(MPU9250)等がI2C接続ですので、DUEからも制御が出来そうです。

実験2:UNO(I2Cマスタ)とDUE(I2Cスレーブ)通信

 次は、UNOをI2Cマスタとして使い、DUEがI2Cスレーブとして動作することを確認しておきます。
 DUEのプログラム(test_i2c.ino)を「#define WIRE Wire」に戻して、コンパイル&書き込みしてください。UNOの方は、前節の実験のまま変更有りません。

 以下の通りに接続します。

Arduino DUE 接続 L/S 接続 Arduino UNO
D4 ×    
3.3V LV
D20(SDA) LV1
D21(SCL) LV2
GND GND GND
  HV 5V
HV1 A4(SDA)
HV2 A5(SCL)
  GND D4

 変更箇所をピンク色に着色しておきました。GNDに落としてあったDUEのD4端子は、GNDとの接続を切断しておきます。今度はDUEのD4端子からHIGHが読み出されるため、DUEがI2Cスレーブとなり、UNOがI2Cマスタとなります。以下実験では、I2CマスタであるUNOのシリアルモニタを使います。

  実験サンプル:【 】内はUNOシリアルモニタからの送信データ
  【snd,hello, due. i am uno】
  OK: 2132[us] sent: hello, due. i am uno

  【rcv】
  OK: 3204[us] recv: hello, due. i am uno・・・・・・・・・・・・・・・・

 スレーブとしての動作も、UNOの時と全く同じ作法でプログラミングできて、正常に動作できることが確認できました。


 外部モジュールの制御にも使用可能ですが、I2Cスレーブにもなれるので、今回の様にMCU間の通信にも使用可能です。MCU間通信だと非同期シリアル(UART)とどっちを使うか迷う所ではありますが、I2Cはバス接続なので1本のバスに複数のマスタやスレーブを接続できるのが最大の強みです(UARTだと一対一通信に限られる)。何にせよ、UNOと同じ作法で使えるのはGood👍でした。


btn_arduino.png

btn_prev.png btn_next.png

home