LCD応用例 カレンダー付き時計〜外字登録を活用しよう〜
2009年7月 ※2012/03 記事を改訂 マイコン工作でLEDチカチカと並ぶポピュラーな工作テーマ、LCDの駆動。 キャラクタ液晶を操作するライブラリはいくつも公開されており、サンプルコードも豊富なので、何かを表示させること自体はすぐにできるでしょう。 これをどう使うか。センサーのデータを表示するもよし。デバッグ出力に使うもよし。 ここではLCD応用例としてカレンダー付き時計を紹介します。 回路図とプログラムのソースを公開しています。 そして意外と知られていない(?)「外字登録」…ユーザー定義文字の設定の仕方を説明します。時計作りよりもこちらが主題です。 |
LCDモジュール SD1602 キャラクタ液晶 | ||||||
秋月電子で販売されている「SD1602HUOB(-XA-G-R)」を使いました。16文字×2行、バックライト付き小型LCDモジュールです。フレームの有無、バックライトの有無/色の違いで、微妙に型番が違う製品もあります。どれも内部で使われているコントローラがHD44780コンパチブルとのことなので、同じ制御プログラムが使えます。 SC1602シリーズのデータシートはSUNLIKE社のサイトにあります。
※このLCDモジュールについてさらに詳しく知りたければHD44780(コントローラ)のデータシートを参照してください。 このLCDモジュールは制御線3本の他に、8bitまたは4bitのデータ線でコマンドやデータを送受信します。 8bitモード/4bitモード、どちらでもコマンド自体は8bitです。
制御ライブラリの用意について このLCDで文字を表示するためのライブラリは、例えば「lcdlib.c」「lcd_lib.c」で検索するとたくさん見つかります。 「これがスタンダードだ」というものは特にありません。勉強のつもりで自分が使いやすいものを自作するのも一興。 ライブラリを自作するならタイミング制御に注意 データシートには信号送信のタイミングチャートが書かれています。このタイミング(マイクロ秒、ミリ秒単位の時間)さえ守れば制御プログラムは動きます。この点が重要です。 空ループで時間待ちをするプログラムだと、マイコンの動作クロックによって時間が変わり、タイミングチャートの動作が守れず、LCDが制御できなくなります。待ち時間を考慮しないプログラムだと、マイコンが1MHz動作では表示できても20MHzでは表示できない(制御できていない)、ということも起こり得ます。 カツカツにプログラムサイズを切り詰めたり動作速度を追求するのでなければ _delay_us(), _delay_ms()を使うのがよいと思います。 そもそも、LCDから通知されるBusyFlagを監視するのが真っ当な制御方法です。まぁそうなのですが、その都度マイコンのI/O制御を切り替えるのが面倒だしコードサイズも増えるし、LCDからの読み込み処理を無くせばR/Wレジスタの配線を省略できるので(GND接続でWrite固定)、BusyFlagを使わない方法、すなわち決め打ちで時間待ちする方法で済ませることが多いようです。 初期化の注意点 LCDの制御開始時に初期化処理が必要です。シーケンスはデータシートに書いてあります。 その中で、3回目の待ち時間「100 s」は「100us」の誤記です。HD44780のデータシートには明記されていました。 初期化シーケンスではLCDのDB7-DB4に0011を3回送出しています。 LCD内部状態が現在8bitモードか4bitモードか不定であるとして、配線が4bit線か8bit線かによらず一旦8bitモードにしています。4bitモードで使用する場合はここで0010を送出して4bitモードへ移行させます。 その後、表示動作を設定しています。 秋月電子でLCDモジュールを購入すると独自編集のデータシートが付いてきます。その説明通りに表示動作を設定し、文字表示のテストをしてみたところ、SD1602には何も表示されませんでした。実は説明が間違っているのです。
Display ONの説明とは裏腹に画面を消去するコマンドを送出しています。ここで自動的に非表示状態が解除されるわけではありません。続けて、文字入力の動作(1文字入力ごとに次の入力位置へ移動するか、など)を設定しています。ここでも非表示状態は解除されません。従って、画面には何も表示されません。 非表示状態を解除する(「表示状態」に設定する)ことも初期化に含めることと、非表示動作自体はやっておきたいのだろうなということを踏まえて、次のように変更しました。※HD44780(コントローラ)のデータシートでもこの手順となっています。
|
ユーザー定義文字を設定する | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
このLCDにはユーザーが定義した文字を格納することができます。RAMなので電源を切ったら定義データも消えてしまいますが、プログラム実行中は書き換え自由です。一度に持てる文字数は8個です。これを活用してLCD工作の応用範囲を広げましょう! ユーザー定義文字の文字コード (図はデータシートより抜粋) 文字コード0x00〜0x07に外字が登録できます。図の赤枠(□)の範囲です。 文字コード0x00〜0x07に外字を登録すると、図の灰色枠(□)の文字コード0x08〜0x0Fの対応箇所にも反映されますが、データの読み書きともこちらの文字コードを使用する必要はありません。 16文字登録できない理由はSet CGRAM Addressコマンドの組み立てを考えていて気付きました。惜しいですが仕方ありません。 コマンドの組み立て方
Set CGRAM AddressコマンドはDB6に1が立っています。それより下位の6ビットDB5〜DB0にデータを書き込むCGRAMアドレスをセットします。 文字の大きさは5x8ドットです。1文字あたり、5bit(を1byteで表現) * 8ライン = 8byte必要です。 つまり8byte分のアドレスを指すのに3bit必要で、これをアドレスカウンタAC2〜AC0にセットします。すると残り3bit(AC5〜AC3)で文字を区別することになります。それで文字コード0x00〜0x07(3bit)として8文字が登録できる(それ以上登録できない)、ということになるようです。 初期化シーケンスのEntry Mode SetでI/Dビットに1(Increment)を設定し、それが変更されていないことを前提として、外字登録の具体的な操作は次のようになります。 まず、アドレスカウンタの上位3bit(AC5〜AC3)に文字コード0x00〜0x07をセットし、下位3bit(AC2〜AC0)は0にします。これでこの文字のデータを格納する8byte領域の先頭アドレスを指していることになります。 その後、Write Data to RAMコマンド8回で8byte(8ライン分)のデータを送出します。 Incrementの作用で1byte格納するごとにアドレスカウンタは自動的にインクリメントされます。 【例】文字コード0x02(CGRAM(3)の位置)に「月」の字形を登録してみる
Set CGRAM Addressコマンドに続けてWrite Data to RAMコマンドを8回実行します。 これで、lcdPutChar(0x02); などとするとLCD画面に「月」が表示されます。 初期化シーケンスのEntry Mode SetでI/Dビットに0(Decrement)を設定した、またはプログラム中で0に変更していた場合は、1ラインごとにアドレスを指定する必要があります。
1ラインごとにSet CGRAM AddressコマンドとWrite Data to RAMコマンドをセットにして、8回実行します。 これで、lcdPutChar(0x02); などとするとLCD画面に「月」が表示されます。 Set CGRAM Addressコマンド7回分の手数が増えますが、Entry Mode SetのI/Dビットの設定に依存しないので、こちらの方法が無難だと思います。 なお、LCDのカーソル(アンダーバー)は8ライン目に表示されます。文字の最下部とカーソルが重ならないようにするには、字形データを5x7の範囲に収めます。8ライン目に字形データがあっても、カーソルが移動すれば8ライン目は自動的に現れます。 |
LCD応用例 カレンダー付き時計 | ||||||||||||||||||||||||||||||||
回路図とプログラム 制御はATtiny2313(内蔵発振器 1MHz)。正確な時間は刻めませんが作例としては十分です。カレンダーは西暦/元号の切り替え対応。時計は24時間制/12時間制の切り替え対応。組み合わせで4通りの表示方法を選べます。 ※写真では「年」が微妙に間違ってますがソースは修正してあります(冷汗)
ハードについて LCDは4bitモードで使用。R/WがATtiny2313のPB5につながっていますが、プログラムでは使用していません。 VR1はLCDのコントラストを決めるボリュームで、10kないし20kΩです[*]。LCDのVo端子がGND電位で最高コントラスト(濃い)、電源電位で最低コントラスト(薄い)になります。 R1はLCDのバックライト(LED)の電流制限抵抗です。20〜100Ωで決めます(上限40mA)。 [*]…10k〜20kΩの範囲で変化させるという意味ではなく、10kΩや20kΩの半固定抵抗を使うという意味です。 ソフトについて このプログラムでは下表の外字を登録、使用しています。 文字コード0x03には曜日を示す文字が入ります。月〜日のフォントを用意し、プログラム内で適宜書き換えています。
日時の調整の仕方 電源投入で次のように表示されます。この状態が「表示モード」で、時計が動いています。
スイッチswL(PD0)で日時の「設定モード」に入ります。目印として「Adj.」と表示されます。 設定モード中はスイッチswL(PD0)で項目の移動、スイッチswR(PD1)で値を変更します。 設定中の項目にカーソル(アンダーバー)が表示されます。 年は西暦、時間は24時間制として調整します。 年/時間制の変更の仕方 表示モードの状態(通常通り時計が動作している状態)でスイッチswR(PD1)を押すと、年、時間制の表示を変更できます。
より正確な時計にするには AVRの内蔵発振器は正確な周波数ではありません。時計としてより正確な時間を刻むには水晶発振器をつなぎ、それに合わせてヒューズビットを書き換えてください。 1MHzの発振器であればプログラムの変更は不要です。その他の周波数の場合はlcdlib.hのF_CPUの値と、LCDClock.cの16ビットタイマーの設定を修正してください。スイッチ入力のチャタリング回避箇所の値も適当に書き換えてください。 |
自作lcdlib.cについて |
LCD画面の表示に関して必要最低限の関数に絞りました。その代わり、コマンドを実行する関数を外部から呼べるようにしたので、プログラム本体側で必要に応じて機能(関数)を追加、拡張できます。 今回の時計プログラムの例では、桁数固定の整数を表示する関数(0詰め、空白詰め)や、プログラム領域上のフォントデータを読み込んで外字登録する関数がそれです。カーソル表示の有無を切り替えるマクロもそうです。 ライブラリを自作するにあたり、すでに公開されているライブラリを2個見比べました。同じデバイス、同じデータシートを基にしているため、今回自作した物も含めどれもほとんど同じ内容になります。それでも制御線のon/offのやり方(タイミング)には違いが見て取れ、「1MHzでは動いても20MHzなら動かないかも?」と思うものもありました。 今回自作したものについては、ATtiny2313内蔵発振器1MHz、同8MHz、セラロック20MHzで正常に表示できています。 自分用に作ったライブラリなので、他の人が使いやすいかどうかは分かりません。参考になるようでしたら好きに使ってください。ちなみに私はアンダースコア区切りの関数名、変数名に馴染めないので、キャメルケースで命名しています。 ※この記事で公開しているlcdlib.c/.hは、この記事公開時点での最新版です。その後、新規プロジェクトで利用する度に改良されています。 |
部品名 | 部品番号 | 値 | 個数 | 参考価格/備考 |
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) 『昼夜逆転』工作室 | [トップページへ戻る] |