目录
将LCD显示屏与FPGA连接之后,需要做的第一件事就是进行LCD驱动(也就是LCD初始化),之后往LCD里写一些字符,调试LCD是否可以正常使用
这里用的是LCD1602如下图:一共2行,一行16个显示块,其地址和屏幕的对应关系如下:


如果想在屏幕左上角显示字符‘A’,那么就把字符‘A’的字符代码41H写入DDRAM的00H地址处即可
但如果要显示CGROM中没有的字符,比如摄氏温标的符号,那么就只有先在CGRAM中定义,然后再在DDRAM中写入这个自定义字符的字符代码即可。具体参考这篇:基于FPGA的LCD1602显示屏驱动_panhongfeng111的博客-CSDN博客_基于fpga的lcd显示

对这里面比较重要的几个接口进行说明:
(1)RS 命令/数据选择引脚当RS为低电平时,选择命令;当RS为高电平时,选择数据。
(2)RW 读/写选择引脚,当RW为低电平时,向LCD1602写入命令或数据;当RW为高电平时,从LCD1602读取状态或数据。如果不需要进行读取操作,可以直接将其接VSS。
(3) E 执行命令的使能引脚。
(4)D0—D7 并行数据输入/输出引脚

读状态:输入RS=0,RW=1,E=高脉冲。输出:D0—D7为状态字。
读数据:输入RS=1,RW=1,E=高脉冲。输出:D0—D7为数据。

写命令:输入RS=0,RW=0,E=高脉冲。输出:无。
写数据:输入RS=1,RW=0,E=高脉冲。输出:无。
根据数据手册,LCD的初始化需要完成下面12步:
1 延时15ms
2 写指令38H(不检测忙信号)
3 延时5ms
4 写指令38H(不检测忙信号)
5 延时5ms
6 写指令38H(不检测忙信号)
7 以后每次写指令、读/写数据操作均需要检测忙信号
8 写指令38H:显示模式设置
9 写指令08H:显示关闭
10 写指令01H:显示清屏
11 写指令06H:显示光标移动设置
12 写指令0CH:显示开及光标设置
这里面有1次15ms延时,和2次5ms延时,写指令是38H,我们可以把1~8步合在一起,延时25ms,写指令38H,那么初始化就可以简化为5步:

初始化完成之后一般往LCD里写入一些字符串,验证一下是否可以正常显示
写的时候,要先指定地址,如果在第一行写入,首地址是00H,再加上DB7的1,即80H
如果在第二行写入,首地址是40H,再加上DB7的1,即C0H
因此在初始化完成之后,加入addr1 write1 addr2 write2 四个状态
把写数据和初始化放在一起,完整状态机如下:

- module lcd(
- input clk ,
- input rst ,
- input delay_en , //延时完成
- output reg lcd_rs ,//状态or数据选择
- output reg lcd_rw ,//读or写选择
- output reg lcd_en ,//使能信号
- output reg [7:0] lcd_data //输出LCD指令
- );
-
-
- reg [7:0] data_display ;//显示数据
-
- reg [5:0] data_cnt; //数据计数器
-
- reg [3:0] state; //状态
-
- parameter IDLE =4'd0;
- parameter S0 =4'd1;
- parameter S1 =4'd2;
- parameter S2 =4'd3;
- parameter S3 =4'd4;
- parameter S4 =4'd5;
- parameter Addr1=4'd6;
- parameter WR1 =4'd7;
- parameter Addr2=4'd8;
- parameter WR2 =4'd9;
- parameter stop =4'd10;
-
- always @(*) begin//根据数据计数器输出显示数据
- case(data_cnt)
- 5'd0: data_display = "H";
- 5'd1: data_display = "E";
- 5'd2: data_display = "L";
- 5'd3: data_display = "L";
- 5'd4: data_display = "O";
- 5'd5: data_display = "W";
- 5'd6: data_display = "-";
- 5'd7: data_display = "W";
- 5'd8: data_display = "O";
- 5'd9: data_display = "R";
- 5'd10: data_display = "L";
- 5'd11: data_display = "D";
- 5'd12: data_display = "1";
- 5'd13: data_display = "6";
- 5'd14: data_display = "0";
- 5'd15: data_display = "2";
- 5'd16: data_display = "h";
- 5'd17: data_display = "e";
- 5'd18: data_display = "l";
- 5'd19: data_display = "l";
- 5'd20: data_display = "o";
- 5'd21: data_display = "w";
- 5'd22: data_display = "h";
- 5'd23: data_display = "e";
- 5'd24: data_display = "l";
- 5'd26: data_display = "l";
- 5'd27: data_display = "o";
- 5'd28: data_display = "w";
- 5'd29: data_display = "-";
- 5'd30: data_display = "-";
- 5'd31: data_display = "-";
-
- default:data_display = "-";
- endcase
- end
-
- always@(posedge clk)begin
- if(rst)begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd0;
- lcd_data<=8'd0;
- data_cnt<=6'd0;
- state <=IDLE;
- end
- else begin
- case(state)
- IDLE :begin
- if(delay_en)begin
- state <=S0;
- end
- else begin
- state <=IDLE;
- end
- end
- S0 :begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
- lcd_data<=8'h38;
- state <=S1;
- end
- S1 :begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
- lcd_data<=8'h08;
- state <=S2;
- end
- S2 :begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
- lcd_data<=8'h01;
- state <=S3;
- end
- S3 :begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
- lcd_data<=8'h06;
- state <=S4;
- end
- S4 :begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
- lcd_data<=8'h0C;
- state <=Addr1;
- end
- Addr1:begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
- lcd_data<=8'h80;//第一行地址
- state <=WR1;
- end
- WR1 :begin
- lcd_rs <=1'd1;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
-
- if(data_cnt==6'd15)begin//第一行写完
- state <=Addr2;
- data_cnt<=6'd0;
- end
- else begin
- data_cnt<=data_cnt+6'd1;
- state <=WR1;
- lcd_data<=data_display;//显示第一行数据
- end
- end
- Addr2:begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
- lcd_data<=8'hC0;//第二行地址
- state <=WR2;
- end
- WR2 :begin
- lcd_rs <=1'd1;
- lcd_rw <=1'd0;
- lcd_en <=1'd1;
-
- if(data_cnt==6'd15)begin//第二行写完
- state <=stop;
- data_cnt<=6'd0;
- end
- else begin
- data_cnt<=data_cnt+6'd1;
- lcd_data<=data_display;//显示第二行数据
- state <=WR2;
- end
- end
- stop :begin
- lcd_rs <=1'd0;
- lcd_rw <=1'd0;
- lcd_en <=1'd0;
- lcd_data<=8'h38;
- state <=IDLE;
- end
- endcase
- end
- end
-
- endmodule
仿真结果如下:
