2025年5月26日月曜日

TOPPERS/ASP - Arduino UNO R4版 その11

前回からの続きです。

このテーマを最初からご覧になる場合はこちらからどうぞ。


RTOSを使用した利点とは?

前回までに「Arduino UNO R4」で、ターミナル・ソフトウェアで入力した文字をシリアル通信でそのまま返すという簡単なプログラムを作成してきました。

そして、同じようなプログラムをTOPPERS/ASPを使用しない場合(「ベアメタル版」と呼んでいます)と使用した場合(「RTOS版」とでもしましょう)の2種類を紹介させていただきました。

これらは何が違っていて、TOPPERS/ASPなどのRTOSを使った場合はどのような点で優れているのか?

苦労してRTOSを載っけても、メリットがなければ意味がありませんからね…。

早速比較してみましょう。

Renesas RA4M1


メインプログラムを比較します。

まずは、TOPPERS/ASPを使用しない場合…ベアメタル版のメインプログラムです。

この記事で作成した「Hinagata」プロジェクトの「hal_entry.c」ファイルの「hal_entry()」関数です。

以下のような内容でした。

  1. ...
  2. void hal_entry(void)
  3. {
  4.     uint8_t c;
  5.     // シリアル通信を開く
  6.     R_SCI_UART_Open(&g_uart2_ctrl, &g_uart2_cfg);
  7.     while (1) {
  8.         // 受信する
  9.         R_SCI_UART_Read(&g_uart2_ctrl, &c, 1);
  10.         // 受信するまで待つ
  11.         while (!recieved);
  12.         // 受信完了フラグをリセットする
  13.         recieved = false;
  14.         // 受信した一文字を送信する
  15.         R_SCI_UART_Write(&g_uart2_ctrl, &c, 1);
  16.     }
  17. }
  18. ...


ここで上のリストで言うところの11から14行目に注目しておいてください。


次は、TOPPERS/ASPを使用した場合…つまりRTOS版のメインプログラムです。

この記事前回の記事で作った「asp_arduino_uno_r4_gcc-template」プロジェクトの「OBJ_template」直下「main.c」ファイルの「main_task()」関数です。

  1. ...
  2. void main_task(intptr_t exinf)
  3. {
  4.     uint8_t c;
  5.     // 割り込み番号とイベント番号の紐付け
  6.     bsp_irq_cfg();
  7.     // 各割り込みを有効化
  8.     ena_int(INTNO_UART2_RXI);
  9.     ena_int(INTNO_UART2_TXI);
  10.     ena_int(INTNO_UART2_TEI);
  11.     ena_int(INTNO_UART2_ERI);
  12.     // シリアル通信を開く
  13.     R_SCI_UART_Open(&g_uart2_ctrl, &g_uart2_cfg);
  14.     while (1) {
  15.         // 受信する
  16.         R_SCI_UART_Read(&g_uart2_ctrl, &c, 1);
  17.         // タスクを待ち状態にする
  18.         slp_tsk();
  19.         // 受信した一文字を送信する
  20.         R_SCI_UART_Write(&g_uart2_ctrl, &c, 1);
  21.     }
  22. }
  23. ...


「bsp_irq_cfg()」とか「ena_int()」などの関数コール以外は、一見ベアメタル版と変わらないように見えます。

しかし、ここで上のリストで言うところの20から21行目に注目してください。

ベアメタル版では「received」グローバル変数がtrueになるまで空ループしていて、それがtrueになったら次の受信に備えて「received」グローバル変数を自らfalseにリセットしています。

一方、RTOS版では、これら一連の処理が「slp_tsk()」というシステムコールに置き換えられていますね?


…一体、これらに何の違いがあるのか?


ベアメタル版の場合「received」グローバル変数をtrueにする…つまり「received = true;」を実行しているのは「Hinagata」プロジェクトの同じ「hal_entry.c」に記述されている「uart2_callback()」コールバック関数内…以下のリストでは7行目です。

  1. ...
  2. void uart2_callback(uart_callback_args_t *p_args)
  3. {
  4.     if (p_args->event == UART_EVENT_RX_COMPLETE) {
  5.         // 受信が完了したら「r_sci_usrt.c」ファイルの
  6.         // 「sci_uart_rxi_isr()」割り込みハンドラからここに来る
  7.         recieved = true;
  8.     }
  9.     if (p_args->event == UART_EVENT_TX_COMPLETE) {
  10.         // 送信が完了したら「r_sci_usrt.c」ファイルの
  11.         // 「sci_uart_tei_isr()」割り込みハンドラからここに来る
  12.     }
  13.     if (p_args->event == UART_EVENT_RX_CHAR) {
  14.         // 一文字受信したら「r_sci_usrt.c」ファイルの
  15.         // 「sci_uart_rxi_isr()」割り込みハンドラからここに来る
  16.     }
  17.     if (p_args->event == UART_EVENT_ERR_FRAMING) {
  18.         // フレーミングエラーを検出した時に「r_sci_usrt.c」ファイルの
  19.         // 「sci_uart_eri_isr()」割り込みハンドラからここに来る
  20.     }
  21.     if (p_args->event == UART_EVENT_BREAK_DETECT) {
  22.         // ブレークを検出した時に「r_sci_usrt.c」ファイルの
  23.         // 「sci_uart_eri_isr()」割り込みハンドラからここに来る
  24.     }
  25.     if (p_args->event == UART_EVENT_TX_DATA_EMPTY) {
  26.         // 送信バッファが空になった時に「r_sci_usrt.c」ファイルの
  27.         // 「sci_uart_txi_isr()」割り込みハンドラからここに来る
  28.     }
  29. }
  30. ...


つまり、ベアメタル版の場合ではシリアル通信の受信割り込みが発生して「uart2_callback()」コールバック関数にて「received」変数がtrueにセットされるまでは、空ループをグルグル回り続けます。

この状態では、CPUは割り込みハンドラ内の処理以外は他に何もできません。

空ループとは言っても処理は処理です。

この間CPUは空ループを回すという非生産的なことだけに全力を傾けている状態です。


一方、RTOS版は…?

「received」変数の操作の代わりに「slp_tsk()」というシステムコールを置きました。

「slp_tsk()」は、これを呼び出したタスクを「起床待ち(スリープ)」にするシステムコールです。

「main_task()」関数は「MAIN_TASK」というIDのタスクであることは同じディレクトリにある「main.cfg」で設定されています。

ですので「slp_tsk()」を呼び出した時点で「MAIN_TASK」はスリープ状態となり、割り込みハンドラや他のタスクが「iwup_tsk(MAIN_TASK);」や「wup_tsk(MAIN_TASK);」してあげない限り、ずっと動作を停止した状態が続きます。

RTOS版で具体的にこれを行っているのは、ベアメタル版と同じく「hal_entry.c」に記述されている「uart2_callback()」コールバック関数内…以下のリストでは6行目です。

  1. ...
  2. void uart2_callback(uart_callback_args_t *p_args)
  3. {
  4.     if (p_args->event == UART_EVENT_RX_COMPLETE) {
  5.         // 待ち状態のタスクを起床させる
  6.         iwup_tsk(MAIN_TASK); // 追記!
  7.     }
  8.     if (p_args->event == UART_EVENT_TX_COMPLETE) {
  9.         // 送信が完了したら「r_sci_usrt.c」ファイルの
  10.         // 「sci_uart_tei_isr()」割り込みハンドラからここに来る
  11.     }
  12.     if (p_args->event == UART_EVENT_RX_CHAR) {
  13.         // 一文字受信したら「r_sci_usrt.c」ファイルの
  14.         // 「sci_uart_rxi_isr()」割り込みハンドラからここに来る
  15.     }
  16.     if (p_args->event == UART_EVENT_ERR_FRAMING) {
  17.         // フレーミングエラーを検出した時に「r_sci_usrt.c」ファイルの
  18.         // 「sci_uart_eri_isr()」割り込みハンドラからここに来る
  19.     }
  20.     if (p_args->event == UART_EVENT_BREAK_DETECT) {
  21.         // ブレークを検出した時に「r_sci_usrt.c」ファイルの
  22.         // 「sci_uart_eri_isr()」割り込みハンドラからここに来る
  23.     }
  24.     if (p_args->event == UART_EVENT_TX_DATA_EMPTY) {
  25.         // 送信バッファが空になった時に「r_sci_usrt.c」ファイルの
  26.         // 「sci_uart_txi_isr()」割り込みハンドラからここに来る
  27.     }
  28. }
  29. ...


割り込みが発生して「iwup_tsk(MAIN_TASK);」を行わない限り、メインプログラムが再開しないのはベアメタル版と同じです。

しかし、ベアメタル版の空ループではなく、RTOS版の場合はタスクをスリープ状態に移管しただけ…という点に違いがあります。

ベアメタル版の空ループの場合、この時実行できる他の処理は、何らかの割り込みが発生した時に呼び出される割り込みハンドラの処理だけです。

一方、RTOS版のスリープ状態の場合は、割り込みハンドラの処理に加えて、他のタスクに処理が移り、文字通り他の処理を行うことができます。

RTOS版で空ループを使った場合も、そこでCPU全体が固まってしまうということはなく、他に優先度の高いタスクが動いていれば、そちらに処理が移ります。

これがマルチタスクと呼ばれているもので、RTOSを使用する最大のメリットの一つです。

この辺りの動きの詳細は、μITRON4.0の仕様書か、TOPPERS/ASPのサンプルプログラムの動きを解説したこちらの記事を参照してください。

今回のRTOS版では、同時に動いているタスクは、メインプログラムである「MAIN_TASK」の他にログを管理する「LOGTASK」があります。

マルチタスクで動作している証拠として、RTOS版を実行した後、ターミナル・ソフトウェアによる操作を何も行わない状態で「LOGTASK」のプログラムにデバッガでブレークを仕掛けてみましょう。

具体的には「syssvc」ディレクトリ直下の「logtask.c」ファイルの140行目、ループの先頭「while ((rercd = syslog_rea_log(&logbuf)) >= 0) {」というコードの辺りが良いでしょうか?

e2 studio


引っかかりましたね?

この時「MAIN_TASK」タスクは「slp_tsk()」により止まっていますが、空ループに入ったベアメタル版とは違い「LOGTASK」という他のタスク、すなわち他の処理が実行できていることが分かります。


今回はRTOS版でも、システム全体で2つのタスクしかありません。

しかし、もっと複雑なアプリケーションの開発において、同時に処理しなければならないことが増えた時にこそ、RTOSを使用するメリットが増大します。

CPUに無駄な処理をさせることなく、待ち時間や休む暇なく、同時に多くの仕事を効率的に動いてもらうことがRTOSの役割です。

こういう言い方だと、ブラック企業のマネージャーみたいでCPUには気の毒ですがね…。


まとめ

組み込みソフトウェアにおいてTOPPERS/ASPなどのRTOSを使用するメリットは、以下の通りです(…だと個人的には思っています!)。


1.マルチタスクが使えること

今まで説明させていただいた通り、これが最大のメリットだと思います。

RTOSを使用しない場合、メインプログラムの処理の他に何かをやらせようとすると、何らかの割り込みハンドラに全ての処理を記述しなければなりません。

そうすると割り込みハンドラの処理が重くなり、他の割り込み処理など、システム全体のパフォーマンスを落としかねません。

ソフトウェア全体の設計も窮屈で難解なものになるでしょう。


2.ソースコードの移植性が高くなること

今回のターゲット「Arduino UNO R4」はARMアーキテクチャのマイコンを搭載しています。

例えば、あるソフトウェアをRTOSを使って開発していて、それをARMではなくRISC-VやRXなどの他のアーキテクチャのハードウェアに移植することを考えてみてください。

あるアーキテクチャでは簡単に実現できることが、別のアーキテクチャでは困難であるケースや、やり方が大きく違うことが多々あります。

本来、OSとはハードウェアとアプリケーションの間に入って、アプリケーションから見たハードウェアの差異を吸収する役割を持つものです。

したがって、もしRTOSを使っていなければ、ソフトウェアのかなりの部分の書き直しが必要となる可能性もあります。

当然、ハードウェア依存部分は使用するハードウェアやマイコンによってその都度書き直す必要のある部分はあるにしても、少なくともアプリケーション部分に関しては、そのまま流用できるはずです。

移植元と移植先のRTOSの仕様が同一であれば、アプリケーションは同じ動作のシステムコールを使用しますので、動作検証の工数も大幅に削減できます。

むしろ、この工数削減こそ貴重です!

が、このメリットは昔ほど重視されません。

こう…、なんでもかんでもARMの時代じゃあ…。


3.ソースコードの可読性が高くなること

RTOSを使用しないベアメタルなソフトウエアにおいては、その作者によってルールが異なります。

プログラマーの中には変わり者も多く(そういう人ほど天才が多いですが…)、俺俺ルール満載、カオスで難読のソースコードも数多くあります。

その点、RTOSを使用した場合はシステムコールの仕様やタスクの構成など、一定のルールが保たれる傾向があります。

(そうじゃなきゃ動かないしねぇ。)

見慣れたRTOSのシステムコールを起点にソースコードを読んでいけば、それが赤の他人が書いたものでも理解は格段に容易になります。

自分の書いたものであっても、しばらくすれば忘れてしまうもの。

その際にもメンテナンス性が向上しますよ。


RTOSの導入は多少手間がかかりますが、開発しているソフトウエアが複雑になればなるほど、手間を上回る十分なメリットが得られると思います。

とはいえRTOSも万能ではありませんので、特にROMやRAMなどのリソースが少ない状況でのご使用は、是非とも慎重なご判断を!


ライセンスについて

このカーネルは「TOPPERSライセンス」で配布しております。

無償ですが、使用に関しては自己責任です。

このカーネルを商用利用する方は、このリンク先の条項に従ってください。


さて、これで「TOPPERS/ASP Arduino UNO R4版」に関するテーマは終了です。

お付き合いいただき、ありがとうございました。

これでようやく次のアーキテクチャの説明に入れます!


<終わり>

2025年4月19日土曜日

TOPPERS/ASP - Arduino UNO R4版 その10

前回からの続きです。

このテーマを最初からご覧になる場合はこちらからどうぞ。


TOPPERS/ASPとFSPを組み合わせて使う~その2

前回ビルドまで通した「OBJ_template」に対し、実際にシリアル通信プログラムを肉付けしていきましょう。

TOPPERS/ASP - Arduino UNO R4版 その8」で作ったベアメタル版と同じ動作のプログラムをTOPPERS/ASP上に実装します。

接続例


結果「OBJ_template」の「main.c」は、以下のようなものとなります。

  • ...
  • #include <kernel.h>
  • #include <t_syslog.h>
  • #include <t_stdlib.h>
  • #include "kernel_cfg.h"
  • #include "hal_data.h"    // 追記!
  • #include "main.h"


  • /*
  •  *  割り込みを OS で受け取り FSP のハンドラへ処理を渡す
  •  *  ( FSP のハンドラは r_sci_uart.c に記述されている)
  •  */
  • // UART 受信割り込みハンドラ
  • void _sci_uart_rxi_isr(void) {
  •     // FSP の UART 受信割り込みハンドラ
  •     sci_uart_rxi_isr();
  • }

  • // UART 送信割り込みハンドラ
  • void _sci_uart_txi_isr(void) {
  •     // FSP の UART 送信割り込みハンドラ
  •     sci_uart_txi_isr();
  • }

  • // UART 送信完了割り込みハンドラ
  • void _sci_uart_tei_isr(void) {
  •     // FSP の UART 送信完了割り込みハンドラ
  •     sci_uart_tei_isr();
  • }

  • // UART エラー割り込みハンドラ
  • void _sci_uart_eri_isr(void) {
  •     // FSP の UART エラー割り込みハンドラ
  •     sci_uart_eri_isr();
  • }

  • /*
  •  *  メインタスク
  •  */
  • void main_task(intptr_t exinf)
  • {
  •     uint8_t c;

  •     // 割り込み番号とイベント番号の紐付け
  •     bsp_irq_cfg();

  •     // 各割り込みを有効化
  •     ena_int(INTNO_UART2_RXI);
  •     ena_int(INTNO_UART2_TXI);
  •     ena_int(INTNO_UART2_TEI);
  •     ena_int(INTNO_UART2_ERI);

  •     // シリアル通信を開く
  •     R_SCI_UART_Open(&g_uart2_ctrl, &g_uart2_cfg);

  •     while (1) {
  •         // 受信する
  •         R_SCI_UART_Read(&g_uart2_ctrl, &c, 1);
  •         // タスクを待ち状態にする
  •         slp_tsk();
  •         // 受信した一文字を送信する
  •         R_SCI_UART_Write(&g_uart2_ctrl, &c, 1);
  •     }
  • }
  • ...


まずは「_sci_uart_xxx_isr()」という関数が4つ並んでいますね。

関数名の頭にアンダーバー(_)が付いている点がポイントです。

これらはコメントの通り、シリアル通信ポートの各種割り込みハンドラです。

実は、前々回のFSP(Flexible Software Package)を使用したベアメタルでも同様の働きをする割り込みハンドラを使っていました。

これらは「e2 studio」で「Hinagata」プロジェクトを作成したときにFSPのコンフィギュレーションツールが吐き出したUARTドライバの中で実装されていて、それが影で動いていましたので特に意識はしなくても良かったのです。

その後、吐き出されたソースは前回TOPPERS/ASPのソースツリーへコピーしましたよね?

それらのハンドラは、未だに以下のパスにその実体があります。


C:\cygwin64\home\<ユーザー名>\asp_arduino_uno_r4_gcc-template\target\arduino_uno_r4_gcc\ra\fsp\src\r_sci_uart.c


この「r_sci_uart.c」の中にUARTの各割り込みハンドラがあります。

その名も…


〇void sci_uart_rxi_isr(void) … UART受信割り込みハンドラ

〇void sci_uart_txi_isr(void) … UART送信割り込みハンドラ

〇void sci_uart_tei_isr(void) … UART送信完了割り込みハンドラ

〇void sci_uart_eri_isr(void) … UARTエラー割り込みハンドラ


…関数名の頭にアンダーバー(_)が付いてない点がポイントです。

つまり「main.c」の冒頭に追記した4つのアンダーバー付き割り込みハンドラは、本来の処理を行うアンダーバー無し割り込みハンドラへ処理を渡すだけの関数ということになります。

なんでこんな面倒くさいことをやっているのか?と言えば…。

TOPPERS/ASPを含むμITRON系のRTOSは、複数のタスクを並行して実行可能にする機能に加え、割り込みも管理します。

したがって、割り込みは一度RTOS側で受け取る必要があります。

(そうじゃないと管理できませんよね。)

RTOSのあずかり知らぬところで、頭越しに勝手にFSPの割り込みハンドラを呼び出されては色々とマズいのです。

そのために、本来の割り込みハンドラの関数名の頭にアンダーバーを付けた形で、RTOSが割り込みを受け取るためのハンドラを作っているのです。

各々の本来の割り込みハンドラへバイパスするだけなんですけどね。

しかしながら、割り込みのリアルタイム性を重視するあまり、あえて割り込みをOS管轄外とするテクニックもなくはないです。

全く違うマイコンですが、この記事で触れていますので興味のある方は参考まで。


次に「main.c」の「main_task()」関数の中に入ります。

「main.cfg」の記述より、RTOSが立ち上がると自動的にこの「main_task()」が実行されることになっています。

そして、いきなり「bsp_irq_cfg()」という関数を呼んでいますね?

これに関しては、ちょっとだけ長い説明が必要でしょう。

一般的なマイコンは、割り込み番号とそれが何の割り込みなのか?(すなわち「イベント」)の関係性は固定されています。

たとえば、以下はSilicon Labs社の「EFM32PG23」というマイコンのデータシートの1ページです。

これは、割り込み(IRQ)番号と、それが引き起されるイベント名の一覧表です。

この中で、このマイコンのシリアル通信の受信割り込み「USART0_RX」の割り込み番号は「9」です。

同様に送信割り込み「USART0_TX」の割り込み番号は「10」ですね?

「EFM32PG23」のデータシート


大半のマイコンは、このように割り込み番号とイベントの関係は固定です。

つまり、この型番のマイコンにおいてはシリアル通信の受信割り込み「USART0_RX」の割り込み番号は「9」って言ったら「9」であり、この世の終わりまでこれが変更されることはありません。

ところが、今回の「Arduino UNO R4」に搭載されているRA4M1マイコンはさにあらず。

上にあげた「EFM32PG23」の割り込み(IRQ)番号とイベント名の一覧表と同内容のRA4M1マイコンの表を以下に示します。

すると、割り込み(IRQ)番号に対し具体的なイベント名が書いておらず「ICU.IELSRxレジスタで選択されたイベント」などと記述されています。

RA4M1マイコンのデータシート - 1


結論から言えば、RA4M1マイコンは割り込み番号とイベントの関係は固定されておらず、自由に設定することができるのです。

例えば、UARTの各種割り込み(IRQ)番号を0~31まで、好きなように割り振ることができるのです。

それを行うためには「ICU.IELSRx」というレジスタに「イベント番号」というものを書き込みます。

では「イベント番号」ってどこで調べればいいの?ってことですが、ちゃんとデータシートに記載されています。

今回使用するシリアル通信ポートは「SCI2」ですので、これが引き起こす各種イベントは赤枠で囲った部分であり、それぞれ左端の「イベント番号」項目の数値に対応します。

イベントテーブル


今回は、以下のようにイベントに割り込み番号(IRQ#)を紐付けます。


〇SCI2_RXI(sci_uart_rxi_isr()) … IRQ#0

SCI2_TXI(sci_uart_txi_isr() … IRQ#1

SCI2_TEI(sci_uart_tei_isr()) … IRQ#2

〇SCI2_ERI(sci_uart_eri_isr()) … IRQ#3


紐付けは「ICU.IELSRx」レジスタに「イベント番号」を書き込むのでしたね?

ですので、本来は以下のようなコードを書かなくてはならないのですが…


R_ICU->IELSR[<割り込み番号>] = <イベント番号>

  • ...
  • R_ICU->IELSR[0] = (uint32_t)0x0A3    // IRQ#0 に SCI2_RXI(イベント番号 : 0x0A3)を設定
  • R_ICU->IELSR[1] = (uint32_t)0x0A4    // IRQ#1 に SCI2_TXI(イベント番号 : 0x0A4)を設定
  • R_ICU->IELSR[2] = (uint32_t)0x0A5    // IRQ#2 に SCI2_TEI(イベント番号 : 0x0A5)を設定
  • R_ICU->IELSR[3] = (uint32_t)0x0A6    // IRQ#3 に SCI2_ERI(イベント番号 : 0x0A6)を設定
  • ...


…この処理をやってくれるのが「bsp_irq_cfg()」という関数なのです。

そもそも、この紐づけはFSPコンフィギュレーションツールで設定したものなので、当然設定内容はFSPが知っています。

なので、同じくFSPコンフィギュレーションツールが吐き出した「bsp_irq_cfg()」をコールします。

前回「Makefile」の「APPL_COBJS」変数に代入した「vector_data.o」オブジェクト、すなわち「vector_data.c」にこの「bsp_irq_cfg()」関数の実装があります。


「bsp_irq_cfg()」の下の行からは「ena_int(INTNO_UART2_XXX)」という記述が4つ並んでいます。

「ena_int()」は、指定された割り込み番号の割り込みを有効にするというμITRONのシステムコールです。

「INTNO_UART2_XXX」の値は「main.h」で定義されています。

その後はベアメタル版と同様にシリアル通信を開いた後、これまたベアメタル版と似たような処理を持つwhileループに入ります。


次に「OBJ_template」の「main.h」を以下の通り修正します。

(「ここから~ここまで」のコメントの範囲で2ヵ所です。)

  • ...
  • /*
  •  *  ターゲット依存の定義
  •  */
  • #include "target_test.h"

  • /*
  •  *  各タスクの優先度の定義
  •  */

  • #define MAIN_PRIORITY    5        /* メインタスクの優先度 */
  •                                 /* HIGH_PRIORITYより高くすること */

  • #define HIGH_PRIORITY    9        /* 並行実行されるタスクの優先度 */
  • #define MID_PRIORITY    10
  • #define LOW_PRIORITY    11

  • /*
  •  * ターゲットに依存する可能性のある定数の定義
  •  */

  • #ifndef TASK_PORTID
  • #define    TASK_PORTID        1            /* 文字入力するシリアルポートID */
  • #endif /* TASK_PORTID */

  • #ifndef STACK_SIZE
  • #define    STACK_SIZE        4096        /* タスクのスタックサイズ */
  • #endif /* STACK_SIZE */

  • #ifndef LOOP_REF
  • #define LOOP_REF        ULONG_C(1000000)    /* 速度計測用のループ回数 */
  • #endif /* LOOP_REF */

  • // ここから ----------------------------------------------------------------
  • #define INHNO_UART2_RXI    (SCI2_RXI_IRQn + 16)
  • #define INTNO_UART2_RXI    (SCI2_RXI_IRQn + 16)
  • #define INHNO_UART2_TXI    (SCI2_TXI_IRQn + 16)
  • #define INTNO_UART2_TXI    (SCI2_TXI_IRQn + 16)
  • #define INHNO_UART2_TEI    (SCI2_TEI_IRQn + 16)
  • #define INTNO_UART2_TEI    (SCI2_TEI_IRQn + 16)
  • #define INHNO_UART2_ERI    (SCI2_ERI_IRQn + 16)
  • #define INTNO_UART2_ERI    (SCI2_ERI_IRQn + 16)
  • #define INTPRI_UART2    -4
  • // ここまで ----------------------------------------------------------------

  • /*
  •  *  関数のプロトタイプ宣言
  •  */
  • #ifndef TOPPERS_MACRO_ONLY

  • extern void    main_task(intptr_t exinf);
  • // ここから ----------------------------------------------------------------
  • extern void _sci_uart_rxi_isr(void);
  • extern void _sci_uart_txi_isr(void);
  • extern void _sci_uart_tei_isr(void);
  • extern void _sci_uart_eri_isr(void);
  • // ここまで ----------------------------------------------------------------

  • #endif /* TOPPERS_MACRO_ONLY */


「main.c」において、RTOSが割り込みを受け取るためのハンドラを4つ追加しました。

そこで、ソースコードの関数の中で「どれが」割り込みハンドラなのか?「何の」割り込みなのかをRTOSに申告する必要があります。

この申告を行うのがコンフィギュレーションファイルです。

「OBJ_template」アプリケーションのコンフィギュレーションファイルは「main.cfg」です。

「main.cfg」を以下の通り修正します。

(「ここから~ここまで」のコメントの範囲です。)

  • ...
  • INCLUDE("target_timer.cfg");
  • INCLUDE("syssvc/syslog.cfg");
  • INCLUDE("syssvc/banner.cfg");
  • INCLUDE("syssvc/serial.cfg");
  • INCLUDE("syssvc/logtask.cfg");

  • #include "main.h"
  • CRE_TSK(MAIN_TASK, { TA_ACT, 0, main_task, MAIN_PRIORITY, STACK_SIZE, NULL });

  • // ここから ----------------------------------------------------------------
  • DEF_INH(INHNO_UART2_RXI, { TA_NULL, _sci_uart_rxi_isr });
  • CFG_INT(INTNO_UART2_RXI, { TA_NULL, INTPRI_UART2 });
  • DEF_INH(INHNO_UART2_TXI, { TA_NULL, _sci_uart_txi_isr });
  • CFG_INT(INTNO_UART2_TXI, { TA_NULL, INTPRI_UART2 });
  • DEF_INH(INHNO_UART2_TEI, { TA_NULL, _sci_uart_tei_isr });
  • CFG_INT(INTNO_UART2_TEI, { TA_NULL, INTPRI_UART2 });
  • DEF_INH(INHNO_UART2_ERI, { TA_NULL, _sci_uart_eri_isr });
  • CFG_INT(INTNO_UART2_ERI, { TA_NULL, INTPRI_UART2 });
  • // ここまで ----------------------------------------------------------------


それぞれの割り込みに対し「DEF_INH」マクロで割り込みハンドラを登録し「CFG_INT」マクロで割り込み優先度を設定しています。

コンフィギュレーションファイルは冒頭で「#include "main.h"」とされていて「INHNO_UART2_XXX」、「INTNO_UART2_XXX」などのパラメータは、修正を行った「main.h」に定義されています。

各パラメータは、それぞれ以下の意味を持ちます。


〇INHNO_UART2_RXI:
〇INHNO_UART2_TXI:
〇INHNO_UART2_TEI:
〇INHNO_UART2_ERI:

今回使用するシリアル通信ポート「UART2」の各種割り込みハンドラ番号を示します。

なお、今回のターゲットである「Arduino UNO R4」はARM-Mコアです。

ARM-Mコアの場合は、この「割り込みハンドラ番号」と下記の「割り込み番号」は同一の値となります。

ところで「main.c」の項でも説明した通り、4つの割り込み番号は0~3と指定しました。

これらは「main.h」の「SCI2_XXX_IRQn」の値を見ていただければ一致していることがお分かりいただけると思います。

しかし、その後ろの「+16」って何でしょう?

ARM-Mコアを採用しているマイコンの場合、ベンダーや型番に関わらず実装しなければいけない16個の割り込みが予め規定されています。

もう一度RA4M1マイコンの割り込み一覧表を見てみましょう。

例外番号0~15までの16個がその割り込みです。

RA4M1マイコンのデータシート - 2


TOPPERS/ASPは「IRQ番号」ではなく、上の表で言うところの「例外番号」で割り込みを認識する仕様となっています。

なので「+16」なのです。

他のアーキテクチャのマイコンを使って同時開発している時など、これを忘れてハマることが結構ありますのでご注意を。

(つい最近やっちまいました…。)


〇INTNO_UART2_RXI:
〇INTNO_UART2_TXI:
〇INTNO_UART2_TEI:
〇INTNO_UART2_ERI:

今回使用するシリアル通信ポート「UART2」の各種割り込み番号を示します。

前述の通り「割り込み番号」と上記の「割り込みハンドラ番号」は同一の値となります。

「+16」の意味も前述と同様です。


〇TA_NULL

何も指定しないという意味のμITRONで定められた定数です。

今回はあまり重要ではないので、説明を省きます。

興味のある方はこちらのμITRON4.0の仕様書を読んでみてください。


〇INTPRI_UART2

使用するシリアル通信ポート「UART2」の割り込み優先度を示します。

今回の場合はFSPコンフィギュレーションツールで設定した時と同じ値にしておきましょう。

もし違う値が設定された場合は、このコンフィギュレーションファイルの値ではなく、FSPでの設定値が優先されます。

ただし、注意点としては値の変換が必要なことです。

例えば、今回UART2の割り込み優先度は「Hinagata」プロジェクト作成時に「12」としました…っていうか、デフォルトでそうなっています。

FSPでの割り込み優先度の設定


しかし、μITRONの割り込み優先度の数値は以下の計算式で求めたマイナスの値を指定する必要があります。


<μITRONの優先度の数値>=<FSPの優先度の数値>-<1<<割込み優先度のビット幅>


<FSPの優先度の数値>は今回の場合「12」です。

そして<割込み優先度のビット幅>については「Arduino UNO R4」に搭載されているRA4M1マイコンは4ビットですので、「1<<4」で「16」となります。

すなわち、知りたい<μITRONの優先度の数値>PRIは…


PRI = 12 - (1 << 4)

PRI = 12 - 16

PRI = -4


…と求まります。

したがって「main.h」の「INTPRI_UART2」を「-4」で定義しているのです。

因みに、値の変換の前後に関わらず「数の少ない方が優先度が高い」設定となります。


さて、最後の最後。

割り込みコールバックルーチンの修正です。

以下のパスにある「hal_entry.c」の「uart2_callback()」関数に注目です。


C:\cygwin64\home\<ユーザー名>\asp_arduino_uno_r4_gcc-template\target\arduino_uno_r4_gcc\src\hal_entry.c


これを以下のように修正します。

(「ここから~ここまで」のコメントの範囲で2ヵ所です。)

  • // ここから ----------------------------------------------------------------
  • #include <kernel.h>        // 追記!
  • #include "kernel_cfg.h"    // 追記!
  • // ここまで ----------------------------------------------------------------
  • #include "hal_data.h"

  • FSP_CPP_HEADER
  • void R_BSP_WarmStart(bsp_warm_start_event_t event);
  • FSP_CPP_FOOTER

  • volatile bool recieved = false; // 受信完了フラグ
  • void uart2_callback(uart_callback_args_t *p_args)
  • {
  •     if (p_args->event == UART_EVENT_RX_COMPLETE) {
  •         // 受信が完了したら「r_sci_usrt.c」ファイルの
  •         // 「sci_uart_rxi_isr()」割り込みハンドラからここに来る
  • // ここから ----------------------------------------------------------------
  •         //recieved = true;        // コメントアウト!
  •         // 待ち状態のタスクを起床させる
  •         iwup_tsk(MAIN_TASK);    // 追記!
  • // ここまで ----------------------------------------------------------------
  •     }
  •     if (p_args->event == UART_EVENT_TX_COMPLETE) {
  •         // 送信が完了したら「r_sci_usrt.c」ファイルの
  •         // 「sci_uart_tei_isr()」割り込みハンドラからここに来る
  •     }
  •     if (p_args->event == UART_EVENT_RX_CHAR) {
  •         // 一文字受信したら「r_sci_usrt.c」ファイルの
  •         // 「sci_uart_rxi_isr()」割り込みハンドラからここに来る
  •     }
  •     if (p_args->event == UART_EVENT_ERR_FRAMING) {
  •         // フレーミングエラーを検出した時に「r_sci_usrt.c」ファイルの
  •         // 「sci_uart_eri_isr()」割り込みハンドラからここに来る
  •     }
  •     if (p_args->event == UART_EVENT_BREAK_DETECT) {
  •         // ブレークを検出した時に「r_sci_usrt.c」ファイルの
  •         // 「sci_uart_eri_isr()」割り込みハンドラからここに来る
  •     }
  •     if (p_args->event == UART_EVENT_TX_DATA_EMPTY) {
  •         // 送信バッファが空になった時に「r_sci_usrt.c」ファイルの
  •         // 「sci_uart_txi_isr()」割り込みハンドラからここに来る
  •     }
  • }
  • ...


※「hal_entry.c」は「target」ディレクトリ直下のファイルです。

本来、アプリケーション毎に変更の可能性がある関数やファイルの修正は「OBJ~」ディレクトリ直下に存在するものに留めるべきです。

今回は説明を簡便にするために「target」ディレクトリ直下の「hal_entry.c」を書き換えています。


冒頭の「kernel.h」や「kernel_cfg.h」のインクルードは、以降の「iwup_tsk(MAIN_TASK);」という処理のために必要です。

まず「iwup_tsk()」は、指定された番号のタスクを起床させるというμITRONのシステムコールです。

元々「wup_tsk()」という同様のシステムコールが存在しますが、こちらはタスクからコールする時に使用します。

「uart2_callback()」関数はタスクではなく割り込みハンドラから直接跳んできています。

ですので、ここでは頭に「i」を付けた「iwup_tsk()」を使用します。

もし「i」を付けないとどうなるか?

少なくともTOPPERS/ASPのこのバージョンでは、正しく動作しません。

タスクからコールしているのに「i」を付けてしまった場合も同様です。

但し、同じμITRON準拠のRTOSの中でもこれらを区別しない実装もあります。

株式会社ミスポさんの「NORTi」などがそうです。

しかし、移植性を考えるとこれらを正しく使い分けるコーディングを推奨します。

(ちなみに「NORTi」のユーザーズガイドは良くまとまっていて「NORTi」に限らずμITRONのリファレンスとして超分かり易いです!


話を戻して…。

「iwup_tsk()」に指定するタスク番号「MAIN_TASK」は「kernel_cfg.h」で定義されています。

今回の場合は「2」番ですね。

ご想像の通り、この番号が示すタスクは「main.c」の「main_task()」関数がその実体です。

  • /* kernel_cfg.h */
  • #ifndef TOPPERS_KERNEL_CFG_H
  • #define TOPPERS_KERNEL_CFG_H

  • #define TNUM_TSKID    2
  • #define TNUM_SEMID    4
  • #define TNUM_FLGID    0
  • #define TNUM_DTQID    0
  • #define TNUM_PDQID    0
  • #define TNUM_MBXID    0
  • #define TNUM_MPFID    0
  • #define TNUM_CYCID    0
  • #define TNUM_ALMID    0

  • #define LOGTASK    1
  • #define MAIN_TASK    2
  • #define SERIAL_RCV_SEM1    1
  • #define SERIAL_SND_SEM1    2
  • #define SERIAL_RCV_SEM2    3
  • #define SERIAL_SND_SEM2    4

  • #endif /* TOPPERS_KERNEL_CFG_H */


以上で修正作業は完了です。

修正箇所は忘れずに保存してから、こちらの記事を参考にプログラムをビルドしてターゲット上で動かしてみてください。

但し、シリアル通信用の信号線はデバッガ⇔ターゲット・ケーブルから出ているもの(SCI9)ではなく、こちらの記事のようにArduinoのピンソケットから取り出したもの(SCI2)をUSB/シリアル通信変換ケーブルに接続してください。

Arduinoのピンソケット


上手く動けば、ベアメタル版と同じ内容のプログラムが走ります。

「TeraTerm」で入力した文字がそのまま表示されるプログラムでしたね!

TeraTerm


次回は、よ~やく最終回。

このRTOS版の解説とベアメタル版との比較です。

ここまで苦労してRTOSを乗っけたけど、それに見合うメリットってあるの?

あるんですよ!(…多分ね。)


<続く>

2025年3月20日木曜日

TOPPERS/ASP - Arduino UNO R4版 その9

前回からの続きです。

このテーマを最初からご覧になる場合はこちらからどうぞ。


TOPPERS/ASPとFSPを組み合わせて使う~その1

さて、前回は「TOPPERS/ASP」を使用せずに完全なベアメタルでのシリアル通信を使用するプログラムを作成しました。

シリアル通信のドライバは、Renesas社純正のドライバ、プロトコル・スタックなどを開発者に提供するソースコードのライブラリであるFSP(Flexible Software Package)を使用しました。

Arduino UNO R4


今回は同様の事を「TOPPERS/ASP」とFSPを組み合わせた形で試用する方法をご紹介します。

前回の「Hinagata」プロジェクトを「TOPPERS/ASP」に移植する形で作業を進めます。

んなもんとっくに消しちゃったよ~って方は、コチラを参考に再度作成してください。

そして、前回のベアメタルでのシリアル通信プログラムが動くところまで作業をお願いします。


まずは「TOPPERS/ASP」のソースコードのダウンロードです。

すでにこのブログを読んでいただいていて、ぶっ通しで作業してくれている方には申し訳ないのですが、なるべく最新のものを使っていただきたいので、再度ダウンロードをお願いします。

以下のページにアクセスしてください。

そうしたら「master」という表示のコンボボックスに注目です。


https://github.com/RyutaroMorita/asp_arduino_uno_r4_gcc

GitHub - 1


このコンボボックスをクリックすると、以下のようにリストが表示されます。

今は「master」が選ばれていますが、その下の「template」をクリックします。

GitHub - 2


すると、コンボボックスが「template」に変わったページに切り替わりましたよね?

次は、そのはるか右側の「<>Code」と書いてあるボタンをクリックしてください。

GitHub - 3


こちらもリストが展開されて、その中に「Download ZIP」というのがあります。

これをクリックすると、Arduino UNO R4版の「TOPPERS/ASP」のソースコードのダウンロードが始まります。

GitHub - 4


ソースコードはZIP形式でダウンロードされますので、展開が必要です。

今回は「Cygwin」傘下の以下のディレクトリに展開したものとして説明します。


C:\cygwin64\home\<ユーザー名>\asp_arduino_uno_r4_gcc-template


このディレクトリ直下は、以下のようになっていることを確認します。

(Winodwsの標準ツールでそのまま展開しちゃうと、もう一層「asp_arduino_uno_r4_gcc-template」ディレクトリが入っちゃうので注意です。)

この中の「OBJ_template」というディレクトリに注目です。

エクスプローラー


「OBJ」ディレクトリの中には、TOPPERS/ASPの純正のサンプルプログラムのソースコードが入っています。

一方「OBJ_template」には、一秒ごとに「main_task is running.」というシリアル通信のメッセージを出力するだけのシンプルなアプリケーションのソースコードが入っています。

これから新しいシリアル通信のアプリケーションを作成する上で、ベースとするソースコードとして用意しました。

「OBJ」をベースにしてしまうと、いろいろ余計な部分を削除する手間がありますので~。

メインプログラムの「main.c」は以下のような感じです。

シンプルでしょ?

  • #include <kernel.h>
  • #include <t_syslog.h>
  • #include <t_stdlib.h>
  • #include "kernel_cfg.h"
  • #include "main.h"

  • /*
  •  * メインタスク
  •  */
  • void main_task(intptr_t exinf)
  • {
  •     while (1) {
  •         syslog(LOG_NOTICE, "main_task is running.");
  •         dly_tsk(1000);
  •     }
  • }


お次は「Hinagata」プロジェクトからFSPを含む必要なソースコードを「TOPPERS/ASP」のソースコードにコピーする作業です。

この作業は「TOPPERS/ASP - Arduino UNO R4版 その4」の記事を参考にしてください。

前回の「Hinagata」プロジェクトの状態ならば、コピーするディレクトリの中にシリアル通信ドライバなども含まれているはずです。

ただし、ビルド作業までやる必要はありません。

後述しますが、どうせエラーが出ます!!

さらに「e2 studio」でのプロジェクトも作っちゃいましょう。

この作業は「TOPPERS/ASP - Arduino UNO R4版 その6」の記事を参考にしてください。

ただし、この記事では「OBJ」ディレクトリに対しての作業でしたが、今回は「OBJ_template」ディレクトリに対する作業となります。

置き換えてお読みください。

以下のように「OBJ_template」をビルドする準備ができましたでしょうか?

ここで「all」というアイコンをダブルクリックすると、ビルドができるはずなのですが…

ビルド・ターゲット


…ビルドの結果は、あえなく撃沈!!

コンソール - 1


エラーメッセージの重要な部分を抜き出すと、以下の通りです。

  • ~/hal_entry.c:53: undefined reference to `R_SCI_UART_Read'
  • ~/hal_entry.c:59: undefined reference to `R_SCI_UART_Write'
  • ~/hal_entry.c:53: undefined reference to `g_uart2_cfg'
  • ~/hal_entry.c:53: undefined reference to `g_uart2_ctrl'


定義されていない関数やら変数やらへの参照があります」と言っています。

これは「Hinagata」プロジェクトのディレクトリから必要なディレクトリやファイルはコピーしましたが、それらがビルドされていないことを意味します。

参照先も名前的に今回追加されたシリアル通信のドライバの関数や変数っぽいですね。

これを改修するには「OBJ_template」ディレクトリの直下にある「Makefile」を修正する必要があります。

「TOPPERS/ASP」の標準的な方法では「Make」コマンドを使用してOSとアプリケーションをビルドすることになっています。

「Make」コマンドは、ビルド対象の「Makefile」を読むことによりコンパイラのオプションや、コンパイルするべきソースコード、参照するインクルードファイルのパスなどを知ります。

したがって、今回のようにシリアル通信ドライバなど、新たに追加されたソースコードをビルドするためには「Makefile」を編集しなければなりません。

今回のエラーメッセージで表示された「R_SCI_UART_Read」などをキーワードに「asp_arduino_uno_r4_gcc-template」ディレクトリを検索すると「Makefile」に加えるべきソースコードが分かります。

その結果を踏まえ「Makefile」を以下のように修正します。

赤い部分が書き加えた箇所です。


C:\cygwin64\home\<ユーザー名>\asp_arduino_uno_r4_gcc-template\OBJ_template\Makefile

  • (153行目から)
  • #
  • # アプリケーションプログラムに関する定義
  • #
  • APPLNAME = main
  • APPLDIR =
  • APPL_CFG = $(APPLNAME).cfg

  • APPL_DIR = $(APPLDIR) $(SRCDIR)/library \
  •     $(TARGETDIR)/ra/fsp/src/r_sci_uart
  • APPL_ASMOBJS =
  • ifdef USE_CXX
  •   APPL_CXXOBJS = $(APPLNAME).o
  •   APPL_COBJS =
  • else
  •   APPL_COBJS = $(APPLNAME).o
  • endif
  • APPL_COBJS := $(APPL_COBJS) log_output.o vasyslog.o t_perror.o strerror.o \
  •     hal_data.o \
  •     r_sci_uart.o \
  •     vector_data.o
  • APPL_CFLAGS =
  • APPL_LIBS =
  • ifdef APPLDIR
  •   INCLUDES := $(INCLUDES) $(foreach dir,$(APPLDIR),-I$(dir))
  • endif


改行を意味する「¥あるいは\」にご注意ください。

つまり「APPL_COBJS」変数にビルドするべきソースコード(…というか拡張子が「.o」なのでソースコードと同名のオブジェクト)を加え(「hal_data.o」と「r_sci_uart.o」)、その上で、新たに加えられた「~/r_sci_uart」ディレクトリ(「r_sci_uart.c」の置いてある場所)を参照するように「APPL_DIR」変数に加えています。

<追記>

「APPL_COBJS」変数に「vector_data.o」も同時に加えるように修正しました!

理由は次回に説明させてください。

…ちょっと面倒くさいですね。

たとえば「Visual Studio」などのリッチな統合開発環境では、プロジェクトに新しいソースコードを加える場合は全てGUIで行えるようになっていますよね?

それに比べて「Makefile」を編集しなければソースコード一つも追加できないのは、あまりにも前時代的です。


しかし、良いこともあります。


少なくとも余計なソースコードがプロジェクトに入ってしまって、プログラムサイズが大きくなったり、原因不明のバグが出てしまう可能性が減ることです。

楽は、ヒトをダメにします。

組み込みエンジニアたる者、プロジェクトに含まれるソースコードは完璧に把握しなければなりません!!

(…まあ、ぶっちゃけ不便だとは思ってます…。)

ともあれ、これで「e2 studio」の画面右側の「ビルド・ターゲット」ビューから「OBJ_template」ディレクトリ直下の「all」アイコンをダブルクリックすれば、無事ビルドが通るはずです。

コンソール - 2


今のところ「OBJ_template」では、前述したシンプルなプログラムが記述されているのみです。

次回は、これを前回のベアメタル版と同じ内容のプログラムに実装し直し、RTOSを使った利点を明らかにしていきたいと思います。

RTOS使った方がメンドイじゃん?…とか言わんといて…。


<続く>

2025年2月4日火曜日

zlib License

OSSライセンスのメインページはこちらからどうぞ。

ライセンスの目次はこちらです。


名称:「zlibライセンス」


Zlib 3D green


タイプ:

・コピーレフト…×

・ライセンス文の掲示…〇(ソースコード頒布のみ)

・コピーライト(著作権)の掲示…〇(ソースコード頒布のみ)

・その他…


原文:


  • Copyright (c) <year> <copyright holder>

  • This software is provided 'as-is', without any express or implied warranty.
  • In no event will the authors be held liable for any damages arising from the use of this software.

  • Permission is granted to anyone to use this software for any purpose,
  • including commercial applications, and to alter it and redistribute it
  • freely, subject to the following restrictions:

  •    1. The origin of this software must not be misrepresented; you must not
  •       claim that you wrote the original software. If you use this software
  •       in a product, an acknowledgment in the product documentation would be
  •       appreciated but is not required.

  •    2. Altered source versions must be plainly marked as such, and must not be
  •       misrepresented as being the original software.

  •    3. This notice may not be removed or altered from any source distribution.


日本語訳:


  • Copyright (c) <年>, <著作権所有者>

  • このソフトウェアは、明示的または黙示的な保証なしに「ありのまま」提供されます。
  • いかなる場合も、著者は、このソフトウェアの使用から生じるいかなる損害に対しても責任を負いません。

  • このソフトウェアを商用アプリケーションを含むあらゆる目的に使用し、
  • 変更して自由に再配布することを誰でも許可されますが、
  • 次の制限が適用されます:

  •    1. このソフトウェアの出所を偽ってはなりません。
  •       オリジナルのソフトウェアを作成したと主張してはなりません。
  •       このソフトウェアを製品で使用する場合は、製品ドキュメントに謝辞を記載していただければ幸いですが、
  •       必須ではありません。

  •    2. 変更されたソース バージョンには、その旨を明記する必要があり、
  •       オリジナルのソフトウェアであると偽って記載してはなりません。

  •    3. この通知は、ソース配布から削除または変更することはできません。


解説:

zlibは、データの圧縮および伸張を行うための有名なライブラリであり、このライセンスはそのために作成されたものです。

一見「二条項BSDライセンス」などの緩いタイプのライセンスと同内容に見えますが、ライセンス文の掲示コピーライト(著作権)の掲示の義務はソースコード頒布時のみに要求されます。

一方、バイナリ頒布の場合は、謝辞を記載して欲しい旨の記述はありますが義務ではありません。

総じて、バイナリ頒布の際には「二条項BSDライセンス」などよりも更に寛大なライセンスと言えます。

ただし、ソースコード頒布の場合には、作者を偽ったりしてはならず、仮に変更した場合はその箇所を明らかにする必要があるなど、オリジナルを尊重する意思が強いライセンスであることが特徴です。


適用例:

きっかけとなった「zlib」を含め、たまに目にするライセンスです。

また、バイナリ頒布に有利な内容であることから組み込み分野と相性が良く、一例としては、Silicon Labs社のマイコンのためのSDKである「Simplicity SDK」は、このライセンスで提供されています。


頒布のために執るべき行動:


ソースコード頒布の場合:

ライセンス条文1により、オリジナルのコピーライト(著作権)はそのまま掲示しましょう。

また、ライセンス条文3により、ライセンス文をそのまま改変することなく配布しましょう。

オリジナルから変更した箇所がある場合には後述します。


バイナリ頒布の場合:

特にありません。

しかし、製品や成果物に「四条項BSDライセンス」など、他に謝辞の掲示が必要なソフトウェアが含まれる場合には、zlibライセンスのソフトウェアの謝辞も追記してあげてください。

すでに掲示場所が存在しているのなら大した手間ではないはずですし、作者への敬意を表しましょう。


その他:

ライセンス条文2により、ソースコード頒布の場合、もしオリジナルを変更した場合はその旨を明記する必要があります。

2025年1月5日日曜日

Simplicity Studioを使ってみた! その4

前回からの続きです。

このテーマを最初からご覧になる場合はこちらからどうぞ。


「Simplicity SDK 32-bit MCU Peripheral Examples」

前回まで、Silicon Labs社のマイコンと、その統合開発環境「Simplicity Studio」を見てきました。

評価ボード - 1


…が、更に色々と使っていくうちに気が付きました。


めちゃくちゃクセが強い上に、情報が少ない!!


特に、普段からSTMicroやRenesasを使っていて、Silicon Labsに乗り換えた場合は特にそう思う方が多いと思います。

開発環境のクセの強さはまだ容認できるとして、問題は情報(ドキュメント)の方です。

まず、メーカーの公式のリファレンスは、マイコンの色々な機能を使う上で重要なことが全く書いていない…訳ではないものの、サラッと、さりげなく、目立つことなく、ゴニョゴニョ…といった感じです。

また、秋葉原で手軽に買えるような評価ボードが皆無であるため、STMicroやRenesasなどと比べてホビーで使用する人も少なく、ネットでの情報も極めて限られています。

(メーカーのユーザーフォーラムは役に立ちますが、聞かないと教えてくれないスタイル?)

こういったマイコンを使いこなす上で、もっとも重要なのはメーカーが提供しているSDKやライブラリの使い方です。

その点では、Silicon Labs社は、豊富でシンプルなサンプルプログラムを提供しており、我々哀れなエンジニアを救済して下さいます。

以下「Simplicity SDK 32-bit MCU Peripheral Examples」のリポジトリです。

(最新のSilicon Labs社製マイコン用のSDKは「Simplicity SDK」といいます。

少し前まで「Gecko SDK」というものもありましたが、これは今後「Simplicity SDK」に置き換えられていくものと思われます。)


https://github.com/SiliconLabs/peripheral_examples_simplicity_sdk

「Simplicity SDK 32-bit MCU Peripheral Examples」ページ - 1


これには随分と助けられましたので、今回は、このサンプルプログラム群の使い方を私自身の備忘録を兼ねて紹介したいと思います。


サンプルプログラムのダウンロード

サンプルプログラムをダウンロードします。

このページはGitHubですので、Gitコマンドを使ってクローンしても良いですが、面倒くさい方は以下のようにZIPでダウンロードも可能です。

「Simplicity SDK 32-bit MCU Peripheral Examples」ページ - 2


保存先は、どこでも構いません。

ただし、ZIPでダウンロードされる方は、当然ダウンロードが完了したら展開する必要があります。

この中には、あらゆるペリフェラル(マイコンの周辺機器)のサンプルプログラムが含まれており、かなりサイズの大きいディレクトリとなります。

ここから必要なサンプルプログラムのプロジェクトを「Simplicity Studio」を使って、ワークスペースにインポートすることになりますが、その前に…。


ワークスペースの確認

サンプルプログラムをインポートする前に、ワークスペースという概念を理解する必要があります。

前回「Simplicity Studio」で「empty」というプロジェクトを作りましたよね?

この「empty」プロジェクトのソースコードや設定ファイルなどは何処に保存されているのでしょう?

その答えが「ワークスペース」です。

デフォルトでは、以下のパスに存在しますので確認しておきましょう。


C:\Users\<ユーザー名>\SimplicityStudio\v5_workspace


前回の「empty」プロジェクトもここにありますね。

エクスプローラー - 1


つまりプロジェクトのインポートとは、他のディレクトリにあるプロジェクトをこのワークスペースに取り込むことを意味します。


サンプルプログラムのインポート

まずは「Simpilicy Studio」を立ち上げましょう。

また、同時に評価ボードと作業用のパソコンをUSBで接続しておきましょう。

現在、左上の「Project Explorer」には「empty」プロジェクトが一つだけ表示されているはずです。

「Simplicity Studio」 - 1


ダウンロードした(更に展開した)サンプルプログラム「Simplicity SDK 32-bit MCU Peripheral Examples」に含まれる多くのプロジェクト、そのうちの一つをインポートしてみましょう。

画面左上の「File」メニューから「import」をクリックします。

「Simplicity Studio」 - 2


「Import Project」ダイアログが開きます。

インポートするプロジェクトが保存されているディレクトリを指定するために「Select a project to import:」コンボボックスの右側の「Browse...」ボタンをクリックします。

「Import Project」ダイアログ - 1


「フォルダーの選択」ダイアログが開きますので、ダウンロードしたサンプルプログラムの場所まで移動します。

GitHubからZIP形式でダウンロードした場合は、展開したディレクトリ名は「peripheral_examples_simplicity_sdk-main」になるでしょう。

更に、そのディレクトリの中から「series2」というディレクトリをダブルクリックします。

「フォルダーの選択」ダイアログ - 1


「series2」ディレクトリの中には、ペリフェラルの名前を持つ相当な数のディレクトリが格納されています。

今回は「gpio」ディレクトリをダブルクリック。

「フォルダーの選択」ダイアログ - 2


まだまだ潜ります。

「gpio」ディレクトリの中にも「gpio」を使用するいくつかのサンプルプログラムが格納されているようです。

ここでは、なるべくシンプルそうな「swith_led_interrupt」ディレクトリをダブルクリックです。

「フォルダーの選択」ダイアログ - 3


最後に「swith_led_interrupt」ディレクトリの中の「SimplicityStudio」ディレクトリを選択状態にしてから、ダイアログ右下の「フォルダーの選択」ボタンをクリックします。

「フォルダーの選択」ダイアログ - 4


「Import Project」ダイアログに戻ると中央のリストにたくさんのプロジェクトが表示されているはずです。

プロジェクト名を頼りに、今使っている評価ボードと、それに搭載されているマイコンの型番用に作られたプロジェクトを選択してください。

同じ型番がない場合は、なるべく近い型番を選択します。

この場合、プロジェクトによっては動作しない可能性がありますが、ほとんどの場合は大丈夫でしょう。

(私の評価ボードは「Z-Wave 800 Pro キット」なので、近い型番は「BRD4204D_EFR32ZG23_switch_led_interrupt~」というのを選択しました。)

プロジェクトを選択状態にしたらダイアログ下部の「Next >」ボタンをクリックしてください。

「Import Project」ダイアログ - 2


以下の表示に切り替わります。

ここでは、特に何もせず「Next >」ボタンをクリックです。

「Import Project」ダイアログ - 3


以下の表示に切り替わります。

ここでインポートするプロジェクトの名前を変更できますが、特に必要がなければ、そのままにしておいた方が無難かと。

Finish」ボタンをクリックして、インポート終了です。

「Import Project」ダイアログ - 4


「Simpilicy Studio」に戻り、左上の「Project Explorer」に注目です。

元々あった「empty」プロジェクトに加え「BRD4204D_EFR32ZG23_switch_led_interrupt」が追加されていることが確認できます。

「Simplicity Studio」 - 3


「ワークスペース」も確認しておきましょう。

ちゃんと「BRD4204D_EFR32ZG23_switch_led_interrupt」ディレクトリが追加されていますね。


C:\Users\<ユーザー名>\SimplicityStudio\v5_workspace

エクスプローラー - 2


サンプルプログラムの書き込みと実行

インポートしたサンプルプログラムをターゲット上で走らせるためには、まずソースコードをコンパイルしなければなりません。

そのあとにコンパイルの結果生成されたバイナリをターゲットに転送、実行…となるわけですが、これらはたった一つの操作で完了します。

それは「Simpilicy Studio」上部に配置されている虫マークをクリックするだけです。

この時「Project Explorer」で「BRD4204D_EFR32ZG23_switch_led_interrupt」プロジェクトが選択状態になっていることが重要です。

もし「empty」プロジェクトが選択状態になっていると、上記の作業が「empty」プロジェクトに対して行われることになりますのでご注意を。

「Simplicity Studio」 - 4


虫マークをクリック後に、以下のダイアログが表示されることがあります。

これは、現在パソコンに接続しているターゲットと、この「BRD4204D_EFR32ZG23_switch_led_interrupt」プロジェクトで設定されているターゲットが一致しない場合に表示されます。

まあ、多少の違い(フラッシュメモリの容量など)ならば問題ないはずなので「OK」ボタンをクリックです。

「Device Selection」ダイアログ


下のポップアップが表示された場合も同様の理由です。

勇気を出して「Yes」ボタンをクリック。

「Connection issue」ポップアップ


しばらくすると、コンパイルが始まり、それが終了するとターゲットにバイナリがダウンロードされます。

これらの作業が終了すると、以下のようにデバッガが起動し「main()」関数の先頭、すなわち「CHIP_Init();」の行でブレークがかかった状態になります。

「Simplicity Studio」 - 5


ここからプログラムを実行したい場合は「Simpilicy Studio」上部に配置されている「▶」マークをクリックします。

「Simplicity Studio」 - 6


どうやら、今回の「swith_led_interrupt」というサンプル・プログラムは、評価ボード上の2つのボタンを押下すると、それぞれに対応するLEDがトグルでON/OFFするという動作を行っているようです。

前回作った「empty」プロジェクトに似ていますが、ボタンの押下を割り込みで取っているところが大きな違いです。

うん、これで割り込みの使い方が理解できますね。

評価ボード - 2


このように「Simplicity SDK 32-bit MCU Peripheral Examples」から目的のペリフェラルのサンプルプログラムをインポートし、その動きを見ることによって、SDKの使い方やマイコンの動作を深く理解することができます。


なお、プログラムを停止したい場合は「Simpilicy Studio」上部に配置されている「■」マークをクリックします。

「Simplicity Studio」 - 7


プロジェクトファイルによるインポートの差異

さて、既にお気付きの方もいらっしゃると思いますが、今回のサンプルプログラム、前々回の「New Project Wizard」を使用して作った場合のプロジェクトと内部のファイル構成が全然違うんです。

GUIによるマイコンのピン設定を行うための「~.pintool」ファイルもありません。

そもそも、プロジェクトファイル自体が「~.slsproj」という見慣れないもの。

「New Project Wizard」を使用して作った場合のプロジェクトファイルは「~.slcp」でした。

まあ「~.slsproj」が簡易版(あるいは旧式)であり「~.slcp」がその発展版(あるいは新式)と解釈すれば良いのですが、不安なのは「New Project Wizard」を使用して作った、すなわち「~.slcp」のプロジェクトも「~.slsproj」と同様にインポートできるのか?…ということです。


試しに、やってみました。


「ワークスペース」にある「~.slcp」プロジェクトである「empty」ディレクトリをどこか別のディレクトリにコピーします。

その上で、このコピーした「empty」ディレクトリをこれまでと同じ手順でインポートします。

すると「Import Project」ダイアログでプロジェクトファイルの存在するディレクトリを指定すれば「~.slcp」ファイルも正しく認識してくれました。

ただし、同時に「~.sls」という謎のファイルも表示されています。

結論から言えば「~.sls」は「Simplicity Studio」でプロジェクトをエクスポートした際に生成されるファイルです。

プロジェクトをディレクトリではなく1つのファイルとして出力してくれるので使い勝手が良いものです。

(Tarballみたいなものかな?)

しかし、今「empty」ディレクトリの中に「empty.sls」は見当たらないし、そもそも作ってもいない(エクスポートしていない)はずなのに、ここに表示されるなんて…かなりの違和感、とっても不思議。

ともあれ、どちらを選択しても「empty」プロジェクトは、正しくインポートできました。

「Import Project」ダイアログ - 5


さて、プロジェクトのインポート方法を見てきました。

冒頭にも申し上げた通り、Silicon Labs社のマイコンは、そのペリフェラルの仕様も含め、たとえ安価な評価ボードが売られていたとしても、これはホビー向けではないなぁ~…という印象です。

色々、落とし穴も多過ぎる…。

今回ご紹介したサンプルプログラム「Simplicity SDK 32-bit MCU Peripheral Examples」の存在が唯一の救いです。

少ない手掛かりを頼りに地道にマイコンの動作を掌握していく…、これも組み込みソフトウェアの醍醐味ではあるのですが…。

まあ、使いこなせれば大きな自己満足にはなるかな!


遅ればせながら…。


明けましておめでとうございます。

今年も皆様にとって良い年になりますように!

戦争やら自然災害やら、今年こそは世の中、ちょっと落ち着いて欲しい…。

(あと、仕事も!)

そんな希望を抱きつつ。


今度こそ…<終わり>

TOPPERS/ASP - Arduino UNO R4版 その11

前回からの続き です。 このテーマを最初からご覧になる場合は こちら からどうぞ。 RTOSを使用した利点とは? 前回 までに「Arduino UNO R4」で、ターミナル・ソフトウェアで入力した文字をシリアル通信でそのまま返すという簡単なプログラムを作成してきました。 そして、...