ArduinoでLCDを扱う

Arduino
Arduino+LCD1602A実験

最近はキャラクタ表示式小型LCDLiquid Crystal Display液晶ディスプレイ)が安価に入手可能です。ここまでマイコンボードの出力はシリアル接続した PC で表示していましたが、LCD を使用できれば PC がなくても様々な情報を表示することができます。今回は定番の LCD コントローラ IC、HD44780 を使った LCD 制御を実験します。

LCDドライバIC・HD44780

概要

HD44780 は、キャラクタ LCD の制御を行う定番のICです。マイコン入門者が IC 単体を使用することはあまりないと思いますが、秋葉原のパーツショップなどで販売されているキャラクタ LCD モジュールには、高い確率で HD44780 (または互換チップ)が搭載されています。

その歴史は日進月歩どころか秒進分歩といわれる電子・コンピュータ業界のなかでは恐ろしいほど古く、1980年代に日立製作所によって開発されました。以来40年を経て、今では LCD コントローラ IC のデファクトスタンダードとなっています。よって、HD44780 の使用法を理解できれば、マイコン用小型 LCD モジュールの多くの機種を使用することができます。

特徴

HD44780には、

  • ドットマトリクス式 LCD のコントローラ
  • 4bit または 8bit パラレル接続データバスを持つ
  • コマンド送信による制御なので、接続する信号線はデータバス+2~3本と比較的少なくて済む
  • 80文字分の表示データバッファを内蔵
  • フォント内蔵(5×8ドット)なので、文字コードを送るだけで表示可能
  • 8文字分のユーザによるフォント定義が可能

といった特徴があります。これを読めば容易に判るように、HD44780 は文字情報の表示に特化しており、画像の表示はできません(フォント定義機能を駆使すればごく簡単な図形なら描画できるかもしれませんが…)

表示パネルについては、チップ単体の基本形では 8文字× 2行なのですが、拡張ドライバなどを組み合わせることで 16文字× 1行、16文字× 2行、20文字× 2行、20文字× 4行、40文字× 2行といった様々なサイズが実現可能です。

ハードウェア仕様

入出力端子

多くの場合、HD44780を使用したLCDモジュールには以下のような外部入出力端子があります。

VDDモジュール自体の電源を供給。電源+(5V)に接続
VSSモジュール自体の電源、および信号グラウンド。GND に接続
V0液晶モジュールの輝度・コントラスト調節
RS=Register Select。データバスにセットされているのがデータかコマンドかの区別。
HIGHのときデータ、LOWのときコマンド。
RW=Read/Write。読取か書込かの区別。
HIGHなら読取、LOWなら書込。読取の必要が無ければプルダウンでLOW固定にしてもOK
E=Enable。HD44780は、データバスにセットされたデータをEの立ち下がりで読み込む
DB0~DB3=Data Bit
8bitモードではデータ下位4ビット。
4bitモードでは使用しない。使用しない場合は開放推奨。
DB4~DB78bitモードではデータ上位4ビット。
4bitモードでは8bitデータを上位4bit・下位4bitにわけてDB4~DB7に2回書込む。

電源および信号レベルは 5V で、Arduino UNOと直結できます。

バックライトLEDつきのモジュールでは以下の端子もあります。

AバックライトLEDのアノード。適切な電流制限抵抗を通してLED用電源+に接続
KバックライトLEDのカソード。LED用電源-に接続

信号タイミング

各信号線のタイミングは下図のようになっています。

このうち、注目すべきはE端子とDB0~DB7端子の間にある3つのタイミングです。

TPW:Enable Pulse Width。E信号をHIGHに保つ時間。最小140ns
TDSW:Data Setup Time。データをDB0~DB7にセットしてから読込までの時間。最小40ns
TH:Data Hold Time。E信号の立ち下がりからDB0~DB7を保つべき時間。最小40ns

上記の時間が短すぎると、データの取りこぼしが発生するようです。

ソフトウェア仕様

命令セット

以下に、各種コマンドを挙げます。コマンドの実行時間は、表示クリアとカーソルホームを除いて 40μs です。

表示クリア
RSRWDB7DB6DB5DB4DB3DB2DB1DB0
0000000001

