書籍”12ステップで作る組込みOS自作入門 “の読書メモです。
2ndステップのお話は、1stステップで作ったプログラムのうちserial.cの説明がメイン。
H8マイコンのシリアル通信仕様は、ルネサスのドキュメントが確かな資料となる。
シリアルについては、以下の「H8/3069R ハードウェアマニュアル」に記載されているので、これを見ながら確認すると分かりやすい(13-1章のSCIに書かれている)。
http://documentation.renesas.com/doc/products/mpumcu/rjj09b0165_h83069rf.pdf
ここで使うシリアル通信はマニュアル上”SCI”と呼ばれている。
シリアル通信
H8はメモリマップドI/Oなので、シリアルの制御は特定のメモリ番地を操作する事で行うことが出来る。具体的に何番地に割り当たっているかは、以下のようになっている。H8/3069Rはシリアルが3チャンネル有るので、レジスタも3つある。
ch addr 名称 略称 R/W 初期値 -- -------- ----------------------------- ---- ------- ------ 0 H'FFFB0 シリアルモードレジスタ SMR R/W H'00 H'FFFB1 ビットレートレジスタ BRR R/W H'FF H'FFFB2 シリアルコントロールレジスタ SCR R/W H'00 H'FFFB3 トランスミットデータレジスタ TDR R/W H'FF H'FFFB4 シリアルステータスレジスタ SSR R/(W)*2 H'84 H'FFFB5 レシーブデータレジスタ RDR R H'00 H'FFFB6 スマートカードモードレジスタ SCMR R/W H'F2 1 H'FFFB8 シリアルモードレジスタ SMR R/W H'00 H'FFFB9 ビットレートレジスタ BRR R/W H'FF H'FFFBA シリアルコントロールレジスタ SCR R/W H'00 H'FFFBB トランスミットデータレジスタ TDR R/W H'FF H'FFFBC シリアルステータスレジスタ SSR R/(W)*2 H'84 H'FFFBD レシーブデータレジスタ RDR R H'00 H'FFFBE スマートカードモードレジスタ SCMR R/W H'F2 2 H'FFFC0 シリアルモードレジスタ SMR R/W H'00 H'FFFC1 ビットレートレジスタ BRR R/W H'FF H'FFFC2 シリアルコントロールレジスタ SCR R/W H'00 H'FFFC3 トランスミットデータレジスタ TDR R/W H'FF H'FFFC4 シリアルステータスレジスタ SSR R/(W)*2 H'84 H'FFFC5 レシーブデータレジスタ RDR R H'00 H'FFFC6 スマートカードモードレジスタ SCMR R/W H'F2 |
この番地は、同じH8マイコンでも型番によって異なる。またH8/3069Rは3つのSCIを持っているけど1つしかないチップもあったりする。
I/Oにマップされたメモリ空間はCPUからすれば単なるメモリなのだけど、一般的にレジスタという呼び方をする。ここで言う”レジスタ”は、H8が持っている汎用レジスタ(ER0~ER7)とは意味が違うので注意が必要
SCIの通信モードには、調歩同期式モードとクロック同期式モードがあるが、ここではクロック同期式で通信する。
また、CPU的にはデータ送受信が行えるかのタイミングを、外部割り込みの形で取得することも取得可能だけど割り込み方式は使用せず、データ書き込みが可能かを一定周期で監視する方式を利用する。
シリアルデバイスの初期化や通信には、これらのメモリを操作していくころになるけど、どうやって初期化すればよいかの手順(プロトコル)は仕様書に記載されている。初期化手順を書くと、以下のようになる。
SCRのTE,REビットを0クリアする SCRのCHK1,CHK0ビットを設定する(0,0で内部クロック使用) SMRに送受信フォーマットを設定する BRRに通信速度を設定する 一定時間スリープする (もしBRRで9600bpsを指定した場合は、1/9600秒以上ウェイト) SCRのTE,REビットを1にセット 通信状態を割り込みで通知して欲しい場合は,SCRのRIE,TIE,TEIE,MPIEビットをセット |
また、クロック同期式でのデータ送信は、以下の手順で行う。
SSRのTDREビットが1である事を確認する TDRに送信するデータを書き込む SSRのTDREビットを0にセットする |
TDREを0にセット後、H8マイコンがそのデータを処理したら、マイコン側がTDREを1にセットしてくれる。最初に1である事を確認しているが、この確認を忘れてしまうとデータ欠落の可能性が発生する。
SCR,SSR等のI/O制御用レジスタは、メモリ上の各ビットに以下のような意味を持たされている。
SCR bit7 TIE bit6 RIE bit5 TE bit4 RE bit3 MPIE bit2 TEIE bit1 CKE1 bit0 CKE0 SSR bit7 TDRE bit6 RDRF bit5 ORER bit4 FER/ERS bit3 PER bit2 TEND bit1 MPB bit0 MPBT BRR 通信速度(bps)を表す値(クロック同期モードの場合) 以下の式で求まるNをBRRレジスタにセットする N = ( 周波数 / 8 * 2^(2n -1) * B ) * 1000000 - 1 B:通信したいビットレート(bps) n:ボーレートジェネレータ入力クロック(詳細はHWマニュアル参照) ※20MHzで9600bpsだと、64をセットする。 |
C言語で、I/Oに紐づいたメモリを操作する時は、以下のようにポインタ変数に番地を直接指定する。
char *smr = (char *)0xffffb0; char *brr = (char *)0xffffb1; char *scr = (char *)0xffffb2; |
さらに、以下のように変数にvolatile指定が必要になる。
volatile char *smr = (volatile char *)0xffffb0; volatile char *brr = (volatile char *)0xffffb1; volatile char *scr = (volatile char *)0xffffb2; scr = 0x00; /* 初期化開始 */ smr = 0; /* 8bit ノンパリティ, ストップビット1 */ brr = 64; /* 20MHz動作時、9600bpsを意味する */ scr = 0x30; /* RxEnable, TxEnable */ |
volatileというのは、コンパイラに対して最適化をしないよう依頼する指示となる。
上記のコードはSCIの初期化処理になるが、普通のC言語のプログラムとしてみると変数scrに0x00をセットした後、0x30をセットしているので、最初の0x00は無駄に見える。
だけど、コンパイラの最適化処理が走ると、その無駄を検出して実行ファイルから”scr = 0x00;”を削除してしまう場合がある。
コンパイラから見ると一見無駄に見えるかもしれないけど、シリアル制御のプロトコル上は意味がある処理なので、最適化を抑制するためにvolatileが必須となる。この辺の考えかたは普通のアプリとは異なり、組み込み向け特有の発想になる。
…上記シリアル通信を行うC言語でのプログラムは1stステップで提示されてましたが、動作を理解するためにアセンブラで書いてみました。
コードはH8/3069R向けではなく、H8-3694F向けに書いたのでI/Oのマッピングが異なっています。
→ [H8マイコン]アセンブラでシリアルポートにHello World
数値出力
2ndステップでは、以下の標準ライブラリ関数を自作してlib.cに追加している。memset, memcpy, memcmp, strlen, strcpy, strcmp, strncmp |
いづれもメモリ操作系の関数なので、C言語で簡単に書くことが出来る。(アセンブラでも書ける)
これらの関数は、ほかにサブ関数を呼ぶ必要が無いもので、こういう関数コールの末尾になるものを”リーフ関数”と呼ぶ。
あと、データの出力にprintf()が欲しいところだけど、printfは実装が面倒なので、簡易版のputxval()関数を定義している。これは指定された値をputs()を使って16進出力してくれる。columnパラメータでは表示幅の指定が行える。
int putxval( unsigned long value, int column ); |
スタートアップ
C言語の入門書だとmain()関数から処理が始まる事になっているけど、実際はその前にスタートアップルーチンから始まる。start() -> main() |
スタートアップルーチンはアセンブラでかかれることも多くcrt.sという名前を付けることが多い。
crtはC RunTime の略。
H8の場合スタートアップルーチンは、割り込みベクタの0番目に関数を登録する事で実行される。
割り込みベクタの0番目はリセットベクタというもので、リセットが掛かった時に行われる割り込み処理になる。

12ステップで作る組込みOS自作入門
関連記事
コメントを残す