[SugerTangYL] LCD1602驱动程序Verilog

本文详细介绍了LCD1602液晶屏的工作原理、引脚功能、控制指令和一个完整的驱动代码实例,帮助理解字符型液晶屏的基础操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

        LCD1602作为基础液晶屏,是许多应用工程师绕不过的器件。藉由对LCD1602的学习,我们能了解到液晶屏的工作原理,对今后其他液晶屏的学习有着良好的铺垫作用。


一、LCD1602

       LCD1602是指显示内容为16x2,即可以显示两行,每个字符由5x7或5x11等点阵字符位组成,每位之间有一个点距的间隔,每行之间也有间隔,起着字符间距和行间距的作用。

        市面上字符液晶大多是基于HD44780液晶芯片的,控制原理完全相同。我们只需要掌握好HD44780的控制程序,便能很方便地应用于市面上大部分的字符型液晶。

        首先先看下LCD1602的引脚图,了解下每个引脚的作用。下图为AlteraDE2开发板的LCD1602模块原理图。

         从图中可知,LCD总共有14个引脚,每个引脚的功能在下方表格中列出。

Signal NameDescription
LCD_DATA[0]数据口0
LCD_DATA[1]数据口1
LCD_DATA[2]数据口2
LCD_DATA[3]数据口3
LCD_DATA[4]数据口4
LCD_DATA[5]数据口5
LCD_DATA[6]数据口6
LCD_DATA[7]数据口7
LCD_RW读写选择端,0为写,1为读
LCD_ENLCD使能端
LCD_RS指令/数据选择端,0为指令,1为数据
LCD_ONLCD电源开关
LCD_BLONLCD背光开关

        通过写入指令或数据,控制内部的驱动芯片,从而实现我们所需的操作,这便是LCD的使用思路,那对于内部的控制器来说,总共有11条控制指令,如下表所示:

lcd1602简介,LCD1602中文资料

         指令1:清显示。指令码01H,光标复位到地址00H位置,显示清空。

        指令2:光标复位。光标返回到地址00H。

        指令3:光标和显示位置设置。设定每次写入1位数据后光标移位方向并且设定一次写入一个字符是否移动。I/D控制光标移动方向,高电平右移,低电平左移;S控制屏幕上所有文字是否左移或右移,高电平表示有效,低电平表示无效。

        指令4:显示开关控制。D:控制整体的显示开与关,高电平表示开显示,低电平表示关显示,但DDRAM中的数据依然保留;C:控制光标的开关,高电平表示开启光标,低电平表示无光标;B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。

        指令5:光标或显示移位。当S/C=0时,R/L控制显示内容和光标一起左移或右移,0为左移,1为右移;当S/C=1时,光标不移动,R/L控制显示内容左移或右移,0为左移,1为右移。

        指令6:功能设定。设定数据总线位数、显示的行数和字形。DL=1时数据总线为8位,DL=0时数据总线是4位;N=0时显示一行,N=1时显示两行;F=0时为5*8点阵或字符,F=1时为5*11点阵或字符。

        指令7:设定CGRAM地址。设定下一个要存入数据的CGRAM地址。在CGRAM中已经内置了192个字符,但留有8个字符空间允许用户自定义字符,编码表如下所示,最左边的红框即是自定义字符区域,其中0000_0000与0000_1000表示同一个字符,所以看似是十六个,实际仍是八个。

         字符需要八行五列来描写(一行作为行间距),因此如果想好写入一个自定义字符,需要连续操作八次。示意图如下

         指令8:设置DDRAM地址。DDRAM即为显存,往显存写入什么内容,屏幕便会显示什么,DDRAM的地址与显示屏对照关系如下,00H到0FH为第一行,40H到4FH为第二行,剩余的DDRAM空间不显示,但可以用指令来移位显示。

         指令9:读忙信号或AC地址。如果DF=1忙碌,无法接受数据或指令;BF=0可以接收数据、指令。也可以读取AC地址。

        指令10:向DDRAM或CGRAM写入数据。

        指令11:从DDRAM或者CGRAM读数据。

二、完整代码

module LCD(
  switch_num,
  key_detemine,
  key_displace,
  ClOCK_50,
  KEY,
  LCD_RW,
  LCD_EN,
  LCD_RS,
  LCD_DATA,
  LCD_ON,
  LCD_BLON,
  key_detemine_feedback,
  key_displace_feedback,
  number_feedback
  );