全表示をクリア。DD RAM に空白文字 0x20 を書き込み、カーソルをホーム位置へ戻します。表示がシフトしていた場合は元の位置に戻ります。

※このファンクションの実行時間は 1.64ms です。

カーソルホーム
RSRWDB7DB6DB5DB4DB3DB2DB1DB0
000000001*

カーソルをホーム位置へ戻します。表示がシフトしていた場合は元の位置に戻ります。ただし DD RAMの内容は変化しません。

なお、DB0 の値は動作に影響を与えません。

※このファンクションの実行時間は 1.64ms です。

エントリーモードセット
RSRWDB7DB6DB5DB4DB3DB2DB1DB0
00000001I/DS

カーソルの進む方向(≒書字方向)および、表示をシフトさせるかどうかを設定します。

I/D(DB1):address Increment / Decrement
0 のときアドレスを -1 する(カーソルまたはブリンクが左に移動する)。
1 のときアドレスを +1 する(カーソルまたはブリンクが右に移動する)。
※日本語や英語は書字方向が右(横書きのとき、文字を左から右に向かって並べていく)なので、通常は 1 に設定するのがいいでしょう。

S(DB0):Shift
0 のとき表示はシフトしない
1 のとき表示はシフトする
シフトとは、書込時に表示が移動する機能です。

表示オン/オフ
RSRWDB7DB6DB5DB4DB3DB2DB1DB0
0000001DCB

表示のオン/オフ、カーソルのオン/オフ、文字のブリンクのオン/オフを設定します。

D(DB2):Display
0 のとき表示をオン
1 のとき表示をオフ
※表示オフは DD RAM のデータを削除しないので、 D=1 にすれば同じ内容が表示されます

C(DB1):Cursor
0 のときカーソルを表示しない
1 のときカーソルを表示する

B(DB0):Blink
0 のときブリンク(カーソル位置の文字を点滅)しない
1 のときブリンクする

カーソル/表示シフト
RSRWDB7DB6DB5DB4DB3DB2DB1DB0
000001S/CR/L**

カーソル移動と表示シフトを行います。このとき、DD RAM の内容は変化しません。

S/C(DB3):Shift/Cursor
R/L(DB2):Right/Left

S/CR/L動作
00カーソル位置を左にシフト
01カーソル位置を右にシフト
10表示全体を左にシフト(カーソルは表示について移動)
11表示全体を右にシフト(カーソルは表示について移動)

DB1 および DB0 の値は動作に影響を与えません。

ファンクションセット
RSRWDB7DB6DB5DB4DB3DB2DB1DB0
00001DLNF**

DL(DB4):interface Data Length
0 のとき 4bit モード( DB4 ~ DB7 端子を使用、DB0 ~ DB3 は使用しない)に設定
1 のとき 8bit モード( DB0 ~ DB7 端子を使用)に設定

N(DB3):Number of lines
0のとき1行
1のとき2行、このとき F の値に関係なく文字フォントは 5×7 ドットマトリクスとなる

F(DB2):Fontsize
0 のとき文字フォントを 5×7 ドットマトリクスに設定する
1 のとき文字フォントを 5×10 ドットマトリクスに設定する

DB1 および DB0 の値は動作に影響を与えません。

CG RAMアドレスセット
RSRWDB7DB6DB5DB4DB3DB2DB1DB0
0001d5d4d3d2d1d0

CG(Charactor Generator)RAM へ読み書きするアドレスをセットします。このコマンドを受信すると、それ以後にLCD コントローラが送受信するデータは CG RAM のデータとなります。

d5~d0(DB5 ~ DB0):アドレス
この 6bit が CG RAM のアドレスを表します。CG RAM は、8文字分のフォントデータ(5×7ドット)を登録できる領域です。1文字分の領域は8バイトです。8バイト×8文字分で64バイト、ちょうど 6bit で表現できる容量です。フォントデータとして使用しない領域は一般データ領域として使用できます。

DD RAMアドレスセット
RSRWDB7DB6DB5DB4DB3DB2DB1DB0
001d6d5d4d3d2d1d0

DD(Display Data)RAM へ読み書きするアドレスをセットします。このコマンドを受信すると、それ以後にLCD コントローラが送受信するデータは DD RAM のデータとなります。

