『昼夜逆転』工作室
[トップページへ戻る]

LCD応用例 カレンダー付き時計~外字登録を活用しよう~

2009年7月
マイコン工作でLチカと並ぶポピュラーなテーマ、LCDモジュールの駆動。 キャラクタLCDを操作するライブラリはいくつも公開されており、サンプルコードも豊富なので何かを表示させること自体はすぐにできるでしょう。
これを使ってセンサーの値を表示するもよし。デバッグ出力に使うもよし。ここでは応用例としてカレンダー付き時計を紹介します。 回路図とプログラムのソースを公開しています。
そして意外と知られていない(?)「外字登録」…ユーザー定義文字の設定方法を説明します。時計作りよりもこちらが主題です。

※2012/03 記事を改訂。
※2019/04 記事を改訂。プログラムを更新(「令和」に対応)。

キャラクタLCDモジュール SD1602 について

本工作では秋月電子通商(以下、秋月)で販売されている「SD1602HUOB(-XA-G-R)」を使いました。16文字×2行、バックライト付き小型LCDモジュールです。 キャラクタLCDモジュールには、SUNLIKE社のSC1602AXiamen Ocular Optics社のGDM1602K など様々な製品がありますが、 どれもHD44780互換コントローラなので同じ制御プログラムが使えます。
HD44780のデータシートを検索

これらのLCDモジュールは制御線3本の他に、8bitまたは4bitのデータ線でコマンドやデータを送受信します。
以下、この記事では4bitモードで作業したことを書いています。
データ線も8本中4本のみ配線した状態です。マイコンはATtiny2313(内蔵発振器1MHz動作)を使用しています。

キャラクタLCDモジュールの動かし方

制御ライブラリを用意する
キャラクタLCD用のライブラリは「lcdlib.c」「lcd_lib.c」などで検索するといくつも見つかります。 「これがスタンダードだ」というものはなく、勉強がてら自作するのも一興です。

ライブラリを自作するならタイミングに注意
データシートを確認し、信号送出のタイミング(マイクロ秒、ミリ秒単位の時間)さえ守れば制御プログラムは動きます。この点が重要です。
回数を決めて空ループで時間待ちをするプログラムだと、マイコンの動作クロックによって待ち時間が変わってしまいます。 そのようなプログラムではマイコンが1MHz動作のときは表示できても20MHzでは表示できない、ということも起こります。 このため、AVRなら _delay_us(), _delay_ms()を使うのがよいと思います。

そもそも、コントローラから通知されるBusyFlagを監視するのが真っ当な制御方法です。 まぁそうなのですが、その都度マイコンのI/Oを制御するのが面倒だし、 LCDモジュールからの読み込み処理を無くせばR/Wレジスタの制御を省略できるので(GND接続してWrite固定)、 BusyFlagを使わない方法(一定時間待つ方法)で済ませることが多いようです。

初期化の注意点
初期化シーケンスはLCDモジュールのデータシートに書いてあります。 その中で、待ち時間が「100 s」とあったら「100us」の誤記です(PDF化のときに'μ'が消えたのでしょう)。
初期化シーケンスではデータ線のDB7~DB4に0011を3回送出しています。 これは、LCD内部状態が現在8bitモードか4bitモードか不定であるとして、配線が4bit線か8bit線かによらず一旦8bitモードにしていることになります。 LCDモジュールを4bitモードで使用する場合は、ここでさらに0010を送出して4bitモードへ移行させます。
その後、表示動作を設定します。

さて、秋月でLCDモジュールを購入すると独自編集のデータシートが付いてきます。 その設定内容に従って文字表示をテストすると… SD1602には何も表示されません。実は説明が間違っています。

送出するコマンド(秋月のデータシートの表記)
0b00001000 (Display OFF) …非表示状態になる。

0b00000001 (Display ON) …これは表示ONではなく画面消去のコマンド。

0b000001[I/D]S (Entry Mode Set) …文字入力動作の設定。([I/D]はこれで1bit)

