メインマイコンボードにあるLED7とLED8を点灯および消灯させるプログラムをC++で書きます.あらかじめデフォルトプロジェクトを流用してプロジェクトを新規作成してください.その後,端子に接続されているLEDを制御するプログラムを作成します.そのために,RX62Nに備わる端子を操作するレジスタについても説明します.
-
LED7とLED8のマイコンとの接続状況
まずはLED7とLED8がマイコンのどの端子とどのように接続されているのか確認しておきましょう.下に接続状況を示します.それぞれのLEDはカソードがマイコン端子と接続されておりますので,Lowをマイコンから出力すると点灯し,Highを出力すると消灯します.また,端子はLED7がポートEビット4,LED8がポートEビット5と接続されています. -
LED7およびLED8のクラス
下にLED7とLED8のクラス図を示します.それぞれLed7とLed8とします.これらのファイルはともにシングルトンになっていることが分かるかと思います.いくつかのメンバ関数(Javaでいうところのメソッド)が備わり,点灯させるためのメンバ関数がturnOn,消灯させるメンバ関数がturnOffです. -
クラスファイルの位置
Led7クラスとLed8クラスのソースはそれぞれLed7.cppとLed8.cppファイルに,ヘッダはそれぞれLed7.hppとLed8.hppに記述します.これらのファイルは,LEDがデバイスであるため,「device」名前空間を作成し,さらにその下に「led」名前空間を作成してそこにおきます.下の図はCubeSuite+において新しいカテゴリを追加した様子を表しています.名前空間をカテゴリとして表すことで,ファイルが増えてきたときに対応できるようにしていきます.
すべてのカテゴリを追加した結果を下に示します.C++ SourcesとC++ Headersの中に,deviceとdisplayとledというカテゴリが追加されています.
次に上記カテゴリにLed7.cppなどのファイルを追加していきます.ここでは,astah*でクラスを定義してあるものとして説明します.なお,スケルトンコードの生成方法はこちらに書かれていますので分からない場合には読んでください.こちらに上記クラスをastah*で書いたファイルを置いてありますのでダウンロードし,スケルトンコードを生成してください.
生成したファイルをCubeSuite+のプロジェクトに追加します.デフォルトプロジェクトを流用してプロジェクトを作成してあるはずですので,C SourcesやC++ Headersなどのフォルダが既に存在しているはずです .それらのフォルダにスケルトンコードを入れてください.例えばLed7.cppの場合,C++ Sources¥device¥display¥ledの下に置いてください.追加方法は追加したいフォルダを右クリックし,追加→既存のファイルを追加を選択します.すべてのファイルを追加しましたら下図のようになります.Led7.cpp,Led8.cpp,Led7.hpp,led8.hppが所定の場所に配置されていることが分かります. -
Led7.cppへの記述(前半)
スケルトンコードには各クラスにあるメンバとメンバ関数,定数を宣言したのみであり,処理つまりプログラムは書かれていません.そこで,ここではプログラムを書きましょう.
まず,Led7.cppです.コンストラクタでは,特に行うことはありません.コピーコンストラクタおよび代入演算子ではコピー元の_singletonをコピー先の_singletonに代入してください.なお,代入演算子の戻り値は*thisとなります.デストラクタには,_singletonをdeleteするように記述してください.ここまでのプログラムを下に示します.
Led7 *Led7::_singleton = NULL; Led7::Led7(){ } Led7::Led7(const Led7& src){ /* シングルトンをコピーしておく */ _singleton = src._singleton; } Led7& Led7::operator=(const Led7& src){ /* シングルトンをコピーしておく */ _singleton = src._singleton; return *this; } Led7::~Led7(){ /* オブジェクトを消す */ delete _singleton; }
次にgetInstanceメンバ関数を記述します.getInstanceでは,はじめて呼ばれたとき,つまり_singletonがNULLのときにLed7をインスタンス化し_singletonへ代入し,さらに初期化を行う_initializeを呼び出します.そして,_singletonを返すことで唯一のオブジェクトである_singletonに外部からアクセスできるのです.下に実装例を示します.Led7* Led7::getInstance(){ /* 初めてLEDを使うとき */ if(_singleton == NULL) { /* メモリの確保 */ _singleton = new Led7(); /* 初期化 */ _singleton->_initialize(); } return _singleton; }
残りの_initialize,turnOnおよびturnOffメンバ関数には,マイコンの端子を操作するプログラムを記述しますので,端子を操作するためのレジスタについて説明したのち,実装方法について説明します.
-
端子を操作するレジスタ
下の表に端子を操作するためのレジスタを示します.その後,それぞれのレジスタについて説明していきます.
名称 メンバ 役割 データディレクションレジスタ DDR 端子の入出力方向を決める データレジスタ DR 出力データを格納する ポートレジスタ PORT 入力データを参照する 入力バッファコントロールレジスタ ICR 入力バッファの有効/ 無効を制御する オープンドレインコントロールレジスタ ODR オープンドレインの有効/ 無効を制御する プルアップ抵抗コントロールレジスタ PCR プルアップ抵抗の有効/ 無効を制御する - データディレクションレジスタ(DDR)
このレジスタにより端子の入出力を決めることができます.端子に対応するビットを0にすると入力端子,1にすると出力端子にすることができます.例として,ポートAビット3の端子を出力端子にするプログラムを下に示します.
PORTA.DDR.BIT.B3 = 1;
- データレジスタ(DR)
このレジスタに出したいデータを書き込みますと,出力端子からデータを出すことができます.例として,ポートAビット3の端子からHighを出力するプログラムを下に示します.なお,このプログラムの前に,DDRにより出力端子にしておく必要があります.
PORTA.DR.BIT.B3 = 1;
- ポートレジスタ(PORT)
マイコンに接続されたスイッチなどの入力装置の状態を読み取るときにPORTレジスタを使用します.つまり,RXマイコンでは出力するときにはDR,入力するときにはPORTというように,入出力の方向によりレジスタが異なります.これはSuperHとは異なる点です.例として,ポートAビット4の端子と接続されているスイッチの状態を取得し,dummy変数に代入するプログラムを下に示します.
PORTA.DDR.BIT.B4 = 0; dummy = PORTA.PORT.BIT.B4;
- 入力バッファコントロールレジスタ(ICR)
周辺モジュールの入力端子,例えばIRQなどの外部割込みを行うとき,あらかじめ対応する端子の入力バッファを有効にする必要があります.この時用いるのがICRです.もし,この操作を怠ると,入力信号は常にHighに固定されてしまいます.気を付けましょう.下にポート1ビット3の端子の入力バッファを有効にするプログラムを示します.
PORT1.ICR.BIT.B3 = 1;
- オープンドレインコントロールレジスタ(ODR)
端子によっては,出力形態をCMOS出力か,NMOSオープンドレイン出力か選べます.このとき使用するのがODRです.使用できるポートは,0,1,2,3,Cです.ポート1ビット2の端子をオープンドレイン出力にするときのプログラムを下に示します.なお,このプログラムとは別にポート1ビット2の端子を出力端子とする必要があります.PORT1.ODR.BIT.B2 = 1;
- プルアップ抵抗コントロールレジスタ(PCR)
端子によっては,端子をプルアップすることができるようになっています.このとき使用するのがPCRです.使用できるポートは9,A,B,C,D,E,Gです.ポート9ビット3の端子をプルアップするときのプログラムを下に示します.なお,このプログラムとは別にポート9ビット3の端子を入力端子とする必要があります.PORT9.PCR.BIT.B3 = 1;
- データディレクションレジスタ(DDR)
- Led7.cppへの記述(後半)
Led7.cppのうち,まだ実装していない_initialize,turnOnおよびturnOffメンバ関数を実装します.まず_initializeでは,LED7が接続されているポートEビット4を出力端子にし,その後,LED7を消灯させるためHighを出力します.出力端子にするには,対応するデータディレクションレジスタを1にすればよいので,PORTE.DDR.BIT.B4 = 1;とすればよいです.また,Highを出力するには対応するデータレジスタを1にすればよいので,PORTE.DR.BIT.B4 = 1;とすればよいです.下に実装例を示します.
void Led7::_initialize(){ /* LED7が接続されているポートEビット4を出力端子にする */ PORTE.DDR.BIT.B4 = 1; /* ポートEビット4から1を出力(=消灯)させる */ PORTE.DR.BIT.B4 = 1; }
次にturnOn,turnOffメンバ関数にプログラムを書きます.点灯させるには端子からLowを出力し,消灯させるにはHighを出力すればよいので,プログラムは下のようになります.
void Led7::turnOn(void){ /* 点灯させるため,Lowを出力する */ PORTE.DR.BIT.B4 = 0; } void Led7::turnOff(void){ /* 消灯させるため,Highを出力する */ PORTE.DR.BIT.B4 = 1; }
以上でLed7.cppへの実装は終わりです.同様にLed8.cppへも実装してください.
演習
Led8.cppを実装してください. -
Main.cppへの記述
Led7.cppおよびLed8.cppへのプログラミングが終了しましたら,それらを使ってLEDを点灯および消灯させてみましょう.今回は,点灯と消灯を一定周期で行うプログラムを作成します.下にシーケンス図を示します.Main関数内にあるウェイトはfor文で1000万回繰り返すものを用います.
プログラム例を下に示します.Led7クラスがdevice::display::led名前空間の中にあるため,using namespaceを利用していることに注意をしてください.
#include "Led7.hpp" #ifdef __cplusplus //#include <ios> // Remove the comment when you use ios //_SINT ios_base::Init::init_cnt; // Remove the comment when you use ios #endif void main(void); #ifdef __cplusplus extern "C" { void abort(void); } #endif using namespace device::display::led; void main(void){ /* LED7のインスタンスを得る */ Led7 *led = Led7::getInstance(); while(1){ /* 無限ループ */ /* 点灯させる */ led->turnOn(); /* ウェイト */ for(int i=0; i<10000000; i++); /* 消灯させる */ led->turnOff(); /* ウェイト */ for(int i=0; i<10000000; i++); } }
演習
LED8も点灯および消灯するか確認するプログラムを作成してください.