◆ソフトウェア編
今回は、LCDモジュールを動かすソフトウェアについて、説明します。
ターゲット・マイコンはHCS08マイコンで、開発ツールはフリー・スケール社のCode Warriorです。
Code Warriorのインストールや使い方はエレキジャックNo.8本文のほかにも、本サイトでも多数の解説があるので、その辺の説明は省略してしまいます。
また、Code Warrior V6.0に対応しています。
エレキジャックNo.8では、USBSPYDER08に付属していたV5.1を使用していましたが、V6.0は対応MCUの追加などのほか、USBSPYDER08の安定度の向上なども得られますので、今回はV6.0を使用しました。
ぜひ、アップデートしてみてください。
○LCDコントローラLSIの内部とインターフェース
まず、今回使用した台湾SUNLIKE社のSD1602HUOBなどがその互換品を使用しているHD44780型のLCDコントローラをソフトウェア的な視点から見てみましょう。
LCDモジュール内のLCDコントローラLSIは、CPUに対して2本の8ビット・レジスタをもっています。
8ビット・レジスタの1本はデータ・レジスタ(DR)と呼ぶレジスタで、LCDに表示するDATAなどをやり取りします。もう1本の8ビット・レジスタは インストラクション・レジスタ(IR)と呼ばれ、LCDモジュールに初期設定や、画面の表示、文字書込の位置などの各種命令を書き込むと、内部のインストラクション・デコーダが自動的にその処理をやってくれるようになっています。
表1中のビジィ・フラグとはインストラクション・デコーダが受取った命令を処理している間、次の命令を受け取れない旨をCPU側に知らせて、次の命令を渡すのを待っていてもらう仕掛けです。
この内部の制御ロジックと外部のCPUをつなぐのに、DB0~DB8, RS, R/W、Eという合計11本のインターフェース端子が用意されています。
DB0~DB7は、設定・表示データをやり取りするDATAバスです。このDATAバスは、初期設定でDB0~DB7を使って8ビット単位でレジスタに読み書きできるモードと4ビット単位で8ビットのレジスタを2回に分けて読み書きするモードが選択できます。
R/Wは、データをLCDモジュールに書き込むのか、LCDモジュールから読み出すのかを指定する端子です。“H”で読み出し、“L”で書き込みをします。
RSは内部レジスタの指定端子です。“H”でデータ・レジスタの読み書きを指定して、“L”でインストラクション・レジスタを指定します。
Eは動作起動信号と呼ばれている、書き込み/読み出しのタイミングをコントローラLSIに知らせる信号です。
まとめると、表 1のようにLCDコントローラの内部レジスタはRS、R/Wで選択され、Eによるタイミングで入出力されます。

