RZ/A1で楽しい組込み開発(2)2017/03/14
プログラムを動かそう
前回で開発環境をそろえましたので、実際にプログラムを作ってボードで動かしてみましょう。LEDをチカチカさせるためのGPIOの制御方法や、メモリ配置の設定、JTAG-ICEを使ってボードで動作するところまで説明します。
ボードを用意しよう
プログラムを作る前にボードを用意します。コンピューテックス製のCEV-RZ/A1L(以降CEVと表記します)を使用します。

このボードにプログラムを載せていくわけですが、まずはボードの仕様を把握するために、マニュアルをダウンロードしましょう。CEVのハードウェアマニュアルは下記のURLからダウンロードできます。
https://www.computex.co.jp/products/pdf/manual/cev/cev-rz_a1l_hardware_manual.pdf
このマニュアルによると、幸い(?)にもポートにつながったLEDが2つあります。ハードウェアマニュアルを引用すると、「モニタ用としてCPU のP7_8 にLED1(赤)が、P7_9 にLED2(赤)が接続されています。」となっています。
このLED、光っていないときは地味で分かりにくいのですが、通電するとまぶしい光を放ちます。これを使ってLチカを実現しましょう。
RZ/A1LのGPIO
CEVで使用しているP7_8、P7_9というのは汎用入出力ポートのことで、一般にGPIOと呼ばれているやつです。GPIOは普通、他の機能と兼用ピンになっていますので、
- このピンを汎用ポートとして使用する
- 入出力の方向
の2つを設定すれば、大体の場合OKです(だと思います...)。
先にダウンロードしたRZ/A1Lのユーザーズマニュアルを開いて、汎用入出力ポートを見てみると、兼用モードを設定するポートモード制御レジスタ(PMCn)と、ポートの入出力を決めるポートモードレジスタ(PMn)がありますので、これらを初期化します。
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
1:兼用モード
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
1:入力モード
PMCやPMについているnはポートグループを表しています。P7_8というのはグループ7の8ビット目ということになります。つまり、PMC7の8ビット目を0にすればポートモードになり、PM7の8ビット目を0にすれば出力モードになります。
実際のデータを出力するのはポートレジスタ(Pn)ですので、このレジスタ(P7の8ビット目)に書き込めばLEDがチカチカしてくれるはずです。
P7_8とP7_9へ書き込むための設定をまとめますと、以下のようになります。
- PMC7の8と9ビット目を0にする。
- PM7の8と9ビット目を0にする。
- P7の8ビット目に1を書き込めばLED1が点灯し、0を書き込めば消灯。
- P7の9ビット目に1を書き込めばLED2が点灯し、0を書き込めば消灯。
コードを追加しよう
e2 studioを閉じている場合は、もう一度起動してください。先ほどのワークスペースからtest.cを開いてコードを追加します。
まず、iodefine.hをインクルードします。先頭に「#include “iodefine.h”」を追加します。ついでに「#ifdef CPPAPP」で囲まれたところは、今回使わないので削除しておきます。
次はmain関数の先頭でGPIOの初期化を行います。GPIOのPMC7とPM7のビット8と9に0を書き込みます。
このエディタの機能として「GPIO.」と打ち込むと、後続するメンバ名を補完してくれるはずです。やってみましょう。

あれ?補完されませんね。
test.cを保存してからもう一度CTRL+Spaceを押してみましょう。

今度はちゃんと表示されました。これは、「#include “iodefine.h”」を追加した後で、ファイルを保存していなかったために、iodefine.hが解析されなかったためのようです。
では、引き続きPMC7を選択します。代入する値として、使わないビットに一応マスクをかけておきましょう。16ビットのビット8と9ですから0x0300ですね。これをビット反転してマスクをかけて代入します。つまり「&= ~0x0300;」となります。PM7も同様に追加します。
GPIO.PMC7 &= ~0x0300; GPIO.PM7 &= ~0x0300;
GPIOの初期化コードを追加したソースコードは以下のようになります。

さあ、いよいよLEDの制御を追加します。
先ほどのポートと同じ要領でP7のビット8と9を書き換えます。まずはLED1をONにします。ORで代入してビット8だけを1にセットします。
GPIO.P7 |= 0x0100;
次はLED1をOFFにします。セットしたビットを0にします。
GPIO.P7 &= ~0x0100;
同様にLED2にも同じことを行います。
GPIO.P7 |= 0x0200; GPIO.P7 &= ~0x0200;
最終的にソースコードはこのようになりました。

ここでは生真面目にポートをビット操作しましたが、ほかにポートを使っているわけではないので、以下のようにしても動作します。
GPIO.P7 = 0x0100; // LED1のONとLED2のOFF GPIO.P7 = 0x0200; // LED2のONとLED1のOFF
では、ビルドしてみましょう。

