本記事の概要
前回の記事から、センシングデバイスをI2C通信で制御する知見を得るため、I2C通信規格について学ぶとともに、ZynqとWio TerminalのマイコンATSAMD51P19とのI2C通信に挑戦しています。
前回の記事へのリンク
本記事の概要
前回までの記事で
- Wio TerminalとZyboの接続
- Zynqで動かすソフトウェアの土台となるハードウェア構成
- Zynqアプリケーションプロジェクトの読み込み
まで完了しました。
最後に、Wio Terminalで動かすソフトウェアのArduino版を作ります。
前回の記事と今後の記事では、以下の順に解説をしています。
- I2C通信の概略とデバイス間の接続方法
- Zynqにおけるブロックデザイン
- サンプルプログラムxiicps_intr_slave_example.cにおけるドライバAPIの動作原理
- Wio Terminalの制御コード(Arduino)(←本記事)
ソースコード例
#include "Wire.h"
int state;
int count;
enum StateType{
STATE_WAIT,
STATE_READ,
STATE_WRITE,
STATE_IDLE
};
#define ZYNQ_ADDR 0x45
#define IIC_SCLK_RATE 400000
#define DATA_NUM 250
void setup() {
Wire.begin(ZYNQ_ADDR); // join i2c0 bus (pin #3,5)
Wire.setClock(IIC_SCLK_RATE);
Serial.begin(9600); // start serial for output
state = STATE_WAIT;
}
void loop() {
char buf[DATA_NUM];
char c;
int ret;
switch (state){
case STATE_WAIT:
Serial.print("----------------------------\n");
Serial.print("Request message from wio to zynq\n");
Wire.requestFrom(ZYNQ_ADDR, DATA_NUM);
delay(500);
if(Wire.available() > 0){
state = STATE_READ;
Serial.print("Read from zynq to wio\n");
count = 0;
}else{
state = state;
}
break;
case STATE_READ:
if(count == DATA_NUM){
Serial.print("read finish!\n");
Serial.print("----------------------------\n");
Serial.print("Write from wio to zynq\n");
state = STATE_WRITE;
count = 0;
}else if( count < DATA_NUM ){
c = Wire.read(); // receive one byte as characterif
buf[count] = c;
Serial.print((int)(buf[count]));
Serial.print(",");
state = state;
count++;
}
break;
case STATE_WRITE:
Wire.beginTransmission(ZYNQ_ADDR); // transmit to device #4
Wire.write(buf, 250); // send to zynq
ret = Wire.endTransmission(); // stop transmitting
if(ret == 0){
Serial.print("write finish!\n");
Serial.print("----------------------------\n");
state = STATE_IDLE;
}else{
if(ret == 1){Serial.print("Error 01: Data length exceeds to buffer length!\n");}
else if(ret == 2){Serial.print("Error 02: No response to address send!\n");}
else if(ret == 3){Serial.print("Error 03: No response to data send!\n");}
else if(ret == 4){Serial.print("Error 04: others\n");}
state = state;
}
break;
case STATE_IDLE:
state = state;
break;
default:
state = state;
break;
}
}
ソースコードの解説
動作概要
前回の記事で、Zynq(スレーブ)の動作について解説しました。
今回は、Zynqの動作に応答するようにWio Terminal(マスター)のプログラムを作成しました。
Wio Terminalの動作は、以下のシーケンスに従うようにしています。
- Zynqのスレーブアドレス(Zynqで設定したIIC_SLAVE_ADDR=0x45)にデータを要求
- スレーブから受信したデータをバッファbuf[]に格納
- 受信完了後、buf[]の値をそのまま送信
この動作をもとにステートマシンを組みました。
状態遷移図
状態遷移図を図のように設計しました。
状態 STATE_WAIT では、ZynqにI2C通信でのメッセージを要求(Wire.requestFrom)し、ZynqからのI2C信号の受信を待ちます。
ZynqとのI2C通信が確立し、読み取り可能なデータがあれば、STATE_READに遷移し、メッセージの受信を開始します。
ここで、Wire.available()は、read()関数で読み取ることのできるバイト数を返す関数ですので、ゼロより大きいと、読み取り可能なデータがあることを意味します。
なお、Wio TerminalにはもともとI2C通信のためのバスが2種類用意されています。
I2C1のポートを使ってI2C通信を行いたいので、ヘッダーファイルWire.hの関数を用いています。
状態 STATE_READ では、 Wire.read()で1バイトずつZynqからデータを読み出し、バッファに格納しています。読み出したいデータ数に達したら、状態STATE_WRITEに遷移します。
状態STATE_WRITEでは、Wire.beginTransmission()で今度はZynqにデータ送信することを伝え、Wire.write(buf, 250)で受信したデータをそのまま送信しています。
無事に送信が完了したら、状態STATE_IDLEに遷移し、処理を終了します。
動作確認
では、実際にZyboとWio Terminalを接続して、動作を確認しましょう。
構成は写真の通りで、Wio Terminalの40ピンとZyboのPmodとをジャンパーピンで接続しています。
別のホストPCで、Wio Terminalがきちんと信号を受信しているかを確認しています。
Wio TerminalとZyboの電源を入れて、プログラムを書き込んでいきます。
Wio TerminalへのArduinoスケッチの書き込み
まず、Wio TerminalへArduinoスケッチを書き込みました。
Wio Terminalへの書き込み方法は、Seeedstudioに詳しい解説が載っていますので割愛します。
実際に、正しく動作をしているか確認するため、TeraTermでシリアルの信号を見ています。
シリアルの信号は、Wio TerminalのUSBを経由してホストPCで確認することが可能です。
TeraTermを起動し、USBシリアルの通信信号を確認すると、このようにスケッチで書き込んだように、
case STATE_WAIT:
Serial.print("----------------------------\n");
Serial.print("Request message from wio to zynq\n");
Wire.requestFrom(ZYNQ_ADDR, DATA_NUM);
delay(500);
if(Wire.available() > 0){
state = STATE_READ;
Serial.print("Read from zynq to wio\n");
count = 0;
}else{
state = state;
}
break;
“Request message from wio to zynq”の文字列が、順次出力されます。
ただし、まだZyboにコンフィグレーションしていないため、スレーブ機器とのI2C通信ができていません。
Zynqへのコンフィグレーション
Vitis IDEで、Zyboにbitファイルのコンフィグレーション、プラットフォームプロジェクト、アプリケーションプロジェクトの書き込みなど、一括で行います。
アプリケーションプロジェクト上で右クリックをし、[Debug As]>[Launch Hardware]と選択します。
動作検証結果
そして、実行すると…
TeraTerm上では、Zynqから読み取った数列(0, 1, …, TEST_BUFFER_SIZE-1)がシリアル伝送されて表示され、その後Zynqへの書き込みが完了したことを示す文が表示されます。
スケッチでは以下の箇所が対応しています。
Serial.print((int)(buf[count]));
Serial.print(",");
Serial.print("read finish!\n");
Serial.print("----------------------------\n");
Serial.print("Write from wio to zynq\n");
Serial.print("write finish!\n");
Serial.print("----------------------------\n");
Zynqの方はどうでしょう?
Vitisのデバッグ画面では、以下の通りSuccessで完了していました。
Zynqも送信バッファと受信バッファの一致を確認できたようです。
まとめ
本記事では、Wio TerminalのArduinoスケッチを紹介した後、実際の動作検証まで完了した結果を紹介しました。
お疲れさまです。
本シリーズを最後までご覧いただきありがとうございました!
コメント