見出し画像

DelphiでLCDに8bitパラレルで描画する

0.はじめに

前回の記事(DelphiでFT232Hを使ってみる)でFT232Hを使えば8bitパラレルの制御ができることがわかりました。また、別の記事(DelphiでLCDに描画する)では、LCDに描画するのにI2C+4bitパラレルでは遅すぎる、ともわかりました。
そうなると、8bitパラレルで描画したらどれくらい早くなるのか、興味が出てきます。ちょうど、20桁、4行のLCDモジュールを持っていて、それについているPCF8574のモジュールは壊れています(と言うより、壊してしまいました)。意味のないPCF8574モジュールを取り外して、8bitパラレルでLCDモジュールに描画して、どの程度の速度で描けるのか、確認してみます。

1.システム構成

PCとFT232HをUSBで接続し、MPSSEで8bitパラレルで制御を行います。
LCDはネットで購入できる16ピンタイプのモジュールです。16桁2行を始めとして色々なタイプがあるようですが、今回は20桁4行を使用します。

システム構成

端子説明は下記となります。

端子説明

2.FT232Hの制御

FT232HはACBusとADBusの8bitx2のI/Oを持っていて、Bit Bang ModeとMPSSEの2通りの制御方法がありますが、今回必要なのは8+3(D0:7とE,RS,R/W)の制御信号です。最初はBit Bang Modeで考えていたのですが、ACBusの制御方法がわからず、ネット記事にも出来ないようなことが書かれていたので、MPSSEに変更しました。

まず、FTDI社から提供されているD2XXUnit.pasを同じディレクトリにコピーして、usesに"D2XXUnit"を追加してMPSSEを設定します。LatencyTimerとTimeoutsの設定は、おまじないのように思っています。

//Device Open
  Open_USB_Device;
  Reset_USB_Device;
//ModeとPortの設定
  AD_Direction:=$FF;  //出力に設定
  AC_Direction:=$FF;
  AD_Value:=$00;      //0に暫定設定
  AC_Value:=$00;
  Set_USB_Device_LatencyTimer(16);
  Set_USB_Device_Timeouts(1000,1000);
  Set_USB_Device_BitMode($00,$00); //Reset
//MPSSEに設定
  Res:=Set_USB_Device_BitMode(AD_Direction,$02);  //$02=MPSSE mode
  if Res=0 then
    begin
      MemoDevice.Lines.Add('MPSSEに設定');
      MemoDevice.Lines.Add('AD Direction : $'+inttohex(AD_Direction));
      MemoDevice.Lines.Add('AC Direction : $'+inttohex(AC_Direction));
    end else
      MemoDevice.Lines.Add('エラー');

ここではACBusを設定していないのですが、特に問題は無いようです・・・

3.LCDモジュールの制御

4bitによる制御と違い、8bit制御は非常に簡単です。
ADBusに書き込みたいデータやインストラクションを設定して、Eを"1"から"0"に切り替えるだけで実行されます。なお、RSはデータ時は"0"、インストラクション時は"1"に設定します。

インストラクション一覧

まずイニシャライズですが、仕様書にはウエイト時間を入れながらFunction setを4回送るように記載されています。標準の8bitで使用しますので、1回だけでも問題はなさそうです。
この場合のイニシャライズにかかる時間は、TimeGetTime関数での計測で約40msecでした。
WaitTimer()を無効にするとイニシャライズ計測時間は0msecとなり、正しくイニシャライズはされません。設定時間は0でもいいので、何らかのウエイト時間は必要です。

//Function set
  AD_Value:=$38;
//1回目
  Write8I(AD_Value);
  WaitTimer(5);
//2回目
  //Write8I(AD_Value);  //無効
  //WaitTimer(1);
//3回目
  //Write8I(AD_Value);  //無効
  //WaitTimer(0);
//4回目
  //Write8I(AD_Value);  //無効
  //WaitTimer(0);
//5回目
  AD_Value:=$08; //表示OFF
  Write8I(AD_Value);
  WaitTimer(0);
//6回目
  AD_Value:=$01; //表示クリア
  Write8I(AD_Value);
  WaitTimer(0);
//7回目
 AD_Value:=$06; //エントリーモード
  Write8I(AD_Value);
  WaitTimer(0);
//8回目
  AD_Value:=$0F; //表示ON
  Write8I(AD_Value);

別procedureのWrite8I()で、実際の書き込み動作を実行しています。
なお、書き込みたいデータは下記の3回で設定します。

 AddToBuffer(設定したいBUS) → ACBus=$82、ADBus=$80
 AddToBuffer(設定したいデータ)→   bit7..bit0で設定
 AddToBuffer(BUSのI/O)     →    初期設定から変更はないと思われる