Display ONの表記とは裏腹に画面を消去するコマンドを送出しています。 ここで自動的に非表示状態が解除されるわけではありません。 続けて、文字入力の動作(1文字入力ごとに次の入力位置へ移動するか、など)を設定しています。 ここでも非表示状態は解除されません。従って、画面には何も表示されません。

非表示状態を解除することを初期化に含め、次のように変更しました。
※HD44780のデータシートでもこの手順となっています。

送出するコマンド(改良版)
0b00001000 (Display OFF)

0b00000001 (Clear Display) …画面消去。

0b000001[I/D]S (Entry Mode Set)

0b00001100 (Display ON) …画面表示ONのコマンド。

ユーザー定義文字を設定する

キャラクタLCDモジュールにはユーザーが定義した文字を格納することができます。 RAMなので電源を切ったら定義したデータも消えてしまいますが、プログラム実行中は書き換え自由です。 一度に持てる文字数は8個です。これを活用してLCD工作の応用範囲を広げましょう!

ユーザー定義文字の文字コード
コード表
文字コード0x00~0x07に外字が登録できます。図の赤枠()の範囲です。
そこに外字を登録すると0x08~0x0F(図の灰色枠())にも反映されますが、 データの読み書きともこちらの文字コードを使用する必要はありません。

16文字登録できない理由は後述するSet CGRAM Addressコマンドの説明を参照してください。惜しいですが仕方ありません。

※図はデータシートより抜粋。


ユーザー定義文字の登録コマンドの組み立て方
コマンド RSR/WDB7DB6DB5DB4DB3DB2DB1DB0 説明
Set CGRAM Address 0001AC5AC4AC3AC2AC1AC0 アドレスカウンタに
CGRAMアドレスをセットする
RS: Register Select bit, R/W: Read/Write bit, DB: Data Bit, AC: Address Counter bit

Set CGRAM Addressコマンドは、DB7に0、DB6に1、DB5~DB0にデータを書き込むCGRAMアドレスをセットします。
文字の大きさは5x8ドットなので、1文字あたり5bit(を1byteで表現) * 8ライン = 8byte必要です。
8byte分のアドレスを表すのに3bit必要で、それをアドレスカウンタAC2~AC0にセットします。 そして残り3bit(AC5~AC3)で文字を区別します。  ※それで文字コード0x00~0x07(3bit)で8文字登録できる(16文字は登録できない)というわけです。

Entry Mode SetコマンドでI/Dビットに1(Increment)を設定した場合、外字登録の具体的な操作は次のようになります。
まず、アドレスカウンタの上位3bit(AC5~AC3)に文字コード0x00~0x07をセットし、下位3bit(AC2~AC0)は0にします。 この段階でこの文字のデータ(ドットパターン)を格納する8byte領域の先頭アドレスを指していることになります。
続けて、8byte(8ライン分)のデータを書き込みます(Write Data to RAM)。 このときIncrementの作用により、1byte格納するごとにアドレスカウンタが自動的にインクリメントされます。

【例】 文字コード0x02(CGRAM(3)の位置)に「月」の字形を登録してみる
ライン ドットパターン 2進表記 AC5AC4AC3AC2AC1AC0
0□■■■■0b00001111 0 1 0 0 0 0
1□■□□■0b00001001 0 1 0 0 0 1
2□■■■■0b00001111 0 1 0 0 1 0
3□■□□■0b00001001 0 1 0 0 1 1
4□■■■■0b00001111 0 1 0 1 0 0
5□■□□■0b00001001 0 1 0 1 0 1
6■□□□■0b00010001 0 1 0 1 1 0
7□□□□□0b00000000 0 1 0 1 1 1
文字コード 0x02 ライン 0~7