d6 ~ d0(DB6 ~ DB0):アドレス
この 7bit が DD RAM のアドレスを表します。DD RAM は 8bit×80文字分の領域を持っています。実メモリはアドレス空間で連続しておらず、0x00 ~ 0x27(=10進数の39)、0x40 ~ 0x67(10進数の64~73)に存在します。この全領域は 7bit のアドレス指定可能です。

1行目の先頭アドレスは 0x00 です。
2行のLCDを使用している場合(ファンクションセットで N=1 とした場合)、1行の文字数とは無関係に2行目の先頭アドレスは 0x40(=10進数の64)となります。つまり、1行目と2行目のデータが連続した領域に格納されません。

1234567891011383940
1行目0x000x010x020x030x040x050x060x070x080x090x0A0x250x260x27
2行目0x400x410x420x430x440x450x460x470x480x490x4A0x650x660x67
行・桁とDD RAMアドレス

2行表示LCDの場合、1行分のメモリは常に40バイトで、表示桁数が40文字に満たない場合はその分だけ各行の後方の領域が未使用となります。たとえば1行20文字表示のLCDならば、
アドレス 0x14~0x27(十進数で20~39)および0x54~0x67(十進数で84~103)の領域は、データの読み書きはできますが表示はされない未使用領域となります。
アドレス 0x28~0x3F(十進数で40~63)および0x68~0x7F(十進数で104~127)の領域は、アドレスの指定はできますが正常に読み書きできません。

なお、未使用領域は一般データ領域として表示以外の用途に使用することができます(もちろんデータがはみ出して表示がおかしくならないように管理するのはプログラマの責任ですが)。

初期設定

初期設定の手順は以下の通りです。

インターフェイスデータ長4bitの場合

※初期化処理中に送信するデータはすべてコマンド(RS=0)です。
※また、ビジー監視をしない場合は常に RW=0 です。

1. 電源オン

電源投入時に一定の条件を満たしている場合は自動的に初期設定が行われますが、通常は以下のインストラクションにより初期設定を行います。

2. VDD が 4.5V に達してから 45ms 以上待つ

LCD の VDD が 4.5V に達するタイミングを計測することは専用の回路を用意しないと難しいでしょう。スクリプトは VDD が4.5V に達する前に開始されていると思われるので、45ms より長めのウェイトを入れるのが適切だと思います。

3. 4bitデータ 0B0011 を送信
DB7DB6DB5DB4
0011

コマンドとして 4bitデータ 0B0011(16進数では 0x3)を送信します。本来はデータ長8bit を設定するファンクションセットコマンドですが、4bit接続では上位4bitのみを送信します。

4. 4.1ms以上待つ

データシートでは、ここで 4.1ms 以上のウェイトを入れることが指定されています。

5. 4bitデータ 0B0011 を送信
DB7DB6DB5DB4
0011

再度 0B0011(16進数では 0x3)を送信します。

6. 100μs以上待つ

データシートでは、ここで 100μs 以上のウェイトを入れることが指定されています。

7. 4bitデータ 0B0011 を送信
DB7DB6DB5DB4
0011

また同じ 0B0011(=0x3)を送信します。ここでいったんデータ長が8bitと設定されます。

8. データ長を 4bit に設定するファンクションセットコマンドを送信

次のコマンドを送信します。

DB7DB6DB5DB4DB3DB2DB1DB0
001DLNF**

ファンクションセットコマンドです。
ここではデータ長4bitの例なので、DL=0 となります。

データ例:
2行LCDの場合は N=1、このとき F の値はなんでもいいので、与えるデータは 0B0010 1000(=0x28) となります。データバスが 4bit なので、2回にわけて 0B0010(=0x2)と 0B1000(=0x8)を送信します。

9. 表示オフする

次のコマンドを送信します。

DB7DB6DB5DB4DB3DB2DB1DB0
00001000

表示オン/オフコントロールコマンドです。
DB2(画面表示)=0、DB1(カーソル)=0、DB0(ブリンク)=0 を設定します。

データ例:
与えるデータは 0B0000 1000(=0x08)で固定です。
データバスが 4bit なので、2回にわけて 0B0000(=0x0)と 0B1000(=0x8)を送信します。

10. 表示クリアする

次のコマンドを送信します

DB7DB6DB5DB4DB3DB2DB1DB0
00000001

