LCDサンプル

回路図

まずは配線に悩む。
4bitモードを使うか、8bitモードを使うかだ。
やはり、ポートを節約するためには、4bitだろうか。

あとは、busyフラグをチェックするかどうか。
busyかどうかをチェックせずに、ウェイトでコマンド完了を待つ方式だと、R/WをGNDに落としてWrite固定でいいらしいけど、ここは正攻法でbusyフラグはチェックするようにしたいな。

となると、必要なピンは、RS、R/W、E、DB4〜DB7で、全部で7ピンか。
回路図はこんな感じで。
見にくいかも……あってるよな?

LCDサンプル

4bitモードでは、DB0〜DB3は使用しないので、オープンにしておく。
このあたりは、このページを参考に。
Eはプルダウンするべきらしい。
ちなみに、プルダウンしなかったら、起動時に行頭に一つスペースが入ったりしたけど、なんでだろう?
PD5が不定でも確実にLowであるべきということかな?
他はプルアップ抵抗が内蔵されているから問題ないとかいうのもぴんとこない。
もっとデジタル回路を勉強しないとダメか……。

Voは液晶の濃度調整を分圧でやってて、Vcc5Vの1/(10+1)の、約0.45Vがかかる。
これでちょうどいい濃度っぽい。
とりあえず配線はこれでOK。
動いた後の動作写真も載せておく。

LCDサンプル

ソースコード

定義とウェイト関数

まずは、includeに各種定数定義と、LEDでも使った待ち関数。
前回のLEDを取り外して、ポートDのみで全てが済むように配線したので、定義もポートDのみです。
そして、ポートDにおけるLCD接続先のビット定義と、データ部分を一気にマスクするための定義。

#include <avr/io.h>
#include <util/delay.h>

#define LCD_PORT	PORTD	// LCD表示に使用する出力レジスタ
#define LCD_PIN		PIND	// LCD表示に使用する入力レジスタ
#define LCD_DDR		DDRD	// LCD表示に使用する方向レジスタ

#define LCD_D4	(1<<0)	// D4ビット位置
#define LCD_D5	(1<<1)	// D5ビット位置
#define LCD_D6	(1<<2)	// D6ビット位置
#define LCD_D7	(1<<3)	// D7ビット位置
#define LCD_E	(1<<5)	// Eビット位置
#define LCD_RW	(1<<6)	// R/Wビット位置
#define LCD_RS	(1<<7)	// RSビット位置
#define LCD_DATMASK	0x0F	// データビットマスク

void delay_ms( int time )
{
	// 指定ms分ループ
	while( time-- ){
		_delay_ms( 1 );
	}
}

基本動作

で、まずはLCDを初期化しなければならないわけだ。
色々なサイトを巡ってみても、初期化とBusyフラグチェックの2箇所に苦労してるっぽい。
ご多分に漏れず、結構な難関でした。

まず基本的な動作として、RS(Register Select)はコマンドモード(0)/データモード(1)の切り替えに使用し、R/Wはライトモード(0)/リードモード(1)の切り替えに。
D4〜D7は、ライトモードのコマンドモードではコマンドパラメータ、データモードでは文字コードやユーザ定義文字のパターンをセットする。
リードモードでのコマンドモードでは、Busyフラグとアドレスカウンタ(データを読み書きするアドレス)の取得、データモードではデータ読み出しが出来る。
アドレスの設定は、ライトモードのコマンドモードのコマンドパラメータで指定できる。
これら各種命令の一覧は、以下。

コマンド

これらコマンド通りに、出力/入力ポートの設定をして、その状態でE(Enable Signal)をLow→High→Lowとすることにより、コマンドが実行される。
この時、LCDの仕様によって、まずEをLowからHighにする前にRSとR/Wを設定してから数ns(ナノ秒)(セットアップ時間)以上待ち、Highの維持を数ns(イネーブルパルス幅)以上行い、HighからLowにしてからRSとR/W、データの変更を行わない時間(ホールド時間)を数ns以上待つ必要があるらしい。
あと、前のEの立ち上がりから、次のEの立ち上がりまでも、一定以上の時間(イネーブルサイクル)以上置く必要がある。
この時間は、データシートに定義されているんだけど、正直、今回買ったやつに関しては、全く当てにできない気がする。
なぜなら、上の図と下の表の、シンボルがほとんど一致してないんだもの……。
読み方を間違っているのかしら……?