「月」の字形
下記のように、Set CGRAM Addressコマンドに続けてデータ書き込みを8回実行します。
これで、lcdPutChar(0x02) などとするとLCD画面に「月」が表示されます。
コマンド/動作 RSR/WDB7DB6DB5DB4DB3DB2DB1DB0 説明
Set CGRAM Address 0001 0 1 0 0 0 0 文字コード0x02の、ライン0のアドレス
Write Data to RAM 10 0 0 0 0 1 1 1 1 ライン0(1ライン目)のデータ
Write Data to RAM 10 0 0 0 0 1 0 0 1 ライン1(2ライン目)のデータ
Write Data to RAM 10 0 0 0 0 0 0 0 0 ライン7(8ライン目)のデータ
一方、Entry Mode SetコマンドでI/Dビットに0(Decrement)を設定した場合、1ラインごとにアドレスを指定する必要があります。 すなわち、1ラインごとにSet CGRAM Addressコマンドとデータ書き込み(Write Data to RAM)をセットで8回実行します。
この方法は自動でインクリメントされる場合に比べ、Set CGRAM Addressコマンド7回分の処理が増えます。 しかしEntry Mode SetコマンドのI/Dビットの値に依存しない方法なので、実装はこちらの方が無難です。
具体的な処理内容は次のようになります。
コマンド/動作 RSR/WDB7DB6DB5DB4DB3DB2DB1DB0 説明
Set CGRAM Address 0001 0 1 0 0 0 0 文字コード0x02の、ライン0のアドレス
Write Data to RAM 10 0 0 0 0 1 1 1 1 ライン0(1ライン目)のデータ
Set CGRAM Address 0001 0 1 0 0 0 1 ライン1のアドレス
Write Data to RAM 10 0 0 0 0 1 0 0 1 ライン1(2ライン目)のデータ
Set CGRAM Address 0001 0 1 0 1 1 1 ライン7のアドレス
Write Data to RAM 10 0 0 0 0 0 0 0 0 ライン7(8ライン目)のデータ
文字とカーソルの位置関係
LCDモジュールのアンダーバー形のカーソルは文字の8ライン目の位置に表示されます。 文字とカーソルが重なっても、カーソルが移動すれば文字の隠れていた部分は自動的に現れます。 文字とカーソル(アンダーバー)が重ならないようにするには、字形(ドットパターン)を5x7の範囲で作るしかありません。 上記「月」の例はそのようになっています。

LCD応用例 カレンダー付き時計

マイコンはATtiny2313(内蔵発振器1MHz動作)。正確な時間は刻めませんが作例としては十分です。 カレンダーは西暦/元号の切り替え対応。時計は24時間制/12時間制の切り替え対応。組み合わせで4通りの表示方法を選べます。

回路図
回路図

動作の様子 動作の様子
※写真は「年」の字形が微妙に間違っていますが、
 プログラムは修正してあります(冷汗)

LCDモジュールは4bitモードで使用。R/WがATtiny2313のPB5に接続されていますが、プログラムでは使用していません。
VR1は文字のコントラストを決めるボリュームで、10kないし20kΩです(*)
LCDモジュールのVoをGND電位にすると、文字のコントラストが最大(濃い)、VCC電位にすると最小(薄い)になります。
R1はバックライト(LED)の電流制限抵抗です。20~100Ωで決めます。このLCDモジュールでは上限40mAです。
(*) 10k~20kΩの範囲で変化させるという意味ではなく、10kΩや20kΩの半固定抵抗を使うという意味です。

より正確な時計にするには
AVRの内蔵発振器は時計向きの精度ではありません。正確な時間を刻むには水晶発振器を使った方がよいです。
1MHzの発振器であればプログラムの変更は不要です。ヒューズビットを外部発振器の設定にしてください。
その他の周波数の場合はlcdlib.h内のF_CPUの値と、LCDClock.cの16ビットタイマーの設定を修正してください。 スイッチ入力のチャタリング回避カ所の値も適当に書き換える必要があります。

プログラム
ダウンロード LCDClockR_v130.zip(プロジェクト一式)
開発環境(リビルド環境): Windows10, AtmelStudio7

本プログラムでは下記のように外字を登録しています。
文字コード0x03には曜日を表す文字が入ります。月~日のフォントを用意し、プログラム内で適宜書き換えています。
【2019/04】 0x04, 0x05を「平」「成」から「令」「和」に変更しました。
文字コード字形説明
0x00
0x01
0x02
0x03(曜日)月,火,水,木,金,土,日
0x04平→令に変更
0x05成→和に変更
0x06A
M
5x8の領域で縦にAとM
0x07P
M
5x8の領域で縦にPとM