表示クリアコマンドです。DD RAMが空白文字(0x20)で埋められます。

データ例:
与えるデータは 0B0000 0001(=0x01)で固定です。
データバスが 4bit なので、2回にわけて 0B0000(=0x0)と 0B0001(=0x1)を送信します。

11. 1.64ms以上待つ

データシートのフローチャートには明記されていませんが、表示クリアコマンドの実行には、他のコマンドよりずっと長い時間がかかります(最長1.64ms)。ビジーフラグの監視をしない場合は 1.64ms 以上のウェイトを入れる必要があります。

12. エントリーモードセット

以下のコマンドを送信します。

DB7DB6DB5DB4DB3DB2DB1DB0
000001I/DS

エントリーモードセットコマンドです。

データ例:
『書字方向右、シフトをしない』と設定する場合、
I/D(DB1)= 1
S(DB0)= 0
となり、送信データは 0B0000 0110(=0x06)となります。
データバス4bitなので、2回にわけて 0B0000(=0x0)と 0B0110(=0x6)を送信します。

13. 表示オンする

以下のコマンドを送信します。

DB7DB6DB5DB4DB3DB2DB1DB0
000011CB

表示オン/オフコントロールコマンドです。
DB2(画面表示)=1、DB1(カーソル)および DB0(ブリンク)は任意です。

データ例:
『カーソル表示なし、ブリンクなし』の場合、
C(DB1)=0
B(DB0)=0
となり、送信データは 0B0000 1100(=0x0C)となります。
データバスが 4bit なので、2回にわけて 0B0000(=0x0)と 0B1110(=0xC)を送信します。

以上で初期設定は終了です。

文字表示

DD RAM アドレスセット

文字の表示位置を指定するには、DD RAM アドレスセットコマンドを使用します。2行LCD の場合、1行の文字数にかかわらず2行目のアドレスは 0x40(=10進数で64)から開始されます。よって、row(=0~1 )行目col(=0~39)文字目にカーソルを移動するには、アドレスカウンタに row×0x40 + col をセットします。

DD RAM アドレスをセットするコマンドは最上位bitが1なので、送信データは以下のようになります。

RSRWDB7DB6DB5DB4DB3DB2DB1DB0
001d6d5d4d3d2d1d0

つまり、row=0~1、col=0~39 として、コマンドモード(RS=0)で

0x80 | (( row*0x40 + col ) & 0x7F)

を送信します。

文字データ送信

文字の表示は、アドレスカウンタに DD RAM のアドレスをセットした後、RS=HIGH にして 1文字分(8bit)ずつ文字コードを送信するだけです。1文字送信すると、自動的に DD RAM のアドレスカウンタがインクリメント(設定によってはデクリメント)されるので、アドレスカウンタを再設定しなくても連続して文字のコードを送れば次の文字は設定した書字方向に従って次の位置に表示されます。

よって、文字列を送信するには、

  1. アドレスカウンタに DD RAM のアドレス(文字列描画開始点)をセット
  2. RS=HIGHにする
  3. 文字列の先頭から、1文字ずつデータを送信
  4. 文字数分 3. を繰り返す

とするだけです。

ただし、1行の文字数が40文字未満のLCDで行末に達してしまった場合、そのまま2行目との間のメモリの未使用領域にデータを書き込んでしまい、画面には表示されません。プログラマの責任で未使用領域にはデータを書き込まないように(またはそもそも文字列の長さが行末を超えないように)する必要があります。

製作&実験

使用部品

部品名・品番数量備考
LCDモジュール LCD1602A1HD44780のキャラクタ液晶ディスプレイモジュール
ブレッドボード1小型のものでOK
可変抵抗器(5kΩ)1半固定抵抗でも可、抵抗値が違っていても可
抵抗器(220Ω)1バックライトLED用電流制限抵抗
ジャンパ線16赤×4、黒×5、オレンジ×3、黄×4

LCDモジュール 1602A

今回使用したのは、HD44780互換チップを搭載したキャラクタLCDモジュール 1602A です。液晶画面サイズは16文字×2行で、型番の 1602 はこの画面サイズを表しているのでしょう。バックライトLED付きです。

上側にピンが来る、この写真の向きが正しい向きです。