input [3:0] switch_num;
input key_detemine,key_displace;
input ClOCK_50;
input [3:0]KEY;
output LCD_RW, LCD_EN, LCD_RS, LCD_ON, LCD_BLON,key_detemine_feedback,key_displace_feedback;
output [7:0]LCD_DATA;
output [3:0] number_feedback;

assign LCD_RW = rw;
assign LCD_EN = CLK_LCD;
assign LCD_RS = rs;
assign LCD_ON = 1;
assign LCD_BLON = 1;
assign LCD_DATA = data;


parameter space = 8'b0010_0000;

parameter CAPITAL_A = 8'b0100_0001;
parameter CAPITAL_B = 8'b0100_0010;
parameter CAPITAL_C = 8'b0100_0011;
parameter CAPITAL_D = 8'b0100_0100;
parameter CAPITAL_E = 8'b0100_0101;
parameter CAPITAL_F = 8'b0100_0110;
parameter CAPITAL_G = 8'b0100_0111;
parameter CAPITAL_H = 8'b0100_1000;
parameter CAPITAL_I = 8'b0100_1001;
parameter CAPITAL_J = 8'b0100_1010;
parameter CAPITAL_K = 8'b0100_1011;
parameter CAPITAL_L = 8'b0100_1100;
parameter CAPITAL_M = 8'b0100_1101;
parameter CAPITAL_N = 8'b0100_1110;
parameter CAPITAL_O = 8'b0100_1111;
parameter CAPITAL_P = 8'b0101_0001;
parameter CAPITAL_Q = 8'b0101_0001;
parameter CAPITAL_R = 8'b0101_0010;
parameter CAPITAL_S = 8'b0101_0011;
parameter CAPITAL_T = 8'b0101_0100;
parameter CAPITAL_U = 8'b0101_0101;
parameter CAPITAL_V = 8'b0101_0110;
parameter CAPITAL_W = 8'b0101_0111;
parameter CAPITAL_X = 8'b0101_1000;
parameter CAPITAL_Y = 8'b0101_1001;
parameter CAPITAL_Z = 8'b0101_1010;

parameter LOWERCASE_a = 8'b0110_0001;
parameter LOWERCASE_b = 8'b0110_0010;
parameter LOWERCASE_c = 8'b0110_0011;
parameter LOWERCASE_d = 8'b0110_0100;
parameter LOWERCASE_e = 8'b0110_0101;
parameter LOWERCASE_f = 8'b0110_0110;
parameter LOWERCASE_g = 8'b0110_0111;
parameter LOWERCASE_h = 8'b0110_1000;
parameter LOWERCASE_i = 8'b0110_1001;
parameter LOWERCASE_j = 8'b0110_1010;
parameter LOWERCASE_k = 8'b0110_1011;
parameter LOWERCASE_l = 8'b0110_1100;
parameter LOWERCASE_m = 8'b0110_1101;
parameter LOWERCASE_n = 8'b0110_1110;
parameter LOWERCASE_o = 8'b0110_1111;
parameter LOWERCASE_p = 8'b0111_0000;
parameter LOWERCASE_q = 8'b0111_0001;
parameter LOWERCASE_r = 8'b0111_0010;
parameter LOWERCASE_s = 8'b0111_0011;
parameter LOWERCASE_t = 8'b0111_0100;
parameter LOWERCASE_u = 8'b0111_0101;
parameter LOWERCASE_v = 8'b0111_0110;
parameter LOWERCASE_w = 8'b0111_0111;
parameter LOWERCASE_x = 8'b0111_1000;
parameter LOWERCASE_y = 8'b0111_1001;
parameter LOWERCASE_z = 8'b0111_1010;

parameter NUMBER_0 = 8'b0011_0000;
parameter NUMBER_1 = 8'b0011_0001;
parameter NUMBER_2 = 8'b0011_0010;
parameter NUMBER_3 = 8'b0011_0011;
parameter NUMBER_4 = 8'b0011_0100;
parameter NUMBER_5 = 8'b0011_0101;
parameter NUMBER_6 = 8'b0011_0110;
parameter NUMBER_7 = 8'b0011_0111;
parameter NUMBER_8 = 8'b0011_1000;
parameter NUMBER_9 = 8'b0011_1001;

