Z80でシリアル通信
TEditorMX(テキストエディタ)をご活用頂ければと思いまして、Z80マイコンボードです。
Z80がまだ現役で活躍しているようです。
ちょっと話が脱線しますが、比較的大規模な制御システムを構築できるZ80マイコンボードを制作してくれているメーカーさんもあります。
以前(20年くらい前ですが)仕事で利用させて頂いたことがある「エーワン株式会社」さんのCATシリーズです。
今でも作ってくださっているのを見たらなんか嬉しくなってきました。
比較的大きなマイコンシステムを作りたい...という方がいるかどうかは分かりませんが、
比較的大きなマイコンシステムも構築できますので今からでもZ80をやってみてもいいのではないかと思います。
Z80のアセンブリ言語はPICと比較しますととても分かり易いのでよろしいのではないかと思います。
話を元に戻して、ここでは秋月電子通商さんの「AKI-80ゴールドキット」を用いてRS-232Cで通信をさせてみたいと思います。
Windows用のプログラムを作る場合には強力なデバッガ(Visual Studioのソースコードデバッガなど)が使えますが、
マイコンの場合にはICEが使える人は恵まれている人(環境)で、私(管理人)のような人はスイッチやLEDなど、数少ない入出力(I/O)を駆使してなんとかするしかありません。
そんな中でRS-232C(特に出力ですが)が使えればかなりデバッグが楽になると思います。
...というわけで、AKI-80ゴールドキットに載っているCPUにはSIOが2チャンネル付いていますので、
これを割り込みを使って入出力できるようにしたいと思います。
RS-232CにはSIOの他にボーレートジェネレータとしてCTCも使用します。
CTCもCPUに内蔵されています。
そのため、1度作ってしまえば後は好きなように使いまわしができるようになりますので頑張って作りましょう。
まず用意するものは...私(管理人)が制作しましたテキストエディタTEditorMX(ver1.40以降)に同梱しましたZ80の回路図とサンプルプログラム、
それと制作した(する)基板です。
以前制作した回路(基板)はCPUに抵抗とLED、DIPSWが付いているだけのもなので、今回はそれにRS-232Cの回路を追加する形になります。
今回私(管理人)が購入した部品は以下の通りです。
他、必要なものは手持ちであったものを使用しています。
名称 | 型番など | 数量 | 購入先 |
---|---|---|---|
RS232CインターフェースIC | ICL3232CPZ | 1個 | 秋月電子通商 |
積層セラミックコンデンサ | 0.1μF | 10個 | 秋月電子通商 |
耐熱電子ワイヤー | AWG22相当 | 1m×10 | 秋月電子通商 |
コネクタ | XG4M-1030-T | 1個 | 楽天 |
コネクタ | DSUB 9pin メス | 1個 | アマゾン |
その他必要なもの
PCと接続するためのRS-232Cクロスケーブル 1本
ケーブルは購入しても良いですが、自作してしまっても良いのではないかと思います。
今回使用するクロスケーブルの結線図は下図の通りです。
今回も使用するコンパイラはSDCCです。
バージョンは3.9.1を使用しますが少しくらい古いバージョンでも問題なく動作するのではないかと思います。
アセンブラもコンパイラ付属のアセンブラになります。
エディタは TEditorMX ver1.60 または TEditorMX ver1.50 を使用します。
ソースファイルアーカイブ2つです
ctc0.zip → CTC設定プログラム
sioa.zip → RS-232C通信プログラム
制作する基板の回路図です(下半分が今回追加するRS-232Cの回路になります)。
(1)CTC設定
まずはCTCを設定し、ボーレートジェネレータとして機能するようにします。
秋月電子さんのAKI-80マイコンボードの説明書にある通りに設定します。
使用するボーレートは9600bpsとしたいと思いますのでCTCの出力は、
9600×16 = 153.6KHz
になるように設定します。ちなみに「×16」は固定値みたいなものなので、
ボーレートの16倍の周期のクロックをCTCで作れば大丈夫...とご理解いただければとりあえず大丈夫です。
注意したい所は「CPUに内蔵しているSIOはCTCの出力がそのまま使える=デューティー50%でなくても動作します」ということです。
ちなみに「デューティー50%」とは1クロック当たりのON(High)時間が50%という意味です。
例えば周期100Hzのクロックでしたら1クロック当たりの時間は10msですので、ON(High)時間が5msであればデューティー50%と表現します。
では、具体的な設定です。
CTC0を使用しますのでCTC0のコントロールワードに0x07、続いて0x05を書き込みます。
最初の0x07の意味ですが、CTC0をタイマモードで使用して割り込みは使用しない、プリスケーラは1/16である、という感じの設定値になります。
続く0x05ですが、入力のプリスケーラ設定の更に1/5のクロックを出力するという意味です。
マイコンボードの説明書によるとCTCの入力が12.288MHzです。プリスケーラは1/16(コントロールワード~0x07~で設定しています)ですので、
それを更に1/5すると153.6KHzになります。欲しい周波数ピッタリになります。
続いて、具体的なプログラムです。
CTCの初期化部は下記のとおりです。
CTC0 .EQU 0x10 ;CTC0 ・ ・ ・ ; CTC0設定 LD A,#0x07 ;タイマモード(1/16プリスケーラ) OUT (CTC0),A ; LD A,#5 ;1/5 (153.6KHz=9600bps) OUT (CTC0),A ; ・ ・
(ボーレート確認用)CTC0設定プログラム(mycrt0.s)は下記のとおりです。
; ; AKI-80ゴールドボード(秋月電子通商さん)と ; SDCCコンパイラと付属のアセンブラ(sdasz80)で作るCTC(PIO)入出力プログラム ; .MODULE CRT0 .AREA _CODE .AREA _DATA ; I/Oアドレス ;CTC CTC0 .equ 0x10 ;CTC0 CTC1 .equ CTC0+1 ;CTC1 CTC2 .equ CTC1+1 ;CTC2 CTC3 .equ CTC2+1 ;CTC3 ;PIO PIOA_D .EQU 0x1C ; PORT A データ PIOA_C .EQU PIOA_D+1 ; PORT A コマンド PIOB_D .EQU PIOA_C+1 ; PORT B データ PIOB_C .EQU PIOB_D+1 ; PORT B コマンド ; 起動時の初期化処理へジャンプ .AREA _HEADER(ABS) ; (ABS)を指定すると絶対アドレス指定になる .ORG 0 jp _init ;初期化ルーチンへジャンプ ; ; 後ほど割り込みベクタを記述する領域です。 ; ; CODEセグメント .AREA _CODE _init:: LD SP, #0xFFFF ;スタックポインタ初期化 ; DI ;とりあえず割り込み禁止 ; CTC0設定 LD A,#0x07 ;タイマモード(1/16プリスケーラ)、割り込み禁止 OUT (CTC0),A ; LD A,#5 ;1/5 (153.6KHz=9600bps) OUT (CTC0),A ; ; PORT A を入力に設定 LD A, #0x4F OUT (PIOA_C),A ; PORT B を出力に設定 LD A, #0x0F OUT (PIOB_C), A TEST_LOOP: IN A,(PIOA_D) ;PIOポートA 入力 OUT (PIOB_D),A ;PIOポートBへ出力 JP TEST_LOOP ; _LOOP_END: JP _LOOP_END
では実際にアセンブル(コンパイル)してプログラムをROMに焼いて動かしてみましょう。
必要なファイルはctc0.zipに含まれていますのでダウンロードして下さい。
プログラムリストはアセンブラプログラム(mycrt0.s)だけです。
アセンブル(コンパイル)はバッチファイル(ctc0_compile.bat)を用います。
プロジェクトファイルctc0.tepをダブルクリックしてTEditorMXの情報ウィンドウを起動し、
更にmycrt0.sアイテムをダブルクリックしてTEditorMXでソースファイルを開いてください。
開いたらコンパイルを実行()してみてください。
上手くいけば下図のように表示されると思います。
もし、コンパイルできないようなときはSDCCが正しくインストールされていないかもしくはTEditorMXのコンパイル設定が正しくできていないと思われますのでご確認ください。
フォルダにctc0.ihxというファイルができていると思いますので、それをROMに焼いて下さい
(ctc0.zipに含まれている同名のファイルを焼いても結構です)。
ちなみにこのihxというファイルはインテルHEXファイルフォーマットと呼ばれるファイルです。
大抵のROMライターはこのフォーマットに対応しているはずです。
焼いたら基板に取り付けてクロックが出力されるかを確認してみましょう。
ちなみに下図の写真の基板にはRS-232C関連の部品がすでに取り付けてありますが、
CTCの出力の確認をするだけなので以前作成した基板そのままでもOKです。
電源を入れますとCTC0の出力(CN3, 20番ピン)にクロックが出力されていると思いますのでオシロスコープなどで確認して下さい。
もし、出力が出ていないときはDIPSWをON/OFFしてみて対応するLEDが消灯/点灯するかをご確認下さい。
LEDが反応しないのであればプログラムが正しく焼けていない可能性が高いですので、やり直して下さい。
RS-232CインターフェースICとコネクタを取り付けた基板の画像です。
CTC0の出力波形です。
1マス1μsで、約6.5マスですので(1÷6.5μs=153.8kHz)です。
目視で読んでほぼ設定値通りになっていることが確認できます。
(2)RS-232C通信
通信プログラムをご覧いただく前にちょっと脱線してSDCC(3.9.1)でI/Oアクセスする方法をご紹介します。
(コマンドプロンプト用やWindows用のプログラムしか書かれたことがない方だと分からないかもしれないと思いますので...)
__sfr __at (I/Oアドレス) (I/Oアドレス名)
と記述するとI/Oアドレス名でI/Oアクセス(IN,OUT命令)することができます。
具体的に記述しますと、例えばCTCだと
__sfr __at 0x10 CTC0; __sfr __at 0x11 CTC1; __sfr __at 0x12 CTC2; __sfr __at 0x13 CTC3;
のように記述(宣言と言っていいのかな?)し、
CTC0 = 0; // OUT命令 reg = CTC1; // IN命令
のように記述するとIN, OUT命令で入出力できます。
もしこのご説明で分からないときはコンパイルしたときに出力される.asmファイルをご覧ください。
きっとご理解いただけると思います。
話を元に戻します。
CTCからクロックが出力されていることが確認できましたら、続いてRS-232Cの回路を基板に追加して下さい。
配線が正しくされていないと全く動作していないように見えますので正確に配線し、念入りにチェックして下さい。
自分で「絶対完璧!」と思えるくらいに仕上がったらいよいよ通信プログラムを焼いて動作させてみましょう。
SIOのチャンネルAを利用したRS-232Cの通信プログラム(sioa.zip)です。
ソースファイルは mycrt0.s, main.c, sio.h の3つです。sio_compile.batでコンパイルして下さい。
ちょっと長いですが以下にソースファイルリスト3つを掲載します。
mycrt0.s
; ; AKI-80ゴールドボード(秋月電子通商さん)と ; SDCCコンパイラと付属のアセンブラ(sdasz80)で作るPIO入出力サンプルプログラム ; .MODULE CRT0 .GLOBL _main .GLOBL _SIOA_TX_Int .GLOBL _SIOA_RX_Int .AREA _CODE .AREA _DATA ; I/Oアドレス ;CTC CTC0 .EQU 0x10 ;CTC0 CTC1 .EQU CTC0+1 ;CTC1 CTC2 .EQU CTC1+1 ;CTC2 CTC3 .EQU CTC2+1 ;CTC3 ;SIO SIOA_D .EQU 0x18 ;SIO A 送受信データ SIOA_C .EQU SIOA_D+1 ;SIO A コマンド SIOB_D .EQU SIOA_C+1 ;SIO B 送受信データ SIOB_C .EQU SIOB_D+1 ;SIO B コマンド ;PIO PIOA_D .EQU 0x1C ; PORT A データ PIOA_C .EQU PIOA_D+1 ; PORT A コマンド PIOB_D .EQU PIOA_C+1 ; PORT B データ PIOB_C .EQU PIOB_D+1 ; PORT B コマンド ; 起動時の初期化処理へジャンプ .AREA _HEADER(ABS) ; (ABS)を指定すると絶対アドレス指定になる .ORG 0 jp _init ;SIO割り込みベクタ ; 0x50 = (I reg × 0x100) + (WR2 & 0xf0) .org 0x50 .dw 0 ;_SIOB_TX_Int ;送信バッファ空割り込み .dw 0 ;外部/ステータス割り込み .dw 0 ;_SIOB_RX_Int ;受信割り込み .dw 0 ;特殊受信状態割り込み .dw _SIOA_TX_Int ;送信バッファ空割り込み .dw 0 ;外部/ステータス割り込み .dw _SIOA_RX_Int ;受信割り込み .dw 0 ;特殊受信状態割り込み ; ; CODEセグメント .AREA _CODE _init:: LD SP, #0xFFFF ;スタックポインタ初期化 DI ;とりあえず割り込み禁止 LD A,#0 ;割り込みレジスタ設定 LD I,A ; IM 2 ;モード2割り込み ; CTC0設定 LD A,#0x07 ;タイマモード(1/16プリスケーラ) OUT (CTC0),A ; LD A,#5 ;1/5 (153.6KHz=9600bps) OUT (CTC0),A ; ; PORT A を入力に設定 LD A, #0x4F OUT (PIOA_C),A ; PORT B を出力に設定 LD A, #0x0F OUT (PIOB_C), A ;TEST_LOOP: ; ここを有効にするとC言語で書いている処理と同等になります ; IN A,(PIOA_D) ; ; OUT (PIOB_D),A ; ; JP TEST_LOOP ; CALL _main _LOOP_END: JP _LOOP_END
sio.h
extern void SIOA_TX_Int( void )__interrupt; extern void SIOA_RX_Int( void )__interrupt; extern void WriteCommandSIOA( char * ); extern short ReadCommandSIOA( void );
main.c
/***************************************************************************** * 2019年5月作成 室井 幸治 * muroix.com (mailto:support@muroix.com) * * AKI-80ゴールドボード(秋月電子通商さん)用のRS-232C通信サンプルプログラム * CPU: TMPZ84C015BF-12(12.288MHz) (Z80互換CPU) * 通信速度 9600bps * CPU内蔵SIO A, CTC0を使用 * 送受信共に割り込みを使用 * * Cコンパイラ SDCC 3.9.1を利用させていただきました。 * アセンブラ SDCC付属のアセンブラです。 * * 本プログラムはコマンドを送受信するだけのプログラムです。 * 受信したデータをそのまま返信し、コマンド(終端:\r \n \0)を1つ受信し終わったら * それもまとめて返信します。(受信コマンドを2重に送信する形になります) * * コマンド(パケット)サイズは最大30byteにしてあります。 * 送受信用リングバッファは共に128byteにしてあります。 * * サンプルプログラムなので無駄な部分もあるかと思います。 * 本プログラムはフリーウェアとします。ご自由に改造するなどしてご利用ください。 * * コンパイル方法: * SDCCがインストールされているWindowsのコマンドプロンプトからsio_compile.batを実行してください。 * sio.ihx(インテルHEXファイル)というファイルが出来上がりますのでROMに焼いて下さい。 * * ファイル構成: * mycrt0.s 初期化プログラム * main.c 通信プログラム本体 * sio.h main.cのヘッダファイル * sio_compile.bat コンパイル用バッチファイル *****************************************************************************/ #include "sio.h" #define RR0_TXEMPTY 0x04 // 第2ビットが送信バッファ空ビット #define RING_BUF_SIZE 128 // 受信リングバッファサイズ #define COMMAND_SIZE 30 // 送受信コマンド(パケット)サイズ // I/O定義 __sfr __at 0x10 CTC0; __sfr __at 0x11 CTC1; __sfr __at 0x12 CTC2; __sfr __at 0x13 CTC3; __sfr __at 0x18 SIOA_D; __sfr __at 0x19 SIOA_C; __sfr __at 0x1a SIOB_D; __sfr __at 0x1b SIOB_C; __sfr __at 0x1c PIOA_D; __sfr __at 0x1d PIOA_C; __sfr __at 0x1e PIOB_D; __sfr __at 0x1f PIOB_C; static volatile char rxRingBuf[RING_BUF_SIZE]; // 受信リングバッファ static volatile char rxCommand[COMMAND_SIZE+1]; // 受信コマンド(パケット)バッファ static volatile short rxDataNum; // 受信リングバッファに格納されている有効なデータ数 static volatile char *rxWtPtr; // 受信書き込み位置ポインタ static volatile char *rxRdPtr; // 受信読み込み位置ポインタ static volatile char *rxWtCommPtr; // 受信コマンド(パケット)バッファ書き込み位置ポインタ static volatile char txRingBuf[RING_BUF_SIZE]; // 送信リングバッファ static volatile char *txWtPtr; // 送信書き込み位置ポインタ static volatile char *txRdPtr; // 送信読み込み位置ポインタ static volatile short txDataNum; // 送信リングバッファに格納されている有効なデータ数 /***************************************************************************** * SIOA送信割り込み * SDCCでは関数名の後に__interruptを付けるとレジスタのスタックへの退避 * と、割り込み禁止、許可、reti命令でリターンが自動的に行われる ******************************************************************************/ void SIOA_TX_Int( void )__interrupt { if( txDataNum > 0 ) { // 送信データが存在するとき SIOA_D = *txRdPtr++; if( txRdPtr >= txRingBuf+RING_BUF_SIZE ) { txRdPtr = txRingBuf; } txDataNum--; // 1文字送信したので-1 } else { SIOA_C = 0x28; // 保留中の送信割込みリセット } } /***************************************************************************** * SIOA先頭文字送信 * 注意:呼び出し側で割込みを禁止してから呼び出すこと! * 送信バッファが空なのを確認してから送信用リングバッファの先頭文字を送信する ******************************************************************************/ void SendSIOA_TX_FirstChar( void ) { char rr; // 送信バッファが空かチェック SIOA_C = 0x00; // WR0 RR0選択 rr = SIOA_C; // RR0読み込み if( ! (rr & RR0_TXEMPTY) ) { // 送信バッファが空でないときは(すぐに割り込みが発生するはずなので) return; // リターン } // 以下は割り込みルーチンと同じ処理である if( txDataNum > 0 ) { // 送信データが存在するとき SIOA_D = *txRdPtr++; if( txRdPtr >= txRingBuf+RING_BUF_SIZE ) { txRdPtr = txRingBuf; } txDataNum--; // 1文字送信したので-1 } } /***************************************************************************** * SIOA受信割り込み * SDCCでは関数名の後に__interruptを付けるとレジスタのスタックへの退避 * と、割り込み禁止、許可、reti命令でリターンが自動的に行われる ******************************************************************************/ void SIOA_RX_Int( void )__interrupt { // 受信データを1バイト読み込み if( rxDataNum < RING_BUF_SIZE-2 ) { // リングバッファにデータを保存可能な時(-2は改行コードを強制的に追加する分) rxDataNum++; // データ数を+1 *rxWtPtr++ = SIOA_D; if( rxWtPtr >= rxRingBuf+RING_BUF_SIZE ) { rxWtPtr = rxRingBuf; } } else { // リングバッファオーバーフローなとき SIOA_D; // 保存できないデータを読み飛ばす *rxWtPtr++ = '\r'; // 改行コードを無理やり追加する if( rxWtPtr >= rxRingBuf+RING_BUF_SIZE ) { rxWtPtr = rxRingBuf; } *rxWtPtr++ = '\n'; if( rxWtPtr >= rxRingBuf+RING_BUF_SIZE ) { rxWtPtr = rxRingBuf; } rxDataNum += 2; } } /***************************************************************************** * SIO初期化 ******************************************************************************/ void InitSIO( void ) { //SIO A 初期化 SIOA_C = 0x18; // WR0 リセット SIOA_C = 0x01; // WR0 WR1選択 SIOA_C = 0x12; // WR1 割り込み許可 // SIOA_C = 0x02; // WR1 割り込み許可(受信割り込み不可) // SIOA_C = 0x10; // WR1 割り込み許可(送信割り込み不可) SIOA_C = 0x03; // WR0 WR3選択 SIOA_C = 0xc1; // WR3 受信 8bit/character, 受信 enable SIOA_C = 0x04; // WR0 WR4選択 SIOA_C = 0x44; // WR4 ×16 clock, 1 stop bit SIOA_C = 0x05; // WR0 WR5選択 // SIOA_C = 0xea; // WR5 DTR on, 送信 8 bit/character, 送信 enable, RTS on SIOA_C = 0x68; // WR5 DTR off, 送信 8 bit/character, 送信 enable, RTS off //SIO B 初期化 SIOB_C = 0x18; // WR0 リセット SIOB_C = 0x01; // WR0 WR1選択 SIOB_C = 0x04; // WR1 status affects vector SIOB_C = 0x02; // WR0 WR2選択 SIOB_C = 0x50; // WR2 割り込みベクタ(下位) // 受信リングバッファ初期化 rxWtPtr = rxRdPtr = rxRingBuf; rxDataNum = 0; // 受信データ数を0で初期化 rxWtCommPtr = rxCommand; / 送信リングバッファ初期化 txWtPtr = txRdPtr = txRingBuf; txDataNum = 0; } /***************************************************************************** * 文字数を数える * * 引数 * lpStr 文字数を数える文字列へのポインタ * 戻り値 * 文字数、異常な時は-1を返す *****************************************************************************/ short strlen( char *lpStr ) { short len; char *p; len = 0; p = lpStr; while( *p++ ) { len++; if( len < 0 ) return( -1 ); // 負数になったとは異常なので-1でリターン } return( len ); } /***************************************************************************** * コマンド(パケット)受信(SIOA) * * 戻り値 * 1コマンド(パケット)を受信したときtrue(受信バイト数), まだ不完全なときfalse(0) * 受信バッファオーバーフローのときtrue(-1*受信バイト数) *****************************************************************************/ short ReadCommandSIOA( void ) { char data; short ret; /* ご注意: * 以下、割り込みを頻繁に禁止&許可していますが、これは割り込み処理が正しく処理されるようにするためです * 関数の最初から最後まで、ずっと割り込みを禁止していても高速に処理されて割り込み処理が問題なく実行されてくれれば * 問題ないのですが...そんな保証はどこにもないので、念のためです。 * ちなみに、割り込みを禁止しているのは割り込みルーチンで操作している変数rxDataNumを参照しているところです。 * rxDataNumはshort型(2バイト)なので割り込みを禁止してからアクセスしないと不具合がでる可能性があります。 */ __asm di // 割り込み禁止 __endasm; if( ! rxDataNum ) { // 受信データがないとき __asm ei // 割り込み許可 __endasm; return( 0 ); } ret = 0; while( rxDataNum > 0 ) { rxDataNum--; // 1バイト読みだしたので-1 __asm ei // 割り込み許可 __endasm; data = *rxRdPtr++; if( rxRdPtr >= rxRingBuf+RING_BUF_SIZE ) { rxRdPtr = rxRingBuf; } if( ! data || (data == '\r') || (data == '\n') ) { // パケットの終端のとき if( data ) { // 改行コード(\d or \n)のとき { // --- 確認用データ送信 char buf[3]; buf[0] = '\r'; buf[1] = '\n'; buf[2] = 0; WriteCommandSIOA( buf ); } // --- *rxWtCommPtr++ = '\r'; *rxWtCommPtr++ = '\n'; *rxWtCommPtr++ = 0; } else { *rxWtCommPtr++ = 0; } ret = (short)(rxWtCommPtr - rxCommand) - 1; rxWtCommPtr = rxCommand; // 書き込み位置をバッファの先頭へ break; // 1コマンド(パケット)受信完了 } else { // 終端以外の普通の文字のとき { // --- 確認用データ送信 char buf[2]; buf[0] = data; buf[1] = 0; WriteCommandSIOA( buf ); } // --- if( rxWtCommPtr >= rxCommand+COMMAND_SIZE-2 ) { // コマンド受信バッファが最後の2バイトのとき *rxWtCommPtr++ = '\n'; // \n *rxWtCommPtr++ = 0; // nul文字 ret = -(short)((rxWtCommPtr - rxCommand) - 1); rxWtCommPtr = rxCommand; break; // 1コマンド(パケット)受信完了 } else { // コマンド受信バッファにまだ余裕があるとき *rxWtCommPtr++ = data; ret = 0; } } __asm di // 割り込み禁止 __endasm; } __asm ei // 割り込み許可 __endasm; return( ret ); } /***************************************************************************** * コマンド(パケット)送信(SIOA) * * 引数 * txData 送信コマンド(パケット)データへのポインタ(最後は必ずnul文字で終了していること!) *****************************************************************************/ void WriteCommandSIOA( char *txData ) { char *p; short len; // 送信有効データ数が1以上のときは送信用リングバッファに単に書き込めばOK p = txData; len = strlen( p ); __asm ei // 割り込み許可(万が一割込みが禁止されていたときは動かなくなってしまうので一応...) __endasm; while( len >= RING_BUF_SIZE - txDataNum ) { // バッファが空くまでループ } __asm di // 割り込み禁止 __endasm; while( *p ) { // まだ送信コマンド文字列があるとき *txWtPtr++ = *p++; txDataNum++; if( txWtPtr >= txRingBuf+RING_BUF_SIZE ) { txWtPtr = txRingBuf; } } // SIOAの送信バッファ空フラグをチェックし、空のときは送信用リングバッファの先頭文字を送信 SendSIOA_TX_FirstChar(); __asm ei // 割り込み許可 __endasm; } /***************************************************************************** * メインルーチン *****************************************************************************/ void main( void ) { short ret; // SIO初期化 InitSIO(); __asm ei // 割り込み許可 __endasm; for(;;) { ret = ReadCommandSIOA(); if( ret > 0 ) { // コマンド(パケット)を受信したとき WriteCommandSIOA( rxCommand ); // 受信データを送信 } else if( ret < 0 ) { WriteCommandSIOA( "\r\n" ); // 改行コードを送信 WriteCommandSIOA( "コマンド文字数が多すぎます。\r\n" ); // エラーメッセージを送信 } } }
プログラムのポイントは送受信共に割り込みを使用していて送信、受信バッファがそれぞれリングバッファになっているところです。 リングバッファとはバッファの先頭と末尾をくっつけてリング状に見立てたもののことで、 こうすることで(一時的に)極端に負荷が掛からないようにしてデータの取りこぼしを防ぎ、また無駄なウェイトを入れずに済むようにしています。 「よく分からない」という方は是非プログラムを読んでみてどうなっているのかを調べてみて下さいね。
無事に焼けましたら基板に取り付けて動かしてみて下さい。 今回、ターミナルソフトはフリーソフトの Multi Port Terminal ver1.00 (金澤ソフト設計さん)を利用させて頂きました。 Vectorさんよりダウンロードさせて頂きました。
ターミナルソフトを起動してキーボードのキーを押すと対応する文字が基板側に送信されます。 受信した基板側はオウム返しでPC側に送信し、LF(\n)またはnul文字(\0)を検出したら1コマンド(パケット)受信したものと解釈し、 受信したコマンド(パケット)をPC側に送信します。また、コマンドバッファがオーバーフローしそうになったらメッセージをPC側に送信します。 下の画像はやりとりの様子です。
I/OとRS-232Cが使えるようになれば、最低限のデバッグ環境は整いました。
あとは作りたいものを作るだけです。
頑張ってくださいね。
最後に、このページからダウンロードできるソースプログラムに関してはフリーウェアとします。 よろしければご自由にお使い下さい。