裏側を観ると、端子がICピッチのピンになっており、ブレッドボードに直接挿すことができます。

ピン配置は以下の通りです。液晶の付いている側からみて、ピンを上側においた状態(基板に挿して使用している状態で上から見た状態)での並び順です。

12345678910111213141516
VSSVDDV0RSRWEDB0DB1DB2DB3DB4DB5DB6DB7AK

別の品番でもこれと同じ配置になっている製品が多いようですが、データシートでよく確認して下さい。

データバスは 8bit のパラレル接続用(前出の設定で4bit接続可)です。ほぼ同じ機能・似たような型番でシリアル接続式のものもあるようなので、購入の際には注意して下さい。

可変抵抗器

LCDのコントラスト調節用です。半固定抵抗でもOKです。ブレッドボードに直接挿せるタイプのものが使いやすいと思います。

抵抗値はぴったり5kΩである必要はなく、だいたいこの程度のオーダーならなんでもいいと思います。

抵抗器

バックライトLEDのための電流制限抵抗です。白色LED1本が使われているように見えるのですが、データシートに VF や IF が書かれていないため、抵抗値は適当です。白色高輝度 LED は VF=3V で標準 IF=20mA くらいのものが多いようなので 100Ω程度の抵抗の方がいいかもしれませんが、正解が不明なので安全を取りました。

回路図

Arduino と 1602A の接続にはデジタル出力端子6つを使用しています。
Arduino の D2端子を 1602A のRS端子に、
Arduino の D3端子を 1602A のEN端子に、
Arduino の D4~D7 端子を それぞれ 1602A のDB4~DB7 端子に接続します。
今回はメモリの読み出しは一切行わないこととし、1062AのRW端子はプルダウンします。

1602A の電源は Arduino の 5V 端子から供給します。

1602A の V0 端子は LCD画面のコントラスト調整端子です。5V~GND間に5kΩの可変抵抗器を挿入して分圧した電位をV0と接続します。

1602A の A 端子およびK 端子はバックライト LED のアノードとカソードです。A端子は電流制限抵抗を経由して5Vに、K端子はGNDに接続します。

配線図

配線はこの図の通りです。LCDのピン配置が違うものもあるようなので(電源だけ逆、というものもあるらしいです…)、回路図とも照らし合わせてよく確認して下さい。

プログラム

キャラクタLCDの制御には LiquidCrystal.h というライブラリがあるのですが、今回は制御プログラムの勉強のためライブラリは使用せずにすべての処理を書き起こします。

C++
int rsPin = 2, enPin = 3;
int dataPin[4] = {4, 5, 6, 7};

// 4bitデータを送信
void send4bits(int value){
  digitalWrite(enPin, HIGH);    // あらかじめ EN を HIGH にしておく
  // 4bitをデータバスにセット
  for(int i=0; i<4; i++){
    digitalWrite(dataPin[i], (value >> i) & 0x01);
  }
  delayMicroseconds(4);
  digitalWrite(enPin, LOW);     // EN の立ち下がりでデータが取り込まれる
  delayMicroseconds(4);
}

// LCDの初期設定
void lcdInit(){
  digitalWrite(rsPin, LOW);     // あらかじめ RS を LOW にセットしておく
  delay(50);                    // 仕様だと『VCCが4.5Vに達してから40ms待つ』だがVCCの監視ができないので余裕を見て50ms待つ
  send4bits(0x3);               // 0b0011を出力
  delayMicroseconds(4100);      // 4.1ms待つ
  send4bits(0x3);               // 0b0011を出力
  delayMicroseconds(100);       // 100μs待つ
  send4bits(0x3);               // 0b0011を出力
  delayMicroseconds(40);        // 40μs待つ
  send4bits(0x2);               // 4bit出力モードに切り替え
  delayMicroseconds(40);        // 40μs待つ

  send4bits(0x2);               // ファンクションセット、4bitモード、2行LCD
  send4bits(0x8);
  delayMicroseconds(40);        // 40μs待つ
  send4bits(0x0);               // 表示オフコマンド、カーソルもブリンクもオフ
  send4bits(0x8);
  delayMicroseconds(40);        // 40μs待つ
  send4bits(0x0);               // 表示クリアコマンド
  send4bits(0x1);
  delayMicroseconds(1640);      // 表示クリアには1.64msほど時間がかかる
  send4bits(0x0);               // エントリーモードセット、AC++、シフトなし
  send4bits(0x6);
  delayMicroseconds(40);        // 40μs待つ
  send4bits(0x0);               // 表示オンコマンド
  send4bits(0xc);
  delayMicroseconds(40);        // 40μs待つ
}

