基于FPGA的LCD1602驱动(含代码)

目录

 LCD1602显示原理

LCD1602接口

LCD1602操作时序

(1)读操作时序

(2)写操作时序

 LCD1602初始化

LCD1602读写数据 


 LCD1602显示原理

将LCD显示屏与FPGA连接之后,需要做的第一件事就是进行LCD驱动(也就是LCD初始化),之后往LCD里写一些字符,调试LCD是否可以正常使用

这里用的是LCD1602如下图:一共2行,一行16个显示块,其地址和屏幕的对应关系如下:

基于FPGA的LCD1602驱动(含代码)_第1张图片基于FPGA的LCD1602驱动(含代码)_第2张图片

 如果想在屏幕左上角显示字符‘A’,那么就把字符‘A’的字符代码41H写入DDRAM的00H地址处即可

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

LCD1602接口

基于FPGA的LCD1602驱动(含代码)_第3张图片

 对这里面比较重要的几个接口进行说明:

(1)RS 命令/数据选择引脚当RS为低电平时,选择命令;当RS为高电平时,选择数据。

(2)RW 读/写选择引脚,当RW为低电平时,向LCD1602写入命令或数据;当RW为高电平时,从LCD1602读取状态或数据。如果不需要进行读取操作,可以直接将其接VSS。

(3) 执行命令的使能引脚。

(4)D0—D7 并行数据输入/输出引脚

LCD1602操作时序

(1)读操作时序

基于FPGA的LCD1602驱动(含代码)_第4张图片

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

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

(2)写操作时序

基于FPGA的LCD1602驱动(含代码)_第5张图片

 写命令:输入RS=0,RW=0,E=高脉冲。输出:无。

 写数据:输入RS=1,RW=0,E=高脉冲。输出:无。
 

 LCD1602初始化

根据数据手册,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步:

基于FPGA的LCD1602驱动(含代码)_第6张图片

LCD1602读写数据 

初始化完成之后一般往LCD里写入一些字符串,验证一下是否可以正常显示

基于FPGA的LCD1602驱动(含代码)_第7张图片写的时候,要先指定地址,如果在第一行写入,首地址是00H,再加上DB7的1,即80H

如果在第二行写入,首地址是00H,再加上DB7的1,即C0H

因此在初始化完成之后,加入addr1 write1 addr2 write2 四个状态

把写数据和初始化放在一起,完整状态机如下:

基于FPGA的LCD1602驱动(含代码)_第8张图片

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

仿真结果如下:

基于FPGA的LCD1602驱动(含代码)_第9张图片

 

你可能感兴趣的:(fpga开发)