はじめに
今回は Zybo Z7-20 上で FreeRTOS を使ってみました。
前半は最小構成として 2つのタスクからUARTへメッセージを送るだけ の動作を確認します。
後半では、GEMを使ったEthernet通信を行うため、ミドルウェアのlwipを組み込み、取得したセンサ値をソケットで上位PCに通信したいと思います。
センサはIICの格安モジュール(BME280+AHT20)を使用します。
今回は、GPIO制御やPL連携まで広げるのではなく、最初に スケジューラが正しく動作しているか、複数タスクが切り替わっているか、UART出力が問題なく見えるか を確認することを目的にします。
今回使用したコードはgitで公開しています
前半の方針
構成はかなりシンプルです。
- FreeRTOSで2つのタスクを生成する
- 各タスクはUARTへ文字列を送信する
- 1秒ごとに
vTaskDelay()で待つ - 優先度は同じにする
確認したいのは以下の3点です。
-
vTaskStartScheduler()後にFreeRTOSが正常に動くこと - 複数のタスクが動作すること
- UARTに継続的にログが出ること
それではやっていきましょう!
PL(FPGA側)の設定
- まずPLの設定をvivadoで行います
- FPGA側の設定のポイントは「timer」を最低一つ追加することです
- この設定が無いと、PS側のvitisのOS設定でFreeRTOSを選択できません
- 詳しく見ていきます
block design にてzynq7 PSを追加
- まずは従来通りにzynq7 PSを追加します。「Apply Board Preset」を使ってデフォルトの設定にします
- これでzyboのハードに合わせた設定が一通り入ります
- 自動でUART、GEM、SDカード、SPIフラッシュなどが使えるようになります
timerを有効にする
- デフォルト設定では無効になっている、timerを有効にします
- PS側で制御できるtimerの「TTC」を有効にします
- USB1が割り当たっていましたが、使用しないのでTTCに変更します
※2個有効にしていますが1個でもOKです
センサ接続用(IIC)を追加
- データ取得用に追加します
- IICインタフェースはPS側に追加することもできますが、他に使いたい機能と被っていたのでPLで設定します
- AXI_IICを使用します。デフォルトではboard interfaceがHDMIになっているのでcustomに変更します
その他:ボード上のLED,SW,BTNを使用
- ボード上にBTNが4つ、SWが4つあり、AXI_GPIOでアクセス出来るようにします
- 割り込み端子としても設定します
- またLEDも4つありますので、こちらも違うAXI_GPIOでアクセス出来るようにします
割り込み設定
- SW,BTNの割り込みと、IICの割り込みをconcatで束ねてzynqのIRQ_F2Pに入力します
- xlconcat は複数の割り込み信号を1本にまとめるための IP です。
今回の構成では、SW/BTN の割り込みと AXI IIC の割り込みを xlconcat に入力し、まとめた信号を Zynq PS の IRQ_F2P へ接続しています。 - これにより、PS は複数の割り込み要因を受け取ることができ、ソフトウェア側で発生元を判別して処理します。
PLの全体像
以上でPL側の設定は完了です
- bitstreamを生成まで行ってください
- XSAを作成して、vitisでplatformを作成します
PS側(vitis)の設定
workspace をsetする
VitisにおけるWorkspaceとはプロジェクト(プラットフォーム・アプリケーションなど)をまとめて管理する作業フォルダです。
Vitis起動時に指定するディレクトリで、その中にソースコード・設定情報・ビルド結果などが整理されて保存されます。
-
operatingsystem: が standalone(ベアメタル)になっていると思いますので、freertosへ変更します
application を作成する
Vitisにおけるapplicationは、実際にZynqのCPU上で動作するソフトウェア(プログラム)そのものです。
C/C++で記述され、コンパイル後は実行ファイル(.elf)として出力されます。
-
finishまで進めます
-
下記の「使用したコード」をコピペします
使用したコード
- 今回使ったコードは以下です。
-
task1とtask2は、どちらも無限ループの中でxil_printf()を使って UART に文字列を送信しています。 - その後、
vTaskDelay(pdMS_TO_TICKS(1000))によって 1秒待機 します。 - このため、UARTターミナル上ではそれぞれのタスクから、一定周期でメッセージが出力される動作になります。
#include "FreeRTOS.h"
#include "task.h"
#include "xil_printf.h"
static void task1(void *pvParameters)
{
(void)pvParameters;
for (;;) {
xil_printf("hello! zybo from task1\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
static void task2(void *pvParameters)
{
(void)pvParameters;
for (;;) {
xil_printf("hello! zybo from task2\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
int main(void)
{
xTaskCreate(task1, "Task1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
xTaskCreate(task2, "Task2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
vTaskStartScheduler();
for (;;);
return 0;
}
サービスコール紹介
- RTOS(FreeRTOS)が提供するAPIをサービスコール(システムコール)と呼びます
- 今回はtask関連の以下のAPIを使用しています
xTaskCreate
-
main()ではxTaskCreate()を2回呼び出して、2つのタスクを生成しています。 - タスク名は
Task1/Task2 - スタックサイズは
configMINIMAL_STACK_SIZE - 優先度はどちらも
tskIDLE_PRIORITY + 1 - どちらのタスクも同じ優先度にしているため、FreeRTOSのスケジューラによって順番に実行されます。
vTaskStartScheduler
-
vTaskStartScheduler()を呼ぶことで、FreeRTOSのスケジューラが開始されます。 - ここまで正常に到達し、なおかつ必要なメモリや設定に問題がなければ、以後は各タスクが実行され続けます。
逆に、ここで動かない場合は以下を疑うことになります。
- FreeRTOSの設定不備
- ヒープ不足
- タスク生成失敗
- クロックやBSP設定の問題
書き込み
出力結果
まとめ
-
今回は Zybo Z7-20 上で FreeRTOS を動作させ、2つのタスクを生成して UART にメッセージを出力する最小構成を作成しました。
-
まずは FreeRTOS を利用するために PL 側で TTC Timer を有効化し、AXI IIC や GPIO、割り込み回路を含むハードウェア構成を作成しました。
-
その後、Vitis 上で FreeRTOS アプリケーションを作成し、タスク生成とスケジューラ起動を確認しました。
-
実行結果からは、同じ優先度の2つのタスクが交互に実行され、UART に継続してメッセージが出力されることを確認できました。
-
これにより、FreeRTOS の基本的なタスク管理とスケジューリングが正常に動作していることが分かります。
-
次回は今回準備した IIC や Ethernet の仕組みを活用し、BME280/AHT20 から取得したセンサデータを lwIP 経由で上位 PC へ送信する構成に発展させていきたいと思います。
