これらの信号は、このLSIの開発された当時の68系マイクロ・プロセッサのCPUバスおよび、類似したバス構造をもつ4ビット・マイコンに直接接続できるよう、設計されたものです。
CPUバスに接続されたLCDモジュールは「2バイトのメモリ」としてCPUから見えるようになっています。開発されてから四半世紀以上の年月がたち、接続される対象であった、単体のCPUは市場から姿を消してしまいました。しかし、HD44780は当時の需要をほぼ完璧に満たす優れた機能と性能、同LSIを搭載した使いやすいLCDモジュールの発売などで圧倒的なシェアを確保しました。
さらに、キャラクタLCDモジュールの世界では以後これ以上の性能・機能を必要とする需要が多くなかったため、現在までそのアーキテクチャが生き残っています。
○HCS08マイコンにつなぐには
先に述べたように、LCDコントローラHD44780は68系マイコンのバスに接続しやすいよう、作られています。HCS08マイコンもモトローラ社の6800MPUの系譜につながる68系マイコンなので、LCDモジュールと簡単に接続することができる…というのはウソです。
HCS08マイコンはワンチップMCUとよばれるマイコンチップで、マイコン動作に必要なCPU,ROM,RAM、I/Oなどはすべて内部でそろえてしまっていて、外部にCPUバスが出ていません。仮に出ていたとしても当時とは動作周波数が桁違いに向上してしまっているので、LCDコントローラは今のCPUの言っていることが早すぎて理解できません。
そこで、HCS08マイコンでI/Oポートをプチプチと操作して当時の68系バスの動作を再現し、I/OポートからLCDコントローラとインターフェースします。
○4ビット・バスでつなぐ
まずは、LCDモジュールに使用するI/Oポートの割り当てです。
エレキジャックNo.8付録のMC9S08QG8CPBEは、16ピンの小さなマイコンです。16ピンの端子のうち、2本は電源とGNDピン、さらに2本をUSBSPYDER08用に使ってしまうので、残り12本しかありません。LCDモジュールが合計11本のインターフェース端子なので、正直に割り当てると、LCDをつないだ後は1本のI/Oピンしか空いているピンはないことになってしまいます。
さすがに残りのI/Oピンが1本ではLCD表示をさせるためセンサなどを接続しようにも、工夫の仕様がないので、8本のDB端子のうち4本を使用する4ビット・モードで使用します。
4ビット・モードでは、8ビット・レジスタとデータをやり取りするに、4ビットずつ2回に分けて行う必要がありますが、どうせソフトウェアでI/Oを操作してインターフェースするので少しの手間をかけるだけです。
また、処理速度の低下もLCDコントローラの内部処理速度に比べたら4ビットずつ2回に分けて書き込む時間は1割程度増えるだけなので、まあ無視できる範囲です。
○4ビット・モードにするには?
では、早速4ビット・モードでプログラミングと行きたいところですが、いったいどうやってLCDモジュールを4ビット・モードにするのでしょう?
LCDモジュールの端子をみると、I/Oバスを8ビット/4ビットに設定する端子らしきものは見当たりません。実は、LCDコントローラは8ビット/4ビットの設定をソフトウェアでコマンドを与えて行うようになっています。でも、電源投入時にLCDコントローラは8ビット・モードで動作するようになっています。
4ビットしか配線していないLCDモジュールを8ビット・モードで起動して、どうしてソフトウェアで設定できるのか?
まずは、表 2のインストラクション:ファンクション・セット のコードを見てください。
この表の6番目、“インストラクション、ファンクション・セット”命令のビット構造にカギがあります。同命令は、最初に上位ビットのDB7から順に001というビット列になっています。これが命令コードです。次のDB4が“DL”ビット、続いてDB3,DB2が“N”,“F”となっています。“DL”ビットが"1"なら8ビット,"0"なら4ビット・モードになります。
つまり、上位4ビットでファンクション・セット命令コード“001”とDLビット=”0”を書き込んでやれば、とりあえず4ビット・モードを指定することができます。
下位4ビットの“N”,“F”ビットは、4ビット・モードに設定には関係ないので、"1"でも"0"でもかまいません。もし、“N”,“F”ビットをセットしたいときは、いったん4ビット・モードにしてしまってから再度ファンクション・セットのインストラクションを発行して、残りの“N”,“F”ビットを改めて設定してやれば問題ないわけです。
その他、HD44780LCDコントローラのインストラクション・セットの設計は、少ないハードウェアで高機能を実現するよう工夫されています。
これで、4ビット・モードにする仕組みはわかったわけですが、実際には電源投入時にいきなり上記の4ビット・モードに指定をしてもLCDコントローラ内部の初期化が済んでいないため、4ビット・モードで動作することができません。
図1のように、LCDコントローラの内部初期化、安定化のために電源投入後、決められた手順と時間で初期化手順を踏んで4ビット・モードしてやる必要があります。