タイミング

この表では、RSとR/Wのセットアップ時間が0nsとなっているため、基本的に待つ必要はないらしい。
本当か……?
そして、EをHighにしてからLowにするまで、140ns(イネーブルパルス幅)の維持。
実際にはEのRise時間、Fall時間の各最大25nsが含まれるので、190nsの維持か。
ライトモードでのデータのセットアップに40nsとあるが、これはこのイネーブルパルス幅に含まれているのでOK。
EをLowにしてから、RSとR/W、データのホールド時間が10ns。
EをHighにしてから、次のEをHighにするタイミングまでのイネーブルサイクルは、1200ns(1.2μs)以上。

で、これをプログラムで実際にどれくらい意識する必要があるのかということだが。
msどころの話じゃなく、μsでもなく、さらにその下のnsというのは、普段意識しないレベルなので、なかなかわかりにくい。

AVRは、最速(レジスタ間計算等)で1MHz/1MIPSであり、この場合、1クロックサイクルで1命令が実行される。
今回は、ATmega168Pを、1MHz駆動させている。(8MHzの内蔵RCで、CKDIV8ヒューズにより、8分周)
ということは、1秒間に実行できる命令は最大1000000個であり、1命令にかかる最短の時間は、1秒の100万分の1で、1μsとなる。
この程度であれば、EをHighにして次の命令ですぐにLowにしたとしても、その間は1μsなので、190ns以上のイネーブルパルス幅を確保しており、さらに次の命令を実行しようとした時には、最初のHighから2μs経過しているので、イネーブルサイクルも仕様を満たしており、ウェイトを置いたりする必要はないと思う。
(実際問題なかった)

では、8MHz駆動の場合は、どれくらい待てばいいのか。
1命令は1MHzの時の8倍の速度になるので、1μs/8で125ns。
イネーブルパルス幅を確保するのに2命令、イネーブルサイクルを満たすためには、EをHighにして2命令待ちEをLowにする4命令で500nsを消費するので、残りは700nsで6命令は必要なはず。
なんだけど、これくらいの指定だと、全然まともに動いてくれない。
何が悪いんだ……。
イネーブルパルス幅に20命令、イネーブルサイクルを満たすために、もう20〜40命令分入れてやって、ようやく安定するレベル。
1MHzの時より待ち時間多いじゃん……。
とりあえずは安定している1MHz駆動で進めていきます……。
そのうち謎が解ければ書く。

ちなみにこれは、コマンド発行のタイミングの話であって、実際にコマンドが処理される時間の話ではないはず。
コマンド実行時間は、上記コマンド表の右側に書いてあり、Busyフラグとアクセスカウンタの読み出し以外には、それなりに時間がかかるようだ。
とはいえ、ライトモードはなんとなくわかるけど、リードモードの処理時間がよくわからない。
色々なサイトを巡ってみても、あまりリードに関して触れているところがない。

ライトモードであれば、EのHigh→Low→Highの間のデータをLCDがその瞬間に読み取って、実際にそれを処理する間Busyとなるというイメージなんだけど、あってるんだろうか?
だとすると、リードモードはEのHigh→Low→Highの間にデータが読めないとダメなわけだから、Busyの読み出しは0μsだからいいとして(?)、Read Data From RAMの43μsは、どこにかかるんだろう?
なんかまだ勘違いしているところがあるのかもしれない。

初期化シーケンス

Lcd_initという初期化関数を作成し、この中でLCDの初期化シーケンスを実行。
この初期化シーケンスは、LCDのデータシートに書いてある。
今回買った、SD1602HULB-XAの初期化シーケンスは、以下のように書かれている。

初期化シーケンス

今回は4bitモードなので、下の方。
画像が汚くて読みにくいか。

