タスク設計とは
今回の講座では、前回紹介した要件に対し、リアルタイムOSのタスク設計を行っていきます。
タスク設計といっても、難しい作業ではありません。機能ごとに分ける作業は誰でも一度は経験があることです。それに、万人に対する正解があるわけではなく、1番念頭に置いておくべきことは、作りやすいか?メンテナンスしやすいか?です。
タスク設計というかRTOSを使った組み込みアプリを開発する前に、RTOSの機能を復習しておきましょう。
組み込みシステム開発の要件
さて、今回の組み込みシステム開発の要件下記内容のものでした。
- RZ/Aの内部にあるADCのデータを10MHzの間隔でサンプリング。
- 取得したデータを100サンプル毎に平均化。
- 平均化したデータをUARTに出力。
- ストップボタンが押されたら、すべてのタスクをストップ。
- スタートボタンが押されたら、すべてのタスクをスタート。
- ボタンが押された時は、UARTにメッセージを出力。
- UARTからの入力は、時刻の設定のみ。
- 1分毎に、現在の時間を表示。
- 一定時間以内でWDTを更新。
- WDTが更新されなかった場合、システムをReset。
図1のような構成を組んでみました。このように何をタスクにするのか、何を関数で実装するのかが、今後のアプリケーション開発では重要なポイントです。
このことから、以下のRTOSのリソースが必要だと言えます。
- タスク 4つ
- イベントフラグ 2つ
- メールボックス 2つ ※図中では、メッセージと表記
- 割り込みサービスルーチン 2つ
- 周期ハンドラ 2つ
この内容をSOLIDに反映していこうと思います。
ADCの機能(要件1)
RZ/Aの内部にあるADCの機能を使用します。ADCのデータを10MHzの間隔でサンプリングして、取得します。機能的にも大きな処理になるので、これはタスク化すべき処理になります。ADCの初期化や設定などが必要になり、サンプリング間隔も100usと速いため、DMAを併用することも検討してみる必要がありそうです。
この機能の実装には、下記のドライバが必要になるでしょう。
adc_init()
ADCの初期化関数adc_cfg()
ADCの設定peri_clk_cfg(PERI_CLK_10M)
周辺機能用のクロック設定get_adc_data()
ADCのデータ取得isr_adc()
ADC割り込み
プロジェクトファイルの追加
SOLID-IDEは、ソースコードを「プロジェクト」で管理することができます。プロジェクトの中には、「サブプロジェクト」単位でアプリケーション及びモジュールを管理することができます。これら「プロジェクト」及び「サブプロジェクト」をまとめたものが、「ソリューション」という単位で管理されます。
動画では、SOLID-IDEにファイルを追加する方法を解説しています。図2のようにソリューション及びプロジェクトから右ボタンメニューで簡単に追加することができます。タスクにした時は、ファイルも分けてプロジェクトまたはサブプロジェクトに追加して管理すると、メンテナンスも容易になります。ファイルの依存関係もわかりやすくなります。
サンプル毎に平均化(要件2)
取得したデータを100サンプル毎に平均化。この処理は、100個のデータの平均化だけなので、これはタスクである必要はなさそうです。なので、今回は単純な100個のデータ配列入力パラメータとして、平均化した値を出力する関数でいいと思います。下記は、これを実装した例です。平均化を行うと、どうしても小数点が発生しますので、float型を使用します。平均化は、2つの値を加算して2で割るという易しいアルゴリズムではありますが、100個の値を加算して100で割るというふうに考えると、それなりの計算量がありそうです。しかし、AP-RZ-A1Hは、384MHzで動作する Cortex-A9プロセッサを搭載しているので、今回のような計算は単純に計算しても問題ありません。また、FPUを搭載しているので、浮動小数点演算も問題ありません。
// 100個のデータ int adc_data[100]; float average(void) { float res = 0.0; float temp = 0.0; int idx; for(idx=0; idx <100; idx++) { temp += (float)adc_data[idx]; } return temp/100; }
FPUの指定
SOLIDスターターキットでプロジェクトを作る際に、「FPUを使用する」を選択すると、FPU命令を使用したカーネルが用意されます。「FPUを使用しない」を選択すると、FPU命令が使われずに代替コードが使用されるので、コードサイズは大きくなります。
平均化を行うアルゴリズム
今後色々な処理を追加したい場合や、計算する対象が高速なものやリアルタイム処理を要求されてくると、そのまま計算する方法ではなく、高速かつ、値の有効性なども評価しつつ、アルゴリズムを選定していかなければなりません。
例えば、平均化の例を使用すると、コンピュータを使った計算では、算術演算を使用する方法と、ビット演算によるアルゴリズムがあります。算術演算は四則演算ができればいいので、算数程度の知識があればアルゴリズムは組めます。ビット演算は、少しトリッキーですが、最もコンピュータらしい計算方法で、処理速度も速い特徴があります。デジタルの世界では、1ビットで「0」と「1」の2値を表します。これを上手に使うと、
(A+B)/2
は、
(A+B)
を計算した後、1ビットシフトさせる。
として計算することができます。四則演算による方法とビット演算による方法では、コードサイズに差も出てきます。ぜひ自分自身で確認してみましょう。
リエントラント
関数にする場合に、気をつけておいた方がいいのは、関数の作り方です。他からも呼ばれたりする場合は、リエントラント(再入可能)な関数にしておく必要があるということです。これは、あるタスクAがこの関数aを呼び出した時に、他のタスクBでもこの関数aを呼ばれた場合を考えます。そうなると、関数aの結果がタスクAとタスクBで同じになってしまい、その先の処理に影響を与えてしまいます。関数aがリエントラントになっていると、タスクAで呼ばれた関数aは、タスクBで呼ばれた関数aとは異なる結果になり、結果に影響されない処理ができるようになります。これは、演算するような関数の場合は重要です。
タスクのスタックサイズ
関数がどのくらいのスタックを消費するかということです。関数がどの程度のスタックを消費するのがなぜ重要かというと、タスクのスタックサイズというものに影響を与えるためです。
タスクは、カーネルからメモリを割り当てられます。そのメモリは有限であり、無限に使えるものではありません。やたら大きくとればいいというものでもありません。その設定が非常に難しいものであります。はじめのうちは適当なサイズでもいいのですが、デバッガ等でデバッグする際は、このサイズも確認してみてください。
SOLIDではタスク用含めてスタックは常にプロテクションされています。リエントラントしスタックを想定以上に消費しスタックを使い切った場合、それを検出してデバッガで動作を停止させ報告します。またタスクスタックの消費状況はRTOSビューア機能にあるので、スタックサイズの見積もりには、これらの機能も活用できます。ぜひ活用しましょう。
UART(要件3、6)
UARTの送信、受信をそれぞれタスク化してみましょう。平均化したデータは、要件2で解説しました。UARTへデータを渡すには、メッセージバッファやデータキュー で実装すると、よりRTOSらしくなります。
割り込みサービスルーチン(要件4、5)
RTOSの割り込みサービスルーチンは、こちらを参考にしてください。割り込みサービスルーチン内で、タスク制御用APIを発行する形になりますが、全てのタスクをこの割り込みで発行するというより、キーとなるタスクを指定することで、割り込み内でのコードを最小化することができます。要件1のタスクがキーになりそうなので、ADC_TSKが起動すると、その中で他のタスクを起動するシーケンスがいいかもしれません。
タスクの停止の場合は、起動しているタスクを止めることになるので、割り込み内でタスクを停止するAPIを発行してしまっても問題ないでしょう。
割り込みの中で発行できるAPIは決められています。TOPPERS ASP3 Release 3.0.0 で、より詳細なRTOSの仕様を確認しましょう。
時刻設定(要件7、8)
一般的には、RTCのカレンダー機能を利用します。RTCは、1msをカウントして時間を知らせるタイプとカレンダー機能を持たせているタイプの2種類があります。RZ/Aは、外部カレンダー機能を持っているので、そちらを活用することもできますが、今回は実装を見送ります。
1分毎を計測するには、RTCのカレンダーアラーム機能を使うのがCPUの負担が少ないですが、要件7との関連もあり、今回は機能の実装を見送ります。
Watch Dog Timer(要件9)
WDT(Watch Dog Timer)の更新は、要件8の割り込みサービスルーチン内でレジスタを更新するのが無駄がありません。周期ハンドラで、1分ごとにレジスタを更新します。更新できないほど、CPUが他のリソースの処理に占有されてしまった場合は、要件10の処理に移行します。
Reset(要件10)
WDTは、RZ/Aが元々持っている機能をそのまま活かします。WDTは、一定期間内にレジスタ更新がないと、システムがハングアップしていると判断し、CPUにResetがかけられる機能です。この機能を使うことで、全く応答しないという状態から復帰するためのリカバリとして、使用されています。
まとめ
このようにブレークダウンすることで、より細かい調整が必要なリソースや、どのように実装していくかの方針が固まっていきます。もしかすると、この先の実装で、必要になってくるタスクが出てきたり、こっちの方がいいかもしれないということもあります。そういった、部分も組み込みシステムを開発していく上ではよくあることなので、そのあたりもどうなるのかお楽しみに!
Visual Studioベースの開発環境「SOLID-IDE」
SOLID-IDEは、Visual Studioベースの開発環境です。今回の動画では、これから開発していく上で必要なファイルの追加方法やインテリセンスについて解説しています。
プロジェクトウィザード
新規にプロジェクトを構築する時に、プロジェクトウィザードがあると、始めやすくなります。
SOLID-OSを活用したプロジェクトはもちろんのこと、BSPの追加やコードの解析など、必要なソースコードのテンプレートが自動生成されます。スタートアップのコードなど、必要なソースコードは含んでいますので、main()
に相当するroot_task()
から開発を始めることができます。これなら、誰でも書き始めやすいですよね。
インテリセンス
インテリセンスは、SOLID-IDEに搭載されている強力なエディタ機能の一つです。CやC++で構造体やクラス定義をした場合や、#defineで定義した値など、一部の入力をするだけで、その候補を出してくれる機能です。
コードを書いていると、全てのメンバ変数などが覚えきれないですが、インテリセンスが搭載されていることで、ドキュメントを見返すことやヘッダーファイルから定義を探す手間を省くことができます。使い方は簡単で、ソースコード上にマウスオーバーするだけ。マウスカーソル下にある変数や関数の型及び引数が確認できます。毎回、ドキュメントを探して型の確認は思いの外、労力を必要とします。インテリセンスを使って、効率よくコードを書いていきましょう。
行番号表示や、ファイル全体のマップ表示もコードのボリュームが一目でわかるので、開発時のストレスを提言する効果があります。
行番号表示
- 「ツール」—「オプション」—「テキストエディタ」—「C/C++」—「全般」
- 「行番号」にチェック
マップモード
- 「ツール」—「オプション」—「テキストエディタ」—「C/C++」—「スクロールバー」
- 「垂直スクロールバーでのマップモードの使用」にチェック
コンパイラ
SOLID-IDEは、コンパイラを指定する機能を持っています。コンパイラ自体は、GCC系のコンパイラやLLVM/Clangなどに対応しています。効率的なコード生成はもちろんのこと、ソースコードエディット中でも選択しているコンパイラの種類に関係なくバックグラウンドでコンパイルしてくれるので、入力中の誤りも瞬時に検出してくれます。一人でコーディングしていると見落としがちなエラーも、コンパイラは見逃さずに警告を出してくれます。
デバッガ
SOLID-IDEは、強力なデバッガを統合する機能を持っています。PARTNER-Jet2と連携すること、実機によるソースコードデバッグが効率的に行えるのは、この組み合わせならでは。JTAG-ICEであるPARTNER-Jet2とPCとはUSB3.0で接続されており、大規模なソフトウェアーも瞬時にダウンロードできるほか、従来からあるブレイクポイント、自動変数表示を始め、SOLID-OSを使用した時のタスクのステータスなど簡単に表示できます。また、ELFローダと連携することで、すでにロードしたモジュールのシンボルを読み込むことができるため、異なるモジュール間のソースコードデバッグを可能にします。
まとめ
今回の講座では、タスク設計の要点と、SOLIDのプロジェクトに反映するまでの方法を解説しました。ぜひ皆さんも何かの仕様を決める時の参考にしてみてください。
SOLID Starter Kit for RZ/A1H
今回使用するSOLID Starter Kit for RZ/A1Hは、評価ボード、デバッガ、IDEなどが、すぐに使えるAll in One Packageになっています。
“SOLID”は、コンパイラ、RTOS、デバッガ、IDE全てが含まれ、またそれぞれが専用設計された、京都マイクロコンピュータの組み込み向けソフトウェア開発プラットフォーム。ITRONベースでありながら、本格的なプログラムローダーや、バグの実行時自動検出機能などを搭載します。
最新情報をメーカーサイトで見る
Amazon.co.jpで買う
こちらも是非
“もっと見る” Cortex-A+RTOS編
ローダーを使いこなして、分割開発の悩みを解決
組み込みシステムにおけるローダーとは、電源が入り、RESET信号が解除されて動き出す最初のプログラムです。ローダーの役割は、プログラムをメモリ上に読み込むためにメモリコントローラの初期化を行い、プログラムが保存されているフラッシュメモリからSRAMやDRAMなどのメモリに展開して実行するまでの動作を行います。
デバッグの効率化とRTOSタスク実装
組み込みアプリ開発において、リアルタイムOS(RTOS)を使うメリットをこれまでに十分説明してきました。今回は、いよいよタスクの実装です。組み込みアプリを構築する上で、最も重要な部分です。使用する「SOLID-OS」は、Toppers/ASP3をベースにしたμITRON準拠のRTOSです。
デバイスドライバの役割
組み込みシステムのアプリケーションを作っている場合、CPUの機能だけを使うことはほぼないと言っていいでしょう。通常、ハードウェアの操作を行うために用意するソフトウェアを「デバイスドライバ」と呼んでおり、パソコンなどでも頻繁に利用されています。