AKI-H8/3048FM/BにはD-sub 25pinコネクタとD-sub 9pinコネクタが用意されて います。事前に「AKI-H8/3048F用マザーボード」 でM/Bに改造を施してあれば、D-sub 25pinコネクタが"SCI1"に、 D-sub 9pinコネクタが"SCI0"に接続されているはずです。折角シリ アルポートが付いているので、これらを利用してPCや他機器と通信してみましょ う。
本章ではD-sub 9pinコネクタに接続されている"SCI0"を利用して通 信を行ってみます。というのもSCI1を利用してLinux上から"cu"で H8/3048Fと通信し、cu を終了(他ターミナルより"killall cu")し た後にh8writeで書き込みを行ってもうまくいかないのです。完全に私がやり 方を知らないだけだと思うのですが、取り敢えずSCI0を選択して逃げます。ど なたか対処方法を知っている方、是非教えて下さい。
H8/3048FはSCI(Serial Communication Interface)を2チャネル(SCI0、SCI1)備 えています。M/Bでホストマシンと接続してプログラムの書き込みを行うのは "SCI1"で、D-sub 25pinのコネクタはSCI1に接続されています。SCI は「調歩同期式通信」と「 ク ロック同期式通信」の2方式をサポートしており、複数のプロセッサ間 のシリアル通信機能(マルチプロセッサ通信機能)も可能です。SCI0はスマート カードインタフェースをサポートしています。TxD(送信部)、RxD(受信部)が独 立しているので全二重通信(送受信を同時に行う事)が可能な上、ダブルバッファ 構造で連続送受信を行う事ができます。またシリアル通信に関して4種類の割 り込み要因を有しています。これに関しては後で詳しく触れます。なお、ビッ トレート(bps)は内蔵ボーレートジェネレータにより任意のビットレートを選 択可能です。詳しくはハードウェアマニュアルの第13章を御参照下さい。
調歩同期式方式はキャラクタ単位で同期をとるシリアルデータ通信方式です。 クロックとは同期している必要はなく、不定期にSCIに到 達するデータを読み込む事が可能な方式です。調歩同期式モードでは 次のような通信フォーマットを選択できます。
| 設定内容 | 種類 |
| データ長 | 7bit/8bit |
| ストップビット長 | 1bit/2bit |
| パリティ | even/odd/none |
また次のような機能も有しています。
| 機能 | 内容 |
| 受信エラー検出 | パリティーエラー・オーバランエラー・フレーミングエラーの検出 |
| ブレーク検出 | フレーミングエラー発生時にRxD端子のレベルを読むことでブレーク検出可能 |
クロックに同期してシリアルデータ通信を行うモードです。通信フォーマット は1種類限定です。以下にまとめます。
| 設定内容 | 種類 |
| データ長 | 8bit |
| 機能 | 内容 |
| 受信エラー検出 | オーバランエラーの検出 |
シリアル通信規格"EIA-232-E(RS-232C)"ではモデムなどの終端装置 (DCE)に関しての規定がありますが、PCなどの端末(DTE)に関しては規定されて いませんので、必要な端子のみ実装されている場合が多いようです。H8/3048F のSCIも必要最低限の端子しか用意されていません。端子構成を次表に示しま す。
| チャネル | 名称 | 入出力 | 機能 |
| 0 | SCK0(Serial ClocK) | I/O | SCI0のクロック入出力 |
| RxD0(Receive Data) | I | SCI0の受信データ入力 | |
| TxD0(Transmit Data) | I | SCI0の送信データ出力 | |
| 1 | SCK1(Serial ClocK) | I/O | SCI1のクロック入出力 |
| RxD1(Receive Data) | I | SCI1の受信データ入力 | |
| TxD1(Transmit Data) | I | SCI1の送信データ出力 |
PCのシリアルポート(/dev/ttyS0、/dev/ttyS1等)、D-sub 9pinコネクタのピン アウトは「AKI-H8/3048F用マザーボード」章 で説明してありますのでご参照下さい。
調歩同期式モード/クロック同期式モード、データフォーマット、ビットレー トなどの設定はI/Oポートなどと同様にH8/3048Fの内部レジスタによって設定 可能です。次表にレジスタ構成を示します。
| 名称 | R/W | チャネル0アドレス | チャネル1アドレス | 初期値 |
| SMR(Serial Mode Register) | R/W | 0xffb0 | 0xffb8 | 0x00 |
| BRR(BitRate Register) | R/W | 0xffb1 | 0xffb9 | 0xff |
| SCR(Serial Control Register) | R/W | 0xffb2 | 0xffba | 0x00 |
| TDR(Transmit Data Register) | R/W | 0xffb3 | 0xffbb | 0xff |
| SSR(Serial Status Register) | R/(W) | 0xffb4 | 0xffbc | 0x84 |
| RDR(Receive Data Register) | R | 0xffb5 | 0xffbd | 0x00 |
以下にSMRの機能をまとめます。SMRはSCIのシリアル通信フォーマットとビッ トレートの設定を行うためのレジスタです。
| SMR | ||||||||
| ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 名称 | C/~A | CHR | PE | O/~E | STOP | MP | CKS1 | CKS0 |
| 初期値 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W |
| 名称 | ビット | 機能 | 設定値 | 初期値 | 補足 |
| C/~A | 7 | SCI動作モード設定 | 0:調歩同期式モード(default)/1:クロック同期式モード | 0 | --- |
| CHR | 6 | 調歩同期式モードのデータ長設定 | 0:8bitデータ/1:7bitデータ | 0 | クロック同期式モードのときは設定値は影響せず |
| PE | 5 | 調歩同期式モードのパリティ設定 | 0:パリティ無、チェック禁止/1:パリティ有、チェック許可 | 0 | クロック同期式モードのときは設定値は影響せず |
| O/~E | 4 | パリティモード設定 | 0:even/1:odd | 0 | クロック同期式モード/PE==0のときは設定値は影響せず |
| STOP | 3 | 調歩同期式モードのストップ長設定 | 0:1 stop bit/1:2 stop bit | 0 | クロック同期式モードのときは設定値は影響せず |
| MP | 2 | マルチプロセッサフォーマット設定 | 0:マルチプロセッサ機能禁止/1:マルチプロセッサフォーマット選択 | 0 | クロック同期式モードのときは設定値は影響せず |
| CKS1、CKS0 | 1、0 | 内蔵ボーレートジェネレータのクロックソース選択 | 00:φ /01:φ/4 /10:φ/16 /11:φ/64 | 0 | --- |
BRRはSMRのCKS1、CKS0ビットと共にシリアル通信のビットレートを設定するた めのレジスタです。各チャネル毎にボーレートジェネレータの制御が独立して いるので、各チャネル毎にビットレートの設定が可能です。「ビットレート」 と「ボーレート」は異なります。詳しくはJFのドキュ メントをご参照下さい。これらはハードウェアマニュアルではきちんと区 別されていると思います。シリアル通信の変調速度は「ビットレート(bps)」 で表されています。
H8/3048FのCPU動作周波数φ==16MHzであるので、主なビットレートの設定は以 下のようになります。φが変化すると設定値も変わります。
| ビットレート(bps) | CKS1 | CKS0 | BRR |
| 600 | 0 | 1 | 207 |
| 1200 | 0 | 1 | 103 |
| 2400 | 0 | 0 | 207 |
| 9600 | 0 | 0 | 51 |
| 19200 | 0 | 0 | 25 |
SCIには以下の4種類の割り込み要因があります。割り込み要因に関してはマザーボードのLED点滅(割り込み有)+タクトスイッ チ「割り込み」項もご参照下さい。
| 割り込み要因 | 内容 | 意味 | 発生時点 | 優先順位 |
| ERI | 受信エラー(ORER、FER、PER) | 受信時にエラーがおきてデータが正常に受信できていません | 受信時に各種エラーが発生した時点 | 高 |
| RXI | 受信データfull | RDRに1byteの受信データが溜っているのでユーザプログラムから読み込んで下さい | 受信エラーが発生せずにRxDからの1byte受信データがRSRからRDRに正常に転送され、 SSR.RDRF=1(後述)となる時点 | ↑ |
| TXI | 送信データempty | ユーザプログラムからTDRに次の1byteの送信データを書き込む事が可能です | TDRからTSRに1byteの送信データが転送され、SSR.TDRE=1(後述)となる時点 | ↑ |
| TEI | 送信終了(TEND) | ユーザプログラムから指示された分の送信データはすべて送信手続きが完了しました | 送信データ1byteのストップビットがTSRからTxDに書き込まれる時点で、 SSR.TDRE=1(後述:TDRにデータが無い)であるとSSR.TEND=1(後述)とされ、TEI割り込みが発生 | 低 |
各割り込み要因はSCRのTIE、RIE、TEIE各ビットで許可/禁止できます。各割り 込み要求はそれぞれ独立に割り込みコントローラに送られます。TXI、RXI割り 込みによってDMACを起動してデータ転送を行う事も可能です。
SCRはSCIの送信/受信動作、調歩同期式モードでのシリアルクロック出力、割 り込み要求の許可/禁止、送信/受信クロックソースの選択を行うレジスタです。 以下にSCRの機能をまとめます。
| SCR | ||||||||
| ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 名称 | TIE | RIE | TE | RE | MPIE | TEIE | CKE1 | CKE0 |
| 初期値 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W |
| 名称 | ビット | 機能 | 設定値 | 初期値 |
| TIE(Transmit Interrupt Enable) | 7 | 送信データempty割り込み(TXI)要求の許可/禁止 | 0:禁止/1:許可 | 0 |
| RIE(Receive Interrupt Enable) | 6 | 受信データfull割り込み(RXI)・受信エラー割り込み(ERI)要求の許可/禁止 | 0:禁止/1:許可 | 0 |
| TE(Transmit Enable) | 5 | 送信動作の許可/禁止 | 0:禁止/1:許可 | 0 |
| RE(Receive Enable) | 4 | 受信動作の許可/禁止 | 0:禁止/1:許可 | 0 |
| MPIE(Multi Processor Interrupt Enable) | 3 | マルチプロセッサ割り込みの許可/禁止 | 0:禁止/1:許可 | 0 |
| TEIE(Transmit End Interrupt Enable) | 2 | 送信終了割り込み(TEI)要求の許可/禁止 | 0:禁止/1:許可 | 0 |
| ビット | 通信モード | ||
| CKE1 | CKE0 | 調歩同期式モード | クロック同期式モード |
| 0 | 0 | 内部CLK/SCK:入出力ポート | 内部CLK/SCK:同期CLK出力 |
| 0 | 1 | 内部CLK/SCK:CLK出力 | 内部CLK/SCK:同期CLK出力 |
| 1 | 0 | 外部CLK/SCK:CLK入力 | 外部CLK/SCK:同期CLK入力 |
| 1 | 1 | 外部CLK/SCK:CLK入力 | 外部CLK/SCK:同期CLK入力 |
通常の使用では (CKE1,CKE0)==(0,0) で問題無いと思います。
ユーザがシリアルデータ送信を要求すると、SCIはTDR(Transmit Data Register)から送信データを一旦TSR(Transmit Shift Register)に転送します。 その後TSRのLSB(ビット0)から順にTxD端子に送信し、8bit(1byte)のデータ送 信を完了すると共にTDRからTSRへ次のデータを転送し、1byte送信を繰り返し ます。ユーザはTSRに触れる事はできず、TDRへのみデータ を書き込む事が可能です。
SSRはSCIの動作状態を示すステータスフラグとマルチプロセッサビットの設定 を行うレジスタです。以下にSSRの機能をまとめます。
| SSR | ||||||||
| ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 名称 | TDRE | RDRF | ORER | FER | PER | TEND | MPB | MPBT |
| 初期値 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| R/W | R/(W) | R/(W) | R/(W) | R/(W) | R/(W) | R | R | R/W |
| 名称 | ビット | 機能 | 設定値 | 初期値 |
| TDRE(Transmit Data Register Empty) | 7 | 送信データ TDR->TSR 転送終了、TDRデータ書き込み可能 | 0:プログラム->TDR 書き込み完了/1:TDR->TSR 転送完了 | 1 |
| RDRF(Receive Data Register Full) | 6 | 受信完了、RDRよりデータ読み込み可能 | - | 0 |
| ORER(Over Run ERror) | 5 | 受信時オーバーランエラー検出 | - | 0 |
| FER(Framing ERror) | 4 | 受信時フレーミングエラー検出 | - | 0 |
| PER(Parity ERror) | 3 | 受信時パリティエラー検出 | - | 0 |
| TEND(Transmit End) | 2 | 送信終了 | - | 1 |
| MPB(Multi Processor Bit) | 1 | 受信したマルチプロセッサビットを格納 | - | 0 |
| MPBT(Multi Processor Bit Transfer) | 0 | 送信時のマルチプロセッサ値を設定 | - | 0 |
SSRは常にCPUからリード/ライト可能ですが、TDRE、RDRF、ORER、FER、PERの 各ビットを"0"クリアするためには、予め "1"をリードしておく必要があります。TEND、MPBビットは 読み込み専用です。
SCIはRxD端子から入力されたデータを、受信した順にRSR(Receive Shift Register)のLSB(ビット0)からセットし、8bit(1byte)のデータ受信完了と共に RDR(Receive Data Register)にデータを転送します。RSRはこの時点で次のデー タをRxD端子から読み込む事が可能となります。ユーザは RSRに触れる事はできず、RDRからのみデータを読み込む事が可能です。
シリアル通信を行う前にSCIの初期化を行う必要があります。調歩同期式・内 部クロック使用に絞って以下に手順をまとめます。
| 順番 | 設定 |
| 1 | SCRのTE、REビットを"0"クリア |
| 2 | SCRに内部クロック設定(CKE1、CKE0の設定)。 この際、TIE、RIE、TE、RE、MPIE、TEIE(7〜2)ビットは 必ず"0"クリア |
| 3 | SMRに送受信フォーマットを設定 |
| 4(内部CLK使用時のみ) | BRRにビットレートの設定を書き込む |
| 5 | 少なくとも"1bit期間"待機 |
| 6 | SCRのTE、REビットを"1"に設定、SCR他ビット設定。 TIEビットは"0"に設定しておく。 |
以上でTxD、RxD端子が利用可能となります。SCI0とSCI1は独立しているので両 ポートを利用する際には当然どちらも初期化することになります。BRRへの設 定終了後、少なくとも「1bit期間待機」してから TE、REビットを"1"にセットし、TIE、RIE、MPIE、TEIEの設 定を行う必要があります。「1bit 期間」に関して次節にまとめます。
SCIの初期設定でBRRの設定後少なくとも「1bit期間待機」する必要があること は既に述べました。ここではこの「1bit期間待機」に関して考えてみたいと思 います。「1bit期間待機」するのはSCIの内部を初期化し、内部状態を確定す るためです。待機しないでデータを送信した場合は不確定なデータが送信され ることになってしまいます。
「1bit期間」とは「ビットレートの逆数」です。 つまり「1bit期間」というのは「1bitを転送する時間」です。たとえばSCIの ビットレートを"9600bps"に設定したとすると1bitあたり "1/9600(sec/bit)"、すなわち"104usec"の時間が必要と なります。SCIの初期化終了後は"104sec"だけ待機する必要がある わけです。正確にはITU タイマ等を利用すればいいのかもしれませんが、限ら れた資源を無駄にはできないので一般には"for文によるループ"等 で時間稼ぎを行います。H8/3048Fのクロック(φ)は"16MHz"ですの で、1ステートあたり"1/16(usec)"となり、 "104/(1/16)=1667"ステート待機すればいいわけです。
この待機動作に関し、H8/3048Fに付属していた(ような気がする) 「『AKI-H8/3048F(フラッシュメモリ内蔵)超高性能マイコンボード』製作集」 中のコードでは以下のように記述されています。また、ハードウェアマニュア ルの「付録A.3」の「命令実行ステート数」を参照しつつ各命令のサイクル数 を併記します。
mov.w #280,r0 ; "280"をr0に書き込む(I=2)
inisc1: ; サブルーチン inisc1
dec.w #1,r0 ; r0から"1"を引きます (I=1)
bne inisc1 ; 条件分岐 (I=2)
; if r0 == "0" then サブルーチンを抜ける
; else サブルーチンの先頭に戻る
|
これをC言語で記述すると以下の通り。
int i; for (i = 280; i > 0; i--); |
ここで"280"という数字が突然出てきました。この値を説明するた めに上記アセンブリコードが何ステートで実行されるかを計算してみます。ス テート数は上記コード中に記述したサイクル数より計算できます。本ページで 使用するAKI-H8/3048Fはモード7で外部に何も(ROM、RAMとも)増設せずに使用 していますので、計算式は以下のようになります。詳しくはハードウェアマニュ アル「付録A.3」をご参照下さい。
Si = 2
実行ステート数 = Si * 2 + 280 * ((Si * 1) + (Si * 2))
= 4 + 280 * (2 + 4)
= 1684
|
よって"280"回ループさせることによって、1684ステートの待機が 可能となることが分かります。以下に各bps毎の待機用ループ回数をまとめま す。
| ビットレート | 待機ステート数 | 最低限ループ回数 | ループ回数(余裕をみた設定値) |
| 600 | 26667 | 4444 | 4450 |
| 1200 | 13333 | 2222 | 2230 |
| 9600 | 1667 | 277 | 280 |
| 19200 | 833 | 139 | 145 |
この表は完全に私の計算のみに基いていますので、 そこのところをご了承下さい。ちなみに私が試したのは600bps、1200bps、9600bpsの3種類だけで、19200bpsに関 しては試していません。あくまで計算上の数値ということでご理解下さい。
SCIの初期化を完了した時点でTxD端子は自動的に送信データ出力端子になりま す。調歩同期式、割り込み無でデータを送信する際にどのような手順を踏む必 要があるかを以下にまとめます。
| 順番 | ユーザプログラム | SCI挙動 |
| 1 | ユーザはTDR->TSRへデータが転送完了した事(SSR.TDRE==1)を確認し、 TDRに送信データを1byte書き込んだ後、SSR.TDRE=0と設定 | SCIはTDRに送信データが書き込まれた(SSR.TDRE==0)後、 TDR->TSRへデータ転送して SSR.TDRE=1 と設定、 TxD端子から外部にシリアル送信開始。 (この際、SCR.TIE==1 であればTXI割り込み要求を発生) |
| 2 | ユーザは送信したいデータをすべてTSRに転送完了するまで1を繰り返す | SCIはストップビット送出時に SSR.TDRE==0->次に送信データがまだあると 判断、SSR.TDRE=1->SSR.TEND=1 をセット後、ストップビットの次に "1"を出力する「マーク状態」へ移行(この際、SCR.TEIE==1 であれ ばTEI割り込み要求を発生) |
| 3 | ユーザは SSR.TEND==1 になるまで待機 | --- |
| 4 | シリアル送信の終了時にブレークを出力する場合は、 DDRを"1"にDRを"0"に設定した後 SCR.TE=0 を設定 | --- |
SCIの初期化を完了した時点でRxD端子も自動的に送信データ入力端子になりま す。調歩同期式のデータ受信手順を以下に示します。
| 順番 | ユーザプログラム | SCI挙動 |
| 1 | ユーザはSSRのORER、PER、FER各ビットを読み込んで受信エラー判定をし、エ ラーが発生していた場合はエラー処理(後述)後、必ずORER、PER、FER 各ビッ トを"0"クリア(必須)。(フレーミングエラー時にRxD端子の値を読 み込むことでブレークの検出が可能) | SCIは通信回線を監視し、スタートビット"0"を検出すると受信開始。 データを1byte読み込んだ後、パリティチェック・ストップビットチェック・ ステータスチェック(SSR.RDRF=="0"->(RSR->RDRのデータ転 送可能))を行う。エラーが発生していた場合、"ERI割り込み要求" を発生。3つのチェックをパスするとSSR.RDRF=1とし、RDRへデータを転送。 (この際、SCR.RIE=="1"->RXI割り込み要求を発生) |
| 2 | ユーザはSSR.RDRF==1となるのを待ち(RXI割り込みで対応でも可)、 RDRの受信データを読み込んだ後、SSR.RDRF=0 と設定。 | --- |
受信エラーが発生した状態では以降の受信動作ができなくなる (受信時にSSR.RDRF=1にセットされなくなる)ので、 エラーフラグ(SSRのOPER、PER、FER各ビット)を 必ず"0"クリアしておく必要があります。
エラー処理は以下のように行います。
| 順番 | ユーザプログラム |
| 1 | SSR.ORER==1->オーバーランエラー処理 |
| 2 | SSR.FER==1->(ブレーク->SCR.RE=0として4へ) (ブレークでなければフレーミングエラー処理をして3へ) |
| 3 | SSR.PER==1->パリティエラー処理 |
| 4 | SSRのOPER、PER、FERを"0"クリア |
SCIの通信は非常に時間のかかるものだということを認識する必要があります。 9600bpsでは1bitあたりの送受信に"104usec"、 1byte(1byte==8bits)あたり"833usec"かかるわけです。また 1200bpsでは1byteあたり"6.67msec"もかかってしまいます。 H8/3048FではCPUのクロックが16MHzなので、この間ただwhile()していただけ では、それぞれ"13333ステート"、"106667ステート"の 間CPUが何も仕事をしないことになります。これは大きな損失です。そこでSCI の送受信に関してはバッファリングを行うことでこれを改善します。使用する バッファは一般に「リングバッファ」(R/B)と呼ば れます。
R/Bは非同期入出力を行う際によく利用される、先頭と末尾が接続されたドー ナツ型のバッファです。ユーザはプログラム上でこのようなバッファを実装し てあげます。末尾まで参照したら次に参照する場所は先頭であると考えればい いので、それほど難しいことではありません。R/Bは送信用と受信用の2つ用意 します。仮に送受信R/Bをそれぞれ以下のように命名します。
| 送受信 | 定義 |
| 送信用R/B | static unsigned char sci_snd_RB[SCI_SND_RB_SIZE] |
| 受信用R/B | static unsigned char sci_rcv_RB[SCI_RCV_RB_SIZE] |
R/Bのサイズはユーザが任意に指定できます。SCI_SND_RB_SIZE、 SCI_RCV_RB_SIZE がそれぞれのR/Bのサイズです。あまり大きく取っても勿体 ないので、本章のプログラムでは"16"とします。R/Bの内容はそれ ぞれ以下のポインタを使用してアクセスします。
| 送受信 | ポインタ名 | 用途 | 参照個所 |
| 送信用 | sci_snd_RB_in | プログラム->送信用R/B 書き込み用 | 送信用R/B書き込み関数 |
| sci_snd_RB_out | 送信用R/B->TDR 書き込み用 | TXI割り込みハンドラ | |
| 受信用 | sci_rcv_RB_in | TDR->受信用R/B 読み込み用 | RXI割り込みハンドラ |
| sci_rcv_RB_out | 受信用R/B->プログラム 読み込み用 | 受信用R/B読み込み関数 |
R/BとTXI/RXI割り込みを使用することにより、ユーザはR/Bに対してリード/ラ イトを行うだけで、あとは割り込みハンドラがシリアル通信を行ってくれるこ とになります。このためCPUはシリアル通信にかかりきりにならず、余った時 間で他の作業を行うことが可能となります。R/Bを参照するために注意すべき 点があります。
当然ですがsci_snd_RB_inがsci_snd_RB_outを追い越すということはTDRに書き 込めていないうちにバッファを書き換えてしまうということです。また sci_snd_RB_outがsci_snd_RB_inを追い越すということはプログラム中で指定 していないのにR/BからTDRへ書き込みを行ってしまうことであり、これは意図 していない行動のはずです。rcv_inとrcv_outに関しても同じことが言えます。
そこでお互いを追い抜かないように気を付けてプログラムしてあげる必要があ ります。使用するR/Bの大きさはユーザが限られた資源の中で任意に指定する 事が可能ですが、R/Bへの入力がR/Bからの出力に追い付いてしまう場合はR/B のサイズが小さすぎることになります。
折角リングバッファ(R/B)を利用しても、割り込み無での送信ではあまり意味 がありません。割り込み無の場合、データが送信できない場合でもビットフラ グを確認したりと何かと無駄が多いので、SCIのデータ送受信では割り込みを 利用した方が得です。具体的には、TXI割り込みハンドラによって送信用R/Bか らTDRへ1byteずつ書き込みを行ったり、RXI割り込みハンドラによってRDRから 受信用R/Bへ1byteずつ読み込みを行ったりといった作業です。リングバッファ (R/B)と割り込みを利用する場合のSCIデータ送受信に必要な関数は以下のよう になります。
| 関数 | 内容 | |
| 送信用 | void putchar_sci0_RB(unsigned char) | SCI0送信用R/Bに1byte書き込む |
| void int_txi0(void) | SCI0のTXI割り込みハンドラ。 送信用R/Bに溜っているキューをTDRに1byte書き込む | |
| 受信用 | unsigned char getchar_sci0_RB(void) | SCI0受信用R/Bから1byte読み込む |
| void int_rxi0(void) | SCI0のRXI割り込みハンドラ。 RDRに溜ったデータを1byteずつ受信用R/Bに読み込む | |
ユーザプログラムが送信用R/Bへ1byte書き込む関数(putchar_sci0_RB)の流れ を以下に示します。
| 順番 | ユーザプログラム |
| 1 | 送信用R/Bに書き込んでいる際に送信動作が行われるのを避けるため、 TXI割り込みを禁止(SCR.TIE=0)。 (厳しいタイミング条件がある場合は、割り込みを禁止せずに、 手間を増やして対処する必要があるかもしれません。) |
| 2 | 送信用R/Bにキューが存在しない状態で最初のデータを出力する場合、 TXI割り込みハンドラからではなく、本関数からTDRに直接書き込んで、 TXI割り込みを許可(SCR.TIE=1)して関数終了。 |
| 3 | 最初のデータでない場合、送信用R/Bにデータを1byte書き込んで 送信用R/B書き込みポインタ(sci_snd_RB_in)をインクリメント。 (R/Bなのでバッファ末尾まで行くと先頭に戻る事に注意。) |
| 4 | 送信用R/B書き込みポインタが送信用R/B読み出しポインタ(sci_snd_RB_out)に 追い付いた(バッファが一杯になった)場合の処理として、 TXI割り込みハンドラでsci_snd_RB_out++となるのを待つ。 |
| 5 | 送信用R/Bにキューが存在することを示すフラグ(sci_snd_RB_queue_flag)を立て、 TXI割り込みを許可(SCR.TIE=1)して関数終了。 |
TXI割り込み要求はTDRからTSRへデータが転送され、シリアル送信が開始され ると発生します。これはプログラムが次のデータをTDRに 書き込み可能であることを意味します。TXI割り込みハンドラはこの割 り込み要求を受けて送信用R/Bに溜っているキューから1byteずつTDRへ書き込 みを行います。
送信用R/Bにキューが溜っていない状態においては、データがTDRに書き込まれ ないとTXI割り込みハンドラが発生しません。そこで、この最初のデータの書 き込みは前述のように送信用R/B書き込み関数内で直接TDRに出力するようにし、 TXI割り込みハンドラでは最初のデータによって生じる割り込みの場合だけ、 TXI割り込み禁止処理だけで関数を終了します。
また、TXI割り込みは送信用R/Bに溜っている最後のキューをTDRに書き込んだ 後も発生するので、この場合だけはif文条件分岐で対応する必要があります。 以下にTXI割り込みハンドラの流れを示します。
| 順番 | ユーザプログラム |
| 1 | 最初のデータの場合だけTXI割り込み禁止として関数終了。 |
| 2 | 送信用R/BからTDRへ1byte書き込み後、書き込み終了フラグをセット (SSR.TDRE=0)。 |
| 3 | 送信用読み出しポインタ(sci_snd_RB_out)をインクリメント。 (R/Bなのでバッファ末尾まで行くと先頭に戻る事に注意。) |
| 4 | 送信用R/Bに溜ったすべてのキューをTDRへ送信完了した場合のみ、 送信用R/Bにデータが無いことを示すフラグをセット (sci_snd_RB_quele_flag=0)して、TXI割り込み禁止(SCR.TIE=0)し、 TXI割り込みハンドラ終了。 |
| 5 | それ以外の場合はそのままTXI割り込みハンドラ終了。 |
受信は一方的に外部からデータが流れてくるものなので、送信ほど都合の良い 作業ができません。通信プロトコルが決まっていて受信用R/Bが溢れたら再送 要求が出来るような仕組みが予め用意されていない限り、来たデータを受け取っ て万が一受信用R/Bが溢れた場合はエラー処理をする程度の事しかできないの です。(受信用R/Bが溢れたかどうかの判定は後述するRXI割り込みハンドラ内 で判定します。)
私がSCI受信(割り込み+R/B)を実装する場合、メインループで受信用R/B書き込 みポインタ(sci_rcv_RB_in)と受信用R/B読み出しポインタ(sci_rcv_RB_out)が 同じ位置にあるかどうかを判別し、違う場所にあれば受信用R/Bにデータがあ るとして受信用R/Bからデータを読み込むようにコーディングします。受信用 R/B書き込み関数やRXI割り込みハンドラ内でデータ存在フラグを立てるといっ た実装方法も容易に考え付きますが、受信動作というものが受動的動作である 以上、RXI割り込み禁止・許可を多用するのも考えものであり、結果としてこ ちらの方がより確からしいと考えての事です。プロの方々はどうコーディング されているのか興味深い所です。
ユーザプログラムが受信用R/Bから1byte読み込む関数(getchar_sci0_RB)の流 れを以下に示します。自由が効かない分、コードは非常にシンプルで済みます。
| 順番 | ユーザプログラム |
| 1 | 受信用R/Bからデータを1byte読み込んで受信用R/B読み出しポインタ (sci_rcv_RB_out)をインクリメント。 (R/Bなのでバッファ末尾まで行くと先頭に戻る事に注意。) |
| 2 | 読み込んだデータを返して関数終了。 |
RxD端子からの受信データはまずRSRに蓄えられた後、1byte溜る毎にRDRに転送 されます。受信エラーが発生していなければSSR.RDRF==1となり、RXI割り込み 要求が発生します。RXI割り込みハンドラではRXI割り込み要求を受けてRDRか ら受信用R/Bへ1byteずつ書き込みを行います。割り込み無の受信動作では、ま ずエラー判定・処理を行ってから受信用R/Bへデータを書き込んでいました。 しかし、割り込みを利用すると、ERI割り込みの方がRXI割り込みよりも優先順 位が高いため、受信エラー処理はすべてERI割り込みハンドラに一任すること が可能となります。
RXI割り込みでは受信用R/Bが溢れたかどうかの判定が可能です。必要であれば エラーフラグを立てる等してエラー処理を行います。以下にRXI割り込みハン ドラの流れを示します。
| 順番 | ユーザプログラム |
| 1 | RDRから受信用R/Bへ1byte書き込み後、読み込み終了フラグをセット (SSR.RDRF=0)。 |
| 2 | 受信用書き込みポインタ(sci_rcv_RB_in)をインクリメント。 (R/Bなのでバッファ末尾まで行くと先頭に戻る事に注意。) |
| 3 | 受信用R/Bがオーバーフローした場合の処理。 (エラーフラグを立てるなり、LEDで通知するなり。) |
| 4 | RXI割り込みハンドラ終了。 |
前述のR/Bのオーバーフローエラーだけではなく、SCI受信動作には、オーバー ランエラー(ORER:Over Run ERror)、フレーミングエラー(FER:Framing ERror)、 パリティエラー(PER:Parity ERror)の3種類のエラーが発生する可能性があり ます。これらの受信エラーが生じるとERI割り込み要求が発生します。ERI割り 込みハンドラでは、発生したエラーの種類を判別して必要に応じた処理を行う 事が可能です。
具体的にはSSR.ORER、SSR.PER、SSR.FERのどのビットが立っているかでエラー の種類を判定します。エラーが発生していた場合はエラー処理後、必ずORER、PER、FERの各ビットを"0"クリアする必要 があります。どれかが"1"となっていると受信が再開できな いので注意が必要です。また、フレーミングエラー時にRxD端子の値を読み込 むことでブレークの検出も可能です。
以下にERI割り込みハンドラの流れを示します。
| 順番 | ユーザプログラム |
| 1 | SSR.ORER==1->ORER処理 |
| 2 | SSR.FER==1->ブレーク判定し、ブレークならば4へ、 ブレークでないならばFER処理 |
| 3 | SSR.PER==1->PER処理 |
| 4 | SSR.ORER、SSR.FER、SSR.PER各フラグを"0"クリア |
以上を踏まえてプログラムを書いていきましょう。本プログラムではSCIの送 受信に割り込みを使用し、以下の機能を実現させます。
ついでに、LCDの下行に"Love & Peace!!!"と"Why not use H8?"を交互表示してみます。ちょっと盛り込み過ぎでしょうか;^p。 受信エラー(ERI割り込み要求)に関しては本プログラムでは特別処理をせず、 フラグ"0"クリアだけでそのまま流すことにします。
本プログラムではSCI0(D-sub 9pinコネクタに接続されている)を利用します。 またシリアル通信は以下の設定とします。
| 項目 | 設定 |
| SCI | SCI0 |
| 通信 | 全二重通信 |
| 通信モード | 調歩同期式モード |
| クロック | 内部クロック |
| データ長 | 8bit |
| パリティ | 無 |
| ストップビット長 | 1bit |
| ビットレート | 9600bps |
この通信設定を踏まえたSCIの初期化を以下にまとめます。「SCI初期化(調歩同期式)」節をご参照下さい。
SCI0.SCR.BYTE = 0x00; SCI0.SMR.BYTE = 0x00; SCI0.BRR = 51; 1bit期間待機 SCI0.SCR.BYTE = 0x30; SCI0.SSR.BYTE &= 0x80; |
このコードをtest_sci.tgzに置いておき ます。Linuxではcuやkermit、WindowsではTera Term等を利用して、ホストマ シンのM/Bと接続したシリアルポートを予め監視しておいてから、実行してみ て下さい。1秒おきにM/BのLCD表示が切り替わり、10秒おきにシリアルポート 越しにH8/3048Fから"May the world be as one!"が送信されてくる のが分かると思います。SCI0送信用R/Bを16bytesしか確保せず、敢えて送信用 R/Bが溢れるようにしてありますので、"May the world be as one!"が表示される度にLED1が光るのが分かると思います。受信用R/Bの オーバーフローはコマンド手動入力なので試せていません。あしからず。