parameter CHARACTER_single_quotes = 8'b0101_1110;



parameter s_idle         = 4'd0;
parameter s_clear        = 4'd1;
parameter s_cursor       = 4'd2;
parameter s_inputmode    = 4'd3;
parameter s_switchmode   = 4'd4;
parameter s_shiftmode    = 4'd5;
parameter s_setfunction  = 4'd6;
parameter s_setgeneraddr = 4'd7;
parameter s_setdataaddr1 = 4'd8;
parameter s_readbasy     = 4'd9;
parameter s_writecgram1  = 4'd10;
parameter s_readram      = 4'd11;
parameter s_setdataaddr2 = 4'd12;
parameter s_writecgram2  = 4'd13;

parameter IDLE         = 8'bzzzz_zzzz;
parameter CLEAR        = 8'b0000_0001;  
parameter CURSOR       = 8'b0000_0010;  
parameter INPUTMODE    = 8'b0000_0110;  
parameter SWITCHMODE   = 8'b0000_1111;  
parameter SHIFTMODE    = 8'b0001_1100;  
parameter SETFUNCTION  = 8'b0011_1100;  
parameter SETGENERADDR = 8'b0100_0000;  
parameter SETDATAADDR  = 8'b1000_0000;  

wire clk, rst;
assign clk = CLK_LCD;
assign rst = !KEY[1];

reg [3:0]state;
reg rw, rs;
reg [7:0]data;
reg [7:0]addr;



