本記事の概要
前回の記事から、センシングデバイスをI2C通信で制御する知見を得るため、I2C通信規格について学ぶとともに、ZynqとWio TerminalのマイコンATSAMD51P19とのI2C通信に挑戦しています。
前回の記事へのリンク
本記事の概要
前回までの記事で
- Wio TerminalとZyboの接続
- Zynqで動かすソフトウェアの土台となるハードウェア構成
まで構想・構築ができたので、次にZynqで動かすソフトウェアを作ります。
ソフトウェアはすでにVitisで提供されているサンプルプログラムxiicps_intr_slave_example.cを用いました。
本記事では、サンプルプログラムの動作原理について調べた内容を解説していきます。
前回の記事と今後の記事では、以下の順に解説をしています。
- I2C通信の概略とデバイス間の接続方法
- Zynqにおけるブロックデザイン
- サンプルプログラムxiicps_intr_slave_example.cにおけるドライバAPIの動作原理(←本記事)
- Wio Terminalの制御コード(Arduino)
Vitis IDEでのアプリケーションの構築
workspaceの立ち上げ
前回の記事でXSAファイルの作成までできたので、XSAファイルからVitisのworkspaceを作成します。
workspaceの作成方法は、Hello worldとLED点滅回路と同様なので省略します。
以下の記事を参考にしていただければと思います。
Vitis IDEが立ち上がったら、アプリケーションプロジェクトを作成します。
今回は、ゼロから作るのではなく、Vitis IDE上にすでに用意されているサンプルプログラムを活用しようと思います。
サンプルプログラムxiicps_intr_slave_example.cを利用する
別の記事にまとめたのですが、 Vitis IDE上には各APIの機能説明してあるドキュメンテーションへのリンクや用例へのリンクがすでに用意されています。
こちらの記事で、I2C通信のペリフェラルに関連するドライバのドキュメンテーションと用例の参照方法を解説しているので、ご覧いただければと思います。
I2Cペリフェラルを制御するドライバに関してのドキュメンテーションを参照したところ、ZynqをI2Cのスレーブ機器として扱うサンプルコードには、以下の2つがあるようです。
今回は、Wio Terminalのマイコンに主導権を握らせて、I2C通信をさせたかったので、割り込みを使う xiicps_intr_slave_example.c を利用します。
xiicps_intr_slave_example のアプリケーションプロジェクトをインポートし、このプロジェクトをそのままビルドして使用しました。
githubにも同様のドキュメンテーションと用例のアプリケーションがあるので、Vitisを立ち上げずにすぐに確認したい方はこちらを参照するのもよいでしょう。
サンプルプログラムにおける処理の解説
単にサンプルプログラムを読み込んだだけでは、あまり理解した気にならないので、用例のソースコードを読み解いていくことにしました。
ソースコードには英語で丁寧な注釈が記載されているので、それを補足するような図解が主になると思います。
動作概要
まず、サンプルプログラムでどういう処理を行っているのか確認しておきます。
xiicps_intr_slave_exampleでは、外部のマスターデバイスからの要求を受けると、
- まず、数列0, 1, …, TEST_BUFFER_SIZE-1を順にマスターデバイスへ送信
- 次に、全く同じデータをマスターデバイスの方から受信することを期待
- 一致したら成功
という一連の処理を行います。
Aardvark社のテスト装置で動作確認できるとのことですが、持っていないので同じことをWio Terminalにやらせる予定です。
次回の記事で掲載予定のWio Terminalのプログラムでは、このシーケンスに従った処理をArduinoスケッチに記述していきます。
処理内容
ソースコードのフローチャート
xiicps_intr_slave_exampleにおけるメインの関数は、IicPsSlaveIntrExampleという関数です。
関数 IicPsSlaveIntrExample の一連のシーケンスは以下のフローチャートの通りです。
初期設定
まず、初期設定として、
- I2Cペリフェラルのインスタンスの初期化
- 割り込みの設定
- I2Cペリフェラルの設定
- 送信バッファと受信バッファの初期化
を順に行っています。
以前、LED点滅回路のタイマー割り込みの記事でインスタンスの概念と割り込みの方法について解説したので、割り込みについて詳しく知りたい方はぜひこの記事もご覧いただければと思います。
I2Cペリフェラルのインスタンスの初期化
まず、I2Cペリフェラルのインスタンスの初期化を行い、アプリケーション上で定義したIicインスタンスと、実際のデバイス上で用意されているI2Cペリフェラルとを紐づけ、IicインスタンスのAPI関数だけでペリフェラルを制御できるようにしています。
割り込みの設定
次に、割り込みの設定を行っています。
関数SetupInterruputSystemが別に定義されており、この関数で必要な設定をすべて行っています。
上で示した図のように、 関数SetupInterruputSystemの処理を実行すると、
- 割り込み命令を受けると…
- GICインスタンスの割り込みハンドラに処理を渡して、
- XiicPS_SlaveInterruptHandlerというAPI関数を呼び出す
という割り込みの設定がなされます。
ポイントは、XiicPS_SlaveInterruptHandlerというAPI関数はどういった処理を行っているのかです。
この関数はスレーブモードで使われる関数で、I2Cペリフェラルにおいてどういったイベントがトリガーとなって割り込みが発生したのかを分類してくれます。
関数の最後で別のハンドラ(関数)を呼び出し、イベントごとに異なった処理を行わせることが可能です。
xiicps_intr_slave_exampleでは、受信完了(RecvComplete)と送信完了(SendComplete)とその他の3イベントに分類して、その後の処理を変えています。
I2Cペリフェラルの設定
割り込みの設定が完了したら、次はIiCインスタンスの各種設定を行い、I2Cペリフェラルを制御します。
I2Cペリフェラルに、以下の3つを設定しています。
- Status Handlerの割り付け(Handlerという別に定義した関数を割り付け)
- スレーブモードとスレーブアドレスの登録
- クロック周波数400kHz
/*
* Setup the handlers for the IIC that will be called from the
* interrupt context when data has been sent and received, specify a
* pointer to the IIC driver instance as the callback reference so
* the handlers are able to access the instance data.
*/
XIicPs_SetStatusHandler(&Iic, (void *) &Iic, Handler);
XIicPs_SetupSlave(&Iic, IIC_SLAVE_ADDR);
/*
* Set the IIC serial clock rate.
*/
XIicPs_SetSClk(&Iic, IIC_SCLK_RATE);
IIC_SLAVE_ADDR(今回は0x45)はZynqのスレーブアドレスで、次回紹介するWio Terminalのプログラムでは、マスター側はこのスレーブアドレスと通信します。
送信バッファと受信バッファの初期化
最後に、データを送受信するための送信バッファと受信バッファの値を初期化していました。
送信バッファに「数列0, 1, …, TEST_BUFFER_SIZE-1」を格納し、受信バッファにゼロを格納します。
以上で初期設定が完了です。
データ転送
次は、データ伝送です。
まず、送信設定を行い、マスター機器への送信が完了したら、次に受信設定を行います。
割り込みを利用したデータ送信
XIicPs_SlaveSendというAPI関数によって、I2Cで送信したい配列を指定しています。
ドライバに用意されているいくつかのAPI関数では、必要なI2Cペリフェラルのレジスタ設定をすべて行ってくれるのでとても有用です。 XIicPs_SlaveSend は、割り込みをトリガーとしてスレーブからデータ送信を行いたい場合のレジスタ設定を行います。
送信完了のイベントが割り込みによって発生したら、whileループを抜けてデータの受信を待ちます。
割り込みを利用したデータ受信
次に、データ受信モードの設定に入ります。
XIicPs_SlaveRecvというAPI関数によって、受信モードへのレジスタ設定と受信したデータを格納するためのバッファを指定しています。
ここでも、受信完了のイベントが割り込みで発生するのをwhileループで待ちます。
受信を完了したら、送信バッファと受信バッファを比較して、一致するか否かを判定して終了です。
まとめ
本記事では、ZynqのI2Cペリフェラルをスレーブモードで制御するアプリケーションを読み込み、アプリケーションでの処理について調べた内容をまとめました。
Vitisでは、非常に多くのAPIが用意されていて、使いこなす・理解するにはある程度の慣れが必要です。
一方、ドキュメンテーションと用例を見つけることができれば、注釈や解説は比較的丁寧に書かれているので、それを活用すれば楽にアプリケーションを構築できます。
お疲れさまです。
次回の記事では、Wio Terminalに作ったマスター側のプログラムの解説をしたのち、実際の動作を確認した結果を掲載します。
最後までご覧いただきありがとうございました!
コメント