まず、ACBusのEを"1"に設定して、次にADBusでインストラクションを設定して一旦送信します。ここで送信を実行しないと、キャラクター書き込みの際に表示漏れが起きていましたので、イニシャライズ時にも同様に送信しています。多分Eの設定時間(Min.140nsec)が短すぎるだけなのでしょうから、BF(ビジーフラグ)を読んで、という処理は行っていません。
その後Eを"0"に設定して、送信して終了です。

procedure TFrmLCD.Write8I(TX_AD:Byte);
begin
  AC_Value:=$01;              //E=1,RS=0

//Set High Byte
  AddToBuffer($82);           //ACBus
  AddToBuffer(AC_Value);      //E=1,RS=0
  AddToBuffer(AC_Direction);  //Output
//Set Low Byte
  AddToBuffer($80);           //ADBus
  AddToBuffer(TX_AD);         //
  AddToBuffer(AD_Direction);  //Output
//ここで一旦送信しないと、表示漏れが出ることがある
  SendBytes(OutIndex);        // send
  OutIndex := 0;
//Set E
  AddToBuffer($82);           //ACBus
  AddToBuffer(AC_Value-1);    //E=0,RS=0
  AddToBuffer(AC_Direction);  //Output
//TX
  SendBytes(OutIndex);        // send
  OutIndex := 0;

end;

イニシャライズが正常に終了すると、左上の桁がブリンキングします(分かりやすいようにブリンキングする設定としています)。

次に描画したいキャラクターのデータを送ります。
Write8I()で描画を開始したい位置を設定し、Write8C()で実際の書き込みを行っています。

//1行目
  Write8I($80);
  for i := 1 to length(edit1.text) do
    begin
      Buf:=edit1.text;
      B:=ord(widechar(Buf[i]));
      Write8C(B);
    end;
//2行目
  Write8I($C0);
  for i := 1 to length(edit3.text) do
    begin
      Buf:=edit3.text;
      B:=ord(widechar(Buf[i]));
      Write8C(B);
    end;
//3行目
  Write8I($94);
  for i := 1 to length(edit2.text) do
    begin
      Buf:=edit2.text;
      B:=ord(widechar(Buf[i]));
      Write8C(B);
    end;
//4行目
  Write8I($D4);
  for i := 1 to length(edit4.text) do
    begin
      Buf:=edit4.text;
      B:=ord(widechar(Buf[i]));
      Write8C(B);
    end;

別procedureのWrite8C()は下記となりますが、Write8I()と違うのはRS(AC1)の値だけです。

procedure TFrmLCD.Write8C(TX_AD:Byte);
begin
  AC_Value:=$03;              //E=1,RS=1

//Set High Byte
  AddToBuffer($82);           //ACBus
  AddToBuffer(AC_Value);      //E=1,RS=1
  AddToBuffer(AC_Direction);  //Output
//Set Low Byte
  AddToBuffer($80);           //ADBus
  AddToBuffer(TX_AD);         //
  AddToBuffer(AD_Direction);  //Output
//ここで一旦送信しないと、表示漏れが出ることがある
  SendBytes(OutIndex);        // send
  OutIndex := 0;
//Set E
  AddToBuffer($82);           //ACBus
  AddToBuffer(AC_Value-1);    //E=0,RS=1
  AddToBuffer(AC_Direction);  //Output
//TX
  SendBytes(OutIndex); // send
  OutIndex := 0;
end;

参考までに、FTDI社を参考にしたSendBytes()とOutIndexのコードも記載しておきます。

procedure TFrmLCD.SendBytes(NumberOfBytes : integer);
var
  i : integer;
begin
  i := Write_USB_Device_Buffer( NumberOfBytes);
  OutIndex := OutIndex - i;
end;

procedure TFrmLCD.AddToBuffer(I:integer);
begin
  FT_Out_Buffer[OutIndex]:= I AND $FF;
  inc(OutIndex);
end;

4.おわりに

20桁4行の80文字を描画させた時、TimeGetTime関数での計測結果はだいたい10msecでした。I2C+4bitパラレルで32文字を描画した時は420msec程度でしたので飛躍的に向上しています。
今更こういうキャラクタータイプのLCDを使う製品はないかもしれませんが、使うのであれば8bitパラレルで、となりますね。
(と言うより、PCF8574のI2C部分で時間がかかっているのでしょうね)

実行結果

なお、今回使用したLCDモジュールは表示数が多いため、Dutyは1/16で使用することになります。表示させた写真を掲載していますが、これはあえて電源電圧を3.8Vくらいまで上げてコントラストを改善した写真であり、実際のコントラストは非常に悪いです。商品化にはLCDの改善や電源電圧の見直しなどが必要です。


いいなと思ったら応援しよう!