void setup() {
  // LCDに接続されている端子をすべて出力に設定
  pinMode(rsPin, OUTPUT);
  pinMode(enPin, OUTPUT);
  for(int i=0; i<4; i++){
    pinMode(dataPin[i], OUTPUT);
  }
  // LCDに初期化コマンドを送信する
  lcdInit();
  // 文字列表示テスト
  send4bits(0x8);               // アドレスカウンタをDD RAM 先頭にセット
  send4bits(0x0);
  digitalWrite(rsPin, HIGH);    // ここからはデータ送信なので rs=HIGH
  String text="Hello,World!";
  for(int i=0;i<text.length();i++){
    char ch=text.charAt(i);
    send4bits(ch>>4);           // 1文字をさらに上位4bitと下位4bitにわけて送る
    send4bits(ch&0xf);
  }
}

void loop() {

}

プログラムの解説

今回は LiquidCrystal.h を使わずに、とにかく文字表示まで漕ぎ着けようとしたので、ちょっとやっつけ仕事気味なプログラムです。2行LCD/データバスは4bit/カーソルやブリンクは非表示、という設定専用という、汎用性の低いプログラムになっています。

定数設定
C++
int rsPin = 2, enPin = 3;
int dataPin[4] = {4, 5, 6, 7};

グローバル変数として、LCDに接続する出力端子番号を定義しています。
rsPin は LCDのRS端子、enPin は E端子、dataPin[0][3] は DB4 ~ DB7 に対応しています。

データ送信関数
C++
// 4bitデータを送信
void send4bits(int value){
  digitalWrite(enPin, HIGH);    // あらかじめ EN を HIGH にしておく
  // 4bitをデータバスにセット
  for(int i=0; i<4; i++){
    digitalWrite(dataPin[i], (value >> i) & 0x01);
  }
  delayMicroseconds(4);
  digitalWrite(enPin, LOW);     // EN の立ち下がりでデータが取り込まれる
  delayMicroseconds(4);
}

まず、4bit のデータを LCD に送信する関数 send4bits() を定義しています。タイミングチャートの通り、

E端子をHIGHにする

DB4端子 ~ DB7端子にデータをセットする

E端子をLOWにする

という手順です。LCD ドライバは E端子の立ち下がりでデータを取り込みます。

E端子を LOW にセットする前後に delayMicroseconds(4) でウェイトを入れています。タイミングチャートによると、E端子を HIGH に保つ時間 TPW は最小 140ns、データバスにデータをセットしてからEをLOWにするまでの時間 TDSW は最小40ns、E端子を LOW にした後にデータバスの信号を保つ時間 TH は最小 10ns で、Arduino の処理速度だとわざわざウェイトを入れなくても行けるかと思ったのですが、ウェイトを入れないとデータの取りこぼしが発生するようです。4μsは各タイミングの最小値より桁違いに大きいのですが、これより短い時間指定だと正常に動かないことがあります(LCDではなくdelayMicroseconds()の問題かもしれません)。

LCDモジュール初期設定
C++
// LCDの初期設定
void lcdInit(){
  digitalWrite(rsPin, LOW);     // あらかじめ RS を LOW にセットしておく
  delay(50);                    // 仕様だと『VCCが4.5Vに達してから40ms待つ』だがVCCの監視ができないので余裕を見て50ms待つ
  send4bits(0x3);               // 0b0011を出力
  delayMicroseconds(4100);      // 4.1ms待つ
  send4bits(0x3);               // 0b0011を出力
  delayMicroseconds(100);       // 100μs待つ
  send4bits(0x3);               // 0b0011を出力
  delayMicroseconds(40);        // 40μs待つ
  send4bits(0x2);               // 4bit出力モードに切り替え
  delayMicroseconds(40);        // 40μs待つ

  send4bits(0x2);               // ファンクションセット、4bitモード、2行LCD
  send4bits(0x8);
  delayMicroseconds(40);        // 40μs待つ
  send4bits(0x0);               // 表示オフコマンド、カーソルもブリンクもオフ
  send4bits(0x8);
  delayMicroseconds(40);        // 40μs待つ
  send4bits(0x0);               // 表示クリアコマンド
  send4bits(0x1);
  delayMicroseconds(1640);      // 表示クリアには1.64msほど時間がかかる
  send4bits(0x0);               // エントリーモードセット、AC++、シフトなし
  send4bits(0x6);
  delayMicroseconds(40);        // 40μs待つ
  send4bits(0x0);               // 表示オンコマンド
  send4bits(0xc);
  delayMicroseconds(40);        // 40μs待つ
}

