マイコン(Master)とZynq(Slave)をI2C通信する方法 (4)マイコンのソフトウェア構成(arduino)

Arduino
スポンサーリンク

本記事の概要

概要

I2C(Inter-Integrated Circuit)規格でマイコンとZynqをデータを通信する方法を解説。実験に用いるマイコンには、Wio TerminalのATSAMD51P19を選択。まず、マイコンの方をマスター(Manager, Master)として、スレーブ(Subordinary, Slave)であるZynqからデータを要求。
本記事では、Wio Terminalのマイコンで動作させるArduinoのスケッチを紹介した後、実際にI2C通信した結果を掲載しました。

前回の記事から、センシングデバイスをI2C通信で制御する知見を得るため、I2C通信規格について学ぶとともに、ZynqとWio TerminalのマイコンATSAMD51P19とのI2C通信に挑戦しています。

前回の記事へのリンク

本記事の概要

前回までの記事で

  • Wio TerminalとZyboの接続
  • Zynqで動かすソフトウェアの土台となるハードウェア構成
  • Zynqアプリケーションプロジェクトの読み込み

まで完了しました。
最後に、Wio Terminalで動かすソフトウェアのArduino版を作ります。

本記事の対象読者:以下の状況に直面している方
  • I2C通信の動作原理、構成について知りたい場合
  • ZynqのプロセッサからI2C通信を行うAPIドライバの使用方法を知りたい場合
  • Wio Terminalを使った、外部機器とのI2C通信の方法を知りたい場合

前回の記事と今後の記事では、以下の順に解説をしています。

  1. I2C通信の概略とデバイス間の接続方法
  2. Zynqにおけるブロックデザイン
  3. サンプルプログラムxiicps_intr_slave_example.cにおけるドライバAPIの動作原理
  4. 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の動作は、以下のシーケンスに従うようにしています。

  1. Zynqのスレーブアドレス(Zynqで設定したIIC_SLAVE_ADDR=0x45)にデータを要求
  2. スレーブから受信したデータをバッファbuf[]に格納
  3. 受信完了後、buf[]の値をそのまま送信

この動作をもとにステートマシンを組みました。

状態遷移図

状態遷移図を図のように設計しました。

状態 STATE_WAIT では、ZynqにI2C通信でのメッセージを要求(Wire.requestFrom)し、ZynqからのI2C信号の受信を待ちます。

ZynqとのI2C通信が確立し、読み取り可能なデータがあれば、STATE_READに遷移し、メッセージの受信を開始します。

ここで、Wire.available()は、read()関数で読み取ることのできるバイト数を返す関数ですので、ゼロより大きいと、読み取り可能なデータがあることを意味します。

Wire.available()

機能:Wire.read()で読み取ることができるバイト数を返す関数。
パラメータ:なし
返り値: 読み取り可能なバイト数

なお、Wio TerminalにはもともとI2C通信のためのバスが2種類用意されています。
I2C1のポートを使ってI2C通信を行いたいので、ヘッダーファイルWire.hの関数を用いています。

Wio TerminalのI2Cポート
  • I2C0: Wio Terminal内部の加速度センサ、背面の40ピンコネクタの27端子(SDA)・28端子(SCL)と接続。Arduinoでは[Wire1.h]で制御可能。
  • I2C1: 背面の40ピンコネクタの3端子(SDA)・5端子(SCL)、側面のGroveコネクタ(#1。左側のポート)と接続。Arduinoでは[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に詳しい解説が載っていますので割愛します。

Page Not Found | Seeed Studio Wiki

実際に、正しく動作をしているか確認するため、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スケッチを紹介した後、実際の動作検証まで完了した結果を紹介しました。

ひがし
ひがし

お疲れさまです。

本シリーズを最後までご覧いただきありがとうございました!

スポンサーリンク


参考:コードの動作を確認したシステムの構成

開発環境

環境
  • 開発用PC: Windows 10, 64bit
    • Vivado Design Suite – HLx Edition – 2020.2
    • Vitis コア開発キット – 2020.2
  • 開発用基板: Zybo Zynq-7010評価ボード(Board Rev.4)
    • Zynq XC7Z010-1CLG400C
  • 開発用マイコン: Wio Terminal
    • ATSAMD51P19

開発ボード Zybo Zynq-7010評価ボード

コメント