[SugerTangYL] LCD1602驱动程序Verilog

news/2025/1/9 10:22:25/

前言

        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) beginif (rst) begin// resetstate <= s_idle;data  <= 8'b0;rw    <= 1'b0;rs    <= 1'b0;addr  <= 8'b1000_0000;endelse begincase (state)s_idle        :beginstate <= s_clear;data  <= IDLE;rw    <= rw;rs    <= rs;ends_clear       :beginstate <= s_inputmode;data  <= CLEAR;rw    <= 1'b0;rs    <= 1'b0;ends_inputmode   :beginstate <= s_switchmode;data  <= INPUTMODE;rw    <= 1'b0;rs    <= 1'b0;ends_switchmode  :beginstate <= s_shiftmode;data  <= SWITCHMODE;rw    <= 1'b0;rs    <= 1'b0;ends_shiftmode   :beginstate <= s_setfunction;data  <= SHIFTMODE;rw    <= 1'b0;rs    <= 1'b0;ends_setfunction :beginstate <= s_setdataaddr1;data  <= SETFUNCTION;rw    <= 1'b0;rs    <= 1'b0;ends_setdataaddr1:beginstate <= s_writecgram1;data  <= addr;rw    <= 1'b0;rs    <= 1'b0;ends_writecgram1 :beginif (addr == 8'b1000_1111) beginstate <= 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;endelse beginstate <= 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                        ends_setdataaddr2:beginstate <= s_writecgram2;data  <= addr;rw    <= 1'b0;rs    <= 1'b0;ends_writecgram2 :beginif (addr == 8'b1100_1111) beginstate <= 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;endelse beginstate <= 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                        enddefault       :beginstate <= state;data  <= data;rw    <= rw;rs    <= rs;endendcaseend
endfunction [7:0]lcd_data;input [7:0]lcd_addr;input [3:0] num_5,num_4,num_3,num_2,num_1,num_0;begincase(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;endcaseend
endfunctionfunction [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
endfunctionreg[2:0] flag=0;
reg [3:0] num_5,num_4=1,num_3,num_2,num_1,num_0;
always @(posedge key_detemine)
begincase (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)
beginif(flag>=3'b101)flag<=0;else flag<=flag+1;
endparameter CLK_LCD_times = 19'd030000;
reg [22:0]cnt;
reg CLK_LCD;
reg CLK_500Hz;always@(posedge ClOCK_50 or posedge rst) beginif(rst) begincnt <= 19'd0;CLK_500Hz <= 1'b0;endelse if(cnt == CLK_LCD_times) begincnt <= 19'd0;CLK_LCD <= ~CLK_LCD;endelse begin cnt <= cnt+1'b1;end 
endassign number_feedback=switch_num;assign key_detemine_feedback = key_detemine ;
assign key_displace_feedback = key_displace;
endmodule

三、实现思路

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

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


http://www.ppmy.cn/news/192973.html

相关文章

Linux系统Realtek网卡驱动安装

Ubuntu有时会不支持电脑的无线网卡&#xff0c;这时就需要自己安装了。 可以在官网找安装文件或者github上找源码编译安装。 Realtek无线网卡的安装包可以在github下载(https://github.com/rtlwifi-linux/rtlwifi_new)&#xff0c;然后进入目录 $make $sudo make install …

HT1622 驱动程

#include <reg51.h> #define uchar unsigned char #define uint unsigned int sbit LCD_CS P1^0; //端口还没按原理图改过来&#xff0c;应为p0^0~p0^3; sbit LCD_RD P1^1; sbit LCD_WR P1^2; sbit LCD_DATA P1^3; / //-------------------------------------------…

物联网LoRa系列-22:LoRa终端--射频芯片SX1261 SX1262的LoRa专用调制解调器与配置

目录结构&#xff1a; 1. SX1261/2功能模块 2. 调制技术的基本原理 3. LoRa调制解调的介绍 4. LoRa调制解调的寄存器设置 1. SX1261/2功能模块&#xff1a; MCU与SX1261/2物理上通过两组接口进行通信&#xff1a; &#xff08;1&#xff09;SPI总线 &#xff08;2&#x…

基于FPGA的LCD1602驱动

一、功能描述 本设计实现LCD_1602的接口&#xff0c;具体功能定义如下&#xff1a; 1、异步复位信号&#xff1b; 2、按下复位键后在LCD_1602液晶屏显示内部设置好的字符,每间隔0.1秒显示一个字符。 二、输入输出信号描述 三、设计思想 1、首先把50MHz的时钟信号转化为10Hz的…

光纤收发器tx和rx的区别?

光纤收发器&#xff0c;是一种将短距离的双绞线电信号和长距离的光信号进行互换的以太网传输媒体转换单元&#xff0c;在很多地方也被称之为光电转换器&#xff08;Fiber Converter&#xff09;。产品一般应用在以太网电缆无法覆盖、必须使用光纤来延长传输距离的实际网络环境中…

液晶LCD1602驱动代码

液晶LCD1602简介 LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏&#xff08;LCD&#xff09;、控制驱动主电路HD44780及其扩展驱动电路HD44100&#xff0c;以及少量电阻、电容元件和结构件等装配在PCB板上而组成。不同厂家生产的LCD1602芯片可…

DP1621国产LCD驱动芯片兼容替代HT1621B

目录 DP1621简介DP1621芯片特性 DP1621简介 DP1621是点阵式存储映射的LCD驱动器芯片&#xff0c;可支持最大128点&#xff08;32SEG * 4COM&#xff09;的 LCD屏&#xff0c;也支持2COM和3COM的LCD屏。单片机可通过3/4个通信脚配置显示参数和发送显示数据&#xff0c;也可通过…

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

目录 LCD1602显示原理 LCD1602接口 LCD1602操作时序 &#xff08;1&#xff09;读操作时序 &#xff08;2&#xff09;写操作时序 LCD1602初始化 LCD1602读写数据 LCD1602显示原理 将LCD显示屏与FPGA连接之后&#xff0c;需要做的第一件事就是进行LCD驱动&#xff08;也…