LCDモジュールの初期設定を行う lcdInit() 関数を定義しています。

HD44780 の初期設定の項で解説したのとまったく同じ手順でデータを送信しています。4bit 接続なので 8bit コマンドを送信する際は上位 4bit と下位 4bit にわけて送信する必要があります。

どの段階でどのコマンドを送信しているかはコメントを見れば判ると思います。LCDドライバでは各コマンドを実行するのに 40μs かかる(表示クリアコマンドのみ 1.64ms = 1640μs かかる)ので、コマンドを送信するたびに delayMicroseconds() 関数でウェイトを入れています。

Arduinoの初期化
C++
void setup() {
  // LCDに接続されている端子をすべて出力に設定
  pinMode(rsPin, OUTPUT);
  pinMode(enPin, OUTPUT);
  for(int i=0; i<4; i++){
    pinMode(dataPin[i], OUTPUT);
  }

setup() 関数の最初の部分です。まずLCDと接続している各端子を pinMode() 関数で出力に設定します。

LCDの初期設定
C++
  // LCDに初期化コマンドを送信する
  lcdInit();

上で定義した lcdInit() 関数を呼び、LCDモジュールの初期設定を行います。

テスト表示
C++
  // 文字列表示テスト
  send4bits(0x8);               // アドレスカウンタをDD RAM 先頭にセット
  send4bits(0x0);
  digitalWrite(rsPin, HIGH);    // ここからはデータ送信なので rs=HIGH
  String text="Hello,World!";
  for(int i=0;i<text.length();i++){
    char ch=text.charAt(i);
    send4bits(ch>>4);           // 1文字をさらに上位4bitと下位4bitにわけて送る
    send4bits(ch&0xf);
  }
}

ここからはテスト表示のための処理です。

まず、

C++
  send4bits(0x8);               // アドレスカウンタをDD RAM 先頭にセット
  send4bits(0x0);

で、コマンド 0x80 を送信することによりアドレスカウンタ(≒カーソル位置)を DD RAM の先頭(画面左上隅に対応)にセットしています(命令セットの項を参照して下さい)。

これ以降はコマンドではなく表示データを送信するので、

C++
  digitalWrite(rsPin, HIGH);    // ここからはデータ送信なので rs=HIGH

により RS をHIGH にセットしています。

※RS(=Register Select)は、LOWのときデータバスの信号がコマンド、HIGHのときデータバスの信号がDD RAMまたはCG RAMに書き込むデータであることを表す

C++
  String text="Hello,World!";
  for(int i=0;i<text.length();i++){
    char ch=text.charAt(i);
    send4bits(ch>>4);           // 1文字をさらに上位4bitと下位4bitにわけて送る
    send4bits(ch&0xf);
  }
}

“Hello,World!” という文字列を用意し、for文によって先頭から順に1文字ずつ取得して変数chに代入し、上位4bit・下位4bitに分けて send4bits() 関数でLCDに送信しています。

※やはり初めて表示するメッセージは Hello,Worldでなくては!

setup()関数はこれでおしまいです。

loop()関数
C++
void loop() {

}

今回は setup() だけでやりたいことがすべて終わっているので、loop() 関数の中身は空です。

実行

プログラムを実行すると、このように液晶画面に『Hello,World!』と表示されます。もし画面が真っ黒だったり真っ白だったりした場合は、VRでコントラストを調節してみて下さい。

まとめ

  • LCDコントローラHD44780は、小型キャラクタLCDに広く使用されている。
  • 簡単なコマンド制御でLCDを制御できる

コメント

タイトルとURLをコピーしました