DEMO9S08QG8評価ボードのシリアル通信機能を使って、 外部記憶装置を作ってみました。 で、この外部記憶って、何の役に立つのだろう。
このアプリケーションは、 DEMO9S08QG8評価ボードのみ必要とします。
このアプリケーションは、以下のような仕様で動作します。
基本的なコンセプトは、 「シリアル通信で会話が出来る」ことです。 会話といっても、もちろん普通の言葉を理解することはできません。 ある文法に従った「コマンド」をシリアル通信で入力し、解釈して、 それに対する反応をシリアル通信で返します。
ここで想定している外部記憶は、 8ビットのデータが16個並んだものです。
16個のデータのうち、 どのデータを利用するかを決定するために 「アドレス」を指定することができます。
16個のデータを区別するために、 「アドレス」は、0から15までの数値をとります。 しかし、十進数を扱うのはプログラムには難しいので、 「アドレス」は、16進表記であらわします。 また、同様の理由で「データ」についても同様に16進表記で表現します。
<16進数文字> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F <16進数> ::= { <16進数文字> }..
このアプリケーションでは、三種類のコマンドを扱います。
<コマンド> ::= <ダンプコマンド> | <読み出しコマンド> | <書き込みコマンド>
外部メモリからは、 コマンドの入力を促すためのプロンプトが出力されます。 このプロンプトは、コマンドの入力が開始されたことを示しています。
<プロンプト出力> ::= "*"
それぞれのコマンドは、以下のセクションで述べる文法が定められており、 文法から外れた入力がされた場合には、 すぐに入力中のコマンドはキャンセルされコマンドの入力開始状態に戻ります。
メモリの内容を一括で表示する機能を「ダンプ」と呼んでいます。 この外部メモリでは、"D"というコマンドを受け取ると メモリの内容を一括表示します。
キーボードから"D"を打っただけで表示をさせても良いのですが、 他のコマンドとの兼ね合いから、 "D"に続いて"Enter"キーを押したときにコマンドが終了するものとします。
<ダンプコマンド> ::= "D" <Enter>
内容の一括表示は、":"で区切られた16個の16進データの羅列で表現します。
<ダンプ出力> ::= { ":" <16進数> }.. <行末>
読み出しコマンドは、指定されたアドレスのデータを表示します。
<読み出しコマンド> ::= "R" <16進数> <Enter>
読み出しの結果は、 アドレスとデータを":"でつないで表現します。
<読み出し出力> ::= <16進数> ":" <16進数> <行末>
書き込みコマンドは、 指定されたアドレスに同じく指定されたデータを書き込みます。 書き込みコマンドの":"の前の部分がアドレス、後ろの部分がデータをあらわします。
<書き込みコマンド> ::= "W" <16進数> ":" <16進数> <Enter>
書き込みの結果は、 読み出し同様、アドレスとデータを":"でつないで表現します。
<書き込み出力> ::= <16進数> ":" <16進数> <行末>
プログラムの開発には、CodeWarriorを使います。 CodeWarriorでは、 一つのプログラムの単位を「プロジェクト」と呼んでいます。 ここでは、新規にプロジェクトを作成します。 ここでは、V5.0を使って説明します。
スタートメニューからCodeWarriorを起動するとCodeWarriorが起動します。
初めてCodeWarriorを起動したときには、 下のような"Startup"ダイアログが出てきます。
出てこない場合には、 メニューから"File → Startup Dialog..."を選ぶと 出てきます。
"Startup"ダイアログの "Create New Project"(新しいプロジェクトを作る)をクリックして "HC(S)08 New Project"ダイアログを開きます。
まずは、"Project Parameters"(プロジェクトのパラメータ)を設定します。
使用する言語に"C"を選び、 プロジェクトを作成する場所を設定します。 この例では、"C:\Projects\CW\Extmemory1"というディレクトリに "Extmemory1.mcp"というプロジェクトファイルを作成しています。 設定を終えたら、「次へ」をクリックします。
画面が変わって、 "Device and Connection"(デバイスと接続方法)の設定に移ります。
デバイスには"HCS08 → HCS08QG Family → MC9S08QG8"を選び、 デフォルトの接続方法には"P&E Multilink/Cyclone Pro"を選び、 「次へ」をクリックします。
3枚目の画面は、 "Add Additional Files"(追加するファイル)の設定です。 プロジェクトで使用するソースコードなどがあれば、 ここで設定します。
今回は、追加するファイルはありませんので、 そのまま「次へ」をクリックします。
4枚目の画面は、 "Processor Expert"(プロセッサエキスパート)の設定です。
"Processor Expert"を選んでこの機能の使用を宣言します。 さらに、「次へ」をクリックします。
5枚目の画面は、"C/C++ Options"の設定です。
すべて、デフォルトのままの設定にします。
startup code | ANSI |
memory model | Small |
floating point format | None |
設定を確認して 「次へ」をクリックします。
最後の画面は、"PC-Lint"の設定です。 PC-lint™というのは、 プログラムの書式を検査するためのプログラムです。 今回は使用しません。
"No"をチェックして 「完了」をクリックすると、 新規プロジェクトの作成が始まります。
MC9S08QG8マイコンには、 いくつかの異なる種類のパッケージが存在します。 その中から、今から使おうとしているパッケージが どのパッケージなのかを指定するのが次のウィンドウです。
今回は、評価ボードに搭載されている 16PIN-DIPパッケージを使いますので、 "MC9S08QG8_16"だけを選択して「OK」をクリックします。
これが、新規プロジェクトのための 最後の設定です。 "Select Configurations"(設定を選べ)というのは、 実にあいまいですが、 ここでは、アプリケーション開発の結果を何に使うかを指定します。 もちろん、独立した製品に仕上げるのが最終目的には 違いないのですが、 プログラムの開発中には、デバッガを接続した状態で プログラムを実行する必要もあります。
"Debug"設定は、このようなデバッガを接続した状態を考慮した 設定を行います。 このため、BKGD端子には、デバッガが接続されアプリケーションでは 使用できなくなります。
一方、"Release"設定は、最終製品での使用を考慮した設定を行うため、 すべての端子がアプリケーションに開放されます。
今回は、デバッグ環境である評価ボードでアプリケーション開発を 行いますので、 "Debug"だけを選択して「OK」をクリックします。
以上で、プロセッサエキスパートを使用する 新規プロジェクトができました。
ここから、 プロセッサエキスパートのプロパティを設定していきます。 ここでは、設定箇所だけ示します。 設定方法の詳細は、三値デジタル太陽計を参照ください。
RxD端子とTxD端子を使って、RS-232C規格でPCと接続します。
まず、非同期シリアルビーンを設定します。 "Bean Selector"から "CPU Internal Peripherals(CPU内部ペリフェラル) → Communication(通信) → AsynchroSerial(非同期シリアル)"を ダブルクリックで呼び出したら、 以下の項目を設定します。
項目名 | 設定する値 |
---|---|
Baud rate(ボーレート) | 9600 |
MC9S08QG8の場合、非同期シリアルがつながるのは"PTB0"と"PTB1"だけですので、 使用するポートは自動的に設定されます。 ここで唯一設定している「ボーレート」とは、 PCとデータを送受信するときの通信速度のことです。 ここでは、一般的な"9600baud"(毎秒9600ビット)を使用します。 最終的に属性は以下のようになります。
以上で必要なビーンの宣言は終わりました。 次は、Processor Expertにソースコードを生成させます。
CodeWarriorのウィンドウの左側にあるプロジェクトペインの ボタンを クリックして、ソースコードを生成させます。
コード生成では、"Extmemory1.c"というファイルも同時に生成されています。 左端の"Project Panel"ウィンドウに"Extmemory1.c"というファイルが見えます。
このファイルには、 マイコンがリセットされた後で実行される "main()"関数が記述されています。 ここをダブルクリックすると、 "Extmemory1.c"ファイルを編集するテキストエディタが開きます。
このテキストエディタで残りの必要なコードを記述します。
このアプリケーションは、 ハードウェアが簡単である代わりに、 多くのソフトウェアを記述していく必要があります。 このセクションでは、 いくつかの便利な関数について説明していきます。
getc(void)関数は、 シリアル通信で文字が一つ受信されるのを待ち、 受信した文字を返します。 このアプリケーションで仮定しているシリアル通信のフォーマットは、 8ビットパリティなしフォーマットです。 このため、この関数で返される値も8ビットの値になります。
/************************************************************** * Get a character from AS1. **************************************************************/ char getc(void) { AS1_TComData c; while (AS1_RecvChar(&c) != ERR_OK) ; return (char)c; }
"Processor Expert"が生成してくれたコードには、 RecvChar(AS1_TComData*)という 一文字受信関数があります。 この関数は、文字が受信出来なかった場合には、 エラーコードを返すことで、すぐに呼び出し元に制御を返してくれます。 getc(void)には、 文字を受信できるまで待つ動作をさせたいので、 RecvChar(AS1_TComData*)が 正常受信コードを返すまでwhile文で待たせる ようにしました。
シリアル通信では、一文字のビット幅をユーザが定義できるように なっています。 "AS1_TComData"型は、このユーザが定義したビット幅のデータを 格納することができる型として定義されます。
例えば、MC9S08QG8の場合、 8ビットのデータを送受信する場合には、 "AS1_TComData"型は"byte"型として定義されます。 また、9ビットのデータを送受信する場合には、 "AS1_TComData"型は"word"型として定義されます。 このように、送受信しているデータのビット幅を ユーザが意識せずにプログラムを書くことが出来るようにという 配慮なのでしょう。
実際にプログラム中でデータを使う場合には、"char"型を使うので、 役に立つ仕様なのかどうか微妙なところです。
putc(char)関数は、 シリアル通信で送信可能になるまで待ち、 文字を一つ送信します。 この関数で送信するデータも8ビットの値です。
/************************************************************** * Put a character to AS1. **************************************************************/ void putc(const char c) { while (AS1_SendChar((AS1_TComData)c) != ERR_OK) ; }
"Processor Expert"が生成してくれたコードには、 SendChar(AS1_TComData)という 一文字送信関数があります。 この関数は、文字が送信出来なかった場合には、 エラーコードを返すことで、すぐに呼び出し元に制御を返してくれます。 putc(char)には、 文字を確実に送信できるまで待つ動作をさせたいので、 SendChar(AS1_TComData)が 正常送信コードを返すまでwhile文で待たせる ようにしました。
仕様でも述べたように、 コマンドでは16進数を使用してインターフェースを行っています。 この関数では、文字を16進数とみなして数値化して返してきます。
/************************************************************** * Convert a HEXADECIMAL character to a byte. * Returns 255 if an invalid character provided. **************************************************************/ byte hex2byte(const char c) { if (c >= '0' && c <='9') { return (c - '0') + 0; } if (c >= 'A' && c <= 'F') { return (c - 'A') + 10; } return 255; }
16進文字として使用可能なのは、'0'から'9'までの数字と 'A'から'F'までの文字のあわせて16種類です。 これを二つの場合に分けて処理を行います。
この関数の前半部分では、'0'から'9'までの数字が与えられた場合の 処理を行っています。 文字'0'とのコードの差を計算すると数値化することができます。
また、後半部分では、'A'から'F'までの文字が与えられた場合の 処理を行っています。
もし、16進文字以外の文字が与えられたら、 数値化に失敗したことを示すために"255"を返します。
この関数では、与えられた文字が16進文字として適合するかどうかを 検査します。
/************************************************************** * Check if a character is a HEXADECIMAL character. * Return TRUE if the character is a HEXADECIMAL character. **************************************************************/ bool isHex(const char c) { return (hex2byte(c) != 255); }
この関数では、hex2byte(char)関数を 呼び出しています。 上で述べたように、hex2byte(char)関数は、 16進文字以外の文字が与えられると"255"を戻します。 この関数では、戻ってきた値が"255"かそれ以外かを検査して 16進文字としての適合性を判断します。
この関数は、 hex2byte(char)と逆の動作をします。 つまり、与えられた数値を16進文字に変換します。
/************************************************************** * Convert a byte data to a HEXADECIMAL character. * Returns '*' if an invalid byte data which is larger than * 15 provided. **************************************************************/ char byte2hex(const byte d) { if (d <= 9) { return '0' + (d - 0); } if (d <= 15) { return 'A' + (d - 10); } return '*'; }
この関数でも二つの場合に分けて処理をしています。
まず、数値が'0'から'9'までの場合には、文字'0'のコードに数値を足して 文字コードを計算しています。
また、数値が'10'から'15'までの場合には、 数値が'10'のときに文字'A'が出てくるように 文字'A'のコードに数値(d - 10)を足して 文字コードを計算しています。
もし、数値が16進の範囲を超える値であった場合には、 '*'を返すことで数値の以上を示します。
上で述べた「便利な関数」群を使って、 コマンドを解釈するための簡単なインタプリタを作成します。
まず、外部記憶領域を宣言します。 仕様の通りに、外部記憶は、8ビットのデータ16個分とします。
/************************************************************** * memory[] : a memory area used by commands. **************************************************************/ byte memory[16];
大域変数としてmemory[16]を宣言します。
インタプリタでは、 読み込んだ文字によってすべき処理を決定していきます。 このため、読み込んだ文字を一時的に覚えておく場所があると 便利です。
/************************************************************** * ch : a last received character from SCI. **************************************************************/ char ch;
このインタプリタでは、シリアル通信から受信した文字を いったnこの変数に格納してから処理を行います。
このインタプリタでは、 一文字でコマンドが完結してしまう仕様になっているので、 一文字分のキャッシュがあれば十分に処理を行うことができます。
ところが、もっと複雑な処理系になると、 一文字分のキャッシュでは足りないものも出てきます。 このような場合には、複数文字のキャッシュを用意したり、 字句解析器(Lexical Analyzer)という別のソフトウェアで 解析させてその結果を使ったりします。
また、BASICインタプリタなどでは、 中間言語という機械に扱いやすい記号列にコンパイルしてから 処理を行う場合もあります。 この場合には、コンパイルという作業に字句解析も含まれています。
ヒューマンインターフェースでは、 入力した文字を人間がすぐに確認できたほうが便利です。 この機能のために「一文字受信し、すぐに同じ文字を送り返す。」 という複合型関数getput(void)を 用意しました。
/************************************************************** * Get a character from AS1 and returns an echo. **************************************************************/ char getput(void) { ch = getc(); putc(ch); return ch; }
ご覧のように、getc(void)で受信した文字を putc(char)で送り返しています。
この関数では、同時に受信した文字をキャッシュに入れる動作も 行っています。
このwordデータ受信関数は、 getput(void)とhex2byte(byte)を 使用して、 シリアル通信で受信した16進数の文字列から 16ビットのword型のデータを得ます。 この関数は、仕様のセクションで述べた<16進数>に相当する 解析を行います。
/************************************************************** * Returns a word data constructed from characters * received by AS1. Any characters can be a terminator * except HEXADECIMAL characters. **************************************************************/ word getWord(void) { word d = 0; while (isHex(getput())) { // (A) Repeat for HEX characters. d = (d << 4)| hex2byte(ch); // (B) Convert to a word. } return d; // (C) Returns a converted word. }
「文字列から得る」といっても、ここでは文字列を作成せずに 一文字ずつ処理を行います。
(A)では、シリアル通信で一文字受信し受信した文字が16進数文字であるか どうかを検査しています。 もし、16進数文字以外の文字を受信した場合は、 while文を抜けて、(C)に向かいます。
(B)では、受信した16進数文字を使って16進数の値を計算します。 ここで使用されているのは、4ビットのシフト関数です。 16進数文字は、一文字で4ビット分を表現しています。 このため、新しい文字が受け入れられるたびに、 word型の値を4ビット分シフトしています。
16進数文字以外の文字が見つかったら、 (C)で変換された値を返します。 この時、文字キャッシュ"ch"には、 「16進文字ではない文字とは何であったか」という 情報が残っていますので、 この関数を呼び出したプログラムで判断することができます。
16進文字が期待されているのに、 一文字も16進数文字が受信されなかった場合にはどうなるのでしょうか。 この関数では、処理を簡単にするために16進文字が"0"個でも 正常に動作してしまい、値"0"を返してきます。 つまり、コマンドとして"R<Enter>"を与えると "R0<Enter>"と同じと解釈してしまいます。
まあ、「仕様」の不備と言えなくもないのですが、 どうやったら、エラーとして検出できるかは、今後の課題とします。
getWord(void)関数とは逆にこの関数では、 16ビットのword型の値を 指定された桁数で16進数表示します。
/************************************************************** * Put a word as a HEXADECIMAL value in a specified number * of characters. **************************************************************/ void putWord(const word data, const byte w) { byte i; // Loop counter byte pos; // Position of a digit. byte digit; // Value of a digit. for (i = w; i-- > 0; ) { // (A) loop for i = (w-1)..0 pos = i * 4; // (B) Get the digit's position. digit = (data >> pos) & 0xF; // (C) Get the digit's value. putc(byte2hex(digit)); // (D) Put a HEX character. } }
表示する桁数は、"w"変数で指定されます。 (A)では、"(w - 1)"を初期値として"0"までの値をとる ループ変数"i"が定義されています。 ループ内の(B)から(D)までは、 "i"の示す桁に対して処理を行っています。
(B)では、"i"の示す桁のビット位置を"pos"変数に求めています。 16進数文字は、一文字が4ビットに相当しますので、 "i"を4倍することで目的のビット位置を知ることができます。
(C)では、(B)で得られたビット位置から16進数一文字分のデータを 取り出して"digit"変数に入れます。 具体的には、wordのデータをビット位置分だけ シフトして、さらに、4ビット分だけを取り出すために 定数"0xF"とAND演算を行っています。
(D)では、(C)で得られた値を16進数文字一文字に変換した上で、 シリアル通信で送信しています。
いよいよ、最後の関数です。 インタプリタ関数では、コマンド一行分の解釈と実行を行います。 もし、解釈の途中でエラーを発見したら即座に呼び出しもとの ルーチンに戻ります。
/************************************************************** * interpret a line. * Returns to invoking routine if any error occurs. **************************************************************/ void interpret(void) { word addr; // Address pointer. word data; // Work area for a data. putc('\r'); // (A) Put CR/LF putc('\n'); putc('*'); // (B) Put a prompt. switch (getput()) { // (C) Get a command character. case 'R': // (D) "Read" command. : return; // (E) Command completed. case 'W': // (F) "Write" command. : return; // (G) Command completed. case 'D': // (H) "Dump command. : return; // (J) Command completed. } }
これが、インタプリタ関数の骨組み部分です。 インタプリタが呼び出されると、まず、 (A)と(B)で改行とプロンプト"*"を送信します。
次に(C)でコマンドの最初の一文字を取り込んで、 その文字にしたがってコマンド処理に分岐します。 分岐する先は、(D)(F)そして(H)の三箇所です。 これ以外の文字が見つかった場合には、 インタプリタを抜けます。
いずれのコマンドも処理が正常に終わった場合には、 (E)(G)および(J)のreturn文で インタプリタを抜けます。
以下では、それぞれのコマンドの処理の詳細について述べます。
コマンドが"R"で始まる場合には、 読み出しコマンドであると認識して処理を行います。
addr = getWord(); // (D1) Get address to be read. if (ch != '\r') return; // (D2) Error for an invalid terminator. putc('\n'); // (D3) Put LF following CR. addr &= 0x0F; // (D4) Limit the address. data = memory[addr]; // (D5) Get a data from memory. putWord(addr, 1); // (D6) Put address. putc(':'); // (D7) Put a separator. putWord(data, 2); // (D8) Put data.
(D1)で、コマンド文字"R"に続くことが期待されるアドレスを取り込みます。 正しい読み出しコマンドのアドレス部分は、<Enter>で終わります。 これを確認するのが、(D2)です。 <Enter>キーは、'\r'(コード13)で表現されます。 もし、<Enter>が押されたのでなければ、 後の処理をキャンセルするため、returnで インタプリタを抜けます。
(D3)では、"Line Feed"(LF / 改行)コードを出力します。 一般のターミナルソフトでは、"Carriage Return"(CR / 復帰)コードに 続いて LF コードを受信することによって改行動作を行います。 CR コードは、getWord(void)関数の中で エコーバックによって出力されているので、 残りの LF コードだけをここで出力しています。 LF コードは、'\n'(コード10)で表現されます。
(D4)では、(D1)で受信したアドレスを加工しています。 (D1)で受信したアドレスは、word型の値をとるため、 0から65535までの値になる可能性があります。 ところが、外部記憶には、0から15までのアドレスしか存在しません。 存在しないアドレスに対してアクセスを行わせないように アドレスの値の下位4ビットだけを取り出します。 この取り出しの操作に使用されるのが'&'(AND / 論理積)演算子です。 (D4)の操作を経るとアドレスは、0から15までのアドレスをとります。
(D5)では、外部記憶の指定されたアドレスからデータを取り出します。
(D5)で取り出した値をシリアル通信で送信するのが、 (D6)(D7)そして(D8)です。 (D6)は16進数一桁のアドレスを送信し、 (D7)はセパレータ文字':'を送信し、 (D8)は16進数二桁のデータを送信します。
コマンドが"W"で始まる場合には、 書き込みコマンドであると認識して処理を行います。
addr = getWord(); // (F1) Get address to be written. if (ch != ':') return; // (F2) Error for an invalid separator. data = getWord(); // (F3) Get data to be written. if (ch != '\r') return; // (F4) Error for an invalid terminator. putc('\n'); // (F5) Put LF following CR. addr &= 0x0F; // (F6) Limit bit width of address. data &= 0xFF; // (F7) Limit bit width of data. memory[addr] = (byte)data; // (F8) Write a data into memory. putWord(addr, 1); // (F9) Put address for confirmation. putc(':'); // (FA) Put a separator. putWord(data, 2); // (FB) Put data for confirmation.
(F1)で、コマンド文字"W"に続くことが期待されるアドレスを取り込みます。 正しい書き込みコマンドのアドレス部分は、 セパレータ文字':'で終わります。 これを確認するのが、(F2)です。 もし、':'が押されたのでなければ、 後の処理をキャンセルするため、returnで インタプリタを抜けます。
(F3)で、セパレータ文字':'に続くことが期待されるデータを取り込みます。 正しい書き込みコマンドのデータ部分は、<Enter>で終わります。 これを確認するのが、(F4)です。 もし、<Enter>が押されたのでなければ、 後の処理をキャンセルするため、returnで インタプリタを抜けます。
(F5)では、読み出し処理と同様の理由で、 "Line Feed"(LF / 改行)コードを出力します。
(F6)と(F7)では、(F1)と(F3)で受信したアドレスとデータを加工しています。 (F1)で受信したアドレスも(F3)で受信したデータも、 word型の値をとるため、 0から65535までの値になる可能性があります。 ところが、外部記憶には、0から15までのアドレスしか存在しませんし、 データは8ビットの値です。 存在しないアドレスや数値を使わせないように アドレスの値の下位4ビットとデータの下位8ビットだけを取り出します。 この取り出しの操作に使用されるのが'&'(AND / 論理積)演算子です。 (F6)と(F7)の操作を経るとアドレスは、 アドレスは0から15までの、データは0から255までの値をとります。
(F8)では、外部記憶の指定されたアドレスにデータを格納します。
(F8)で取り出した値をシリアル通信で送信するのが、 (F9)(FA)そして(FB)です。 (F9)は16進数一桁のアドレスを送信し、 (FA)はセパレータ文字':'を送信し、 (FB)は16進数二桁のデータを送信します。
コマンドが"D"で始まる場合には、 ダンプコマンドであると認識して処理を行います。
getput(); // (H1) Get a terminator cahracter. if (ch != '\r') return; // (H2) Error for an invalid terminator. putc('\n'); // (H3) Put LF following CR. for (addr = 0; addr < sizeof(memory); addr++) { // (H4) Scan all address. putc(':'); // (H5) Put a separator. putWord(memory[addr], 2); // (H6) Put a data at the address. }
ダンプコマンドは、"D"一文字のコマンドです。 (H1)で"D"に続く文字を受信し、 (H2)で <Enter> かどうかを確認します。 もし、<Enter>が押されたのでなければ、 後の処理をキャンセルするため、returnで インタプリタを抜けます。
(H3)では、読み出し処理と同様の理由で、 "Line Feed"(LF / 改行)コードを出力します。
(H4)は、外部記憶のすべての領域を走査するためのループ宣言です。 変数"addr"は、0から変数"memory"の最後の要素の位置まで 変化していきます。
(H5)と(H6)は、 実際のダンプ表示を行う部分です。 (H5)はセパレータ文字':'を送信し、 (H6)は外部記憶の変数"addr"で示されるアドレスにある 16進数二桁のデータを送信します。
忘れてはいけないのが、メインルーチンの変更です。
必要なのは、for文で始まる3行だけです。
/* Write your code here */ /* For example: for(;;) { } */ for (;;) { interpret(); }
このアプリケーションでは、メインルーチンに無限ループを配置し、 その中で延々とinterpret(void)関数を 呼び出します。
以上でプログラムの準備は出来ました。
プログラムが完成したら、テストしてみます。
アプリケーションの実行のために マイコンにプログラムを書き込みます。
評価ボードをPCにつなぐ前に 評価ボード上のジャンパを設定しなくてはなりません。 この設定は、評価ボード上のRS-232Cトランシーバを利用可能な 状態にするために必要です。
基板表面にある"COM_EN"と表示されたジャンパをはずします。 私は、はずしたジャンパが行方不明にならないように ピンヘッダの真ん中のピンにジャンパを残しています。
DEMO9S08QG8評価ボードをUSBケーブルで接続します。
ドロップダウンリストが"P&E Multilink/Cyclone Pro"になっているのを 確認して、 アイコンを クリックするとコンパイル、リンクの後、デバッガが立ち上がります。
USBインターフェースの設定を確認したら、 "Connect"をクリックします。
「Yes」をクリックして、マイコンに書き込みます。
ここで、ツールボタンの アイコンを クリックするとマイコンは自律走行を始めます。 ですが、このアプリケーションは、シリアルインターフェースを相手に 動作するのでこのままでは何も起こりません。 そこで、ターミナルソフトアプリケーションを起動して、 会話をします。
Windowsには、オペレーティングシステム標準の「ハイパーターミナル」という ターミナルソフトが付属しています。 今回は、これを使います。
まず、「ハイパーターミナル」を起動します。 WindowsXPでは、 「スタート → すべてのプログラム → アクセサリ → 通信 → ハイパーターミナル」という結構深いところにあります。
ハイパーターミナルを起動すると、 「接続の設定」というダイアログが現れます。
このダイアログでは、名前とアイコンを設定します。 名前は、これから設定するハイパーターミナルの設定内容を保存する ファイルの名前として、 また、アイコンは保存されたファイルのアイコンとして使用されます。 適当な名前とアイコンを設定したら、「OK」をクリックします。 ここでは、"9600-8N1"という名前をつけました。
次のダイアログも「接続の設定」という名前がついています。
ここでは、"COM1"ポートを使用しますので、 接続方法コンボボックスに"COM1"を設定して、「OK」をクリックします。
これで、ターミナルソフトの設定は終わりです。
次にシリアルケーブルをつなぎます。
これで、すべての準備が整いました。
ここから、一連のコマンドをテストしていきます。
ツールボタンの アイコンを クリックして、マイコンのプログラムを起動します。
すると、プロンプトが表示されます。 これで、マイコンがコマンドを待っている状態であることが確認できます。
次に書き込みコマンドをタイプしてみます。
表示を見る限り、正しく書き込まれたように見えます。 ただし、ここで返ってくる値は、 コマンドで与えられた値であって、外部記憶の内容ではありません。 そのため、確認には、次の読み出しの確認が必要です。
次に読み出しコマンドをタイプしてみます。
書き込みコマンドで書き込まれた値が確認でき、 書き込みを行っていないところは初期値として"00"が入っているのが 確認できます。
次にダンプコマンドをタイプしてみます。
確かに外部記憶の16バイトすべての値が表示されました。
次に未定義なコマンド文字をタイプしてみます。
すると、未定義コマンド文字をタイプしたとたんに プロンプトが返ってきます。 未定義コマンドでコマンド処理が中断されたのがわかります。
シリアルインターフェースを介して16バイトのデータを送受信する事が できる事はわかりましたが、 これだけでは実用的なアプリケーションではありません。 ここから、どういった、応用が考えられるでしょうか。
2006-03-08 発行。