1. LCD1602ディスプレイの原理
1.ピン機能
内部の機能ブロック図を次の図に示します。
一般に、LCD1602には16ピンがあります。さまざまなメーカーのLCD1602は若干異なる場合がありますが、基本的には同じです。16ピンの機能は次のとおりです。
ピン番号 | ピン名 | 電圧レベル | 特徴 |
1 | VSS | 0V | パワーグラウンド |
2 | VDD | + 5V | 正の電力 |
3 | V0 | 0V | 電圧バイアス |
4 | RS | H / L | コマンド/データ |
5 | R / W | H / L | 読み書き |
6 | E | H / L | 有効にする |
7〜14 | DB0〜DB7 | H / L | データポート |
15 | 鉛 | + 5V | 正のバックライト |
16 | LEDK | 0V | バックライトネガ |
この表の説明:
1. VSSを電源グランドに接続します。
2. VDDを+ 5Vに接続します。
3. VOは、液晶ディスプレイのバイアス信号で、10K 3296精密ポテンショメーターに接続できます。または同じ抵抗のRM065 / RM063青と白の調整可能な抵抗器。下記参照。
4. RSはコマンド/データ選択ピンで、マイクロコントローラのI / Oに接続されており、RSがローの場合はコマンドが選択され、RSがハイの場合はデータが選択されます。
5. RWは読み取り/書き込み選択ピンであり、マイクロコントローラのI / Oに接続されており、RWがローの場合、コマンドまたはデータをLCD1602に書き込み、RWがハイの場合、ステータスまたはデータをLCD1602から読み取ります。読み取り操作を実行する必要がない場合は、VSSに直接接続できます。
6.コマンドを実行するためのイネーブルピンであるEは、マイクロコントローラーのI / Oに接続されています。
7. D0〜D7、パラレルデータ入力/出力ピン。マイクロコントローラーのP0〜P3の任意の8つのI / Oポートに接続できます。ポートP0に接続する場合、ポートP0は4.7K-10Kプルアップ抵抗に接続する必要があります。4線パラレルドライブの場合、必要なI / Oポートは4つだけです。
8.バックライトアノード。10〜47オームの電流制限抵抗をVDDに接続できます。
9. Kバックライトの負極、VSSに接続します。下の図を参照してください。
2.基本的な操作
LCD1602の基本的な操作は4つのタイプに分けられます:
1.ステータスの読み取り:入力RS = 0、RW = 1、E =高パルス。出力:D0〜D7はステータスワードです。
2.データの読み取り:入力RS = 1、RW = 1、E =高パルス。出力:D0〜D7はデータです。
3.書き込みコマンド:入力RS = 0、RW = 0、E =高パルス。出力:なし。
4.データの書き込み:入力RS = 1、RW = 0、E =高パルス。出力:なし。
動作タイミング図と制約を読む:
書き込み操作のタイミング図:
タイミング時間パラメーター:
3. DDRAM、CGROMおよびCGRAM
DDRAM(表示データRAM)は、表示する文字コードを登録するための表示データRAMです。合計80バイト、アドレスと画面の対応関係は次のとおりです。
DDRAMはコンピュータのビデオメモリに相当し、画面に文字を表示するために、文字コードをビデオメモリに送り、画面に文字を表示できるようにします。同様に、LCD1602には合計80バイトのビデオメモリ、つまりDDRAMがあります。ただし、LCD1602の表示画面は16×2しかありませんので、DDRAMに書き込んだ文字コードをすべて画面に表示できるわけではなく、上図の範囲に書かれた文字のみ表示でき、範囲外に書かれています。文字は表示できません。このように、プログラムで次の「カーソルまたは表示移動コマンド」を使用して、表示されている表示範囲にキャラクターをゆっくりと移動し、キャラクターの移動効果を確認できます。
前述のように、LCD画面に文字を表示するために、文字コードがDDRAMに送信されます。たとえば、画面の左上隅に文字「A」を表示する場合は、DDRAMの00Hアドレスに文字「A」の文字コード41Hを書き込みます。書き方については、後ほど説明があります。では、なぜこのコードの文字を対応する位置に表示できるように、文字コードをDDRAMに書き込むのでしょうか。LCD1602は一種の文字ドットマトリックスディスプレイであることはわかっています。文字のフォントを表示するには、この文字のフォントデータが必要です。文字のフォントデータとは何ですか?下の図を見て理解してください。
上図左側が文字「A」のフォントデータ、右側が左側の「○」が0、「■」が1のデータです。したがって、文字「A」が表示されます。下の図からわかるように、文字「A」の上位4ビットは0100で、下位4ビットは0001です。合わせて01000001b、つまり41Hです。これはたまたま文字のASCIIコードと一致しているため、非常に便利ですPCでは構文P2 = 'A'を使用できます。コンパイル後、たまたまこのキャラクターのキャラクターコードになります。
フォントメモリは、LCDROM2モジュール、つまりCGROMとCGRAMに統合されています。HD44780には、192の一般的に使用される文字のフォントが内蔵されており、それらは文字ジェネレータCGROM(Character Generator ROM)に格納され、8つのユーザー定義文字生成RAMがあります。 、CGRAM(Character Generator RAM)と呼ばれます。次の図(図12)は、CGROMおよびCGRAMと文字の対応を示しています。ROMとRAMの名前から、ROMはLCD1602モジュールですでに固化されており、読み取りのみが可能で、RAMは読み取りと書き込みが可能であることもわかります。つまり、CGROMにすでに存在する文字だけを画面に表示する必要がある場合は、その文字コードをDDRAMに書き込むだけでよく、CGROMにない文字(摂氏温度スケールのシンボルなど)を表示したい場合、それからCGRAMでのみ定義し、DDRAMにこのカスタムキャラクターの文字コードを書き込みます。CGROMの立体化された文字とは異なり、CGRAMには文字がありません。したがって、CGROMに存在しない文字をDDRAMに書き込むには、使用する前にCGRAMで定義する必要があります。プログラムが終了すると、CGRAMで定義された文字は存在しなくなり、次回使用するときに再定義する必要があります。
上の図は、5×8ドットマトリックス文字と5×10ドットマトリックス文字のフォントとカーソル位置を示しています。まず、5×8のドットマトリックスについて説明します。8行5列です。次に、そのような文字を定義するのに8バイトかかり、各バイトの最初の3ビットは使用されません。たとえば、摂氏温度スケールに記号{0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00}を定義します。
上の図は、CGRAMアドレスを設定するための命令を示しています。この命令の形式から、合計6ビットのaaaaaaを持っていることがわかります。これは、合計64アドレス、つまり64バイトを表すことができます。5×8のドットマトリックス文字は合計8バイトを占めるため、合計8文字をこれらの64バイト用にカスタマイズできます。つまり、上図の6ビットアドレスのDB5DB4DB3は8つのカスタム文字を表すために使用され、DB2DB1DB0は各文字の8バイトを表すために使用されます。DB5DB4DB3で表される8つのカスタム文字(0〜7)は、DDRAMに書き込まれる文字コードです。CGRAMで定義できるカスタム文字は8つだけです。つまり、0から7までの文字コードは8つしかありませんが、次の表には合計16文字のコードがあります(××××0000b--×× ××1111b)。実際、図に示されているように、8つのカスタム文字しか表現できません(××××0000b =××××1000b、××××0001b =××××1001b ...など)。つまり、DDRAMに書き込まれる文字コード0と文字コード8は同じカスタム文字です。5×10ドットマトリックスの各文字は合計16バイトのスペースを占有するため、CGRAMで定義できるカスタム文字は4つだけです。
では、CGRAMのキャラクターをカスタマイズする方法は?上記の概要では、DDRAM書き込み命令と同様のCGRAMアドレス設定命令があり、特定のカスタム文字のフォントデータを設定してから、上記の方法に従ってCGRAMアドレスを設定し、これを順番に書き込むことがわかっています。フォントデータは大丈夫です。以下の例で説明します。
4. LCD1602の手順
(1)。作業モード設定手順
×:気にしない、つまり、このビットは0または1、通常は0にすることができます。
DL:データインターフェースのビット数を設定します。
DL = 1:8ビットデータインターフェイス(D7 — D0)。
DL = 0:4ビットデータインターフェイス(D7〜D4)。
N = 0:1行表示。
N = 1:2行表示。
F = 0:5×8ドットマトリックス文字。
F = 1:5×10ドットマトリックス文字。
注:これは書き込みコマンドワードであるため、RSとRWはどちらも0です。LCD1602は、シリアルモードではなく、パラレルモードでのみ駆動できます。また、パラレルモードでは、8ビットデータインターフェイスまたは4ビットデータインターフェイスを選択できます。ここでは、8ビットデータインターフェイス(D7〜D0)を選択します。私たちの設定は、8ビットデータインターフェイス、2行表示、5x8ドットマトリックス、つまり0x00111000、つまり0x38です。(注:NFが10または11であることの影響は同じで、どちらも2行5x8ドットマトリックスです。2行5x10ドットマトリックスでは表示できないため、つまり、ここでは0x38または0x3cを使用することは同じですの)。
(2)。スイッチ制御命令の表示
D = 1:表示オン、D = 0:表示オフ。
C = 1:カーソルを表示、C = 0:カーソルを表示しない。
B = 1:カーソルは点滅します。B= 0:カーソルは点滅しません。
注:ここでの設定は、ディスプレイがオン、カーソルが表示されない、カーソルが点滅しない、設定ワードが0x0cであるということです。
(3)。モード設定指示入力
I / D = 1:新しいデータを書き込んだ後、カーソルが右に移動します。
I / D = 0:新しいデータを書き込んだ後、カーソルは左に移動します。
S = 1:動きを表示します。
S = 0:ディスプレイは動かない。
注:ここでの設定は0x06です。
(4)。カーソルまたはディスプレイ移動命令
説明:このコマンドは、画面全体を移動する必要がある場合に非常に役立ち、画面のスクロール表示効果を実現できます。このコマンドは、初期化時には使用されません。
(5)。画面消去コマンド
説明:画面表示内容をクリアします。カーソルが画面の左上隅に戻ります。このコマンドの実行にはしばらく時間がかかります。
(6)。カーソルホーム命令
注:カーソルは画面の左上隅に戻ります。画面に表示される内容は変わりません。
(7)。CGRAMアドレス設定命令
説明:このコマンドは上記で導入されました。次の例で使用法を説明します。
(8)。DDRAMアドレス設定コマンド
説明:このコマンドは、DDRAMアドレスを設定するために使用されます。DDRAMの読み取りと書き込みを行う前に、最初にDDRAMアドレスを設定し、次に読み取りと書き込みを行います。前に述べたように、DDRAMはLCD1602のディスプレイメモリです。表示したい場合は、表示する文字をDDRAMに書き込む必要があります。同様に、DDRAMの特定のアドレスにある文字を知りたい場合は、最初にDDRAMアドレスを設定してから、それをマイクロコントローラーに読み込む必要があります。
(9)。ビジー信号とアドレスカウンターACを読み取る
説明:このコマンドは、LCD1602のステータスを読み取るために使用されます。マイクロコントローラーにとって、LCD1602は遅いデバイスです。マイクロコントローラが命令を送信すると、命令が実行されます。このとき、シングルチップマイコンが次の命令を再度送信すると、LCD1602が遅く、前の命令がまだ実行されていないため、新しい命令を受け付けず、新しい命令が失われます。したがって、この読み出しビジー命令を使用して、LCD1602がビジーであるかどうか、およびLCD1602がマイクロコントローラから命令を受信できるかどうかを判断できます。BF = 1の場合、LCD1602はビジーであり、マイクロコントローラーからの命令を受け入れることができないことを意味し、BF = 0の場合、LCD1602はアイドル状態であり、マイクロコントローラーからの命令を受信できることを意味します。RS = 0はコマンドであることを意味し、RW = 1は読み取りであることを意味します。この命令には別の副産物があります。つまり、アドレスカウンタACの値を取得できます。LCD1602は、次の読み取りおよび書き込みCGRAMまたはDDRAMの位置を記録するために、アドレスカウンターACを維持します。次の点を強調しておく必要があります。この命令は正常に実行されませんでした。多くのネチズンが同じことをしているようです。幸いなことに、私たちには別の方法があります。それは遅延することです。各命令の実行時間を見て、いくつかの実験を行った後、命令の遅延を特定できます。このようにして、前の命令が実行された後に次の命令を実行することができます。
(10)。CGRAMまたはDDRAM命令へのデータの書き込み
説明:RS = 1、データ、RW = 0、書き込み。命令を実行するときは、最初にDB7〜DB0に書き込むデータを設定してから、書き込みコマンドを実行します。
(11)。CGRAMまたはDDRAMからのデータコマンドの読み取り
説明:RS = 1、データ、RW = 1、読み取り。最初にCGRAMまたはDDRAMのアドレスを設定してから、読み取りコマンドを実行します。データはDB7-DB0に読み込まれます。
5. LCD1602の一般的な初期変更手順(FPGAで初期化する必要があります)
- 15ミリ秒の遅延
- 書き込み命令38H(ビジー信号を検出しない)
- 15ミリ秒の遅延
- 書き込み命令38H(ビジー信号を検出しない)
- 15ミリ秒の遅延
- 書き込み命令38H(ビジー信号を検出しない)
- 後続の書き込み命令ごとに、データの読み取り/書き込み操作でビジー信号を検出する必要があります
- 書き込み命令38H:表示モード設定
- 書き込みコマンド08H:表示オフ
- 書き込みコマンド01H:クリア画面を表示
- 書き込み命令06H:カーソル移動設定の表示
- 書き込みコマンド0CH:表示とカーソル設定
11と12の2つのシーケンスは交換できません。書き込み中に変更した場合、デバッグできません。通常、左奥の2つのシーケンスを入れ替えます。
2、FPGA実装
実現機能:LCDディスプレイに2行を表示し、最初の行はPan-Hong-Fengを表示し、2行目はLCD1602-Testを表示します。
コードは次のとおりです:(コードは少し面倒で、コメントするには面倒すぎる)
// Company :
// Engineer :
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534 PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date : 2020-09-07 15:48:40
// Revise Data : 2020-09-08 09:53:32
// File Name : lcd.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions : Vivado 2019.2
// Revision : V1.1
// Editor : sublime text3, tab size (4)
// Description : LCD1602 driver
module lcd(
input clk ,
input rst_n ,
output reg lcd_rs ,
output wire lcd_rw ,
output reg lcd_en ,
output reg [7:0] lcd_data
);
reg [17:0] cnt ;
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [4:0] char_cnt ;
reg [7:0] data_display ;
localparam
IDLE = 4'd0 ,
INIT = 4'd1 ,
S0 = 4'd2 ,
S1 = 4'd3 ,
S2 = 4'd4 ,
S3 = 4'd5 ,
ROW1_ADDR = 4'd6 ,
WRITE = 4'd7 ,
ROW2_ADDR = 4'd8 ,
stop = 4'd9 ;
assign lcd_rw = 1'b0;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 17'd0;
end
else begin
if (cnt==17'd100_000 - 1) begin
cnt <= 17'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
lcd_en <= 0;
end
else if (cnt==17'd50_000 - 1) begin
lcd_en <= 1;
end
else if (cnt==17'd100_000 - 1) begin
lcd_en <= 0;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
char_cnt <= 0;
end
else if (state_c==WRITE && cnt==17'd50_000 - 1) begin
if (char_cnt==5'd24) begin
char_cnt <= 5'd0;
end
else begin
char_cnt <= char_cnt + 1'b1;
end
end
end
always @(*) begin
case(char_cnt)
5'd0: data_display = "P";
5'd1: data_display = "a";
5'd2: data_display = "n";
5'd3: data_display = "-";
5'd4: data_display = "H";
5'd5: data_display = "o";
5'd6: data_display = "n";
5'd7: data_display = "g";
5'd8: data_display = "-";
5'd9: data_display = "F";
5'd10: data_display = "e";
5'd11: data_display = "n";
5'd12: data_display = "g";
5'd13: data_display = "L";
5'd14: data_display = "C";
5'd15: data_display = "D";
5'd16: data_display = "1";
5'd17: data_display = "6";
5'd18: data_display = "0";
5'd19: data_display = "2";
5'd20: data_display = "-";
5'd21: data_display = "T";
5'd22: data_display = "e";
5'd23: data_display = "s";
5'd24: data_display = "t";
default:data_display = "P";
endcase
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_c <= IDLE;
end
else if(cnt==17'd50_000 - 1) begin
state_c <= state_n;
end
end
reg [19:0] cnt_15ms;
reg flag ;
always@(posedge clk or negedge rst_n)begin
if (!rst_n) begin
cnt_15ms <= 0;
end
else if (state_c == IDLE) begin
cnt_15ms <= cnt_15ms + 1'b1;
end
end
always@(posedge clk or negedge rst_n)begin
if (!rst_n) begin
flag <= 0;
end
else if (state_c==IDLE && cnt_15ms==20'd750000) begin
flag <= 1;
end
end
always @(*) begin
case(state_c)
IDLE :
begin
if (flag) begin
state_n = INIT;
end
else begin
state_n = state_c;
end
end
INIT :
begin
state_n = S0;
end
S0 :
begin
state_n = S1;
end
S1 :
begin
state_n = S2;
end
S2 :
begin
state_n = S3;
end
S3 :
begin
state_n = ROW1_ADDR;
end
ROW1_ADDR:
begin
state_n = WRITE;
end
WRITE :
begin
if (char_cnt==5'd12) begin
state_n = ROW2_ADDR;
end
else if (char_cnt==5'd24) begin
state_n = stop;
end
else begin
state_n = state_c;
end
end
ROW2_ADDR:
begin
state_n = WRITE;
end
stop :
begin
state_n = stop;
end
default:state_n = IDLE;
endcase
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
lcd_data <= 8'd0;
end
else begin
case(state_c)
IDLE :begin lcd_data <= 8'h38; lcd_rs <= 0;end
INIT :begin lcd_data <= 8'h38; lcd_rs <= 0;end
S0 :begin lcd_data <= 8'h08; lcd_rs <= 0;end
S1 :begin lcd_data <= 8'h01; lcd_rs <= 0;end
S2 :begin lcd_data <= 8'h06; lcd_rs <= 0;end
S3 :begin lcd_data <= 8'h0c; lcd_rs <= 0;end
ROW1_ADDR :begin lcd_data <= 8'h80; lcd_rs <= 0;end
WRITE :begin lcd_data <= data_display; lcd_rs <= 1;end
ROW2_ADDR :begin lcd_data <= 8'hc0; lcd_rs <= 0;end
stop :begin lcd_data <= 8'h38; lcd_rs <= 0;end
default:;
endcase
end
end
endmodule
テストコード:
`timescale 1ns/1ns
module lcd_tb (); /* this is automatically generated */
reg rst_n;
reg clk;
wire lcd_rs;
wire lcd_rw;
wire lcd_en;
wire [7:0] lcd_data;
lcd inst_lcd
(
.clk (clk),
.rst_n (rst_n),
.lcd_rs (lcd_rs),
.lcd_rw (lcd_rw),
.lcd_en (lcd_en),
.lcd_data (lcd_data)
);
initial clk = 0;
always #10 clk = ~clk;
initial begin
#1;
rst_n = 0;
#200;
rst_n = 1;
#200;
#100000000;
$stop;
end
endmodule
シミュレーション波形:
物理的な地図: