- 1回目: 開発環境の準備
- 2回目: Hello Worldプロジェクト
- 3回目: PSのGPIOでLチカ
- 4回目: PLのAXI GPIOでPSからLチカ
- 5回目: PLだけでLチカ
- 6回目: 自作IPでLチカ
- 7回目: ブートイメージを作る
- 8回目: Linux起動する
- 9回目: Linuxカーネルを少しカスタマイズする
- 10回目: LinuxのRootFSをカスタマイズする / PythonでHello World
- 11回目: LinuxユーザアプリケーションでLチカ
- 12回目: LinuxカーネルモジュールでLチカ
- 13回目: LAN(Ethernet 0)を使う
- 14回目: Linuxユーザアプリをデバッグする / RootFSに取り込む
- 15回目: Linux起動時にアプリケーションを自動実行させる
- 16回目: Linuxから自作IPをUIOで制御する
- 17回目: Linuxで自作IPのデバイスドライバを作る
- 18回目: IoT化してスマホからLチカ <--- 今回の内容
環境
- 開発用PC: Windows 10 64-bit
- Vivado 2017.4 WebPACKライセンス
- Xilinx SDK 2017.4 (C アプリケーションの実装)
- Visual Studio Code (Pythonスクリプトの実装)
- 開発用PC (Linux): Ubuntu 16.04 本家 (日本語版じゃない) (on VirtualBox 5.2.4)
- PetaLinux 2017.4
- ターゲットボード: ZYBO (Z7-20)
IoT化してスマホからLチカ
今回は最終回として、これまでやってきた内容を組み合わせて、スマートフォンからZYBO上のLEDやボタンを制御してみます。ラズパイでよくある、こういうやつです(ラズパイ + Bottleでお手軽にIoT環境を作る)。
デモ動画と作り方: https://youtu.be/z613J8RVpog
(Xilinxのブログで紹介されました!! (https://forums.xilinx.com/t5/Xcell-Daily-Blog/Hands-on-Watch-YouTube-video-maker-takeshi-i-create-an-IoT/ba-p/822967 ))
全体像
全体像は以下の通りです。
下から見ていきます。ZYBO Z7-20ボード上には、PLに接続されたLED x 4、ボタン x 4、スイッチ x 4、カラーLED x 2と、PSに接続されたLED x 1、ボタン x 2があります。PL側のLED、ボタン、スイッチの制御用に、myip
というIPを作ります。これは、AXI GPIOとほぼ同等ですが、練習のために自作IPとしました。また、カラーLEDのPWM制御用にmypwm
というIPを作成します。PS側のLEDはOSの機能でそのまま制御します。ボタンは今回は使いません。(1時間くらハマったけど、ボタンのGPIO956/957の制御が出来なかった)。
自作IPであるmyip
とmypwm
は、User space IO(UIO)として制御します。それぞれ、/dev/uio0
、/dev/uio1
になります。これは、デバイスツリーで設定します。
自作IPの制御用に、Cアプリケーション(myip_controller
)を作成します。入出力として名前付きパイプを持ちます。入力パイプで、LED制御コマンド(ON/OFFやカラーLEDのDuty比)を受け付けます。出力パイプで、ボタンとスイッチの状態を出力します。
最上位層にPythonスクリプト(ServerIoT
)がいます。ServerIoT
は、bottleを使用してサーバーとして動作します。WebAPIとして、setStatus
とgetStatus
をPOST(JSON)形式で提供します。setStatus
が呼ばれたら、PLデバイスの制御コマンドをパイプ経由でmyip_controller
に出力します。PSのLEDは直接制御します。getStatus
が呼ばれたら、パイプ経由でmyip_controller
からボタンとスイッチの状態を取得して、呼び元にJSON形式で返します。また、公開ルートパスにアクセスされたら、index.html
を出力します。このindex.html
がユーザにとってのビューになります。index.html
内にボタンなどが配置されており、ボタンが押されるなどのイベントに応じて、main.js
というJavaScriptを呼び出し、このJavaScriptが先ほどのWebAPIを呼び出します。
ポイント
実は、Cアプリケーションを省略して、Pythonだけで/dev/uio0,1
の制御をすることも可能です。今回は練習のために、あえてCアプリケーションで実装しました。
CとPythonの連携については、Boost.Pythonやctypesを使用することもできます。この場合、C側は共有ライブラリ(.so)になります。個人的にこのパターンは以前やったことがあるのと、開発が面倒(soのテストアプリが必要)なので、今回はやめました。
代わりに、名前付きパイプを使用して、文字列でやり取りします。これによって、Python側とCアプリケーションがバイナリ的に疎結合になり、テストが非常にやりやすくなります。C側の開発をするときは、入出力がパイプなので、コンソールからのcatやechoで動作確認できます。Python側も同様です。さらに、入出力がパイプになることで、ハードウェア制御が無くなり、ホストPC上で開発/確認ができます。(ただし、パイプを使うので、Windows コマンドプロンプトだと不可。また、PS GPIOの確認はできない)
各ブロックの責務と詳細
- ServerIoT
- Pythonスクリプト。WindowsPCで開発。デバッグはmsysとZYBO実機
- サーバーとして、WebAPIとビューを提供する
- WebAPI(setStatus)で指示されたとおりに、名前付きパイプにLED制御コマンドを出力。PS GPIOは自分で制御する
- WebAPI(getStatus)を受けたら、名前付きパイプからボタンとスイッチの状態を取得する。その後、JSON形式にして呼び元(main.js)に返す
- myip_controller
- C++アプリケーション。WindowsPC上のXilinx SDKで開発/デバッグ
- 自作IP(
/dev/uio0
、/dev/uio1
)の制御
- Linuxイメージ (カーネル/RootFS)
- Ubuntu上のPetaLinuxで作成
- PythonとC++ライブラリをインストール
- ServerIoTとmyip_controllerを含む。また、これらを起動デーモンとして開始する
- デバイスツリー設定(
system-user.dtsi
)によって、自作IPをUIOデバイスとして認識させる
- hdf
- ハードウェア情報 (PSの設定と、PLのビットストリーム)。
- Windows PC上のVivadoで開発
ソースコード
- Vivadoプロジェクト
- Xilinx SDKプロジェクト (
myip_controller
) - PetaLinux プロジェクト
- https://github.com/take-iwiw/ZYBO_IoT_PetaLinux
-
project-spec/meta-user/recipes-apps/myinit/files/
内に、以下ファイルを配置- Xilinx SDKで作成済みの
myip_controller
のバイナリ -
ServerIoT
用のPythonスクリプトとhtml,JavaScriptファイル一式 - init.dスクリプト
- Xilinx SDKで作成済みの
Build方法
ハードウェア(hdf)やビルド済みのバイナリは、既にPebaLinuxプロジェクトに含まれています。そのため、ビルドするだけであれば、PetaLinux プロジェクト (https://github.com/take-iwiw/ZYBO_IoT_PetaLinux) だけあれば大丈夫です。
git clone https://github.com/take-iwiw/ZYBO_IoT_PetaLinux.git
cd ZYBO_IoT_PetaLinux/PjIoT
# 下記ファイルの`SERVER_URL`にZYBOのIPアドレスを設定する。コミットされているのは僕の環境の値 (192.168.1.87)
code project-spec/meta-user/recipes-apps/myinit/files/ServerIoT/static/js/main.js &
petalinux-build
petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/design_iot_wrapper.bit --u-boot
出来上がった、images/linux/BOOT.bin
とimages/linux/image.ub
をFAT32フォーマットのSDカードに書き込みます。その後、同じネットワーク内にあるブラウザからhttp://192.168.1.87:8080/
にアクセスします。
Pythonスクリプトを変更したい場合は、project-spec/meta-user/recipes-apps/myinit/files/ServerIoT/
を直接編集します。
Cアプリケーション(myip_controller
)を変更したい場合は、https://github.com/take-iwiw/ZYBO_IoT_SDK を取得して、Xilinx SDKで変更/デバッグします。出来上がったバイナリ(myip_controller.elf
)をPetaLinuxのproject-spec/meta-user/recipes-apps/myinit/files/
にコピーします。
ハードウェアデザインを変更したい場合は、https://github.com/take-iwiw/ZYBO_IoT_Vivado を取得して、変更後、hdfを出力します。その後、PetaLinux側にコピーして、petalinux-config --get-hw-description=.
した後、再ビルドします。
本プロジェクトの作り方
一からプロジェクトを始める際の、作業の流れを記載します。コードは省略します。代わりに、GitHubへのリンクを載せるようにします。
成果物ベースでのフローは以下のようになります。
1. ハードウェアの作成 (Vivado on Windows)
Vivadoで新規プロジェクトを作成します。以下のようなハードウェアを作り、ビットストリーム付きのhdfをエクスポートしておきます。
以後、ブロックデザイン名はdesign_iot
とします。そのため、出来上がるhdfはdesign_iot_wrapper.hdf
として説明/コマンドなどを記載します。
- PSを配置 (Ethernet0のMDIO接続を修正)
-
myip
を作成して、配置- コードは、https://github.com/take-iwiw/ZYBO_IoT_Vivado/tree/master/project_iot/ip_repo/myip_1.0/hdl
- 4-bit出力 x 1 (MY_OUT)と、4-bit入力 x 2 (MY_IN0、MY_IN1)
MY_OUT = slv_reg0[3:0];
slv_reg1[3:0] <= MY_IN0;
slv_reg2[3:0] <= MY_IN1;
- 名前はmyip_0
- I/O Ports設定 (制約ファイル)で、MY_OUTはLED、MY_IN0はボタン、MY_IN1はスイッチに接続する
-
mypwm
を作成して、配置- コードは、https://github.com/take-iwiw/ZYBO_IoT_Vivado/tree/master/project_iot/ip_repo/mypwm_1.0/hdl
- 出力 x 6 (PWM_OUT)
- slv_reg0[7:0] ~ slv_reg5[7:0]で各出力のDuty比を設定 (0 ~ 255)
- 名前はmypwm_0
- I/O Ports設定 (制約ファイル)で、PWM_OUTはカラーLEDのRGBに接続する (3色 x 2)
必要に応じて、この時点でシミュレーションを回して、ハードウェアが期待通りに動いているかを確認します。僕は面倒だったので、SDK上でstandaloneプロジェクトを作り、デバッグ画面でレジスタに読み書きして、期待通りの動作をしているかを確認しました。
完成したdesign_iot_wrapper.hdf
を、Ubuntu側にコピーしておきます。
2. ベースとなるLinuxイメージの作成 (PetaLinux on Ubuntu)
Ubuntu上で、新たにPetaLinuxプロジェクトを作ります。先ほど作成したdesign_iot_wrapper.hdf
を、~/work/peta/PjIoT
にコピーしておきます。そして、このハードウェア情報を基に、プロジェクトのコンフィグを行います。(ビルドはまだ)
cd ~/work/peta
petalinux-create --type project --template zynq --name PjIoT
cd PjIoT/
petalinux-config --get-hw-description=.
ビルドに進む前に、いくつかやることがあります。
まず、デバイスツリーを変更します。myip
とmypwm
をUIOデバイスとします。
/include/ "system-conf.dtsi"
/ {
chosen {
bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";
};
};
&myip_0 {
compatible = "generic-uio";
};
&mypwm_0 {
compatible = "generic-uio";
};
続いて、RootFSのコンフィグを変えて、必要なツールやライブラリをインストールします。menuconfig上で、pythonとcurlとlibstdc++をインストールします。念のためPythonは、全てのパッケージを入れておきます。(本当は、bottleが使っているのだけを入れるべき)。
設定が終わったら、ビルドします。
petalinux-config -c rootfs
# Filesystem Packages → devel → python → python
# Filesystem Packages → console → network → curl
# Filesystem Packages → misc → gcc-runtime -> libstdc++
petalinux-build
petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/design_iot_wrapper.bit --u-boot
完成したイメージ(images/linux/BOOT.bin
とimages/linux/image.ub
)をSDカード(FAT32)に入れて、ZYBOを起動します。pythonがインストールされていることを確認します。また、ifconfigでIPアドレスを確認しておきます。
3-a. Cアプリケーションの開発 (Xilinx SDK on Windows)
myip
(/dev/uio0
)とmypwm
(/dev/uio1
)を制御するアプリケーションを開発します。僕はクラスを使いたかったので、実際にはC++アプリケーションになります。
Xilinx SDK上で、メニューバー -> File -> New -> Application Projectで、以下のプロジェクトを作ります。
- ProjectName = myip_controller
- OS Platform = linux
- Processor Type = ps7_cortexa9
- Language = C++
- Linux System Root / Linux Toolchain = 非チェック
ここ(https://github.com/take-iwiw/ZYBO_IoT_SDK/tree/master/myip_controller )にあるようなコードを実装/ビルドします。その後、TCFでデバッグします。デバッグ方法についてはこちら(https://qiita.com/take-iwiw/items/8134ca7098622219b177 )を参考にしてください。
デバッグ開始前に、ZYBO上で、下記コマンドで名前付きパイプを作っておきます。
mkfifo pipe_c2p
mkfifo pipe_p2c
chmod 666 pipe_p2c
chmod 666 pipe_c2p
パイプによるインターフェースは以下のような文字列にします。
- pipe_p2c (Python → CアプリへのLEDの制御コマンド)
- "ON,OFF,ON,OFF,100,100,100,100,100,100"
- ↑はそれぞれ、LD0,LD1,LD2,LD3,R0,G0,B0,R1,G1,B1、に対応。Duty比は0~100
- pipe_c2p (Cアプリ → Pythonへのボタンとスイッチの状態通知)
- "{"btn0":"OFF", "btn1":"OFF", "btn2":"OFF", "btn3":"OFF", "sw0":"OFF", "sw1":"OFF", "sw2":"OFF", "sw3":"OFF"}"
そのため、ZYBOのターミナル上で、以下のようなコマンドによって動作確認をします。
echo "ON,OFF,ON,OFF,100,100,100,100,100,100" > pipe_p2c
cat pipe_c2p
このように、名前付きパイプと文字列によるシンプルなインターフェースを採用したため、Python側のコーディングの前に、ほぼ本番と同じ状態でのテストが可能となりました。
3-b. Pythonアプリケーションの開発
WebサーバーとなるPythonスクリプトを開発します。開発場所はどこでもいいです。僕は、最初はWindows上で開発していましたが、途中から動作確認は直絶ZYBOで行いました。Windows上でVSCodeでコーディング → SFTP(拡張機能)でZYBOに自動コピー → ZYBOで実行、という流れです。ただし、ZYBOを再起動したらファイルが消えるのでご注意ください。
コード一式は、こちら(https://github.com/take-iwiw/ZYBO_IoT_PetaLinux/tree/master/PjIoT/project-spec/meta-user/recipes-apps/myinit/files/ServerIoT )にあります。WebサーバやPythonなどの詳細はこの記事(https://qiita.com/take-iwiw/items/3f86281312646fd9d893 )をご参考にしてください。
見た目をよくするために、Bootstrapを取り込んでいます。また、Bottleは、bottle.pyをファイルとして直接配置しています。これは、ZYBO上で追加パッケージのinstallなどをしたくないためです(だから、1ファイルで済むbottleを使用することにしました)。
ある程度出来上がったら、先ほどのCアプリケーションを実行して、結合確認をしてみます。スマホやPCのブラウザから、http://192.168.1.87:8080/ にアクセスして、LED等の制御ができたらOKです。
4. 全てを含んだLinuxイメージの作成 (PetaLinux on Ubuntu)
ここまでの開発では、CアプリケーションはelfバイナリをTCFでデバッグ、PythonスクリプトはSFTPでアップロードしていました。今使用しているイメージでは、RootFSはinitramfsとして、DDR上にマウントされるので、再起動のたびに消えてしまいます。また、実行するのに、毎回手間がかかります。
必要なバイナリやスクリプトを含み、かつ、それらを起動時に自動実行するようにします。なお、Cアプリケーションについては、PetaLinuxでビルドするのではなく、Xilinx SDKでビルド済みの実行ファイルを取り込むことにします。
基本的には、15回目: Linux起動時にアプリケーションを自動実行させる、の通りです。以下コマンドで、myinitという起動用のinit.dデーモンを作ります。レシピと、shスクリプトを編集する必要があります。中身は、(https://github.com/take-iwiw/ZYBO_IoT_PetaLinux/tree/master/PjIoT/project-spec/meta-user/recipes-apps/myinit )をご確認ください。
petalinux-create -t apps --template install -n myinit --enable
code project-spec/meta-user/recipes-apps/myinit/myinit.bb &
code project-spec/meta-user/recipes-apps/myinit/files/myinit &
code project-spec/meta-user/recipes-apps/myinit/files/myinit_run.sh &
petalinux-create
でmyinit
を作ったら、Xilinx SDKでビルド済みのmyip_controller.elf
と、Pythonスクリプト一式をフォルダ(ServerIoT)ごと、project-spec/meta-user/recipes-apps/myinit/files/
にコピーしておきます。
レシピと、shスクリプトの編集と、ファイルのコピーが終わったら、下記コマンドで、image.ubを再作成します。
petalinux-build -c rootfs
petalinux-build -x package
メモ
本来、Cアプリケーション(自作IPの制御)とPythonスクリプト(サーバー)は、別々のデーモンとして動かすべきです。が、今回は横着をして、同一のスクリプト(myinit_run.sh
) 内で、Cアプリケーション(自作IPの制御)をバックグラウンドとして動かすことにしました。
なお、名前付きパイプの作成も、myinit_run.sh
内で行っています。
Pythonスクリプト(サーバー)の方は、1ファイルだけではなく、htmlなど、複数のファイルを含むディレクトリをインストールする必要があります。そのため、レシピ内で少し工夫が必要でした。(https://github.com/take-iwiw/ZYBO_IoT_PetaLinux/blob/master/PjIoT/project-spec/meta-user/recipes-apps/myinit/myinit.bb )。ただ、この方法で正しいかは不明。
動作確認する
作成したimage.ubを、SDカードに上書きコピーして、ZYBOを起動します。
すると、電源ONするだけで、http://192.168.1.87:8080/ のページが有効になり、LED制御などが出来るはずです。
おわりに
今回は最終回として、今までやってきた内容をほぼ全て詰め込んでみました。また、ZYBOをIoTっぽく使うときのベースとなるプロジェクトにできたと思います。
本連載はこれで終わりです。ツールのインストールから始まり、IPの作成、ベアメタル実装、Linuxまで、一通りの内容を抑えることが出来たと思います。