always @(posedge clk or posedge rst) begin
  if (rst) begin
    // reset
    state <= s_idle;
    data  <= 8'b0;
    rw    <= 1'b0;
    rs    <= 1'b0;
    addr  <= 8'b1000_0000;
  end
  else begin
    case (state)
      s_idle        :begin
                        state <= s_clear;
                        data  <= IDLE;
                        rw    <= rw;
                        rs    <= rs;
                     end
      s_clear       :begin
                        state <= s_inputmode;
                        data  <= CLEAR;
                        rw    <= 1'b0;
                        rs    <= 1'b0;
                     end
      s_inputmode   :begin
                        state <= s_switchmode;
                        data  <= INPUTMODE;
                        rw    <= 1'b0;
                        rs    <= 1'b0;
                     end
      s_switchmode  :begin
                        state <= s_shiftmode;
                        data  <= SWITCHMODE;
                        rw    <= 1'b0;
                        rs    <= 1'b0;
                     end
      s_shiftmode   :begin
                        state <= s_setfunction;
                        data  <= SHIFTMODE;
                        rw    <= 1'b0;
                        rs    <= 1'b0;
                     end
      s_setfunction :begin
                        state <= s_setdataaddr1;
                        data  <= SETFUNCTION;
                        rw    <= 1'b0;
                        rs    <= 1'b0;
                     end
      s_setdataaddr1:begin
                        state <= s_writecgram1;
                        data  <= addr;
                        rw    <= 1'b0;
                        rs    <= 1'b0;
                     end
      s_writecgram1 :begin
                        if (addr == 8'b1000_1111) begin
                          state <= s_setdataaddr2;
                          data  <= lcd_data(addr,num_5,num_4,num_3,num_2,num_1,num_0);
                          rw    <= 1'b0;
                          rs    <= 1'b1;
                          addr  <= 8'b1100_0000;
                        end
                        else begin
                          state <= s_writecgram1;
                          data  <= lcd_data(addr,num_5,num_4,num_3,num_2,num_1,num_0);
                          rw    <= 1'b0;
                          rs    <= 1'b1;
                          addr  <= addr + 1'b1;
                        end                        
                     end
      s_setdataaddr2:begin
                        state <= s_writecgram2;
                        data  <= addr;
                        rw    <= 1'b0;
                        rs    <= 1'b0;
                     end
      s_writecgram2 :begin
                        if (addr == 8'b1100_1111) begin
                          state <= s_setdataaddr1;
                          data  <= lcd_data(addr,num_5,num_4,num_3,num_2,num_1,num_0);
                          rw    <= 1'b0;
                          rs    <= 1'b1;
                          addr  <= 8'b1000_0000;
                        end
                        else begin
                          state <= s_writecgram2;
                          data  <= lcd_data(addr,num_5,num_4,num_3,num_2,num_1,num_0);
                          rw    <= 1'b0;
                          rs    <= 1'b1;
                          addr  <= addr + 1'b1;
                        end                        
                     end
      default       :begin
                        state <= state;
                        data  <= data;
                        rw    <= rw;
                        rs    <= rs;
                     end
    endcase
  end
end

function [7:0]lcd_data;
  input [7:0]lcd_addr;
  input [3:0] num_5,num_4,num_3,num_2,num_1,num_0;
  begin
    case(lcd_addr)
      8'h80  :lcd_data = CAPITAL_S;
      8'h81  :lcd_data = LOWERCASE_t;
      8'h82  :lcd_data = LOWERCASE_e;
      8'h83  :lcd_data = LOWERCASE_p;
      8'h84  :lcd_data = CAPITAL_L;
      8'h85  :lcd_data = LOWERCASE_e;
      8'h86  :lcd_data = LOWERCASE_n;
      8'h87  :lcd_data = LOWERCASE_g;
      8'h88  :lcd_data = LOWERCASE_t;
      8'h89  :lcd_data = LOWERCASE_h;
      8'h8b  :lcd_data = LOWERCASE_i;
      8'h8c  :lcd_data = LOWERCASE_s;
      
      8'hc2  :lcd_data = NUMBER_2;
      8'hc3  :lcd_data = NUMBER_4;
      8'hc4  :lcd_data = CHARACTER_single_quotes;
      8'hc5  :lcd_data = LOWERCASE_h;
      8'hc6  :lcd_data = number(num_5);
      8'hc7  :lcd_data = number(num_4);
      8'hc8  :lcd_data = number(num_3);
      8'hc9  :lcd_data = number(num_2);
      8'hca  :lcd_data = number(num_1);
      8'hcb  :lcd_data = number(num_0);
      default:lcd_data = space;
    endcase
  end
endfunction

function [7:0]number;
input [3:0] num;
  begin 
	case(num)
	0 : number = NUMBER_0;
	1 : number = NUMBER_1;
	2:number = NUMBER_2;
	3:number = NUMBER_3;
	4:number = NUMBER_4;
	5:number = NUMBER_5;
	6:number = NUMBER_6;
	7:number = NUMBER_7;
	8:number = NUMBER_8;
	9:number = NUMBER_9;
	10:number = LOWERCASE_a;
	11:number = LOWERCASE_b;
	12:number = LOWERCASE_c;
	13:number = LOWERCASE_d;
	14:number = LOWERCASE_e;
	15:number = LOWERCASE_f;
	default:;
	endcase
end
endfunction

reg[2:0] flag=0;
reg [3:0] num_5,num_4=1,num_3,num_2,num_1,num_0;
always @(posedge key_detemine)
begin
		case (flag)
			3'b000:num_0<=switch_num;
			3'b001:num_1<=switch_num;
			3'b010:num_2<=switch_num;
			3'b011:num_3<=switch_num;
			3'b100:num_4<=switch_num;
			3'b101:num_5<=switch_num;
			default:;
		endcase
end
always @(posedge key_displace)
begin
	if(flag>=3'b101)
		flag<=0;
	else flag<=flag+1;
end



parameter CLK_LCD_times = 19'd030000;
reg [22:0]cnt;
reg CLK_LCD;
reg CLK_500Hz;

always@(posedge ClOCK_50 or posedge rst) begin
  if(rst) begin
    cnt <= 19'd0;
    CLK_500Hz <= 1'b0;
  end
  else if(cnt == CLK_LCD_times) begin
    cnt <= 19'd0;
    CLK_LCD <= ~CLK_LCD;
  end
  else begin 
    cnt <= cnt+1'b1;
  end 
end


assign number_feedback=switch_num;

assign key_detemine_feedback = key_detemine ;
assign key_displace_feedback = key_displace;
endmodule

三、实现思路

        对LCD的驱动采用状态机的方式编写,状态转移图如下:

         我采用的是一直刷新显示的思路,因此不方便观察光标,也没法精准控制光标,读者可以尝试修改状态转移图,使得输出显示一次之后回落到空闲状态,然后等待各种信号控制。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值