動作の様子
【2019/04】 写真追加。
ライブラリを自作してみた感想
ライブラリは最小限の関数にするよう心掛けました。 その代わりコマンドを実行する関数を外部から呼べるようにしたので、プログラム本体側で関数を追加できます。 本プログラムでは、桁数固定の整数を表示する関数(0詰め、空白詰め)や、 プログラム領域上のフォントデータを読み込んで外字登録する関数、カーソル表示の有無を切り替えるマクロなどを追加しました。

ライブラリを自作するにあたり、他の人が作ったものを2つ見比べました。 同じデバイス、同じデータシートを基にしているため、今回自作したものも含めどれもほとんど同じ内容になります。 それでも制御線のon/offのやり方(タイミング)には違いが見られ、「1MHzでは動いても20MHzなら動かないかも?」と思うものもありました。 今回自作したライブラリは、ATtiny2313内蔵発振器1MHz、同8MHz、セラロック20MHzで正常に動作しています。
※上記リンクよりダウンロードできるlcdlib.cは、この記事の初公開時点でのバージョンです。その後の更新版には差し替えていません。

カレンダー付き時計の使い方

電源投入で下記のように表示されます。時計は動いています。この状態が「表示モード」です。

2000年 1月 1日(月)
 0:00:00

日時の調整方法
表示モードの状態でスイッチswL(PD0)を押すと日時の「設定モード」に入ります。右下に「Adj.」が表示されます。
設定モード中はスイッチswL(PD0)で項目を移動し、スイッチswR(PD1)で値を変更します。
設定中の項目にカーソル(アンダーバー)が表示されます。
年は西暦、時間は24時間制として調整します。

表示モード(時計動作中)
↓スイッチswLで設定モードへ移行
↓「年」の十の位を設定 0~9
↓「年」の一の位を設定 0~9
↓「月」を設定 1~12
↓「日」を設定 1~31
↓「曜日」を設定 月~日
↓「時」を設定 0~23
↓「分」を設定 00~59
↓「秒」を設定 00にリセット
表示モードへ復帰(「Adj.」が消える)
調整
調整中の様子。「年」の十の位にカーソルが表示されている。

年/時間制の変更方法
表示モードの状態でスイッチswR(PD1)を押すと、年、時間制の表現を変更できます。

↓西暦/24時間制
↓元号/24時間制
↓西暦/12時間制
↓元号/12時間制
始めに戻る

部品について

LCDモジュールはお好みで選んで構いません。端子の並び順に注意して配線してください。
【2019/04】 価格は2009年当時のものです。

LCD応用例 カレンダー付き時計 部品一覧  (回路図はここをクリック
部品名部品番号個数参考価格/備考
AVR(マイコン) U ATtiny2313 1 100円(秋月
LCDモジュール
キャラクタ液晶
LCD SD1602HUOB
(-XA-G-R)
1 900円(秋月)
バックライト:オレンジ
抵抗 R1 20~100Ω 1 上記LCDには100Ωが数個、
オマケで入っています
半固定抵抗 VR1 10kΩ 1 30円
積層セラミックコンデンサ C1 0.1uF [104] 1 10個100円
タクトスイッチ SW1,SW2 -- 2 10個180円(千石電商 店頭価格)

◆ ◆ ◆

LCDモジュールは外字登録のやり方が分かると面白くなってきます。 デバッグ出力に使うだけではもったいないですね。レベルメーターのバー表示もやってみましたが、 同じネタばかりになるのでカレンダー付き時計を作りました。
ここでは紹介しませんが、電光掲示板のように文字列がサーッと流れて行くようにもできます。 それはソフトで意識せずともハードでやってくれます。便利。 光り物好きとしてはバックライトや液晶の色違いをいくつか試してみたくもなります。綺麗。


 
(C) 『昼夜逆転』工作室
[トップページへ戻る]