[読書メモ]12ステップで作る組込みOS自作入門: 2ndステップ シリアル通信

書籍”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番目はリセットベクタというもので、リセットが掛かった時に行われる割り込み処理になる。

4877832394
12ステップで作る組込みOS自作入門

関連記事

コメントを残す

メールアドレスが公開されることはありません。