TA的每日心情 | 开心 2019-11-19 15:19 |
---|
签到天数: 1 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
本帖最后由 Allevi 于 2018-11-26 09:42 编辑 4 T. I Z3 X5 d6 M/ _
% B3 i* f( Q% Y/ V基于FPGA的LCD1602动态显示---Verilog实现
9 Z j4 b. C! Y. I一.LCD1602简要介绍
+ _6 P1 `1 L/ u; k9 g6 j- f
' I. ~4 B" T3 d+ y/ F0 q$ ]) |7 r kLCD1602,根据名称可以知道,就是能显示2行,每行16个字符的液晶,只能显示字母,数字和符号等字符,不能显示汉字,图片。如下图:
" c& ~8 {1 q: d9 z4 s
9 @; I' ?& Y5 t9 d: F
* Y! A9 y: z. r; m; I
市面上卖的的LCD1602操作基本上都是相同的,只是带不带背光之分。其控制芯片都是HD44780及其兼容芯片,所以控制接口都是一样,控制时序可以说是68并口时序。1LCD602控制线主要有4根:
) i3 f" Y; b+ [# _7 G( i+ D8 c4 U9 Y6 F3 J$ J( \5 i
(1)RS:数据/指令选择端,当RS = 0,写指令;当RS = 1,写数据。' c. `. x9 W) Q* j$ ^# M& o
& S) S& ^4 I B, K$ S2 a4 i
(2)RW:读/写选择端,当RW = 0,写指令/数据;当RW = 1,读状态/数据。* C- K; H) w U _
5 F# _% u/ N# r) }# N
(3)EN:使能端,下降沿使指令/数据生效。
8 n" e) R/ \* T2 d% \
0 Q }' e F) N. K' v/ i(4)Data[7:0]:8根并行数据口。; D% c- |5 e# W$ W$ f+ L
/ O8 J5 S" I( N5 O% M+ `
1 K. ^ o4 Q! n+ o6 M, q
3 b" t2 W8 T5 o9 o$ k x* a
y" F$ Z9 c& N+ F- y& x; l/ I) F指令方面只讲解一下显示模式设置指令0x38,0x31的区别。其实模式设置指令就是上图中的指令6:0x38:设置8位格式,2行,5*7;0x31:设置8位格式,2行,5*7。为什么要介绍0x31呢,一般单片机驱动LCD1602都是0x38的?/ B1 J/ y1 Z8 s6 C7 m
1 ?+ ^2 P+ H+ G/ X3 D% I) L由于一般的LCD1602都是VDD = 5V驱动的,而有些FPGA开发板上的LCD1602接口是由3.3V供电的。也就是VDD = 3.3V,这样就会引起供电不足的问题,所以经过试验得到,当VDD = 3.3V时,显示模式设置指令写入0x38时,LCD1602显示很暗,看不到;进而改为0x31时,只显示1行,LCD1602就正常显示了。这个要引起注意,下面我的代码就是只显示1行的。
. R6 i- ^0 z# n6 ?其他指令详解请查看数据手册。
2 |0 D* l; b! c* C
5 Q/ m$ u6 H9 t" ]! u3 x9 w1 |2 [4 ~' ~5 [
二.FPGA驱动LCD1602思路9 d M! V, m+ m0 }( U
- f" @1 F0 c3 n. N8 u- z+ L6 ?1 CFPGA驱动LCD1602,其实就是通过同步状态机模拟单片机驱动LCD1602,由并行模拟单步执行,状态过程就是先初始化LCD1602,然后写地址,最后写入显示数据。- y2 q6 u) h& X' g
6 Z% Q R$ [" C5 B+ G! b: s) r, e1.首先,我们要明白LCD1602是慢速器件。如果直接用FPGA 外接的几十兆时钟直接驱动肯定是不行的,所以要对FPGA时钟进行分频驱动,或者计数延时使能驱动。5 C3 B6 F1 i, r1 ]- ~# R
3 A) U h! X4 P6 _/ _这里我采用的计数延时使能驱动,代码中通过计数器定时得出lcd_clk_en信号线驱动。要注意的是不同厂家生产的LCD1602的时序延时都不同,但大多数都是纳秒级的,这里我采用的是间隔500ns使能驱动,最好延时长一些比较可靠,这个可以自己尝试修正。$ l0 t! i Q+ M* w+ O* L. E- G8 F, E
+ n$ g/ n- p2 _3 A2.LCD1602的初始化过程需要明白。大家估计都用单片机驱动过LCD1602,这里FPGA驱动LCD1602的初始化过程也是一样的。主要是以下4条指令的配置: P. I) ?4 p( l) V$ G5 e# o0 N
" |( x4 l. G1 g4 }. G5 V
(1)显示模式设置Mode_Set:8’h38 0 ]9 ~! h, m9 K% v5 S- E% C
9 [+ Q# f% [0 C, m
(2)显示开/关及光标设置Cursor_Set:8’h0c2 p3 d' j A2 a
& ^0 N$ [1 `/ B0 V2 j& k. Q(3)显示地址设置Address_Set:8’h068 L1 ?( \! ?$ T& h' `" z
' n3 y! V, R6 s+ z) M
(4)清屏设置Clear_Set:8'h01
% l/ z" }8 q+ h5 H; |3 U, B, Z {1 [' \# P# Q. w5 F
这里需要注意是写指令,所以RS = 0,并且写完指令后,EN下降沿使能。* z2 L8 U F: n3 c8 H! ?/ N
M7 }! l7 t# V3.初始化完成后,还需要写入地址,第一行初始地址:8’h80;第二行初始地址:8’h80 + 8”h40 = 8’hc0。这里RS = 0,并且写完地址后,EN下降沿使能。# t/ n& ^2 |! U9 G2 ^' N7 l
, L/ K2 o! A& i5 X7 P
4.写入地址后,就可以显示字符啦。但需要注意LCD1602写入设置地址指令8’h06后,地址是随每写入一个数据后,默认自加一的。这个一定要明白,不然作动态显示时,就会出现问题。一定要把握我们的数据是要显示在哪个位置,而LCD1602写入地址是会默认地址指针加一的。这里RS = 1,并且写完数据后,EN下降沿使能。
8 S& }7 E/ T7 n5 f! |5 Y- H
( M2 c. e+ R0 Z1 R5.由于我们要动态显示,所以数据要刷新。这里由于我们采用的是同步状态机模拟LCD1602的控制时序,所以在显示完最后的数据后,状态要跳回写入地址状态,以便进行动态刷新。这个很重要,不只是保证刷新,更是保证地址没有偏移。
1 N- e/ x& |. T E: V# x6 {) \7 {9 L/ g
以上就是大致的思路步骤了,大家可以结合下面的代码进行分析消化,只有你完全弄清LCD1602的控制时序,指令要点,才能完全把LCD1602玩弄于手掌之中,不然似懂非懂,终究会有无法理解的问题出现。 g$ |5 P( q0 w, N! X! A4 g; R1 J
8 P7 X1 Z- H3 c6 T8 `9 \# @) o$ W5 X
三.Verilog 代码! A- s9 \9 R" X; v$ a
% m; J3 z7 U$ h/ m1 N8 ]/ m本来想把LCD1602_Driver封装成个模块的,然后直接向其写入地址,数据即可显示的,后来由于能力问题,发现不怎么好写,就没有封装成模块了。不过下面动态显示的代码还是可以给大家一个参考的,虽然代码时序不是很严谨。
9 l2 m' v1 j4 Y- J- ]; S
, o0 I1 q' r8 h" @/ i& d下面代码功能主要是完成一个0-99s的计数器,动态刷新。主要是在LCD1602上动态显示:“Cnt:00”。
4 @, u1 s$ a/ [0 R( f4 i& v/***********************************************************************
4 V) @1 x9 j+ E& N! m7 k( K& @0 q************************* name:LCD1602_Driver *************************
, h1 C- f% ?- b***********************************************************************/
( I( k' y3 n- O) y. V7 dmodule lcd1602_driver(input clk, //50M; B3 k- F: {" I5 {+ F
input rst_n,
. E0 g7 c4 b# { output lcd_p, //Backlight Source +) D4 a* Z8 l0 D# _
output lcd_n, //Backlight Source -
- j; y! L* d% I9 ~2 Q6 b; _7 { output reg lcd_rs, //0:write order; 1:write data
/ `' b! K5 \" g; u% | output lcd_rw, //0:write data; 1:read data
6 h# @4 Y! U5 q* R/ m" c& Y: u output reg lcd_en, //negedge
7 r# Q+ G- u- i+ h" @" x7 { output reg [7:0] lcd_data);
( R4 b8 L0 }6 ~3 g E # U5 E. V+ o4 P6 }( k
//--------------------lcd1602 order----------------------------- Z% x$ y3 u4 k( [; K p8 T" _
parameter Mode_Set = 8'h31,# X, ^9 y( S: H$ j1 Q- T
Cursor_Set = 8'h0c,9 p5 j% w; p A/ e2 q5 }
Address_Set = 8'h06,* S+ U; _( \6 v. ]/ k6 q6 e3 t' s* t
Clear_Set = 8'h01;
9 F, n3 n& I- @4 N7 N1 C1 m. T8 ~ Q8 o3 J1 P
/****************************LCD1602 Display Data****************************/
8 d( D6 K3 O6 o% t6 |$ Twire [7:0] data0,data1; //counter data, O- i3 t9 ~) r2 |
wire [7:0] addr; //write address
1 ^. O# Z3 Y" H+ c//---------------------------------1s counter-----------------------------------6 Q! N! i7 ^, A& s4 F
reg [31:0] cnt1;
$ S( o2 R: G4 D* F. ]; N& Mreg [7:0] data_r0,data_r1;
" u3 ]1 u3 l7 }( | C3 galways@(posedge clk or negedge rst_n)
2 j: g$ I8 w3 _$ N: l; ]4 G; hbegin
. y7 `' B" ?/ ^$ j, e9 f if(!rst_n)
' n, q Y. k/ W# J3 {6 ?# m" A begin5 T0 ^& P" Z( W
cnt1 <= 1'b0;" [3 |0 t" N- P+ p% j5 k
data_r0 <= 1'b0;4 y; h7 [7 W1 h# N: ], `6 y- c
data_r1 <= 1'b0;: f/ M( V, U. s" {! C
end
0 y/ i! `2 X4 a9 ]: W$ [ else if(cnt1==32'd50000000)0 t( N# E2 H3 B% U$ K
begin
]& Q3 }- Q2 `5 N; l8 \ if(data_r0==8'd9)8 E! l& `+ X5 J
begin* c+ {6 W2 `) x" c( f
data_r0 <= 1'b0;$ D& H x) X( N* A
if(data_r1==8'd9)4 K- S% n% [+ U- P6 h- S4 d
data_r1 <= 1'b0;
% ?6 V( h1 `$ R3 {# K% z else
; K& Y. j" v# c1 J, G& y9 c; S0 l data_r1 <= data_r1 + 1'b1;
9 ^0 U, n! q; j1 l+ C end
* U4 r4 {& K, S$ j0 X else
" K* h# I \, m$ Y% | data_r0 <= data_r0 + 1'b1;
e9 C$ N. R4 D- Z cnt1 <= 1'b0;# W! W0 p5 C+ G
end3 W) y4 X( B5 k
else. y' q; Z- K' }1 p+ _) t
cnt1 <= cnt1 + 1'b1;
, O6 }* e; x$ Bend* m# M) `7 M; Y+ j
3 }) B! g' i( l& hassign data0 = 8'h30 + data_r0 ; + ^$ L2 ] h; L' G' k2 o
assign data1 = 8'h30 + data_r1 ;; ]7 L" C7 k3 z2 e/ q6 N4 [
& o: V+ f# O. e4 m: e+ e$ q//-------------------address------------------
3 j0 T& `" w7 J* I7 l% i3 Vassign addr = 8'h80;0 E6 C. d- q9 z7 X$ _( s! e: P
2 J8 a& I8 L9 m G
/****************************LCD1602 Driver****************************/
9 `4 S C/ C; C6 c- ^# L//-----------------------lcd1602 clk_en---------------------
' |3 P! ]; H2 I' P) k6 M/ Treg [31:0] cnt;; D; C8 J* U3 y& s+ O3 p B
reg lcd_clk_en;
& ]# ]: k% Y. X7 h1 _always @(posedge clk or negedge rst_n)
3 V% g: G. {7 {8 |begin
) n# J0 r, L1 x" E, Y. {) {: D$ h- _6 O! M if(!rst_n)* L/ Z' v* o$ `* _0 G4 D
begin
* M2 ^- ^4 s, m: p, y" O cnt <= 1'b0;
! l3 i' _* i2 v R( } lcd_clk_en <= 1'b0;
8 [0 Q8 }2 Y+ b* J; q end
. G: O8 ]3 m& L7 t+ x. m* j' n# ^ else if(cnt == 32'h24999) //500us `9 \% j* q- B# C
begin
4 q4 @1 [; w; a# h7 ~2 n lcd_clk_en <= 1'b1;
, A5 X" [ `7 o cnt <= 1'b0;6 v% x0 Z6 u6 Q" H/ ^ b
end
" F5 t9 Z1 k% B0 k else
0 { f, E5 O/ w4 g' ?0 `1 e" @: w. o begin3 ^& W9 @3 M6 j8 l2 C; |
cnt <= cnt + 1'b1;3 i! A9 D2 e9 R2 \
lcd_clk_en <= 1'b0;
. \$ D0 q- e1 v) [ end
- |( S& \+ M7 {. N; @end
3 O6 e& e7 U5 I% r4 R
- h7 U$ G+ c7 C6 \2 y2 Y//-----------------------lcd1602 display state-------------------------------------------# {- o% z+ D' e# W" Z
reg [4:0] state;$ H5 L, l- R8 K' ?
always@(posedge clk or negedge rst_n)2 l2 m" Z' ]; ^, m7 y, ]4 R
begin) N" u3 t* h/ l3 O4 }# }) _
if(!rst_n)" Z2 X1 u3 Q; ]( Q [9 P. a' s
begin
* q5 Q) Y7 Y8 Q! b6 y state <= 1'b0;
}5 n, g5 N% M/ a lcd_rs <= 1'b0;
. s* i: h1 a' I lcd_en <= 1'b0;
3 f; @6 T4 \ l/ W3 z lcd_data <= 1'b0;
" z0 _3 g6 V* {. p0 O" h end
0 ]& V$ j: U+ c) F3 t! b else if(lcd_clk_en)
+ k3 u i D) {8 d; W5 G begin
3 u3 f t* }9 ^: N) p case(state)
1 X7 A& Y7 k* c5 d //-------------------init_state---------------------9 g2 }4 P4 ~+ E' W1 t( G
5'd0: begin ' G5 Z$ ]) H8 ], Z" k5 E1 e0 s
lcd_rs <= 1'b0;+ U' S+ m2 Q; x* |, e
lcd_en <= 1'b1;
$ @$ c8 C" f) u2 n7 j lcd_data <= Mode_Set;
5 F8 o8 M& H! i" ~% Y$ c state <= state + 1'd1;
& N0 V! p& I* L% U! Z# H m% Q end
5 V% ~4 o; d4 \. x% ~- N 5'd1: begin
) H6 O. N- A* A+ J lcd_en <= 1'b0;! s4 C- g/ ]6 E$ M% \" {7 T
state <= state + 1'd1;, ~- g6 u7 C V6 b* t4 k. n
end
( U# Y9 W+ k. E8 d% k7 _3 k) S0 [ 5'd2: begin
5 H% u' b2 }. ]3 y2 c lcd_rs <= 1'b0;
0 ]6 t' N) D: q: M lcd_en <= 1'b1;! T5 e |' Y6 W. S# z5 Y$ r1 \" D* U: n
lcd_data <= Cursor_Set;
: `5 Z" ^* }4 B# b l state <= state + 1'd1;" w8 E/ b: s. s" D/ J# s3 N0 f) q
end% v1 j! \3 C& r9 y `. O4 m+ V
5'd3: begin
& N8 D5 q d s: T& b lcd_en <= 1'b0;
$ \* }, [- |6 e2 C% m }! t5 L state <= state + 1'd1;
* i# b- N' H* N/ [ end0 n, @- m2 l. D# A/ f( @
5'd4: begin
, a4 X; ?: R* \5 y' u* ~9 {( P lcd_rs <= 1'b0;
% n, N, d) u2 h7 ? lcd_en <= 1'b1;
: i) I/ q( P4 @$ W# F+ W0 c lcd_data <= Address_Set;3 R% ?6 y% W3 Y& V( h
state <= state + 1'd1;
# M5 d; e" K. ?+ ? end" K. d; g" A# o
5'd5: begin5 u0 `& C/ f' b4 t& A x$ V6 I
lcd_en <= 1'b0;' v* c3 Z& H. p- `: _: u1 Z
state <= state + 1'd1;4 T; J! H, T" \# G) `. Q
end& ^4 I' T% G5 l1 q' [" d
5'd6: begin
( X) E9 P8 x! _: S lcd_rs <= 1'b0;# x9 P" B* J/ ]
lcd_en <= 1'b1;# v# A; \# w* J" s* ~' ^
lcd_data <= Clear_Set;8 B1 C+ \- x1 Y: ^. I3 X6 _
state <= state + 1'd1;
8 S+ J+ u r5 p2 x7 k9 c P/ S0 o end' x& h! }# t* Y; H
5'd7: begin
- W/ L% P" [$ W1 @' r lcd_en <= 1'b0; l( p' w: ]9 E( }
state <= state + 1'd1;
& p0 ?1 u: }: W end
0 x" b6 r2 ?( r! ?6 y0 Y
2 k g* d- c6 y r; v& _ //--------------------work state--------------------
( t9 Q6 Z" m# \; \0 h 5'd8: begin + ]( e, ?0 [+ m
lcd_rs <= 1'b0;
& n) e' X/ W& v2 E3 h7 O lcd_en <= 1'b1;/ V! D" B$ M8 j E3 G
lcd_data <= addr; //write addr, H. d) E$ _ Y
state <= state + 1'd1;, b) S& T; N9 T# U) T
end9 B3 f5 B( r3 M" |
5'd9: begin; O$ _1 P6 E7 W
lcd_en <= 1'b0;
Y6 Z4 O( J1 W) @: F7 C4 B state <= state + 1'd1;
# @8 M' E5 Y9 P8 ? end
; K3 V" m4 V) P" o1 h 5'd10: begin
6 F" O3 K6 C; U1 e/ m; ]" o4 K lcd_rs <= 1'b1;0 p0 h4 i$ L0 K- d8 e: j! W
lcd_en <= 1'b1;, M2 ?$ l- ?. ?
lcd_data <= "C"; //write data
/ T, K1 R) S5 H! K0 h) [ state <= state + 1'd1;/ Y& b1 L* _& x5 E. i7 T: [
end( l5 R! o: M' D5 F
5'd11: begin* W8 W7 L0 l2 l5 n9 |
lcd_en <= 1'b0;
: l' H2 a; k+ I1 \2 p% a state <= state + 1'd1;
" T0 E; c, r8 |0 A3 n& S0 I end
! u; P- U! ]$ {6 B7 R 5'd12: begin1 m" j5 C' p3 R2 _& R6 c
lcd_rs <= 1'b1;' n. o$ f1 m, h% M) q- o" @
lcd_en <= 1'b1;, r+ t# q& F2 }8 d; |' l
lcd_data <= "n"; //write data
1 T0 W: b- \9 U& b6 M1 S( Q state <= state + 1'd1;
' ~* c5 ?- C9 p: v D/ B$ t0 m end0 U7 U8 M1 F2 D+ K1 t
5'd13: begin+ ~; F1 ?6 j% z7 L/ t
lcd_en <= 1'b0;
7 _5 m6 i$ o0 o/ Y, T" d% l4 x- e3 u state <= state + 1'd1;
0 s4 Q# t% q$ u! L; t( _: W( e end
+ c/ A4 E3 X2 s' j4 L' N 5'd14: begin
+ y4 b: J1 K( y& @ lcd_rs <= 1'b1;/ C: z) x' C* L+ G* o
lcd_en <= 1'b1;
8 S# [: e$ Z, m; h; F( @, @ lcd_data <= "t"; //write data
: I. b* m, \2 d O state <= state + 1'd1;
1 J: P: I" ^2 v4 `. r end
/ `1 z" P! s1 \8 s" ~3 H' E 5'd15: begin
* T; z2 E, p/ y* m9 k lcd_en <= 1'b0;
: L8 f! e/ B# o/ j9 O6 p1 h4 n state <= state + 1'd1;' m, S, i4 d2 S- L7 c
end
/ j) C6 [" Q$ h z$ O1 ] 5'd16: begin
( N$ a5 X6 X, ?: V' E! p8 T8 |+ f, F lcd_rs <= 1'b1;
, b9 _% \' G% w/ k/ T+ n lcd_en <= 1'b1;) O% u f1 |1 C5 M( ~1 A
lcd_data <= ":"; //write data
: L1 S7 \& P9 [' T) j9 A8 S; c state <= state + 1'd1;5 [+ A6 _ z( j6 }5 _% J
end
1 y$ ^% B0 t( x' i) P% K 5'd17: begin* s, Z" j1 W/ v7 C% @' c& V
lcd_en <= 1'b0;6 d9 N4 {( R# c; z
state <= state + 1'd1;
- f/ r* d6 T! Q1 v end * n( n& b* T: a; f0 ^5 _& `4 U
5'd18: begin5 z6 D/ \" n* g y/ a0 i" g [
lcd_rs <= 1'b1;
7 V' E& W9 `( N6 ~+ l1 X* N lcd_en <= 1'b1;
8 V# Y; Z4 ^# Y8 X2 E" \' W lcd_data <= data1; //write data: tens digit" ~$ c/ H! O: Y" v5 A9 }0 h/ ]& V" Z
state <= state + 1'd1;
" @3 [- a( t4 X( {# O end( {! G) y& y. Z' c
5'd19: begin
& t5 i4 L% t: Y" J5 J lcd_en <= 1'b0;
( V! b; ]( l5 r* s8 Y& _% D$ H: S state <= state + 1'd1;: M" @0 t! r$ T+ x8 Q! c9 I
end
% h/ C7 R# ~7 B9 h2 \ 5'd20: begin
, U$ l+ ?" n5 W7 h lcd_rs <= 1'b1;# h( |8 x2 n# e( L# Y I" R
lcd_en <= 1'b1;
# Y. ~3 P# @8 L8 l5 n; U" v lcd_data <= data0; //write data: single digit3 l7 d7 h: j5 j+ o1 Q
state <= state + 1'd1;
# Q% b2 y# y7 J9 R" i end5 W( l9 m! }1 K+ t7 S1 _5 @
5'd21: begin1 `) l6 F- k% D: w9 E ? P
lcd_en <= 1'b0;
% x+ f' ?' _* {7 W* E9 k9 u state <= 5'd8;+ \+ d. C g% P5 @' R6 i" l
end: `: F* H7 k( k, ?
default: state <= 5'bxxxxx;
6 e8 e/ g+ u% c+ ?: u endcase6 F4 s& J8 I, A" }+ ?
end
" I! K& [8 J3 M' _+ lend0 I& @8 }7 ^& X5 X Q# h
% w9 c/ l, O- E& n5 u6 qassign lcd_rw = 1'b0; //only write* E; {3 T& W% m( j7 G
, w" \# p9 R8 k" n: k# V% [: G//------------------backlight driver----------------
' d. h# K; o% C4 P, [assign lcd_n = 1'b0;
/ Q3 P( }% Q' c9 ? B* oassign lcd_p = 1'b1;/ y) W7 M/ }6 F
- H4 Y/ U/ A4 q* r. V% }; xendmodule7 D( t& m+ U3 i, x# L( J- p
|
4 o% [* q, l% |" L2 G |
|