前回からの続きです。
このテーマを最初からご覧になる場合はこちらからどうぞ。
「e2 Studio」で普通にFSPを使う
さて、これまでにも何回か言及している通り、この「TOPPERS/ASP Arduinp UNO R4版」を使った「Arduino」は、もはやArduinoではありません。
便利な「Arduino IDE」も使えませんし、豊富なArduino用のライブラリも使えません。
つまり、安価なRenesas RAマイコン評価ボードになってしまったとも言えます。
しかし、単にライブラリという意味では「Arduino IDE」ではなく「e2 studio」を使用した開発でもFSP(Flexible Software Package)が使用できます。
このFSPについては、過去記事を参照してください。
FSPをうまく利用すれば、Arduino用のライブラリとまでは言えませんが、RAマイコンの持つ色々な機能をライブラリとして利用したソフトウェアを楽に開発できます。
いきなり「TOPPERS/ASP Arduinp UNO R4版」上でFSPを使うのは少々ハードルが高いです。
ですので、今回は、OSレスでフツーにFSPを使用する方法をご紹介します。
そのためには、以前作成した「Hinagata」プロジェクトを「e2 studio」で開きます。
んなもんとっくに消しちゃったよ~って方は、コチラを参考に再度作成してください。
今回は、この「Hinagata」プロジェクトでFSPを使用して、シリアル通信ポートを使えるようにライブラリを追加しましょう。
どのピンにシリアルポートを割り当てましょうか?
シリアル通信には、TXD(送信)、RXD(受信)、GNDの計3本の線が必要です。
GNDは適当で良いとして…、TXD(送信)、RXD(受信)は、以下の回路図のピンを使用することにしましょう!
絵にすると、以下の通りです。
これで…
「SCI2」というシリアルポートの送信(TXD)信号を「P302」というポートに
「SCI2」というシリアルポートの受信(RXD)信号を「P301」というポートに、それぞれ設定すれば良いことが分かります。
これを「e2 studio」で「Hinagata」プロジェクトに設定すればよいのですが、マイコンのピンを機能に割り付ける作業は、実は以前の記事で行っています。
その時に、今回使用する「P302」も「P301」も「SCI2」というシリアルポートで使用できるように既に設定されています。
したがって、残りの作業はシリアル通信のライブラリを設定することだけです!
「e2 studio」の左側の「プロジェクト・エクスプローラー」で「Hinagata」プロジェクト以下に「configuration.xml」というファイルがありますので、これをダブルクリックしてください。
すると、画面中央には「[Hinagata]FSP Configuration」というタブが追加されると思います。
次に、開いた「[Hinagata]FSP Configuration」タブの下にもいくつかのタブがありますので、この中から「Stacks」というタブをクリックします。
以下のように、なにやら「HAL Common Stacks」と題された表示に切り替わります。
この画面で必要なスタック…ライブラリとかドライバを追加していきます。
今回はシリアル通信、すなわちUARTのスタックを追加すれば良いわけですね。
「HAL Common Stacks」という表示の右側に「New Stack >」というボタンがありますので、これをクリックするとメニューが表示されます。
メニューの中から「Connectivity」を、更に展開されるメニューで今回のお目当てである「UART(r_sci_uart)」を順にクリックします。
すると、以下のように「HAL Common Stacks」の表示が切り替わり、UARTスタックが追加されたことが分かります。
追加されたUARTスタックですが、デフォルトのままでは都合が悪いので、プロパティをいじらないとなりません。
それには「[Hinagata]FSP Configuration」タブの下のビューの中の「プロパティー」タブをクリックします。
以下は、分かりやすいように拡大した「プロパティー」タブです。
ここには、追加されたUARTスタックの各種設定が表示されています。
これらの中から以下の項目を変更します。
前述した通り、今回は「SCI2」というシリアルポートを使用したいので、それに沿った変更を行います。
Name:g_uart2
Channel:2
Callback:uart2_callback
次に、以下の項目を確認しておきます。
ボーレートが115200bpsに設定されていることは、覚えておきましょう。
また、こちらも前述の通り、送信(TXD_MOSI)信号が「P302」というポートに、受信(RXD_MISO)信号が「P301」というポートに、それぞれ設定されていることを確認します。
Baud Rate:115200
TXD_MOSI:P302
RXD_MISO:P301
オッケー。
じゃあ、この状態でUARTスタックを追加した新しい「Hinagata」プロジェクトのソースコードを出力しましょう。
メインビューの「[Hinagata]FSP Configuration」タブの右上にある「Generate Project Content」ボタンをクリックします。
こんなポップアップが出たら「続行」ボタンをクリックで。
そういや、UARTスタックの追加とかプロパティの変更とか、保存してなかったや…。
処理が終わると、UARTスタックが追加された「Hinagata」プロジェクトが出来上がっているはずです。
試しに画面左側の「プロジェクト・エクスプローラー」で「ra_gen」ディレクトリ以下の「hal_data.c」をダブルクリックして、ソースコードを見てみましょう。
ソースコードの中にUARTスタックのプロパティで設定した「g_uart2」という文字列がいくつか確認できますね?
更に、新しい「Hinagata」プロジェクトのソースツリーの中には「uart」と記述されたソースコードやヘッダファイルがいくつも見つかります。
これらが追加されたUARTスタックの本体です。
さて、UARTスタックを手に入れた新しい「Hinagata」プロジェクトですが、これをビルドして動かしても何も起こりません。
UARTスタックを使う…すなわちシリアル通信を行うアプリケーションを実装しないと本当に正しく動くのか分かりませんよね?
というわけで、簡単なプログラムを実装して動作確認をしてみましょう。
プログラムを記述するのは「src」ディレクトリ以下の「hal_entry.c」です。
さっそく開いてみましょう。
この「hal_entry.c」ファイルに、以下のように「received」というフラグと「uart2_callback()」という関数を記述します。
- #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;
- }
- 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()」割り込みハンドラからここに来る
- }
- }
- // ここまで ----------------------------------------------------------------
- /*******************************************************************************************************************//**
- * main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
- * is called by main() when no RTOS is used.
- **********************************************************************************************************************/
- void hal_entry(void)
- ...
お次は、その下の「hal_entry()」内に以下のコードを加えます。
- /*******************************************************************************************************************//**
- * main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
- * is called by main() when no RTOS is used.
- **********************************************************************************************************************/
- void hal_entry(void)
- {
- /* TODO: add your own code here */
- // ここから ----------------------------------------------------------------
- uint8_t c;
- // シリアル通信を開く
- R_SCI_UART_Open(&g_uart2_ctrl, &g_uart2_cfg);
- while (1) {
- // 受信する
- R_SCI_UART_Read(&g_uart2_ctrl, &c, 1);
- // 受信するまで待つ
- while (!recieved);
- // 受信完了フラグをリセットする
- recieved = false;
- // 受信した一文字を送信する
- R_SCI_UART_Write(&g_uart2_ctrl, &c, 1);
- }
- // ここまで ----------------------------------------------------------------
- #if BSP_TZ_SECURE_BUILD
- /* Enter non-secure code */
- R_BSP_NonSecureEnter();
- #endif
- }
- …
なにをやっているのか?…というのは、まあ、コメントに書いた通りなのですが。
まず、追加した「uart2_callback()」というのは、コールバック関数というもので、チャンネル2のシリアルポートに割り込みが発生した場合に処理が飛んでくる関数です。
「uart2_callback」という名称については、UARTスタックを追加した時「callback」プロパティで設定しましたよね?
で、割り込みが発生して「uart2_callback()」関数が飛んできて、その中で「p_args->event」変数の内容を調べて、それが受信完了割り込み…すなわち「UART_EVENT_RX_COMPLETE」というフラグを含んでいた場合は、冒頭で定義した「received」変数を「true」にする…という処理をしています。
割り込みには受信完了のほか、送信完了やエラー発生など、色々なものがあります。
上記の例では、ご丁寧に受信完了以外の条件分岐も記述されていますが、何も実装していないし、今回は受信完了割り込みしか使わないので、面倒だったらそれ以外のif文は省いてオッケーです。
さて、次に「hal_entry()」です。
これは、最初から実装されている関数です。
C言語のプログラムは、一般的に「main()」という関数から処理が始まります。
しかしながらFSPでは、その役割は「hal_entry()」が担っています。
とはいえ、これはFSPのお作法というか…実際「Hinagata」プロジェクトのソースツリーの中にも「main.c」ファイルが存在し、その中に「main()」関数も記述されています。
この「main()」は開始早々この「hal_entry()」を呼んでいるので「hal_entry()」は、実質「main()」と同じですね。
(こんな回りくどい実装をしているのは「main()」を直接いじれなくなるRTOSを使う場合を想定している旨が、関数の上のコメントに書いてありますね。)
追記した部分ですが「c」という変数を定義した後に「R_SCI_UART_Open()」という関数を呼んでいます。
これでUARTスタック、すなわちシリアルポートを使用する準備をします。
その後、whileループに入ります。
ループの冒頭で「R_SCI_UART_Read()」という関数を呼んでいます。
これは、シリアル通信の受信を行う関数です。
この関数は、受信が終わるまでロックする…ということもなく、素通りします。
受信ができたらその時点で、受信データは関数に渡した「c」のポインタに格納される仕組みとなっています。
さっきから頭に「R_SCI_UART_うんちゃら」という名前の関数が出てきていますが、これこそが今回追加したUARTスタックで提供されている関数です。
これらの関数は、今回使用するものの他にも多く用意されています。
詳しくは、こちらを御覧ください。
さて「R_SCI_UART_Read()」で受信を指示した後は、更にループに入ります。
このループは「received」が「true」にならない限り、ここでずっとグルグル回っているという処理、すなわち無限ループになります。
「received」の初期値は「false」ですから、最初は必ずグルグル回ります。
その間に受信完了割り込みが起きて、前述の「uart2_callback()」の中で「received」が「true」に変化したら、ようやくこの無限ループから次に進むことができます。
このループ、要はシリアル通信の受信待ちです。
ループを抜けると、次の受信に備えて「received」を「false」に戻しておきます。
この時には既に「c」の中には受信した1文字のデータが格納されているはずです。
次の「R_SCI_UART_Write()」関数で、その受信した「c」の中の1文字のデータを今度はそのまま送信します。
送信後は、また受信処理に戻り、それを永遠に繰り返します。
すなわち今回のアプリケーションは、シリアル通信で1文字受信すると、それと同じ1文字を送信する…というテストプログラムになります。
さて、思惑通りに動くかな?
物理的な接続です!
まずは、Arduinoとデバッガーとパソコンを接続します。
このページを参考にして欲しいのですが、唯一違うのがシリアル通信部分。
今回は変換ケーブルから出ている信号線ではなくて、Arduinoのピンソケットから取りましょう。
ピンソケットの位置は、今一度、このページの冒頭の図を参考に。
GND、SCI_TXD2およびSCI_RXD2の3本ですね。
こんな感じ。
USB/シリアル通信変換ケーブル側の配線は、上からTXD、RXD、GNDの順番でこんな感じです。
これで「Hinagata」プロジェクトを走らせましょう。
まずは「プロジェクト・エクスプローラー」で「Hinagata」プロジェクトを右クリック、出てきたメニューから「プロジェクトのビルド」を左クリックしてください。
ビルドが終わったら、再び「プロジェクト・エクスプローラー」で「Hinagata」プロジェクトを右クリック、出てきたメニューから、今度は「デバッグ」を左クリック、更に表示されたメニューから「Renesas GDB Hardware Debugging」を左クリックします。
以下のダイアログが表示されたら「E2 Lite (ARM)」を選択状態にして、ダイアログ下部の「OK」ボタンをクリック。
再度、以下のダイアログが表示されたら「R7FA4M1AB」を選択状態にして、ダイアログ下部の「OK」ボタンをクリックです。
これでオッk…ああ!?
まあ、とりあえず「OK」ボタンをクリックで。
これを修正するには、このページの中盤くらいにある「エミュレーターから電源供給」の項目を「いいえ」に設定する必要があるようです。
デバッグの構成は「Hinagata.elf」という名前で、この時点で作成されています。
設定できたら「デバッグ」ボタンをクリックして、デバッグ開始です!
デバッグが開始されると、いくつかのポイントで自動的にブレークがかかります(2回くらいだっけか?)。
ブレークがかからなくなるまで「▶」ボタンをクリックしてプログラムを続行させましょう。
次に「TeraTerm」の起動です。
今回は、UARTスタックのプロパティで115200に設定したのですよね?
接続ができたら「TeraTerm」の画面でキーボードから何か文字を入力してみてください。
このように、入力した文字が「TeraTerm」にそのまま表示されれば動作確認は完了です!
「Hinagata」プロジェクトに無事UARTスタックが追加され、それが正常に動いていることが確認できました。
今回はUARTスタック、すなわちシリアル通信のライブラリを追加しましたが、I2CやSPIなどの他の通信の場合でも、FSPの使い勝手は概ね一緒です。
更には、AD変換やタイマーなど、FSPを使えばデバイスドライバを作成することなく簡単にこれらの機能をファームウェアで使用することが可能です。
便利な時代になりましたね~。
さて、今回はTOPPERS/ASPとは関係なく、フツーにFSPを使うだけの記事となってしまいました。
次回は、今回と同じプログラムを「TOPPERS/ASP Arduino UNO R4版」で動かす例を紹介します。
便利なFSPとTOPPERS/ASPを組み合わせて使うことができますよ!
あとは、今回のプログラムは受信待ちに「received」をフラグに無限ループを使うという、ある意味でダサい実装となってしまいました。
それがTOPPERS/ASPなどのRTOSを使用することで、どれだけスマートな実装になるのかも説明させていただきます。
…なにげに前回の投稿から2ヶ月も経っていたのですね。
ブログを書くのも趣味の一つなのですが、本業が忙しくてそれができないようだと、ライフワークバランスが崩壊しているなぁ~…と思います。
ライフのためのワークであって、ワークのためのライフでは本末転倒だと思う今日このごろです。
<続く>