表示編2


表示編1で説明したモジュールを作ってみた。
初めてのプログラムなので、課題がたくさんある。
  • ファイルの分割方法
  • ポートの制御方法
  • タイマ割り込みの発生方法
  • LCDの制御方法
    ってところかな?


  • ファイルの分割方法

    モジュール化するためにはソースを分割して作らなければならない。それぞれを別々にアセンブルし、できたオブジェクトファイルをリンカでまとめるというのはパソコン上のC言語と同じ。

    パソコン上と少し違うのはアドレスに制限があること。パソコンの場合は、全てがRAMで、プログラムはどこに置いても動いた(OSが対応していたら)。マイコンの場合は、ベクタテーブルは0番地から始めないといけないし、プログラムはROMに置かないといけないし、変数領域はRAMに置かないと書き換えられない。定数領域はROMに置かないと電源を切る度に消えてしまう。

    書き込むアドレスを指定する方法として「.org」というアセンブラ疑似命令がある。この命令を使うとロケーションカウンタを変更することができる。しかし、プログラムを複数に分割すると、アドレスが重なってしまう可能性がある。そこで「.section」を使う。

    例えば、プログラム部分の初めには次の1行を入れておく。

        .section  ROM,CODE,ALIGN=2

    ROMはセクション名。リンカで「-START=ROM(000100)」というようにアドレスを指定する。複数のファイルに同じセクション名があると、すべて連結され、H'000100からの領域に書き込まれる。
    ALIGN=2は連結したときの各先頭アドレスを2の倍数にしなさいと指定している。あるセクションの最後に奇数バイトのデータがあるなどしてサイズが奇数になった場合、これに連結される次のセクションの先頭アドレスが奇数アドレスになってしまう。H8は命令は2バイト単位で、奇数アドレスから始まることは許されていない。そこで、間にダミーを詰めて、偶数アドレスになるようにしている。

    変数はRAM領域を使う。.sectionは次のように指定する。

        .section  RAM,DATA,ALIGN=2

    この後に、.resで領域を確保する。プログラムを一つのファイルにしてしまった場合は、各変数のアドレスを「.equ」で決めてもよい。しかし、モジュール化すると、一つにリンクするときに重ならないように書き換えなければならない。ミスで重なっていたとしても警告は出ない。.sectionと.resでアドレスを定義するとそんな面倒はなくなる。

    ベクタ領域は1つしか存在しないので次のようにしておけばよい。

        .section  VECTOR,DATA,LOCATE=H'00000

    一つしか存在しないので、LOCATEでアドレスを指定している。もちろん、プログラム中に2つ以上存在することは許されない。


  • ポートの制御方法

    これはサンプルから抜き出せばよい。I/Oポート関係のレジスタは単純で、ポートごとに1〜3つのレジスタが用意されているだけ。
    データの読み書きをするデータレジスタ。入力ピンか出力ピンかを指定するデータディレクションレジスタ。プルアップのON/OFFを制御する入力プルアップMOSコントロールレジスタの3つ。

    LCDを制御するのはポート3で、全て出力なので、

        mov.b  #H'ff,R0L  ;ポート3を出力に設定
        mov.b  R0L,@P3DDR

    とするだけで設定完了。

    I/Oポート関係のお話。
    H8のI/Oポートのデータレジスタを読むと、入力用のピンは入力値を、出力用のピンはデータレジスタに書き込んだ値が返ってくるようになっている。ハードウェアマニュアルに明記してあるし、ポートごとのブロック図を見てもよくわかる。
    マイコンの種類によっては、ピンの電圧の値がそのまま返ってくるものもあるけど、H8の場合はビット操作のことも考えてあるようだ。H8には何故かオープンドレイン出力がないので、あまり関係のない話だけど。


  • タイマ割り込みの発生方法

    初めてプログラムを作るっていうのに、いきなりタイマ割り込み。まぁ、そんなに複雑でもないんだけど(Windows上で割り込みを使うのに比べれば月とスッポン)。タイマの使い方は10章の「16ビットインテグレーテッドタイマユニット」に載っている。読むと延々とレジスタの説明が。わけがわからん単語が続く。後から気づいたのだが、10.4から読むのが正解みたい。今回必要なのは、1秒間に1000回の割り込みだけ。PWMがどーたらこーたら、インプットキャプチャがどーたらこーたらは関係ない。

    H8のタイマユニット内には、5つのカウンタがある。それぞれいろいろな使い方ができて複雑なんだけど、タイマ割り込みだけなら簡単。カウンタは指定されたクロックで1ずつ増えていく。カウンタの値が指定しておいた値といっしょになると割り込みを発生させ、カウンタを再び0に戻すようにしておけばよい。他の機能は無視。

    どのタイマを使ってもいいんだけど、気分でタイマ1を選んだ。クロックは分周しなくても16ビットに収まったので16MHzを使うことにする。1kHzの割り込みが欲しいので、16MHz/1kHzで16000をGRAに設定しておく。GRBはいらない。カウンタの値がGRAと等しくなれば(コンペアマッチ)割り込みが発生するように設定する。

    割り込みベクタでジャンプ先のアドレスを指定し、割り込みルーチンにとばす。割り込みルーチンの最後はrtsではなくてrteで終わる。

    ところが実際に試してみると、かなり速いペースで割り込みが入る。結構悩んだが原因は簡単だった。カウンタのコンペアマッチによってタイマステータスレジスタ(TSR)のビットが1になり、それが割り込みの引き金になるのだが、それは手動で0に下ろさないといけないのだった。早速movで書き込むが症状は同じ・・・。どうやら一度読み込まないと0を書き込めないようだ(p.366)。読み込むというのは割り込みが発生したってことじゃないのね。movをやめてbclrにしたら直った。bclrは特定のビットを0にする命令だけど、この結果より一度読み込んでからビットを操作して書き込んでいることがわかる。

    各レジスタの設定内容はソースを参照。


  • LCDの制御方法

    操作するのは20*4のLCD。初期化はデータシート通り行う。

    文字を適当に書き込んでみる。信号を出力するときのウェイトはそれほどいらない。文字と文字の間のウェイトはかなり必要みたい。文字を書き込んでいくと、右端まで行き、3行目に飛ぶ。次は2行目で、最後に4行目。そして、左上に戻る。

    ウェイトにさえ気をつければ簡単に扱えるようだ。


    あとは、命令表を見ながらプログラムを打ち込んでいくだけ。

    命令表をまとめたので資料のページに置いた。印刷して半分におれば結構見やすい・・・かな?

    作っていて気になったのが、ローカルラベルがないこと。X68000のハイスピードアセンブラで慣れてしまって、ローカルラベルが使えないとラベル名を考えるのに時間を食ってしまう。そこでプリプロセッサを作った。ここで公開している。マクロとかも使えるようにしたかったけど、行数が変わると困るからなぁ。


    今回のプログラム

    LCD001.LZH 6934 bytes・・・ドキュメント

    最新版は成果物のページ


    続く
    ご意見・ご感想は kumon@sam.hi-ho.ne.jp までどうぞ。

    戻る