前回からだいぶ間が空いてしまいました。
ブレーク信号を受信するとTOPPERS/JSPカーネルがハングしてしまっていたのですが、これは受信割込ハンドラだけでなくエラー受信割込ハンドラを有効にすることで回避できました。また、実際はブレーク受信時の処理を書く必要はなかったのです・・・まったく、一体何のためにあれだけ苦労したのかと。
ちなみに、今回この問題が起きたわけは、もっと深いところにありました。深いとは言ってもソフトではなく、ハードです。
実は、H8 3069 LANボードに載っているH8 3069には3チャネルのシリアルコントローラが載っているんですが、チャネル0とチャネル1はRS-232C用に信号レベルの変換を行うチップが接続され、そのチップ経由でチャネル1がD-sub 9ピンの雌コネクタに接続されています。このチャネル1をシリアルコンソールとして利用するわけです。
ところで、今回はまっていたのはこのRS-232C向けに出力されているチャネル0を使った通信だったのですが、RS-232C用に信号レベルを変換されてしまうと都合の悪いデバイスに接続をしなければならなかったので、このH8からレベルシフタへの配線パターンをカットして、そこからリード線を引き出す様な使い方をしていたんですね。
ちなみに、RS-232Cとはシリアル通信に対してHIGHとLOWの電圧を定めたもので、HIGHが+12V、LOWが-12Vで作動します。通常のシリアル通信(UART)にはHIGHレベルの電圧についての規定はなく、LOWレベルはグラウンドで動作します。
TOPPERS/JSPのシリアルドライバは、チャネル0のUARTがレベルシフタに直結している事を想定していた為に、今回の問題が起こった様でした。
ま、基板に直接手を入れる様な事をすれば、この様な想定外の事態が起こる、ということなんでしょうな。ともかく良い勉強になりました。
さてさて、H8向けのTOPPERS/JSPには、シリアルインタフェースドライバ内にシリアル受信割込ハンドラとシリアル受信エラー割込ハンドラの2つが定義可能です。しかし、後者は #ifdef の条件分岐によってコメントアウトされており、前者内でエラーの割込も処理するようになっています。
ちなみに、実装は jsp/config/h8/hw_serial.c 内の SCI_in_handler です。
さて、SCI_in_handler の実装を見てみましょう。
void SCI_in_handler(ID sioid)
{
SIOPCB *pcb;
UB status;
pcb = get_siopcb(sioid);
status = sil_reb_mem((VP)(pcb->inib->base + H8SSR));
if (status & (H8SSR_ORER | H8SSR_FER | H8SSR_PER)) {
/* エラー処理 */
/* エラーフラグをクリア */
sil_wrb_mem((VP)(pcb->inib->base + H8SSR), status & ~(H8SSR_ORER | H8SSR_FER | H8SSR_PER));
}
if (status & H8SSR_RDRF) {
if (pcb->openflag) {
/* 受信可能コールバックルーチンを呼出す。*/
SCI_ierdy_rcv(pcb->exinf);
} else {
sil_wrb_mem((VP)(pcb->inib->base + H8SSR), status & ~H8SSR_RDRF);
}
}
}
ううむ・・・真ん中当たりに「エラー処理」と書かれて放置されている(っぽい)辺りが怪しい。ここに書けば良いのかな。
とりあえず、フレーミングエラーの場合はシリアルチャネルのRxDを調べて、ブレーク信号かどうかを判断すれば良いそうです。こんな処理を入れちゃいましょう。
/* フレーミングエラー発生時 */
if (status & H8SSR_FER){
/* RDR (Receive Data Register) がブレーク信号受信状態 (全ビットがLOW = 0x00) であるかどうかをチェック */
if (sil_reb_mem((VP)(pcb->inib->base + H8RDR)) == 0x00){
/* ブレーク信号受信時には SCR (Serial Control Register) の RE (Receive Enable) フラグを落とす */
sil_wrb_mem((VP)(pcb->inib->base + H8SCR),
sil_reb_mem((VP)(pcb->inib->base + H8SCR)) & ~H8SCR_RE);
}
}
RxDを調べるとブレーク信号かどうか分かるって言うんですが、それってRDR (Receive Data Register) の値を調べれば良いって事と等価ですよね?RSR (Receive Shift Register) は直接CPUから読み書きできませんので、これで合ってると思うんですが。
そして、このコード修正をカーネルに反映してアプリを再コンパイルします。カーネルをアプリとは別にコンパイルしている場合は、
% cd jsp/(kernel_build_dir)
% make realclean
% make depend
% make libkernel.a
とかやっときましょう。たぶん最後の行の「make libkernel.a」だけで良い筈ですが、チキンな私は「make realclean」からやっときました(^^;)
さて、アプリをコンパイルしてH8に転送し、実行します。そして、シリアルコンソールのminicomから「Ctrl-a f」でブレーク信号を送信しますが・・・あ、シリアルコンソールからのデバッグ出力が止まった。カーネルがハングしたっぽい。
何でやねん!!
今入れたコードは何だったんだ・・・orz
(苦悩は続く・・・)
あああ、、、やってしまった。
前回のエントリでは、一生懸命SH2向けのTOPPERS/JSP実装をベースにH8 3069F向けのブレーク信号検出時の割り込みハンドラについて想定しましたが、やってしまいました。そもそも、H8にはブレーク信号検出割り込みというものは無いのです。代わりに、受信エラー割り込みの割り込みハンドラ内で、ブレーク信号の検出時の挙動を書いてやる必要が有りそうです。
ちなみに、受信エラー割り込みハンドラは既に jsp/config/h8/hw_serial.c 内に実装されており、シリアルステータスレジスタ(SSR)の3つのフラグ(ORER: オーバーランエラー, FER: フレーミングエラー, 及び PER: パリティエラー)をチェックし、フラグが立っていれば単にそれらをクリアする、という実装になっています。
ここに何かを実装すれば良い気がします。
引き続き備忘録です。
TOPPERS/JSPカーネルのH8向け実装には、シリアルインタフェースの受信時にブレーク信号を検出した際の割り込みハンドラが実装されていない様です。これで相当にハマりました。しょうがないので、自分で作ることにします。
幸いな事に、TOPPERS/JSPカーネルのソースツリーを調べると、SH2プロセッサのSH7615向けにはブレーク信号検出の割り込みハンドラが実装されていますので、それを参考にします。というか、他のプロセッサ/プラットフォーム向けにはブレーク信号検出の割り込みハンドラが実装されていない様に見受けられます。みんな必要なかったんでしょうか。。。謎だ。
さて、以下は作業予定ですので、実際にこの通りやってうまくいかない可能性も有ります。作業が終わったらまたアップデートする予定です。
作業のレシピはこんな感じ。
ソース:
- jsp/config/sh2/sh7615scif.c
- 割り込みハンドラの実体である sh2scif_isr_siop_brk 関数があります
- シリアルチャネル0向けの割り込みハンドラである sh2scif_isr_brk 関数があります(その実体は sh2scif_isr_siop_brk 関数です)
- シリアルチャネル1向けの割り込みハンドラである sh2scif_isr2_brk 関数があります(その実体は sh2scif_isr_siop_brk 関数です)
- jsp/config/sh2/hsb7616it/hw_serial.h
- 割り込みハンドラの名前を付け変える以下の#defineディレクティブがあります
- #define sio_handler_brk sh2scif_isr_brk
- #define sio_handler2_brk sh2scif_isr2_brk
- jsp/config/sh2/hsb7616it/hw_serial.cfg
- 割り込みハンドラを登録する以下の静的APIがあります
- DEF_INH(INHNO_SERIAL_BRK, { TA_HLNG, sio_handler_brk });
- DEF_INH(INHNO_SERIAL2_BRK, { TA_HLNG, sio_handler2_brk });
なんか、これだけ材料があったら負ける気がしません(笑)。
さて、私がターゲットにしているのはH8 3069を搭載した秋月電子製マイコンボード(H8 3069F LANボード)ですので、上記のレシピで以下の様な読み替えを行います。
- sh2 => h8
- hsb7616it => akih8_3069f
また、シリアルチャネルの数が最大3に増えます。
この読み替えを行うと、H8 3069F LANボード向けには以下の作業をすることになります。
作業:
- jsp/config/h8/akih8_3069fscif.c を作成する
- 割り込みハンドラの実体である h8scif_isr_siop_brk 関数を実装する
- シリアルチャネル0向けの割り込みハンドラである h8scif_isr_brk 関数を実装する(その実体は h8scif_isr_siop_brk 関数である)
- シリアルチャネル1向けの割り込みハンドラである h8cif_isr2_brk 関数を実装する(その実体は h8scif_isr_siop_brk 関数である)
- シリアルチャネル2向けの割り込みハンドラである h8cif_isr3_brk 関数を実装する(その実体は h8scif_isr_siop_brk 関数である)
- jsp/config/h8/akih8_3069f/hw_serial.h を作成する
- 割り込みハンドラの名前を付け変える以下の#defineディレクティブを定義する
- #define sio_handler_brk h8scif_isr_brk
- #define sio_handler2_brk h8scif_isr2_brk
- #define sio_handler3_brk h8scif_isr3_brk
- jsp/config/h8/akih8_3069f/hw_serial.cfg を作成する
- 割り込みハンドラを登録する以下の静的APIを記述する
- DEF_INH(INHNO_SERIAL_BRK, { TA_HLNG, sio_handler_brk });
- DEF_INH(INHNO_SERIAL2_BRK, { TA_HLNG, sio_handler2_brk });
- DEF_INH(INHNO_SERIAL3_BRK, { TA_HLNG, sio_handler3_brk });
ここで、H8プロセッサ、及びH8 3069F LANボードにおける、TOPPERS/JSPのファイル構成を考慮して、少し修正を施します。
- jsp/config/h8/akih8_3069fscif.c を作成する
- 既存の jsp/config/h8/hw_serial.c に追記する
- jsp/config/h8/akih8_3069f/hw_serial.h を作成する
- 既存の jsp/config/h8/hw_serial.h に追記する
- jsp/config/h8/akih8_3069f/hw_serial.cfg を作成する
- 既存の jsp/config/h8/hw_serial.cfg に追記する
さらに、既存のH8向けシリアルインタフェースドライバの関数名を踏襲しましょう。修正された作業内容は以下の通りです。
- 既存の jsp/config/h8/hw_serial.c に追記する
- 割り込みハンドラの実体である SCI_brk_handler 関数を実装する
- シリアルチャネル0向けの割り込みハンドラである sio_brk_handler 関数を実装する(その実体は SCI_brk_handler 関数である)
- シリアルチャネル1向けの割り込みハンドラである sio_brk2_handler 関数を実装する(その実体は SCI_brk_handler 関数である)
- シリアルチャネル2向けの割り込みハンドラである sio_brk3_handler 関数を実装する(その実体は SCI_brk_handler 関数である)
- 既存の jsp/config/h8/hw_serial.h に追記する
- 既存の jsp/config/h8/hw_serial.cfg に追記する
- 割り込みハンドラを登録する以下の静的APIを記述する
- DEF_INH(INHNO_SERIAL_BRK, { TA_HLNG, sio_brk_handler });
- DEF_INH(INHNO_SERIAL2_BRK, { TA_HLNG, sio_brk2_handler });
- DEF_INH(INHNO_SERIAL3_BRK, { TA_HLNG, sio_brk3_handler });
さて、これでやることが決まりました。
作業を始めますか。。。