○タイミングを計ってLCDにコマンドを送る
このように、HD44780型のLCDコントローラでは、電源投入後に決められたタイミングで8ビット・モードに設定するコマンドを3回発行してから初めて4ビット・モードに設定でする「おまじない」のような手順で設定する必要があります。
この「おまじない」をすると、HD44780の中でイロイロ動いて4ビット・モードに切り替わるのです。
実際には、図中、左側のフローのようにタイミングに余裕をみてソフトウェアに実装しました。
ハードウェアの解説であるように、LCDモジュールはR/W端子は”L”レベルに固定して、LCDへは書き込み動作のみ可能で、読み出しは行えないようにしています。つまり、CPUからLCDコントローラの状態を知ることができないことになります。
相手の状態がわらないのは非常に不安です。
LCDモジュールから読み出せる情報は、表示RAMやフォントRAMの内容、現在の書き込みポインタの位置、それにLCDコントローラがコマンド実行中を示すBUSYフラグです。
これらの情報のうちBUSYフラグ以外は、そもそもCPUから書き込んだ値なのでCPU側で管理できます。
BUSYフラグはLCDコントローラの都合をあらわす値なので、CPU側で知る術がありません。しかし、LCDコントローラは表2のように各コマンドの最大実行時間が示されているので、発行したコマンドはこの時間内に処理されている(はず)とみなして次のコマンドを発行してしまっても、基本的に不都合はありません。
この最大実行時間は、LCDコントローラのクロック周波数250kHzでの実行時間です。また、互換コントローラは、実行時間やクロック周波数に若干の違いがあります。いずれにしても、ホスト側のCPUの処理に比べて桁違いに遅いので、処理のタイミングはCPU側で待ってやる必要あります。
今回の例では、CPUを時間待ちLOOP関数のDelay100USを使って発行したコマンドに相応する時間を消費させてから、次のコマンドを発行します。
コマンド処理時間の見積もりは、表2の最大実行時間ピッタリとせずに、バラツキ時間とMCUクロックの誤差を含めた上で十分な余裕をもって実行させます。
具体的には、表 2あるように実行時間(max)はLCDコントローラのクロック周波数が250kHzのときのもので、コントローラのクロックはデバイス固有のバラツキや5V~3Vの電源電圧によって2倍くらい変化します。
また、先に述べたように使用したSD1602HUOB搭載のLCDコントローラはHD44780互換品なので、処理タイミングが微妙にちがいます。したがって、互換チップの動作や、電源電圧、CPUクロックの誤差など若干のマージンを見込んで、カタログ上の実行時間の2.5倍程度を確保しておきましょう。
次回は、Code WarriorでHCS08マイコンに実装する様子を説明します。
まず、今回使用した台湾SUNLIKE社のSD1602HUOBなどがその互換品を使用しているHD44780型のLCDコントローラをソフトウェア的な視点から見てみましょう。
LCDモジュール内のLCDコントローラLSIは、CPUに対して2本の8ビット・レジスタをもっています。
8ビット・レジスタの1本はデータ・レジスタ(DR)と呼ぶレジスタで、LCDに表示するDATAなどをやり取りします。もう1本の8ビット・レジスタは インストラクション・レジスタ(IR)と呼ばれ、LCDモジュールに初期設定や、画面の表示、文字書込の位置などの各種命令を書き込むと、内部のインストラクション・デコーダが自動的にその処理をやってくれるようになっています。
表1中のビジィ・フラグとはインストラクション・デコーダが受取った命令を処理している間、次の命令を受け取れない旨をCPU側に知らせて、次の命令を渡すのを待っていてもらう仕掛けです。
この内部の制御ロジックと外部のCPUをつなぐのに、DB0~DB8, RS, R/W、Eという合計11本のインターフェース端子が用意されています。
DB0~DB7は、設定・表示データをやり取りするDATAバスです。このDATAバスは、初期設定でDB0~DB7を使って8ビット単位でレジスタに読み書きできるモードと4ビット単位で8ビットのレジスタを2回に分けて読み書きするモードが選択できます。
R/Wは、データをLCDモジュールに書き込むのか、LCDモジュールから読み出すのかを指定する端子です。“H”で読み出し、“L”で書き込みをします。
RSは内部レジスタの指定端子です。“H”でデータ・レジスタの読み書きを指定して、“L”でインストラクション・レジスタを指定します。
Eは動作起動信号と呼ばれている、書き込み/読み出しのタイミングをコントローラLSIに知らせる信号です。
まとめると、表 1のようにLCDコントローラの内部レジスタはRS、R/Wで選択され、Eによるタイミングで入出力されます。
これらの信号は、このLSIの開発された当時の68系マイクロ・プロセッサのCPUバスおよび、類似したバス構造をもつ4ビット・マイコンに直接接続できるよう、設計されたものです。
CPUバスに接続されたLCDモジュールは「2バイトのメモリ」としてCPUから見えるようになっています。開発されてから四半世紀以上の年月がたち、接続される対象であった、単体のCPUは市場から姿を消してしまいました。しかし、HD44780は当時の需要をほぼ完璧に満たす優れた機能と性能、同LSIを搭載した使いやすいLCDモジュールの発売などで圧倒的なシェアを確保しました。
さらに、キャラクタLCDモジュールの世界では以後これ以上の性能・機能を必要とする需要が多くなかったため、現在までそのアーキテクチャが生き残っています。
○HCS08マイコンにつなぐには
先に述べたように、LCDコントローラHD44780は68系マイコンのバスに接続しやすいよう、作られています。HCS08マイコンもモトローラ社の6800MPUの系譜につながる68系マイコンなので、LCDモジュールと簡単に接続することができる…というのはウソです。
HCS08マイコンはワンチップMCUとよばれるマイコンチップで、マイコン動作に必要なCPU,ROM,RAM、I/Oなどはすべて内部でそろえてしまっていて、外部にCPUバスが出ていません。仮に出ていたとしても当時とは動作周波数が桁違いに向上してしまっているので、LCDコントローラは今のCPUの言っていることが早すぎて理解できません。
そこで、HCS08マイコンでI/Oポートをプチプチと操作して当時の68系バスの動作を再現し、I/OポートからLCDコントローラとインターフェースします。
○4ビット・バスでつなぐ
まずは、LCDモジュールに使用するI/Oポートの割り当てです。
エレキジャックNo.8付録のMC9S08QG8CPBEは、16ピンの小さなマイコンです。16ピンの端子のうち、2本は電源とGNDピン、さらに2本をUSBSPYDER08用に使ってしまうので、残り12本しかありません。LCDモジュールが合計11本のインターフェース端子なので、正直に割り当てると、LCDをつないだ後は1本のI/Oピンしか空いているピンはないことになってしまいます。
さすがに残りのI/Oピンが1本ではLCD表示をさせるためセンサなどを接続しようにも、工夫の仕様がないので、8本のDB端子のうち4本を使用する4ビット・モードで使用します。
4ビット・モードでは、8ビット・レジスタとデータをやり取りするに、4ビットずつ2回に分けて行う必要がありますが、どうせソフトウェアでI/Oを操作してインターフェースするので少しの手間をかけるだけです。
また、処理速度の低下もLCDコントローラの内部処理速度に比べたら4ビットずつ2回に分けて書き込む時間は1割程度増えるだけなので、まあ無視できる範囲です。
○4ビット・モードにするには?
では、早速4ビット・モードでプログラミングと行きたいところですが、いったいどうやってLCDモジュールを4ビット・モードにするのでしょう?
LCDモジュールの端子をみると、I/Oバスを8ビット/4ビットに設定する端子らしきものは見当たりません。実は、LCDコントローラは8ビット/4ビットの設定をソフトウェアでコマンドを与えて行うようになっています。でも、電源投入時にLCDコントローラは8ビット・モードで動作するようになっています。
4ビットしか配線していないLCDモジュールを8ビット・モードで起動して、どうしてソフトウェアで設定できるのか?
まずは、表 2のインストラクション:ファンクション・セット のコードを見てください。
つまり、上位4ビットでファンクション・セット命令コード“001”とDLビット=”0”を書き込んでやれば、とりあえず4ビット・モードを指定することができます。
下位4ビットの“N”,“F”ビットは、4ビット・モードに設定には関係ないので、"1"でも"0"でもかまいません。もし、“N”,“F”ビットをセットしたいときは、いったん4ビット・モードにしてしまってから再度ファンクション・セットのインストラクションを発行して、残りの“N”,“F”ビットを改めて設定してやれば問題ないわけです。
その他、HD44780LCDコントローラのインストラクション・セットの設計は、少ないハードウェアで高機能を実現するよう工夫されています。
これで、4ビット・モードにする仕組みはわかったわけですが、実際には電源投入時にいきなり上記の4ビット・モードに指定をしてもLCDコントローラ内部の初期化が済んでいないため、4ビット・モードで動作することができません。
図1のように、LCDコントローラの内部初期化、安定化のために電源投入後、決められた手順と時間で初期化手順を踏んで4ビット・モードしてやる必要があります。
○タイミングを計ってLCDにコマンドを送る
このように、HD44780型のLCDコントローラでは、電源投入後に決められたタイミングで8ビット・モードに設定するコマンドを3回発行してから初めて4ビット・モードに設定でする「おまじない」のような手順で設定する必要があります。
この「おまじない」をすると、HD44780の中でイロイロ動いて4ビット・モードに切り替わるのです。
実際には、図中、左側のフローのようにタイミングに余裕をみてソフトウェアに実装しました。
ハードウェアの解説であるように、LCDモジュールはR/W端子は”L”レベルに固定して、LCDへは書き込み動作のみ可能で、読み出しは行えないようにしています。つまり、CPUからLCDコントローラの状態を知ることができないことになります。
相手の状態がわらないのは非常に不安です。
LCDモジュールから読み出せる情報は、表示RAMやフォントRAMの内容、現在の書き込みポインタの位置、それにLCDコントローラがコマンド実行中を示すBUSYフラグです。
これらの情報のうちBUSYフラグ以外は、そもそもCPUから書き込んだ値なのでCPU側で管理できます。
BUSYフラグはLCDコントローラの都合をあらわす値なので、CPU側で知る術がありません。しかし、LCDコントローラは表2のように各コマンドの最大実行時間が示されているので、発行したコマンドはこの時間内に処理されている(はず)とみなして次のコマンドを発行してしまっても、基本的に不都合はありません。
この最大実行時間は、LCDコントローラのクロック周波数250kHzでの実行時間です。また、互換コントローラは、実行時間やクロック周波数に若干の違いがあります。いずれにしても、ホスト側のCPUの処理に比べて桁違いに遅いので、処理のタイミングはCPU側で待ってやる必要あります。
今回の例では、CPUを時間待ちLOOP関数のDelay100USを使って発行したコマンドに相応する時間を消費させてから、次のコマンドを発行します。
コマンド処理時間の見積もりは、表2の最大実行時間ピッタリとせずに、バラツキ時間とMCUクロックの誤差を含めた上で十分な余裕をもって実行させます。
具体的には、表 2あるように実行時間(max)はLCDコントローラのクロック周波数が250kHzのときのもので、コントローラのクロックはデバイス固有のバラツキや5V~3Vの電源電圧によって2倍くらい変化します。
また、先に述べたように使用したSD1602HUOB搭載のLCDコントローラはHD44780互換品なので、処理タイミングが微妙にちがいます。したがって、互換チップの動作や、電源電圧、CPUクロックの誤差など若干のマージンを見込んで、カタログ上の実行時間の2.5倍程度を確保しておきましょう。
次回は、Code WarriorでHCS08マイコンに実装する様子を説明します。
<寺尾 大二>