無事ビルドが通りましたので、これをCEVボードにダウンロードして実行します。
ここでCPUの初期化はどうなっているのか、疑問に感じられる方もおられるでしょう。実はプロジェクト作成時にreset_program.asmというソースファイルが作られて、最小限の初期化が行われるようになっています。
また、このプログラムは何番地で動作するのかメモリマップも気になるところだと思います。プログラムの配置アドレスはリンカによって決められますので、リンカの設定をのぞいてみましょう。
リンカのセクションを確認しよう
コンパイラやリンカのオプションを表示するには、プロジェクト・エクスプローラーのルート「test [HardwareDebug]」を右クリックし、[Renesas Tool Settings]を選択します。プロジェクト・エクスプローラーのルートを右クリックしないと、リンカの設定が表示されませんので注意してください。
ここで[Linker][セクション]をクリックすると、セクションの表示に切り替わります。

この表示を見ると、4つのセクションでアドレスが設定されています。
.text (0x20020100)
.data (0x20060100)
.stack (0x20061100)
0x20020000番地はRZ/A1Lの内蔵RAMを指しています。したがって、このプログラムはすべて内蔵RAMで動作するということになります。RZ/A1Lの内蔵RAMは3MBありますので、余裕でプログラムを走らせることができます。
JTAG-ICEを接続しよう
それでは、実際にプログラムをCEVボードにダウンロードしてみます。使用するJTAG-ICEはもちろんPALMiCE3です。CEVボードとの接続は20ピンのハーフピッチのJTAGコネクタです。ボードへの電源はパソコンからのUSBで供給します。
接続するとこんな感じになります。

CSIDEを起動する
PALMiCE3が接続できたら、デバッガCSIDEを起動します。ここからはCSIDEがインストールされている前提で話を進めますのでご了承ください。詳しくはPALMiCE3 ARMのマニュアルをご覧ください。
CSIDEを起動し、[設定]メニューの[ターゲットシステムの設定]を選択すると、CPUの選択画面が表示されます。ここで、メーカ名は[Renesas]、CPUは[RZ/A1]を選択し、[起動する]ボタンを押します。

次にターゲットシステムの設定ウィンドウが表示されますので、[更新]ボタンを押してください

これで起動完了です。
オブジェクトファイルのダウンロード
さて、次はビルドしたオブジェクトファイルをダウンロードします。ファイルロードのボタンを押して、ファイルを選択します。[ファイル名]の項目に「*.x」を入力して、HardwareDebugのフォルダに移動します。

そうすると、オブジェクトファイル「test.x」が表示されますので、このファイルを選んで[追加]ボタンを押します。以下の画面のように登録されます。

ここで[ダウンロード]ボタンを押せば、ダウンロードが開始されます。

上記のロードアドレスを見ると0x20020000 - 0x20023DCFとなっていますので、内蔵RAM空間であることが分かります。
ダウンロードができたら、コードウィンドウを開いてみましょう。

ちゃんとダウンロードできたようですが、まだ安心してはいけません。
ここでミックス表示に切り替えて、アセンブラソースと逆アセンブラの対応が一致するかどうか確認します。

はい、ちゃんと一致しているようですね。プログラムが正しくメモリに書き込まれて、デバッグ情報と一致しているかどうかを確認しておくと、後々トラブルを起こさなくて済みます。
プログラムの実行
では、ソースモードに戻して、main関数まで実行します。CTRL+Uを押して、「main」と入力すればmain関数まで実行して停止します。

うまく行ったようです。mainまでの初期化でトラブることはよくありますので、すんなりいく方が珍しいと思います。
では、恐る恐るステップ実行して動作確認してみましょう。まずはGPIOの初期化の2行をステップ実行します。

無事通過しました。
次に27行目をステップ実行してLED1を点灯してみます。

うまく行きました。さらにステップ実行すると、LED1の消灯 ⇒ LED2の点灯 ⇒ LED2の消灯もOKでした。
では、実行してチカチカさせてみましょう。F5キーを押します。

チカチカが速すぎて、両方点灯しているように見えます(これは想定内です、、)。
ONとOFFの間にディレイを入れてチカチカの速度を調整します。本当なら、タイマーを使うところですが、早くチカチカが見たいのでソフトウェアタイマーを入れます。
void sleep(int n) { volatile int i; for (i = 0; i < n*10000; i++); }
まあ、こんな関数でいいのではないかと思います。変数iをvolatile宣言している理由は、最適化によりループがなくなってしまうのを防ぐためです。
この関数をmain関数の前に配置し、LED1のONとOFFの間、LED2のONとOFFの間で呼び出します。同じタイミングでチカチカさせても芸がないので、sleepの引数を調整してタイミングをずらしてみます。

LED1でsleep(50)とし、LED2はその倍のsleep(100)で点灯するようにしました。
ビルド後、ICEで再ロードして実行すると、今度はうまく、LEDがチカチカしました。いいタイミングで点滅を繰り返していて、見ていると癒されます。

CSIDEのプロジェクトを保存する
ここで一度、CSIDEを終了して設定をプロジェクトファイルに保存しておきましょう。[ファイル]メニューの[名前をつけて保存][プロジェクト]で保存します。
この時、e2 studioのワークスペースフォルダ内に保存しておくと、e2 studioのプロジェクト・エクスプローラーからcside.cpfをダブルクリックすることで起動できますので便利です。

次回はシリアルフラッシュメモリへ書き込んで、パワーオンでLチカを実現させます。