H8Tinyコンソールボードを動かしてみよう Rev1.0 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう はじめに このテキストは、弊社製コンソールボード“NB304”を使ってプログラムを勉強することを目的として作られ ています。 これからハードウェア制御のプログラムを作ってみたい、またはCPUに触れてみたいといった初心者の 方々を対象とし、トランジスタ技術誌などで取り上げられているルネサステクノロジ製の“H8Tiny”と呼ばれ ている、ごく一般的なCPUを使用します。 テキストの内容についても、LEDランプを点灯させる、ブザーを鳴らす、スイッチを読み込む、LCDに表 示するなどの、入出力機能やタイマ機能といったCPUにとって最も基本で重要な部分に絞り込み、これら をコンソールボードでどのように実現するかについて、わかりやすく説明しています。 通常、新しいことを始める前には分厚い専門書やそれに関わる多くの資料を読み、わかりにくい部分はイ ンターネットで検索したり、誰かに聞いてみたりしないとなかなか上手くいきません。 新しいことを始めるためには当たり前のことなのですが、初心者にはこれが大きな壁となってしまい、挫折 してしまうのです。 そこで、このテキストは、“とりあえず動かしてみる”を念頭に置き構成されています。 プログラムの作成から、それをCPUに書き込み、動かすまでの大まかな流れを非常に簡単に説明してあ りますので、テキスト通りに実行すればCPUは確実に動作します。 本テキストを活用することで、“自分がCPUを動かせる”ことのすばらしさに感動するに違いありません。 また、本テキストがCPUへの興味を持つきっかけになると確信しています。 ii SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 目次 はじめに ........................................................................................................................................... ii 目次 ................................................................................................................................................ iii 第1章 とりあえず動かす..................................................................................................................1 1−1 早速、コンソールボードを組み立てます .................................................................................1 1−2 それでは電源を入れて動かしてみましょう ..............................................................................2 1−3 今度は、別のプログラムを書き込んでみましょう.......................................................................3 1−4 書き込んだプログラムの動作を確認してみましょう ...................................................................5 第2章 LEDランプを光らせる ..........................................................................................................6 2−1 まずはLEDランプを光らせてみましょう ..................................................................................6 2−2 CPUポートの仕組み ............................................................................................................7 2−3 それでは、早速プログラムを書いてみましょう ..........................................................................8 2−4 開発ツールを使ってHEXファイルを作成してみましょう ......................................................... 11 2−5 次はLEDランプを1つずつ、ずらしながら点灯させてみましょう ..............................................16 第3章 スイッチの読み込み............................................................................................................24 3−1 ポートを使ってスイッチの状態を読み込んでみましょう...........................................................24 3−2 スイッチの読み込みをプログラムしてみましょう......................................................................25 3−3 スイッチの状態をLEDランプに反映させてみましょう .............................................................25 3−4 チャタリング処理.................................................................................................................29 第4章 ブザーを鳴らす..................................................................................................................34 4−1 タイマ機能を使ってブザーを鳴らしてみましょう.....................................................................34 4−2 タイマ部分のプログラムを考えてみましょう............................................................................35 4−3 スイッチを押すとブザーが鳴るようにする ..............................................................................36 第5章 LCDに文字を表示する ......................................................................................................40 5−1 LCDへのアクセスタイミング ................................................................................................40 5−2 アクセス波形をプログラムで書いてみましょう ........................................................................41 5−3 LCDのイニシャライズ .........................................................................................................42 5−4 LCDに文字を表示させる....................................................................................................43 第6章 まとめ ................................................................................................................................49 6−1 コンソールボードの全機能をまとめて動かす.........................................................................49 6−2 キーマトリックスを読み取る ..................................................................................................50 6−3 チャタリング対策に割り込みを使う........................................................................................52 6−4 割り込み内の処理 ..............................................................................................................53 6−5 割り込みの初期設定...........................................................................................................55 6−6 スイッチ入力判定処理 ........................................................................................................60 付録1 ダウンロードアダプタ関連........................................................................................................a 参考文献 ...........................................................................................................................................b iii SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 第1章 とりあえず動かす 1−1 早速、コンソールボードを組み立てます LCDの接続部分とDCジャックを半田付けします。 LCDを接続し、ネジとスペーサで固定します。 基板固定用のスペーサを付けて完成です。(写真4) 写真1 同梱品一式 写真2 半田付け部品1 コンソールボードにはこの他にユーザーズマニュ LCDコネクタ、DCジャックそれからプログラムダウ アルが同梱されています。 ンロード用にピンヘッダを半田付けしします。 写真3 半田付け部品2 写真4 組み立て完了 コネクタHD2用のピンヘッダも半田付けする。 基板に固定用のスペーサをつけて完成です。 1 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 1−2 それでは電源を入れて動かしてみましょう DCジャックにACアダプタ(5V)を差し込みます。 LCDにSuntechと表示されました。(写真5) スイッチを押すとLEDランプが光り、ブザーが鳴ります。また、スイッチに応じた番号がLCDに表示さ れます。(写真7) 写真5 電源を入れる 写真6 LCDコントラストボリューム 半田付けしたDCジャックに※ダウンロードアダプ LCDのコントラスト調整用ボリュームです。 タCセットにあるACアダプタを差し込むとLCDに もし、LCDに何も表示されない場合は、ボードの Suntechの文字が表示されます。 裏に付いているボリュームをドライバなどでどちら もしご自分でACアダプタを入手される場合は5 かに回して下さい。 V・1A以上の容量でプラグの直径が5.5mm(セ ンタ+)の物をご用意ください。 写真7 スイッチを押してみる スイッチを押すとLEDランプが光り、ブザーが鳴り ます。また、スイッチに応じた番号がLCDに表示 されます。 ※ 付録1 ダウンロードアダプタ関連をご覧下さい 2 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 1−3 今度は、別のプログラムを書き込んでみましょう 添付CD 1章フォルダ内のファイル WELCOME.motを書き込みます。 書き込みツールは、※ダウンロードアダプタを使います。 また書き込みソフトは※H8WriteProg を使います。 まず、コンソールボードとダウンロードアダプタを接続します。(写真8) さきにダウンロードアダプタのフラットケーブルをHD2に接続し、次にACアダプタをDCジャックに挿し て下さい。(必ずこの順番で行って下さい) 続いてH8WriteProgを操作方法に従って実行すると書き込み終了です。(以下操作方法) ※ ダウンロードアダプタ及び H8WriteProg については、付録1のダウンロードアダプタ関連をご覧下さい。 コンソールボードもダウンロードアダプタ も裏面で接続出来る様にピンヘッダを裏 面に半田付けして下さい。 写真8 ダウンロードアダプタのフラットケーブルのコネクタ で、2列のうち1ピン側(1ピンマーク有り)の列に HD2 のピンヘッダを挿入して下さい。 3 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう H8WriteProg操作方法 ① H8WriteProgのアイコンをクリックしソフトウェアを起動します。起動すると図 1 のような画面が表示 されます。 ② ターゲットは、デバイスのプルダウンリストより、CPUボードに実装されているCPU型名を選択して 下さい。(今回はH8/3672です。) 周波数は、16と入力して下さい。 ③ 信は、ダウンロードアダプタを接続したシリアルポートを選択してください。 ボーレートはH8Tiny CPUボードの場合、9600を選択してください。 ④ 最後に、送信(書き込む)ファイルを指定してください。今回は、WELCOME.mot です。 CPU型名を選択してくだ さい。 数値を10進数で直接入力し てください。 書き込みファイルを指 定してください。 図1.設定画面 ⑤ 送信ボタンをクリックしてください。 ⑥ 送信が正しく行われると、図2のような画面が表示され、送信完了です。 図2.送信完了の画面 4 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 1−4 書き込んだプログラムの動作を確認してみましょう ダウンロードアダプタとACアダプタを抜いてACアダプタを再接続します。 すると、LCDの表示がSuntechからWelcomeに変わりました。(写真9) 確かにプログラムが書き換えられたことがわかります。 プログラムがどういう仕組みになっているのか、またどのように開発したらよいかについて、次章で勉強 していきましょう。 写真9 5 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 第2章 LEDランプを光らせる 2−1 まずはLEDランプを光らせてみましょう コンソールボードには4つのLEDランプが付いており、CPUとの接続は下図のようになっています。 ということは、CPUのポートがハイレベルつまり5Vを出力すればLEDランプは光ります。 CPUのポートP8のLEDランプが接続している端子に1を書き込むことで5Vを出力します。 逆に0を書き込むことでLEDランプは消灯します。 ※回路図のP80∼P83は、ポートP8のビット0∼3を意味します。 回路図1 P80 LED1 P81 LED2 CPU P82 LED3 LED4 P83 GND ※ CPUのポートから出力できる電流には制限があり、最大値を超えた場合にはCPUそのものを壊してし まう可能性があります。 出力可能な電流値を超えないようにハードウェアを設計する必要があります。 6 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 2−2 CPUポートの仕組み まずは、ポートというものについて説明します。 一般に、“ワンチップCPU”と呼ばれるCPUには“ポート“と呼ばれる入出力可能な端子が有ります。 出力用のポートは、その先につながっている端子に信号を与え、入力用のポートはハードウェアからの 信号を読み込みます。 それらの信号を絡み合わせることで、外部のハードウェアを制御します。 つまり、ワンチップCPUの場合、ポートの入出力ができれば何でも(ハードウェアが許す範囲で)できる ことになります。 それでは、コンソールボードを例にとって説明します。 ポートP8のLEDが接続されている端子に1を書き込むには、まずポートP8のアドレスを調べます。 H8/3672シリーズハードウェアマニュアル(以後ハードウェアマニュアル)より、アドレスは FFDB(16 進数)です。 このアドレスの下位4ビット(ビット0∼3)に1を書けば良いことがわかります。 その前に、ポートP8が出力に設定されていなければポートデータレジスタに1がセットされても端子に 5Vは出力されません。 ポートのアドレスを調べた要領で、ポート8の方向(入力/出力)を決めるP8ポートコントロールレジス タのアドレスを調べます。 ハードウェアマニュアルより FFEB(16進数)がアドレスと判ります。 このレジスタのビットを1にすることでそれに応じた端子が出力になります。 7 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 2−3 それでは、早速プログラムを書いてみましょう H8TinyCPUのポートのレジスタにアクセスする方法は、ポインタを使って直接そのアドレスにアクセス する方法と、構造体/共用体を使う方法があります。 (構造体/共用体を使う場合でも実体を取るときにアドレスを定義しておく必要があります) ここでは、ポインタを使う方法を説明していきます。 リスト1 ポインタを使ったポートデータレジスタへのアクセス *(char *)0xffdb = 0x0f; 先頭の‘*’は、ポインタの指し示すアドレスの中身を意味します。 続く“(char *)”で、これに続く数値を“8ビット単位でアクセスされるアドレス”という型に変換していま す。 このように型を変換することを“キャスト”と言います。 次の数値“0xffdb”は、ハードウェアマニュアルで調べたポートP8 ポートデータレジスタのアドレスで す。 つまり、ここまでで、ポートP8 ポートデータレジスタのアドレスの中身を意味します。 ‘=’から後ろは代入を意味しますので、ポートデータレジスタに“0x0f”を書き込むことができます。 P8の下位4ビット全てに1、上位4ビットには0を書き込むことができました。(リスト1) ∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼ ポイント!! ここでは *(char *)アドレス = 書きこむデータ; と書けば「指定のアドレスにデータが書きこめる」と丸暗記しても良いのですが、 ポインタ・キャストは C 言語を使う上で絶対避けてとおることができない重要なものです。 きちんと習得しておきましょう ∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼ 8 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう CPUの起動時には、ポートの状態が不定になっているものがあります。 そういった場合には、意図しないデータの出力を防ぐためにポートを一旦入力方向にし、出力させた いデータを書き込んだ後でポートを出力方向に設定します。 H8Tinyでは、ポートは初期状態が入力になっていますので心配ありませんが、そういった場合を想 定してプログラムを変更してみます。 ※ 一部のLSIではこの方法が使えない場合があります。 1. 意図しないデータの出力を防ぐために、P8を一旦入力方向にします。(リスト2の1行目) 2. それから、リスト1のように出力したいデータを書き込みます。(リスト2の2行目) 3. その後、ポートの方向を出力方向にします。(リスト2の3行目) これでLEDランプが全て点灯します。 この部分を続けて書くと、リスト2のようになります。 リスト2 *(char *)0xffeb = 0x00; /* P8を入力に設定 */ *(char *)0xffdb = 0x0f; /* P8に出力データをセット */ *(char *)0xffeb = 0x0f; /* P8のLEDが接続された4ビットを出力に設定 */ LEDランプの点灯部分(リスト2)に加え、CPUの初期設定やポートのイニシャライズを含めたプログラ ムは以下のようになります。(LED_TEST1.c) リスト3−1 void main(void) { /* I/Oの初期化 */ *(char *)0xffd4 = 0x00; /* ポート1に0x00を設定 */ *(char *)0xffd5 = 0x00; /* ポート2に0x00を設定 */ *(char *)0xffd8 = 0x00; /* LCDデータセット(ポート5に0x00を設定) */ *(char *)0xffda = 0xff; /* LCD信号全てHigh */ *(char *)0xffe0 = 0x00; /* ポート1を汎用ポートに設定 */ *(char *)0xffd0 = 0x00; /* ポート1 プルアップしない */ *(char *)0xffe4 = 0x00; /* ポート1を入力に設定 */ *(char *)0xffe5 = 0x00; /* ポート2を入力に設定 */ *(char *)0xffe1 = 0x00; /* ポート5を汎用ポートに設定 */ *(char *)0xffd1 = 0x00; /* ポート5 プルアップしない */ *(char *)0xffe8 = 0xff; /* LCDデータ出力端子に設定 */ *(char *)0xffea = 0x70; /* LCD信号出力端子に設定 */ 9 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう リスト3−2(3−1からの続き) /* メイン(LED点灯) */ *(char *)0xffeb = 0x00; /* ポート8を入力に設定 */ *(char *)0xffdb = 0x0f; /* P8のLEDが接続されたビットを1にする */ *(char *)0xffeb = 0x0f; /* P8のLEDが接続されたビットを出力に設定 */ while(1); /* プログラム実行終了(無限ループ) */ } それでは、1−3でやった要領で実際にプログラムを書き込んでみましょう。書き込む HEX ファイルは 添付CD 2章フォルダ内の LED_TEST1.mot です。 書き込みが終わったら、電源を入れ直します。 4つのLEDランプが全部点灯しましたね。(写真10) 写真10 10 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 2−4 開発ツールを使ってHEXファイルを作成してみましょう ルネサステクノロジのホームページで、Tiny/SLP専用無償版コンパイラをダウンロードできますので、 これを使うことにします。 ダウンロード、インストールの方法はルネサステクノロジのホームページを参照して下さい。 インストールが終了したら、HEW2を起動します。 "新規プロジェクトワークスペースの作成"をチェックして OK を押し、プロジェクトを作成します。 続いて“ワークスペース名”を入力(この例ではLED_TEST1とします)し、“プロジェクトタイプ”の “Application”を選択して OK を押します。 11 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう “CPU Series”で“Tiny”を、“CPU Type”で“3664F”をそれぞれ選択し、Step8までNextで進 みStep9のダイアログでFinishを押します。 12 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう Project Summaryのダイアログが表示されますので、OKを押してプロジェクトを作成します。 次にワークスペースウィンドウ内“C Source File”の下にある“LED_TEST1.c”を選択し、右クリ ックの“ファイル削除”で“LED_TEST1.c”を削除します。 13 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 今度は、プログラムをビルドするためにファイルを追加します。“プロジェクト”の“ファイルの追加”を選 択し、サンプルプログラムで提供している“LED_TEST.c”を追加します。 サンプルプログラムを表示したいときは、ワークスペースウィンドウ内の“LED_TEST1.c”をダブルク リックします。 14 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう それでは、ビルドしてみましょう。 メニューから”ビルド“の”全てをビルド“を選択し実行すると、ビルドが完了し、エラーが無いというメッセ ージ(丸で囲まれた部分)が出てビルドが完了しました。 ここで表示されている“1 Warnings”は、“C”というセクション(コードエリア)が見つからないために出 力された警告です。 動作上支障がありませんので、ここでは無視します。 CPUに書き込むためのファイルが“Debug”ディレクトリの中にmotの拡張子で出来ました。 ※ HEW2は標準状態でインストールした場合、C:¥HEW2がワークスペースディレクトリになっています ので、”C:¥HEW2¥LED_TEST1¥LED_TEST1¥Debug”の中にファイルが作られます。 このファイルは、先ほどCPUに書き込んだサンプルプログラムと全く同じものです。 CPUに書き込んで実行してみてください。 15 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 2−5 次はLEDランプを1つずつ、ずらしながら点灯させてみましょう 前回、4つのLEDランプを全部点灯させましたが、今度はLEDランプを1つずつずらしながら点灯させ ていくプログラムに変更してみましょう。 また、ここでは構造体/共用体を使ったポートアクセスの方法についても説明します。 H8TinyCPUの内蔵I/Oポートレジスタは、0xFF80番地以降に連続して割り付けられています。 このような場合、構造体/共用体を使ってI/Oポートレジスタにアクセスすることができます。 HEW2では、I/Oポートレジスタの構造体を定義したファイル“iodefine.h”が自動生成されていま す。 構造体の内容までは説明しませんが、“iodefine.h”の最後に20行ほどアドレスを指定して実体を取 っている部分があります。 これでI/Oポートレジスタにアクセスすることができるようになっています。 例 シンボル値の設定 : 絶対値やアドレス値をシンボルで置き換えることが出来る。 IO.PDR8.BYTE = 0x00; /* ポートデータレジスタ8に0を書き込む */ また、ビットフィールドも定義されていますので、必要なビットのみ入出力することもできます。 IO.PDR8.BIT.B0 = 1; /* ポートデータレジスタ8のビット0に1を書き込む */ ここからは、この構造体を使ってプログラムを作っていきます。 LEDランプをずらしながら点灯させるには、ビットシフトをしてそのデータをポートに出力します。 ビットシフトとは、データのビットの並びはそのままに、左または右にnビットずらすことを言います。 左1ビットシフトの場合は1(初期値)→2→4→8…となり、右1ビットシフトでは逆に8(初期値)→4→2 →1→0(これ以下はありません)となります。 通常、左シフトには「左シフト演算子」“<<”、右シフトには「右シフト演算子」“>>”を使いますが、上 の数値を見てお気づきの通り、左シフトは掛け算、右シフトは割り算で代用できます。 それでは、これらを踏まえてプログラムを書いてみましょう。 16 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう HEW2を起動し、前回と同じ要領でプロジェクトを作成します。 今回のワークスペース名は“LED_TEST2”とします。 但し、今回は“C Source File”の下にある“LED_TEST2.c”を削除せず、このファイルを編集し て使います。 リスト中の“//”以降、改行まではコメントです。 C言語では通常“/*”から“*/”までをコメントとして扱いますが、コンパイラによっては“//”が許 されている物もあります。 H8Tiny用のコンパイラでは“//”が使えますので、今後積極的に使っていきます。 最初に“iodefine.h”を使えるようにします。 ソースファイルの先頭に、 #include “iodefine.h” と書いておきます。 リスト4−1 #include "iodefine.h" 次にmain関数の中身を考えます。 LEDのポートに1→2→4→8→1…と書き込むのですから、何か変数を使わなければならないことが 予想できます。 使いそうな変数は、 ① LEDを点灯させるためのデータ ② データを書き込む部分を4回ループさせるためのカウンタ これらを関数の先頭で定義します。(ローカル変数) リスト4−2 ローカル変数の定義 void main(void) { char data; // LEDを点灯させるためのデータ int i; // ループカウンタ 17 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 次にリスト3−1のI/Oの初期化を打ち込みます。 ポートのアクセスには“iodefine.h”の構造体を使います。 リスト4−3 I/Oの初期化部分 // I/Oの初期化 IO.PDR1.BYTE = 0x00; // ポート1に0x00を設定 IO.PDR2.BYTE = 0x00; // ポート2に0x00を設定 IO.PDR5.BYTE = 0x00; // LCDデータセット(ポート5に0x00を設定) IO.PDR7.BYTE = 0xff; // LCD信号全てHigh IO.PMR1.BYTE = 0x00; // ポート1を汎用ポートに設定 IO.PUCR1.BYTE = 0x00; // ポート1 プルアップしない IO.PCR1 = 0x00; // ポート1を入力に設定 IO.PCR2 = 0x00; // ポート2を入力に設定 IO.PMR5.BYTE = 0x00; // ポート5を汎用ポートに設定 IO.PUCR5.BYTE = 0x00; // ポート5 プルアップしない IO.PCR5 = 0xff; // LCDデータ出力端子に設定 IO.PCR7 = 0x70; // LCD信号出力端子に設定 IO.PCR8 = 0x00; // ポート8を入力に設定 IO.PDR8.BYTE = 0x01; // P8に初期値を出力する IO.PCR8 = 0x0f; // P8のLEDが接続されたビットを出力に設定 続けてLEDを点灯させる部分を書いていきます。 リスト4−4 LEDを点灯させる部分 // メイン(LEDをずらしながら点灯) while(1){ // 無限ループ data = 0x01; // LED点灯のための初期値 for(i = 0 ; i < 4 ; i++){ // 4回ループする IO.PDR8.BYTE = data; // 点灯データの出力 data = data << 1; // 1ビット左シフトする } } 最後に、main関数を閉じます。 } 18 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう data = data << 1; の部分は、data <<= 1; と書くこともできます。 “<<=”は左シフト代入演算子と言い、左シフトした内容を代入するための演算子です。 C言語にはこのほかにも単純代入演算子(=)と、他の演算子を組み合わせた“○○代入演算子”とい う演算子が用意されています。 ここでは書き込むデータを1ビットずつシフトさせていきましたが、少し考え方を変えるだけで何通りか の書き方が考えられます。 どの書き方を採用するかはプログラムの読み易さ、実行速度、ビルド後のメモリ消費量等を考慮して決 めていくことになります。 以下に他の書き方を4種類記載しておきますので、何かの時に参考にして下さい。 ① データは固定値にしてシフトするビット数を変えるというプログラムの組み方。 この場合、dataという変数は不要です。 リスト4−4a // メイン(LEDをずらしながら点灯) while(1){ for(i = 0 ; i < 4 ; i++){ // 無限ループ // 4回ループする IO.PDR8.BYTE = 1 << i; // 点灯データの出力 } } ② リスト4−4aを元に、forループを無くしてしまう組み方。 リスト4−4b // メイン(LEDをずらしながら点灯) i = 0; while(1){ // 無限ループ IO.PDR8.BYTE = 1 << (i & 0x03); i++; // 点灯データの出力 // 左シフト数加算 } 19 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう ③ 配列変数(テーブル)を使ったプログラムの組み方。 この場合、dataという変数は配列で定義します。 この方法の利点は、点灯させる順番の変更が容易なことです。 (データの並び順を変えれば自由に設定できる) リスト4−4c char data[] = { 1, 2, 4, 8 };// ローカル変数のため、関数の先頭で定義する // メイン(LEDをずらしながら点灯) while(1){ // 無限ループ for(i = 0 ; i < 4 ; i++){ // 4回ループする IO.PDR8.BYTE = data[i]; // 点灯データの出力 } } ④ switch∼case文を使ったプログラムの組み方。 一番単純な方法です。 リスト4−4d main()関数先頭の“int i;” // メイン(LEDをずらしながら点灯) を置き換えて下さい。 unsigned char i = 0; while(1){ // 無限ループ switch(i & 0x03){ case 0: IO.PDR8.BYTE = 0x01;// 点灯データの出力 break; case 1: IO.PDR8.BYTE = 0x02;// 点灯データの出力 break; case 2: IO.PDR8.BYTE = 0x04;// 点灯データの出力 break; case 3: IO.PDR8.BYTE = 0x08;// 点灯データの出力 break; } i++; } 20 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう リスト4−4のままでは早すぎて、全てのLEDランプが光っているようにしか見えません。 動いていることが判るようにするには、1回シフトするごとに待ち時間を入れる必要があります。 ウェイト関数を使ってLEDランプの点灯消灯タイミングを考慮してプログラムを完成させると、リスト4に なります。(リスト4−1 + 4−2 + 4−3 + 4−4(タイミング考慮) + ウェイト関数) リスト4 #include “iodefine.h” // プロトタイプ宣言 void main(void); void wait(int); void wait1mS(void); void main(void) { char data; // LEDを点灯させるためのデータ int i; // ループカウンタ // I/Oの初期化 IO.PDR1.BYTE = 0x00; // ポート1に0x00を設定 IO.PDR2.BYTE = 0x00; // ポート2に0x00を設定 IO.PDR5.BYTE = 0x00; // LCDデータセット(ポート5に0x00を設定) IO.PDR7.BYTE = 0xff; // LCD信号全てHigh IO.PMR1.BYTE = 0x00; // ポート1を汎用ポートに設定 IO.PUCR1.BYTE = 0x00; // ポート1 プルアップしない IO.PCR1 = 0x00; // ポート1を入力に設定 IO.PCR2 = 0x00; // ポート2を入力に設定 IO.PMR5.BYTE = 0x00; // ポート5を汎用ポートに設定 IO.PUCR5.BYTE = 0x00; // ポート5 プルアップしない IO.PCR5 = 0xff; // LCDデータ出力端子に設定 IO.PCR7 = 0x70; // LCD信号出力端子に設定 IO.PCR8 = 0x00; // ポート8を入力に設定 IO.PDR8.BYTE = 0x01; // P8に初期値を出力する IO.PCR8 = 0x0f; // P8のLEDが接続されたビットを出力に設定 ∼ 次ページへ ∼ 21 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう // メイン(LEDをずらしながら点灯) while(1){ // 無限ループ data = 0x01; // LED点灯のための初期値 for(i = 0 ; i < 4 ; i++){ // 4回ループする IO.PDR8.BYTE = data; // 点灯データの出力 data = data << 1; // 1ビット左シフトする wait(1000); // 1秒待つ } } } // nミリ秒待つウェイト関数 void wait(int time) { int i; // ループカウンタ for(i = 0 ; i < time ; i++) // time回数分ループ wait1mS(); // 1ミリ秒のウェイト関数を呼び出す } // 1ミリ秒待つウェイト関数 void wait1mS(void) { int i; // ループカウンタ for(i = 0 ; i < 2662 ; i++); // 1ミリ秒の間ループする } wait1mS関数で、C言語で全て書く場合の欠点が出てしまいました。 1ミリ秒の間ループするために使われている数値“2662”とは一体何でしょうか? 実は、この数値には理論も何もなく、単に実験して決定した数値です。 C言語では、実際にCPUが実行できるコード(マシン語)への変換は、完全にコンパイラ任せです。 コンパイラが変わったり、コンパイラの最適化オプションを変更したりすると、再度実験して値を変更し なければなりません。 そういったことが起きないようにするために、通常はインラインアセンブラという書き方や、CPUがもとも と持っているタイマ割り込み(後で説明します)という機能を使います。 今はそこまでする必要がありませんのでこのままの値を使って下さい。 22 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう それでは、打ち込んだプログラムをビルドしてみましょう。 “Debug”ディレクトリの中に“LED_TEST2.mot”が出来ましたね。 早速コンソールボードに書き込んで動作確認をしましょう。 LEDランプが1つずつ移動しながら点滅します。 ∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼ ポイント!! Debug(デバッグ)とはプログラム作成中にチェックなどを行うモードのことです。 HEW2に限らずプログラムの開発統合環境ではデバッグモードが初期状態になっています。 これに対して、チェックが完了し製品に書き込むプログラムを作成する時はRelease(リリース) モードを使用します。 皆さんが作ったプログラムはデバッグモードでコンパイルされています。 このテキストで扱う内容ではデバッグもリリースもほとんど差が無いため、HEW2の設定を変更 せず、デバッグモードのまま使用しています。興味の有る方はモードをリリースに変更してみて ください。Releaseディレクトリが作成され、その中にLED_TEST2.motができています。 これを実行すると、デバッグモードよりほんの少し動作速度が速くなっています。 人間の間隔でわからないかもしれませんけどね。 ∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼∼ 23 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 第3章 スイッチの読み込み 3−1 ポートを使ってスイッチの状態を読み込んでみましょう コンソールボードには、5列4行のマトリックスで20個のスイッチが付いています。 まず、マトリックスの1列のみを例にとって説明します。 CPUとの接続は下図のようになっています。 P1の方向レジスタを出力方向に設定し、ハイレベルを出すようにポートに1を書き込みます。 入力側のポートPBは抵抗を介してGNDに接続されているので、スイッチが押されない限りローレベル、 つまり0が検出されます。 ところが、スイッチSW1∼5のどれかまたは全部が押されると、ポートP1からダイオードを経由して5V が出力され、抵抗の両端に電位差が生じます。 抵抗とつながっているポートPBには5Vが検出され、ハイレベルとなります。 これでPBは、スイッチが押されるとハイレベル、押されないとローレベルとなり、スイッチのON/OFF の検出が出来るのです。 回路図では上からSW7、SW13、SW9、SW5、SW1と なっていますが、今回はこの番号を付けています 回路図2 P16 P15 P12 P11 CPU P10 SW5 SW4 SW3 SW2 SW1 ダイオード (前の章の回路と良く似 ていますが発光ダイオ ードではありません) PB0 抵抗 スイッチ ONで 5V スイッチ OFFで 0V GND 24 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 3−2 スイッチの読み込みをプログラムしてみましょう 1. スイッチの接続されているP1の端子をハイレベルにし、ポート方向も出力側にします。(リスト5− 1の2∼3行目) 2. PB端子がAD端子と共用になっているため入力ポートとして使うときこの処理が必要です。(この CPU特有な処理です。)(リスト5−1の4行目) 3. ポートPBの0ビット目を読み取ります。 これで、変数swにスイッチの状態が格納されました。(リスト5−1の5行目) もしSW1だけを読み取りたい場合は、方向レジスタは このままでポートに0x01を書くようにすればよい。 リスト5−1 char sw; // スイッチ読み込み内容 IO.PDR1.BYTE = 0x67; // ポート1をハイレベルに設定 IO.PCR1 = 0x67; // ポート1の必要なビットを出力に設定 AD.CSR.BYTE = 0x03; // アナログ入力チャネルを端子3に設定 while(1){ // 無限ループ sw = IO.PDRB.BIT.B0; // PB0の状態読み込み 3−3 スイッチの状態をLEDランプに反映させてみましょう スイッチが押されているときにLEDランプを点灯し、それ以外は消灯するプログラムを書いてみましょ う。 リスト5−1に続けてスイッチの状態を判断するプログラムを書きます。(リスト5−2) これでスイッチを押すとLED1が点灯するプログラムが出来ました。 リスト5−2 if(sw == 1) IO.PDR8.BIT.B0 = 1; // LED1を点灯 else IO.PDR8.BIT.B0 = 0; // LED1を消灯 } 25 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう リスト5−1、リスト5−2にCPUポートのイニシャライズを含めたプログラムは“SW_TEST1.c”です。 添付CD 3章フォルダ内にありますので、ダウンロードアダプタでコンソールボードに書き込んでみま しょう。(SW_TEST1.mot) スイッチを押すとLED1が点灯しますね。 この行のどのスイッチを押してもLED1が点 写真10 灯しますね。(複数押しにも反応しますね) 26 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう スイッチを押すたびにLEDランプの点灯が右にシフトするプログラムを書いてみましょう。 先ほど作成したスイッチの読み取り部分(リスト5−1,2)と、2章で作ったLEDランプを1ビットずつシフ トするプログラム(リスト4−2)を応用して使ってみましょう。 今回のワークスペース名は“CHATA_TEST1”とします。 リスト6(CHATA_TEST1.c) #include "iodefine.h" void main(void) { char data; // I/Oの初期化 IO.PDR1.BYTE = 0x00; // ポート1に0x00を設定 IO.PDR2.BYTE = 0x00; // ポート2に0x00を設定 IO.PDR5.BYTE = 0x00; // LCDデータセット(ポート5に0x00を設定) IO.PDR7.BYTE = 0xff; // LCD信号全てHigh IO.PMR1.BYTE = 0x00; // ポート1を汎用ポートに設定 IO.PUCR1.BYTE = 0x00; // ポート1 プルアップしない IO.PCR1 = 0x00; // ポート1を入力に設定 IO.PCR2 = 0x00; // ポート2を入力に設定 IO.PMR5.BYTE = 0x00; // ポート5を汎用ポートに設定 IO.PUCR5.BYTE = 0x00; // ポート5 プルアップしない IO.PCR5 = 0xff; // LCDデータ出力端子に設定 IO.PCR7 = 0x70; // LCD信号出力端子に設定 IO.PCR8 = 0x00; // ポート8を入力に設定 IO.PDR8.BYTE = 0x00; // P8に初期値(全て消灯)を出力する IO.PCR8 = 0x0f; // P8のLEDが接続されたビットを出力に設定 IO.PDR1.BYTE = 0x67; // ポート1をハイレベルに設定 IO.PCR1 = 0x67; // ポート1の必要なビットを出力に設定 AD.CSR.BYTE = 0x03; // アナログ入力チャネルを端子3に設定 ∼ 次ページへ ∼ 27 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう data = 0x01; // LED点灯のための初期値 while(1){ // 無限ループ while(IO.PDRB.BIT.B0 == 0); // スイッチが押されるまで待つ IO.PDR8.BYTE = data; // 点灯データの出力 data <<= 1; // 1ビット左シフトする if(data > 0x08) // LED4を越えた? data = 0x01; // LED点灯の初期値に戻す while(IO.PDRB.BIT.B0 == 1); // スイッチが放されるまで待つ } } これでスイッチが押されるたびにLEDランプの点灯が1つずつシフトするプログラムが出来ました。 ビルドし、CPUに書き込んで実行してみて下さい。 スイッチを押すたびにLEDが1つずつシフトしていきます。 今度はスイッチをゆっくり(そっと)押してみてください。 LEDが勝手にシフトしてしまいます。 この現象はリスト6のプログラムでは想定していなかった現象です。 このままにしておくと、装置が誤動作してしまうかも知れません。 実はこの現象はスイッチを普通に押しても発生することがあります。 この現象については次項で説明します。 28 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 3−4 チャタリング処理 先程、作成した“CHATA_TEST1.c”は、うまく動いてくれませんね、これは、チャタリングが発生し ているからです。 チャタリングとは、スイッチなどの接点の状態が移行する際、ON/OFFを繰り返しながら最終的に安 定する事を言います。 コンソールボードに使われているスイッチは、数100uS∼数mSの期間を経て安定すると言われてい ます。 人間にとってはほんの一瞬ですが、CPUにとってはとても長い時間です。 チャタリング概念図 チャタリング期間(数100uS∼数mS) ON OFF 安定期間(この期間でスイッチが読まれればよい) チャタリング対策にはいくつかの方法がありますが。下記の方法を使ってプログラムを作成してみましょ う。 この方法で安定期間内にスイッチの読み込みが可能になるはずです。 ① 複数回のスイッチ読み込みを行い読み込みの間隔はチャタリング期間より十分長く取る。 ② 1回目と2回目のスイッチの状態が一致した時のみ正常にスイッチが読み込まれたと判断する。 29 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう まず、リスト6−1のスイッチ読み込み部を、方法①に従って10mSの間を空けて2回読み取るように変 更します。 さらに、方法②を盛り込み、リスト7−1にします。 変数sw1とsw2つまり1回目と2回目のスイッチの読み込み値を比べます。 一致していれば、LEDランプの点灯処理へ入ります。 リスト7−1 data = 0x01; // LED点灯のための初期値 while(1){ // 無限ループ sw1 = IO.PDRB.BIT.B0; // スイッチが状態読み込み1回目 wait(10); // 10mS待つ sw2 = IO.PDRB.BIT.B0; // スイッチが状態読み込み2回目 if((sw1 == 1) && (sw2 == 1)){ // 2回とも押されている IO.PDR8.BYTE = data; // 点灯データの出力 data <<= 1; // 1ビット左シフトする if(data > 0x08) // LED4を越えた? data = 0x01; // LED点灯の初期値に戻す while((sw1 == 1) ││ (sw2 == 1)){ // スイッチが放されるまで待つ sw1 = IO.PDRB.BIT.B0; // スイッチが状態読み込み1回目 wait(10); // 10mS待つ sw2 = IO.PDRB.BIT.B0; // スイッチが状態読み込み2回目 } } } 10mSの間隔でスイッチの読み取りを行い、n回目の値とn+1の値を変数sw1とsw2にそれぞれ保 管する。 リスト7−1の6行目、11行目のスイッチの状態判定は、以下のように書き換えることも出来ます。 if((sw1 & sw2) == 1){ // 2回とも押されている while((sw1 │ sw2) == 1){ // スイッチが放されるまで待つ (sw1 & sw2)では変数sw1とsw2の論理積の結果が1であれば点灯データの出力へ進みます。 (sw1 | sw2)では変数sw1とsw2の論理和の結果が0になるまでスイッチを読み込み続けます。 ※ 今はスイッチが押されたかどうかしか見ていませんので、1と比較しています。 30 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう そろそろmain()関数が長くなってきました。 決まり切っているI/Oポートの初期設定部分や、2カ所で同じことをやっているスイッチの読み込み部 分を関数化してすっきりさせてみましょう。 I/Oポートの初期設定の関数は、“ioinit()”とし、“main()”のI/Oポート初期設定部分をそのまま 抜き出します。 スイッチ読み込み部分の関数は、“SW_Read()”とし、スイッチが押されていれば1、押されていなけ れば0を返すという仕様にしておきます。 リスト7−1に必要な処理を加え、関数化などの変更を施したものがリスト7です。 添付CD 3章フォルダ内に“CHATA_TEST2.c”がありますので、ビルドして書き込んで動作させ てみましょう。 リスト7 #include "iodefine.h" // プロトタイプ宣言 void main(void); void IOinit(void); int SW_Read(void); void wait(int); void wait1mS(void); void main(void) { char data; IOinit(); // I/Oの初期化 data = 0x01; // LED点灯のための初期値 while(1){ // 無限ループ while(SW_Read() == 0); // スイッチが押されるまで待つ IO.PDR8.BYTE = data; // 点灯データの出力 data <<= 1; // 1ビット左シフトする if(data > 0x08) // LED4を越えた? data = 0x01; // LED点灯の初期値に戻す while(SW_Read() == 1); // スイッチが放されるまで待つ } } ∼ 次ページへ ∼ 31 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう // I/Oの初期化 void IOinit(void) { IO.PDR1.BYTE = 0x00; // ポート1に0x00を設定 IO.PDR2.BYTE = 0x00; // ポート2に0x00を設定 IO.PDR5.BYTE = 0x00; // LCDデータセット IO.PDR7.BYTE = 0xff; // LCD信号全てHigh IO.PMR1.BYTE = 0x00; // ポート1を汎用ポートに設定 IO.PUCR1.BYTE = 0x00; // ポート1 プルアップしない IO.PCR1 = 0x00; // ポート1を入力に設定 IO.PCR2 = 0x00; // ポート2を入力に設定 IO.PMR5.BYTE = 0x00; // ポート5を汎用ポートに設定 IO.PUCR5.BYTE = 0x00; // ポート5 プルアップしない IO.PCR5 = 0xff; // LCDデータ出力端子に設定 IO.PCR7 = 0x70; // LCD信号出力端子に設定 IO.PCR8 = 0x00; // ポート8を入力に設定 IO.PDR8.BYTE = 0x00; // P8に初期値(全て消灯)を出力する IO.PCR8 = 0x0f; // P8のLEDが接続されたビットを出力に設定 IO.PDR1.BYTE = 0x67; // ポート1をハイレベルに設定 IO.PCR1 = 0x67; // ポート1の必要なビットを出力に設定 AD.CSR.BYTE = 0x03; // アナログ入力チャネルを端子3に設定 } // スイッチの状態読み込み int SW_Read(void) { int sw1,sw2; sw1 = IO.PDRB.BIT.B0; // スイッチが状態読み込み1回目 wait(10); // 10mS待つ sw2 = IO.PDRB.BIT.B0; // スイッチが状態読み込み2回目 return(sw1 & sw2); // 2回とも押されていれば1を返す } ∼ 次ページへ ∼ 32 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう // nミリ秒待つウェイト関数 void wait(int time) { int i; // ループカウンタ for(i = 0 ; i < time ; i++) // time回数分ループ wait1mS(); // 1ミリ秒のウェイト関数を呼び出す } // 1ミリ秒待つウェイト関数 void wait1mS(void) { int i; // ループカウンタ for(i = 0 ; i < 2662 ; i++); // 1ミリ秒の間ループする } 前と同じように普通にスイッチを押してみて下さい。 スイッチを押すたびにLEDが1つずつシフトしていきます。 今度はスイッチをゆっくり(そっと)押してみてください。 LEDが勝手にシフトするという現象が出なくなっているはずです。 これでチャタリング対策が効いていることが確認できます。 33 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 第4章 ブザーを鳴らす 4−1 タイマ機能を使ってブザーを鳴らしてみましょう ブザーには、自励振と呼ばれる発信回路を内蔵したタイプと他励振と呼ばれる発信回路を内蔵してい ないタイプがあります。コンソールボードに使われているのは他励振タイプです。 このため、ブザーに接続されているポート端子から矩形波(パルス)を与えることで鳴らすことが出来ま す。 CPU周辺機能のタイマ機能を使ってブザーを鳴らすことにします。 タイマとは、名前の通り時間を管理できるものです。基本となるクロックをカウントしそのクロック数(時 間)を前もって指定しておけば、その時間が来れば知らせてくれたり、指定したイベントを発生させたりし てくれるものです。 今回は、パルスを発生させるわけですから、タイマWのアウトプットコンペア機能を使うことにします。 動作原理は、下の図のようになります。 カウント開始ビット(CTS)を1にすると、TCNTが0x0000からカウントアップし、GRDの値と同じにな るとP84(FTIOD)端子の出力を反転(トグル)します。 また、その時GRA=GRDにしておくとTCNTの値を0x0000にリセットし、カウントアップ動作を再開 します。 この動作を繰り返し、パルス波形を出力します。 TCNTの値 250=GRA=GRD 0 CTS P84 34 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 4−2 タイマ部分のプログラムを考えてみましょう タイマWの初期設定が完了する前に動作しないようにまず、カウンタを停止します。 またタイマWの割込も禁止にします。(リスト8−1の1∼2行目) カウンタTCNTとGRAの値が同じでTCMTをクリアする設定(ビット7)、カウントソースを内部クロック8 分周に設定(ビット6∼4)、FTIOD端子初期レベルをローレベルに設定(ビット3)、その他を0に設定し ます。(リスト8−1の3∼4行目) ブザーとつながっているP84(FTIOD)端子をアウトプットコンペア出力に設定し、トグル出力となるよう に設定します。(リスト8−1の5∼6行目) カウンタTCNTに初期値0x0000を設定し、トグルするタイミングとカウンタをリセットするタイミングをG RDとGRAにそれぞれ設定します。(リスト8−1の7∼10行目) 今回ブザーへのパルス周期を4KHzとするので、システムクロック0.625uSの8分周を250回カウント すれば、パルスのトグルタイミングになります。 GRA、GRDへセットする値250は、9行目の式から逆算します。 最後にCTSビットを1にするとブザーが鳴ります。(リスト8−1の11行目) リスト8−1 TW.TMRW.BYTE = 0x00; // カウント停止、その他イニシャル TW.TIERW.BYTE = 0x00; // タイマW割り込み禁止 TW.TCRW.BYTE = 0xb0; // コンペアマッチでTCNTクリア // φ/8、TOD=0 TW.TIOR0.BYTE = 0x00; // FTIOD以外通常ポートに設定 TW.TIOR1.BYTE = 0x30; // FTIODコンペアマッチトグル出力に設定 TW.TCNT = 0x0000; // カウンタTCNTに0(初期値)を設定 TW.GRD = 250; // トグル出力周期を設定 // 250×8×0.625×2=250μS(4KHz) TW.GRA = 250; // TCNTをクリアする値を設定 TW.TMRW.BIT.CTS = 1; // タイマWスタート(ブザーON) 35 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 4−3 スイッチを押すとブザーが鳴るようにする リスト8−1のままだと、電源を入れたとたんにブザーが鳴り始め、電源を切らない限り止まりません。 そこで前回スイッチを押すとLEDランプが点灯しましたが、LEDランプの代わりにブザーを鳴らしてみ ましょう。 但し、ブザーに直流電圧をかけるとブザーが正常に動作しなくなる事がありますので、ブザーをOFF する場合はブザーの両端に電圧がかからないようなプログラムにします。 LEDランプをシフトしながら点灯させたときに使ったリスト7のmain()関数の7∼10行目を、ブザーを 鳴らす様に変更します。 さらに12行目にブザーを鳴らさないようにする部分を追加します。(リスト8−3) 変更部分では、基本的にCTSビットを操作するだけでブザーは制御できますが、OFFしたときにブザ ーの両端に直流電圧がかからないように、汎用ポートに戻します。 このためポートを前もってローレベルにする必要があります。この部分が、リスト8−2になります。 リスト8−2 IO.PCR8 = 0x00; // LED、ブザーポートを入力に設定 IO.PDR8.BYTE = 0x00; // LED、ブザー全ポートをOFF IO.PCR8 = 0x1f; // LED、ブザーポートを出力に設定 リスト8−3 while(SW_Read() == 0); // スイッチが押されるまで待つ TW.TIOR1.BYTE = 0x30; // FTIODコンペアマッチトグル出力に設定 TW.TMRW.BIT.CTS = 1; // タイマWスタート(ブザーON) while(SW_Read() == 1); // スイッチが放されるまで待つ TW.TIOR1.BYTE = 0x00; // タイマ出力端子を通常ポートに設定 TW.TMRW.BIT.CTS = 0; // タイマWストップ(ブザーOFF) リスト7からの変更部分 36 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう それでは、整理してみます。 まず、LEDとブザーポートを初期設定(リスト8−2)し、タイマWの初期設定(リスト8−1)を行い、次に スイッチのON/OFFによりブザーを鳴らす部分(リスト7から変更したリスト8−3)を加えます。 これで目的のプログラムになりました。 これにCPUの初期設定やポートのイニシャライズを含めたプログラムがリスト8です。 添付CD 4章フォルダ内に“BUZZER_TEST.c”がありますので、ビルドして書き込んで動作させて みましょう。 リスト8 #include "iodefine.h" // プロトタイプ宣言 void main(void); void IOinit(void); int SW_Read(void); void wait(int); void wait1mS(void); void main(void) { IOinit(); // I/Oの初期化 while(1){ // 無限ループ while(SW_Read() == 0); // スイッチが押されるまで待つ TW.TIOR1.BYTE = 0x30; // FTIODコンペアマッチトグル出力に設定 TW.TMRW.BIT.CTS = 1; // タイマWスタート(ブザーON) while(SW_Read() == 1); // スイッチが放されるまで待つ TW.TIOR1.BYTE = 0x00; // タイマ出力端子を通常ポートに設定 TW.TMRW.BIT.CTS = 0; // タイマWストップ(ブザーOFF) } } ∼ 次ページへ ∼ 37 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう // I/Oの初期化 void IOinit(void) { IO.PDR1.BYTE = 0x00; // ポート1に0x00を設定 IO.PDR2.BYTE = 0x00; // ポート2に0x00を設定 IO.PDR5.BYTE = 0x00; // LCDデータセット(ポート5に0x00を設定) IO.PDR7.BYTE = 0xff; // LCD信号全てHigh IO.PMR1.BYTE = 0x00; // ポート1を汎用ポートに設定 IO.PUCR1.BYTE = 0x00; // ポート1 プルアップしない IO.PCR1 = 0x00; // ポート1を入力に設定 IO.PCR2 = 0x00; // ポート2を入力に設定 IO.PMR5.BYTE = 0x00; // ポート5を汎用ポートに設定 IO.PUCR5.BYTE = 0x00; // ポート5 プルアップしない IO.PCR5 = 0xff; // LCDデータ出力端子に設定 IO.PCR7 = 0x70; // LCD信号出力端子に設定 IO.PCR8 = 0x00; // LED、ブザーポートを入力に設定 IO.PDR8.BYTE = 0x00; // LED、ブザー全ポートをOFF IO.PCR8 = 0x1f; // LED、ブザーポートを出力に設定 IO.PDR1.BYTE = 0x67; // ポート1をハイレベルに設定 IO.PCR1 = 0x67; // ポート1の必要なビットを出力に設定 AD.CSR.BYTE = 0x03; // アナログ入力チャネルを端子3に設定 TW.TMRW.BYTE = 0x00; // カウント停止、その他イニシャル TW.TIERW.BYTE = 0x00; // タイマW割り込み禁止 TW.TCRW.BYTE = 0xb0; // コンペアマッチでTCNTクリア // φ/8、TOD=0 TW.TIOR0.BYTE = 0x00; // タイマ出力端子を通常ポートに設定 TW.TIOR1.BYTE = 0x00; // TW.TCNT = 0x0000; // カウンタTCNTに0(初期値)を設定 TW.GRD = 250; // トグル出力周期を設定 // 250×8×0.625×2=250μS(4KHz) TW.GRA = 250; // TCNTをクリアする値を設定 } ∼ 次ページへ ∼ 38 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう // スイッチの状態読み込み int SW_Read(void) { int sw1,sw2; sw1 = IO.PDRB.BIT.B0; // スイッチが状態読み込み1回目 wait(10); // 10mS待つ sw2 = IO.PDRB.BIT.B0; // スイッチが状態読み込み2回目 return(sw1 & sw2); // 2回とも押されていれば1を返す } // nミリ秒待つウェイト関数 void wait(int time) { int i; // ループカウンタ for(i = 0 ; i < time ; i++) // time回数分ループ wait1mS(); // 1ミリ秒のウェイト関数を呼び出す } // 1ミリ秒待つウェイト関数 void wait1mS(void) { int i; // ループカウンタ for(i = 0 ; i < 2662 ; i++); // 1ミリ秒の間ループする } 39 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 第5章 LCDに文字を表示する 5−1 LCDへのアクセスタイミング LCDへは図のようなタイミングでアクセス(書き込み)し、データやコマンドを送ることでLCDを制御す ることが可能となります。 LCDメーカや種類によってタイミングがちがいますので、それぞれメーカなどから出している資料に従 ってタイミングを考慮し設計しましょう。 min値のタイミングの取り扱い例 表より min40nSとありますので RS 信号や R/W 信号をセットしてから 少なくとも40nSたってからE信号をハイレベルにするようにします。 タイミングチャート1 RS tAS tAH tEF R/W pWEH tAH E tER tDSW tH DB0∼DB7 tCYCE RS・・・LCDに対してハイレベル:DBがデ 表1 項目 記号 min max 単位 Enable cycle time tCYCE 500 − nS Enable pulse width pWEH 220 − nS Enable rise/fall time tER,tEF − 25 nS Set-up time tAS 40 − nS Address hold time tAH 10 − nS Data set-up time tDSW 60 − nS Data delay time tDSW 60 120 nS Data hold time tH 10 − nS ータ、ローレベル:コマンド R/W・・・ハイレベル:LCDから読み出 し、ローレベル:LCDへの書き込み E・・・書き込み及び読み出し信号 DB・・・データ ※ タイミングチャート1や表1はメーカーデータシートからの抜粋です。 40 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 5−2 アクセス波形をプログラムで書いてみましょう タイミングチャート1の波形をCPUのポートを使って作ります。 プログラムで書くとリスト9−1のようになります。 この関数は、DBへ出力する値と、RS信号のレベル(コマンドか、データか)を引数として受け取り、LC Dに出力するという機能を持ちます。 書き込みの場合、RS信号、DB信号を設定した後、E信号を赤線の様に変化させれば、LCDに書き 込みが出来るのです。 また、このときタイミング(のmin/maxの値)も考慮しなくてはいけません。 リスト9−1の説明は、以下のようになります。 1. DB信号端子(ポート5)にデータをセットします。 2. RS信号(ポート7のビット6)をコマンド書き込み関数では0、データ書き込み関数では1に変化さ せます。 (R/W信号は、読み出しをしないという前提で、初期設定でローレベルに固定しています) 3. E信号を一度ハイレベルにし、続けてローレベルにします。 E信号のパルス幅は220nS以上必要ですが、コンソールボードに搭載されているH8TinyCPU の処理速度ではこれより短くなることはありませんので、1,0の出力を続けて書いて構いません。 リスト9−1 void LCD_Write(char data) { IO.PDR5.BYTE = data; // データ/コマンドを出力 IO.PDR7.BIT.B6 = 1; // RS信号を設定 IO.PDR7.BIT.B5 = 1; // E信号をHighにする IO.PDR7.BIT.B5 = 0; // E信号をLowにする wait(1); // 書き込みサイクル待ち } void LCD_Command(char command) { IO.PDR5.BYTE = command; // データ/コマンドを出力 IO.PDR7.BIT.B6 = 0; // RS信号を設定 IO.PDR7.BIT.B5 = 1; // E信号をHighにする IO.PDR7.BIT.B5 = 0; // E信号をLowにする wait(1); // 書き込みサイクル待ち } 41 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 5−3 LCDのイニシャライズ 電源投入時には、LCDをイニシャライズ(初期化)する必要があります。 下のリストのようにLCDにコマンドを書き込むことで、LCDをイニシャライズできます。 (メーカーが推奨するイニシャライズシーケンスに乗っ取って行います。) リスト9−2 // LCDコントローラーのコマンド #define CLS 0x01 // 表示クリア #define Home 0x02 // カーソルホーム #define EntSet 0x07 // エントリーモードセット // カーソル位置:インクリメント // 書き込み後、カーソル移動 #define DisOn 0x0e // 表示ON、カーソルON、ブリンクOFF #define DisOff 0x08 // 表示OFF、カーソルOFF、ブリンクOFF #define CurRSft 0x14 // カーソル右シフト #define CurLSft 0x10 // カーソル左シフト #define DisRSft 0x1c // 表示右シフト #define DisLSft 0x18 // 表示左シフト #define FuncSet 0x38 // ファンクションセット // 8ビットI/F、2行表示、5×7ドット #define CGAdd 0x40 // セットCGRAMアドレス #define DDAdd 0x80 // セットDDRAMアドレス // LCDの初期化 void LCDinit(void) { wait(50); // 電源安定後、15mS以上待つ LCD_Command(FuncSet); // ファンクションセット1回目 wait(5); // 4.1mS以上待つ LCD_Command(FuncSet); // ファンクションセット2回目 wait(1); // 0.1mS以上待つ LCD_Command(FuncSet); // ファンクションセット3回目 LCD_Command(FuncSet); // ファンクションセット4回目 LCD_Command(DisOff); // 表示OFF LCD_Command(CLS); // 表示クリア LCD_Command(EntSet); // エントリーモードセット LCD_Command(DisOn); // 表示ON } 42 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 5−4 LCDに文字を表示させる LCDのイニシャライズが終了したら、文字を表示させてみましょう。 ここでは、どこかで見たような文字列“Hello World !!”と表示させてみます。 C言語の標準関数“printf”は使うことが出来ませんので、もっと簡単な関数を自分で作る必要があ ります。 コンソールボードに付いているLCDの場合、RS信号をハイレベルにし、アスキーコードを書き込む だけで文字が表示されます。 1文字書き込まれるごとに表示位置が次の桁に移りますので、LCDの表示桁数(16桁)までは、連 続して書き込むことが出来ます。(リスト9−3) リスト9−3 void HelloWorld(void) { LCD_Write('H'); // 1文字表示 LCD_Write('e'); // 1文字表示 LCD_Write('l'); // 1文字表示 LCD_Write('l'); // 1文字表示 LCD_Write('o'); // 1文字表示 LCD_Write(' '); // 1文字表示 LCD_Write('W'); // 1文字表示 LCD_Write('o'); // 1文字表示 LCD_Write('r'); // 1文字表示 LCD_Write('l'); // 1文字表示 LCD_Write('d'); // 1文字表示 LCD_Write(' '); // 1文字表示 LCD_Write('!'); // 1文字表示 LCD_Write('!'); // 1文字表示 } 43 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう リスト9−3のままでは“Hello World !!”以外表示できません。 もっと融通の利く関数を作ってみましょう。 引数で渡された文字列をLCDに表示するようにすれば、“printf”の代わりで文字列を表示できる 関数が出来ます。 リスト9−4 void Print(char *data) { while(*data != 0x00){ // 文字が無くなるまで LCD_Write(*data); // 1文字表示 data++; // 次の文字 } } このLCDでは通常制御コードに割り当てられているアスキーコードの内、0x00∼0x0fまでの部分 にCGRAM(外字が定義できるエリア)というものが割り当てられています。 このため、“¥r”や“¥n”(0x0d,0x0a)を制御コードとして使うと、ここに登録した外字が使えなくな ってしまうため、リスト9−4の関数では“¥r”や“¥n”による改行制御が使えないようにしてあります。 また、外字を使わない場合でも、機器組み込み用のプログラムでは表示位置や表示内容をプログラ マが管理できますので、“¥r”,“¥n”による改行制御は必要がありません。 代わりに、「何行目の何桁から表示する」という指定が出来るようになっていれば、一応は使える関 数ということになります。 それでは、リスト9−4の関数を改造してみます。 リスト9−5 void Print(int line, int column, char *data) { if(line == 1) // 1行目 LCD_Command(DDAdd + 0x00 + (column - 1)); if(line == 2) // DDRAMアドレスセット // 2行目 LCD_Command(DDAdd + 0x40 + (column - 1)); while(*data != 0x00){ // DDRAMアドレスセット // 文字が無くなるまで LCD_Write(*data); // 1文字表示 data++; // 次の文字 } } 44 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 今まで説明したリスト9−1∼5までと、いつものCPU関係のイニシャル部分が含まれているプログラム がリスト9です。 ワークスペース名は“LCD_TEST”とします。 添付CD 5章のフォルダ内の“LCD_TEST.c”になりますので、ビルドした後、ダウンロードアダプ タで、コンソールボードに書き込んでみましょう。 リスト9 #include "iodefine.h" // LCDコントローラーのコマンド #define CLS 0x01 // 表示クリア #define Home 0x02 // カーソルホーム #define EntSet 0x07 // エントリーモードセット // カーソル位置:インクリメント // 書き込み後、カーソル移動 #define DisOn 0x0e // 表示ON、カーソルON、ブリンクOFF #define DisOff 0x08 // 表示OFF、カーソルOFF、ブリンクOFF #define CurRSft 0x14 // カーソル右シフト #define CurLSft 0x10 // カーソル左シフト #define DisRSft 0x1c // 表示右シフト #define DisLSft 0x18 // 表示左シフト #define FuncSet 0x38 // ファンクションセット // 8ビットI/F、2行表示、5×7ドット #define CGAdd 0x40 // セットCGRAMアドレス #define DDAdd 0x80 // セットDDRAMアドレス // プロトタイプ宣言 void main(void); void IOinit(void); void LCDinit(void); void Print(int, int, char *); void LCD_Write(char); void LCD_Command(char); void wait(int); void wait1mS(void); ∼ 次ページへ ∼ 45 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう void main(void) { IOinit(); // I/Oの初期化 LCDinit(); // LCDの初期設定 Print(1,1,"Hello World !!"); // LCDに表示する while(1); // 無限ループ } // I/Oの初期化 void IOinit(void) { IO.PDR1.BYTE = 0x00; // ポート1に0x00を設定 IO.PDR2.BYTE = 0x00; // ポート2に0x00を設定 IO.PDR5.BYTE = 0x00; // LCDデータセット(ポート5に0x00を設定) IO.PDR7.BYTE = 0x00; // LCD信号全てLow IO.PMR1.BYTE = 0x00; // ポート1を汎用ポートに設定 IO.PUCR1.BYTE = 0x00; // ポート1 プルアップしない IO.PCR1 = 0x00; // ポート1を入力に設定 IO.PCR2 = 0x00; // ポート2を入力に設定 IO.PMR5.BYTE = 0x00; // ポート5を汎用ポートに設定 IO.PUCR5.BYTE = 0x00; // ポート5 プルアップしない IO.PCR5 = 0xff; // LCDデータ出力端子に設定 IO.PCR7 = 0x70; // LCD信号出力端子に設定 IO.PCR8 = 0x00; // LED、ブザーポートを入力に設定 IO.PDR8.BYTE = 0x00; // LED、ブザー全ポートをOFF IO.PCR8 = 0x1f; // LED、ブザーポートを出力に設定 } ∼ 次ページへ ∼ 46 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう // LCDの初期化 void LCDinit(void) { wait(50); // 電源安定後、15mS以上待つ LCD_Command(FuncSet); // ファンクションセット1回目 wait(5); // 4.1mS以上待つ LCD_Command(FuncSet); // ファンクションセット2回目 wait(1); // 0.1mS以上待つ LCD_Command(FuncSet); // ファンクションセット3回目 LCD_Command(FuncSet); // ファンクションセット4回目 LCD_Command(DisOff); // 表示OFF LCD_Command(CLS); // 表示クリア LCD_Command(EntSet); // エントリーモードセット LCD_Command(DisOn); // 表示ON } // LCDに文字を表示する void Print(int line, int column, char *data) { if(line == 1) // 1行目 LCD_Command(DDAdd + 0x00 + (column - 1)); if(line == 2) // DDRAMアドレスセット // 2行目 LCD_Command(DDAdd + 0x40 + (column - 1)); while(*data != 0x00){ // DDRAMアドレスセット // 文字が無くなるまで LCD_Write(*data); // 1文字表示 data++; // 次の文字 } } ∼ 次ページへ ∼ 47 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう // LCDへデータ書き込み void LCD_Write(char data) { IO.PDR5.BYTE = data; // データ/コマンドを出力 IO.PDR7.BIT.B6 = 1; // RS信号を設定 IO.PDR7.BIT.B5 = 1; // E信号をHighにする IO.PDR7.BIT.B5 = 0; // E信号をLowにする wait(1); // 書き込みサイクル待ち } // LCDへコマンド書き込み void LCD_Command(char command) { IO.PDR5.BYTE = command; // データ/コマンドを出力 IO.PDR7.BIT.B6 = 0; // RS信号を設定 IO.PDR7.BIT.B5 = 1; // E信号をHighにする IO.PDR7.BIT.B5 = 0; // E信号をLowにする wait(1); // 書き込みサイクル待ち } // nミリ秒待つウェイト関数 void wait(int time) { int i; // ループカウンタ for(i = 0 ; i < time ; i++) // time回数分ループ wait1mS(); // 1ミリ秒のウェイト関数を呼び出す } // 1ミリ秒待つウェイト関数 void wait1mS(void) { int i; // ループカウンタ for(i = 0 ; i < 2662 ; i++); // 1ミリ秒の間ループする } 48 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 第6章 まとめ 6−1 コンソールボードの全機能をまとめて動かす それでは、今まで作成してきたプロフラムを応用してコンソールボードの全機能をプログラムでまとめて 動作させてみましょう。 何をさせるか下記にあげてみます。 ① 電源を投入すると、LCDに“Hello CPU”と表示する。 ② 4×5のスイッチマトリックスを読み込み、押されたスイッチに応じて番号をLCDに表示させる。 ③ 押されたスイッチの行の違いにより点灯させるLEDランプを変える。 ④ スイッチが押されている間は、ブザーを鳴らす。 ※ スイッチの同時複数押しは無効(スイッチが押されていない)とする。 尚、今回のワークスペース名は“HELLO”とします。 ここまでで説明されていないスイッチマトリックスの読み込みや、割り込み処理、また、今まで全く変更し なかったソースファイル“resetprg.c”、“intprg.c”についても次ページ以降で説明していきます。 49 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 6−2 キーマトリックスを読み取る スイッチマトリックスの読み込みについては、今までやっていませんでしたので、ここで説明します。 スイッチの数が多かったり、CPUのポートが少なかったりした場合、マトリックスを組んでスイッチの読 み込みを行います。 CPUのポートを入力と出力に分けスイッチの値を読み込みます。 ここでは、5×2のマトリックスを例に挙げて説明します。 ポートP10∼P16の5本のいずれか1本のみにハイレベルを出力し、ポートPB0∼PB1で2ビットの色 分けしてある2つのスイッチの状態を読み込みます。 これをポートP10∼16の端子分繰り返し、スイッチの数だけ読み取るようにします。(リスト10−1) 読み込まれたキースイッチ10個の情報は、scan_d[n](n:0∼4)の下位2ビットに0か1で保存されま す。 回路図3 CPU P16 P15 出力部 P12 P11 P10 入力部 SW5 SW10 SW4 SW9 SW3 SW8 SW2 SW7 SW1 SW6 PB0 PB1 50 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう リスト10−1 // スイッチ状態読み込み char SW_Read(void) H8TinyCPU特有の入力処理。 { ポートBの特性上、アナログ入力チャネルを char sw1, sw2; ずらして2回読み込む必要がある。 AD.CSR.BYTE = 0x00; // アナログ入力チャネルを端子0に設定 sw1 = (IO.PDRB.BYTE & 0x0e); // ポートB ビット1∼3読み込み AD.CSR.BYTE = 0x03; // アナログ入力チャネルを端子3に設定 sw2 = (IO.PDRB.BYTE & 0x07); // ポートB ビット0∼2読み込み return(sw1 │ sw2); // 2回のORをとって4ビット分とする } // スイッチマトリックススキャン void Key_Scan(void) { const char scan_line[5] = { 0x01, 0x02, 0x04, 0x20, 0x40 }; // スキャンのための出力データ char scan_d[5]; // スイッチ読み込みデータ int i; for(i = 0 ; i < 5 ; i++) // スイッチ読み込みデータクリア scan_d[i] = 0; for(i = 0 ; i < 5 ; i++){ // 5ライン分スキャンする IO.PDR1.BYTE = scan_line[i]; // スキャンのための信号出力 scan_d[i] = SW_Read(); // スイッチの状態を読み込む } } スイッチマトリックススキャン関数“Key_Scan()”の中では、まず読み込みデータの保管場所をクリア し、ポート1にビットをずらしながらハイレベルを出力して、スイッチの状態を読み込んでいます。 ポート1で使っている端子がP10,P11,P12,P15,P16と飛んでいるため、出力するデータはビット シフトを使わずに配列で定義してあります。 下側のforループで、‘i’が0の時に回路図3の水色の部分、1の時は青色の部分、2の時はピンク色の 部分、3の時は緑色の部分、4の時には赤色の部分のスイッチを読み込んでいます。 51 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 6−3 チャタリング対策に割り込みを使う チャタリング対策は、3−4では必要時間待たせるサブルーチンを実行しウェイト時間を作りましたが、 効率が悪いため割り込みを使って処理を行います。 イメージはこうです。 チャタリング期間より長く割り込みタイマを設定し、その時間が来たら割り込みを発生させ、割り込みの 中でチャタリング対策の一部の処理を行い、終了すればまた、メインに戻り通常通りプログラムを実行しま す。 こうするとCPUが次のチャタリング対策処理まで待つ必要が無く、何も気にせず通常の処理を行って いればいいのです。 タイマが必要な時間を割り込みが知らせてくれるので非常に効率的です。 割込概念図 メイン処理 割込処理 割込発生 割り込み処理終了 52 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 6−4 割り込み内の処理 20個のスイッチをスキャンライン毎に4個づつ5列に分け、スイッチマトリックスの読み込みを10mSごと に実行し、1回毎に別なアドレスに保存します。 プログラムではグローバル変数“scan_d[0][n]”(n=0∼4)と“scan_d[1][n]”(n=0∼4)です。 電源投入後最初の割り込み処理での誤検出を避けるために、“main()”関数の中でscan_d[0][n]、 scan_d[1][n]を0(スイッチが押されていない状態)に初期化しておくことを忘れないで下さい。 scan_d[0][n]は前回のスイッチの状態を保存し、scan_d[1][n]に今回の割り込みで読み込んだ 内容を保存します。 そのため、割り込み処理の先頭でscan_d[1][n]の内容をscan_d[0][n]にコピーしてからスイッチ の読み込み処理を行うようにします。 この内容を比較し、同じであれば正式な入力と判断し、違っていればチャタリング中と判断します。 尚、割り込みのタイミングを作り出すタイマVは、最長でも2040μSまでしか設定できません。 そこで、割り込み処理に入ってきた回数をカウントして、10mSになったときにスイッチの読み込みを行 うようにします。(2mS周期の割り込みで5回) この割り込み回数のカウンタもグローバル変数とし、割り込みが発生する前に0クリアしておきます。 リスト10−3が割り込み処理のプログラムです。 このプログラムは“intprg.c”に記述します。 ※ “intprg.c”は割り込み処理の関数を記述するためのソースファイルです。 この中に記述することで、ベクターアドレスの定義や割り込み終了命令を記述するといった作業が 不要になります。(コンパイラ/リンカが処理してくれます) まず、”intprg.c”を開き、先頭(#includeの行と#pragmaの行の間あたり)に、リスト10−2の内容 を記述しておきます。 リスト10−2 #include “iodefine.h” // プロトタイプ宣言(#includeの行と#pragmaの行の間) extern void Key_Scan(void); extern void Key_Check(void); // グローバル変数(#includeの行と#pragmaの行の間) extern char scan_d[2][5]; // スイッチ読み込みデータ extern int Int_Counter; // 割り込み回数カウンタ 53 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 次に、 // vector 22 Timer V __interrupt(vect=22) void INT_TimerV(void) {/* sleep(); */} という行を探します。 プログラムの実体は、“{/* sleep(); */}”の中に書き込みます。 リスト10−3 // vector 22 Timer V __interrupt(vect=22) void INT_TimerV(void) { int i; if(++Int_Counter == 5){ // 割り込み回数カウンタをインクリメント for(i = 0 ; i < 5 ; i++) // 前回のスイッチ読み込み値をコピー scan_d[0][i] = scan_d[1][i]; Key_Scan(); // スイッチの読み込み Key_Check(); // スイッチ判定処理 Int_Counter = 0; // 割り込み回数カウンタをクリア } TV.TCSRV.BIT.CMFA = 0; // コンペアマッチフラグAをクリア } 割り込み処理に入ってきた時点で割り込み回数カウンタをインクリメントし、5回目か(10mSたったか) どうかを確認します。(リスト10−3の6行目) 5回目であれば、前回のスイッチ読み込み状態をコピーします。(7∼8行目) コピーが済んだ後に、スイッチの読み込みを行います。(9行目) この時点で、scan_d[1][n]に新しいスイッチの状態が読み込まれます。 その後、スイッチの判定処理を行います。(10行目) スイッチの判定処理では、読み込んだスイッチの状態が前回(scan_d[0][n])と同じかどうか比較し、 押されているスイッチの番号を取得します。 複数同時押しの処理もこの中で行います。 そして、割り込み回数カウンタを0にリセットします。(11行目) ※ この0クリアを忘れると、スイッチの読み込みが約2分10秒間隔になってしまいます。 最後にコンペアマッチフラグAをクリアして割り込み処理を終了します。(13行目) ※ この0クリアを忘れると、割り込みが発生しなくなってしまいます。 54 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 6−5 割り込みの初期設定 割り込みを実行するためには、割り込み時間を決めるためのソースクロックの設定やカウンタ値の設定 など、タイマVの初期設定をしておかなければなりません。 また、初期設定が終わらないうちに割り込みが発生しないように、割り込みを禁止にしておき、諸々の 初期設定が終了した後で割り込みを許可する必要があります。 では、まず初期設定が終わるまで割り込みが発生しないように、割り込みを禁止しておくように変更し てみましょう。 “resetprg.c”を開いて“set_imask_ccr(0);”と書いてある行を探します。 見つかったらその行の先頭に“//”を書いて、コメントにしてしまいます。 この“set_imask_ccr(0);”が割り込みを許可している行ですので、これで割り込みが発生しなくな ります。 “HELLO.c”を開き、先頭部分で使用するグローバル変数を宣言しておきます。 宣言する変数は、先ほどの割り込み処理で使用する変数と、スイッチの入力があった場合にスイッチ 番号を入れるための変数です。 // グローバル変数 char scan_d[2][5]; // スイッチ読み込みデータ int Int_Counter; // 割り込み回数カウンタ char Key_No; // スイッチ番号 55 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 次に“main()”関数の中で、グローバル変数の初期化、I/Oの初期化を行い、割り込みを許可しま す。 その後、LCDに“Hello CPU”と表示させ、スイッチの入力を待ちます。 スイッチの入力があれば、ブザーを鳴らし、LEDを点灯し、LCDにスイッチ番号を表示させます。 スイッチの入力が無くなれば、ブザーをOFFし、LEDも消灯します。 割り込みを許可するには、先ほど“resetprg.c”でコメントにした“set_imask_ccr(0)”を使います。 この“set_imask_ccr(0)”は“machine.h”で定義されているマクロです。 ソースプログラムの先頭に“#include <machine.h>”と記述しておきましょう。 リスト10−4 void main(void) { int i; char key_no_old; for(i = 0 ; i < 5 ; i++){ // スイッチ読み込みデータクリア scan_d[0][i] = 0; scan_d[1][i] = 0; } Int_Counter = 0; // 割り込み回数カウンタクリア Key_No = 0; // スイッチ番号クリア key_no_old = Key_No; IOinit(); // I/Oの初期化 set_imask_ccr(0); // 割り込み許可 LCDinit(); // LCDの初期設定 Print(1,1,"Hello CPU"); // LCDに表示する ∼ 次ページへ ∼ 56 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう while(1){ // 無限ループ if(Key_No != key_no_old){ // 以前のスイッチ番号と変わった key_no_old = Key_No; // 前のスイッチ状態を保存 if(Key_No != 0x00){ // スイッチの入力があった LCD_Command(DDAdd + 0x40); // LCDの表示位置を2行目の先頭にする LCD_Write(Key_No); // スイッチ番号を表示 LED_Set(Key_No); // LEDを点灯 Buzzer(ON); // ブザー鳴動開始 } else{ Buzzer(OFF); // ブザー鳴動停止 LED_Set(OFF); // LEDを消灯 } } } } 57 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう I/Oの初期化にタイマVの初期化部分を追加します。 まず、TCSRVのフラグと端子への出力方法を初期化します。(リスト10−5の5行目) 次にタイムコンスタントレジスタAに値を設定します。(6行目) タイマVがカウントを開始して、この値になったときに割り込みが発生します。 今回は2mS周期で割り込みを発生させますので、250にします。 7行目と8行目でタイマVに入力されるクロックソースと、カウンタのクリア条件を設定しています。 ここでは、内部クロックを128分周したものをクロックソースとし、コンペアマッチA(コンスタントレジスタ Aの設定値とカウンタの値が一致した)でカウンタをクリアするように設定しています。 最後にコンペアマッチAによる割り込みを許可しています。 これで、2mS周期での割り込みが発生できるようになります。 リスト10−5 // I/Oの初期化 void IOinit(void) { : TV.TCSRV.BYTE = 0x00; // タイマコントロール/ステータスレジスタV設定 TV.TCORA = 250; // タイムコンスタントレジスタAに250を設定 // 250×128÷16=2mS TV.TCRV1.BYTE = 0x01; // TCKS0ビットのみ1 TV.TCRV0.BYTE = 0x0b; // コンペアマッチAでカウンタクリア // 内部クロック128分周 カウンタスタート TV.TCRV0.BIT.CMIEA = 1; // コンペアマッチA 割り込み許可 } LCDの点灯/消灯処理は、スイッチ番号からどの行のスイッチかを判断し、その行に対応したLEDを 点灯します。 これは、switch∼case文を使えば簡単に実現できます。 リスト10−6 // LED点灯処理 void LED_Set(char position) { ∼ 次ページへ ∼ 58 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう switch(position){ case '1': case '2': case '3': case '4': case '5': IO.PDR8.BYTE = 0x01; // LED1を点灯 break; case '6': case '7': case '8': case '9': case 'A': IO.PDR8.BYTE = 0x02; // LED2を点灯 break; case 'B': case 'C': case 'D': case 'E': case 'F': IO.PDR8.BYTE = 0x04; // LED3を点灯 break; case 'G': case 'H': case 'I': case 'J': case 'K': IO.PDR8.BYTE = 0x08; // LED4を点灯 break; case OFF: IO.PDR8.BYTE = 0x00; // 全LEDを消灯 break; default: IO.PDR8.BYTE = 0x0f; // 全LEDを点灯 break; } } 59 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 6−6 スイッチ入力判定処理 押されたスイッチの有効/無効の判定と、有効の場合にスイッチ番号を取得する関数を作ります。 この関数は、割り込み処理から呼び出されている“Key_Check()”という関数です。 “Key_Scan()”で保存したスイッチの読み込みデータを使います。 つまり、scan_d[0][n]とscan_d[1][n]を比較し、同じ(0以外)であり、1つのスイッチしか押されて いなければスイッチが押されたと判断し、それに応じたスイッチ番号をグローバル変数“Key_No”に入 れます。 複数押しを無効とするために、まず5つの列について確認します。 ここでは、確認のためにカウンタを使います。 押されている列があればカウンタをインクリメントします。 つまり、カウンタが0であれば押されていないことになり、また、2以上になっていれば2列以上のどこか のスイッチが同時に押されたと言うことになり、無効と判断することが出来ます。 1列のみ押されていた場合には、押されたスイッチを確認します。 ここでは単純に0x01,0x02,0x04,0x08との比較を行います。 これで列の中での複数押しは無効とすることが出来ます。 最後に押されていた行と列をもとに、スイッチ番号を変数に入れます。 このスイッチ番号は“テーブル”(配列)で定義していますので、LEDの点灯を無視すれば自由に変更 することが出来ます。 リスト10−7 // スイッチ入力確認 void Key_Check(void) { const char SW_No[4][5] = { { '1', '2', '3', '4', '5' }, { '6', '7', '8', '9', 'A' }, { 'B', 'C', 'D', 'E', 'F' }, { 'G', 'H', 'I', 'J', 'K' } }; int y; int i,count; ∼ 次ページへ ∼ 60 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう Key_No = 0x00; // スイッチ番号をクリア count = 0; // カウンタをクリア for(i = 0 ; i < 5 ; i++){ // 列の確認 if((scan_d[0][i] & scan_d[1][i]) != 0){ // スイッチが押されている count++; // カウンタをインクリメント y = i; // Yポジション } } if(count == 1){ // 列の複数押し無し switch(scan_d[1][y]){ // 行の確認 case 0x01: // 1行目 Key_No = SW_No[0][y]; break; case 0x02: // 2行目 Key_No = SW_No[1][y]; break; case 0x04: // 3行目 Key_No = SW_No[2][y]; break; case 0x08: // 4行目 Key_No = SW_No[3][y]; break; default: // 複数押し break; } } } これで6−1の②、③の項目については出来ました。 ①、④については、以前の章でやったものそのものですのでここでは、特に触れないことにします。 ①∼④の機能をまとめたプログラムが添付CD 6章フォルダ内にありますので参照してみて下さい。 (“HELLO.c”、“intprg.c”、“resetprg.c”) またコンパイルしてみてコンソールボードに書き込みをしてみて下さい。 コンソールボードの機能をフルに同時に使うことがで出来ましたね。 61 SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 付録1 ダウンロードアダプタ関連 この製品は、弊社のH8シリーズCPUボード用のオンボードフラッシュ書き込みツールです。 パソコンのRS232Cを使用しCPUボードやコンソールボードと接続し、開発したプログラムを簡単にダ ウンロード出来る便利なツールです。 また、シリアルインタフェースの電圧レベル変換基板としても使用可能です。 書き込みソフトは、弊社製H8WriteProgまたは、ルネサステクノロジ社製 Flash Development Toolkit が使用出来ます。 H8WriteProgは、ダウンロードアダプタ購入時にURLを添付しますのでダウンロードして下さい。 ご購入の際は、ダウンロードアダプタ単体の他に、ACアダプタ(5V)とRS232Cケーブルがセットにな ったものも用意しています。 ご利用内容に合わせお求め下さい。 ダウンロードアダプタ(H8シリーズ) ● アダプタ基板 ● 接続ケーブル ● ピンヘッダ ダウンロードアダプタ Cセット ● ダウンロードアダプタ (基板、ケーブル、ピンヘッダ) ● ACアダプタ(5V) ● RS−232Cケーブル a SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 参考文献 ルネサステクノロジ社 H8/300Hシリーズ プログラミングマニュアル H8S、H8/300Hシリーズ クロスアセンブラ ユーザーズマニュアル H8/3672シリーズ ハードウェアマニュアル オーム社 H8 マイコン完全マニュアル b SUNTECH Corp. H8Tinyコンソールボードを動かしてみよう 有限会社 サンテック 〒399-0712 長野県塩尻市塩尻町434 TEL 0263-53-2463 FAX 0263-53-8195 URL http://www.suntech.incoming.jp/index.htm e-mail web@suntech-jp.com SUNTECH Corp.
© Copyright 2024 Paperzz