まず、PowerONして起動してから、15ms以上待つ。
次にコマンド表にあるファンクションセットを使用し、DB4のビットを立て、8bitモードに設定して、このDB4〜DB7のコマンドを、4.1ms以上、100μs以上の間をあけて、連続で3回送る。
印刷ではμが抜けて、100μsが100sになってしまっているという罠。
100秒も待ってらんねーYO!
この4bitのコマンドを3回送ることで、元が何のモードだろうと、4bitモードで8bitコマンドの後半の4bitを送る前という中途半端な状態であろうと、8bitモードに初期化されることになる。

確実に8bitモードになったところで、ここでまたファンクションセットを使用し、4bitモードの設定を1回だけ行う。

どこのページにも書いてあることだけど、ここまではBusyフラグは使えない。
そしてほとんどのページに、ここからBusyフラグが使えるとか書いてあるんだけど、少なくともこのLCD(SD1602HULB-XA)では、この後に行うDisplay ON/OFF、Clear Displayと、Entry Modeの設定では、Busyフラグは使えないようだ。
(以下のデータシートの記述より)
>BF cannot be checked after the following instructions.
>When BF is not checked, the waiting time between instructions is longer than the execution time.
なので、初期化シーケンスの一連の処理では、各コマンド送信の間に、時間決め打ちのウェイトを指定してやるのがよさそう。

あとは、これらのコマンドを4bit単位で送ってやるので、4bitコマンド送信関数を作って、引数にコマンドを書いてやることにしよう。
以下、初期化部分のソースコード。
4bitコマンド送信関数の中身はこの後。

void Lcd_init( void )
{
	// 最初は全てのポートを出力に設定
	LCD_DDR	|= ( LCD_E | LCD_RW | LCD_RS | LCD_DATMASK );
	// 信号もLowにしておく
	LCD_PORT &= ~( LCD_E | LCD_RW | LCD_RS | LCD_DATMASK );

	// 最初はBusyチェックが効かないので、時間待ちする
	delay_ms( 15 );		// 15ms待ち
	lcd_setcmd4( 0x3 );	// 8bitモード設定
	delay_ms( 5 );		// 4.1ms以上待ちなので、5ms待ち
	lcd_setcmd4( 0x3 );	// 8bitモード設定
	delay_ms( 1 );		// 100μs以上待ちなので、とりあえず1ms待っておく
	lcd_setcmd4( 0x3 );	// 8bitモード設定
	delay_ms( 1 );		// ファンクションセットは39μs以上待ちなので、とりあえず1ms待っておく

	// 初期化シーケンスに書いてある通り、4bitの設定は4bitのみ渡せれば有効なので、1回のみ
	lcd_setcmd4( 0x2 );	// 4bitモード設定
	delay_ms( 1 );		// ファンクションセットは39μs以上待ちなので、とりあえず1ms待っておく

	// ここからは4bitモード動作なので、8bitの命令を4bitずつ2回に分けて送ってやる
	lcd_setcmd4( 0x2 );	// ファンクションセット、4bitモード
	lcd_setcmd4( 0x8 );	// 2Line表示、5*8ドットフォント
	delay_ms( 1 );		// ファンクションセットは39μs以上待ちなので、とりあえず1ms待っておく
	lcd_setcmd4( 0x0 );	// Display ON/OFF設定
	lcd_setcmd4( 0xF );	// Display ON / Cursor ON / Blinking ON
	delay_ms( 1 );		// Display ON/OFF設定は39μs以上待ちなので、とりあえず1ms待っておく
	lcd_setcmd4( 0x0 );	// Clear Displayコマンド
	lcd_setcmd4( 0x1 );	// スペースでDDRAMが埋められ、アドレスカウンタがDDRAMの00H(先頭)に移動
	delay_ms( 2 );		// Clear Displayコマンドは1.53ms以上待ちなので、2ms待ち
	lcd_setcmd4( 0x0 );	// Entry Mode
	lcd_setcmd4( 0x6 );	// インクリメントモード / ディスプレイシフトなし
	delay_ms( 1 );		// Entry Modeコマンドは39μs以上待ちなので、とりあえず1ms待っておく
}

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS