基于Verilog HDL LCD1602显示器的设计

这篇具有很好参考价值的文章主要介绍了基于Verilog HDL LCD1602显示器的设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

昨天刚结束FPGA的课程设计,做的题目是用Verilog HDL编写LCD1602字符显示程序,并在开发板DE2-115上进行演示,实现的功能是显示移动字符和滚动字符,并通过一个开关来控制模式的切换。此次课程设计参考了网站上许多前辈大佬的文章,在他们的基础上进行修改。但发现许多的文章仅仅介绍了如何显示静态字符,而没有介绍滚动字符显示如何编写,遂由此写下这篇博客,希望对有需要的人有所帮助。

一、设计任务

基于Verilog HDL 液晶显示控制器的设计
(1)掌握LCD1602字符型液晶显示器工作和时序原理;
(2)在LCD1602液晶显示器上面实现静态字符;
(3)扩展功能:在LCD1602液晶显示器实现动态字符。


二、综合设计部分

1.设计原理及方案

(1)LCD1602介绍

基于Verilog HDL LCD1602显示器的设计,fpga开发

LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符,显示容量:16×2个字符,每个字符为5*7点阵。

①引脚

基于Verilog HDL LCD1602显示器的设计,fpga开发

该设计只需要向LCD写入指令或者字符数据,故RW引脚可以直接置低电平。注:在DE2-115中使用的LCD模块并不含背光单元,故而LCD_BLON信号在用户工程中的设定是无效的,因此模块代码中不添加LCD_BLON输出(此提示可以在DE2-115数据手册LCD1602模块中看到)。

②操作时序

该设计只需要写操作,故此处只介绍LCD1602的写操作时序。RW=0,RS=0/1(控制写入的是字符数据还是指令)
基于Verilog HDL LCD1602显示器的设计,fpga开发
基于Verilog HDL LCD1602显示器的设计,fpga开发

首先将要写入的数据DB0-DB7准备好,再拉高E并维持一段时间(大于150ns),最后将E拉低,DB0-DB7的数据写入LCD。通过控制RS等于0或者1来实现指令/字符数据的写入。
注:这个地方有个问题,E脉冲宽度给150ns根本就不行,最后参考单片机中的教程,整个周期给了2ms,E脉冲宽度给了1ms,最终实现了。(此条提示参考:基于FPGA的LCD1602驱动设计(含有代码))

③指令集

LCD1602有非常多的指令,此处只介绍本设计所用到的几条指令。
0X38:开显示
0X08:显示关,不显示光标,光标不闪烁
0X0C:显示开,不显示光标,光标不闪烁
0X01:清除屏幕显示内容,光标返回屏幕左上角
0X06:写入数据光标右移,不显示移动
0X18:整屏字符左移
0X80:第一行首个字符的位置
0XC4:第二行中间字符的位置
注:字符位置的设置可参考基于FPGA的LCD1602驱动(含代码)
完整指令集如下
基于Verilog HDL LCD1602显示器的设计,fpga开发

(2)LCD1602驱动流程

①LCD初始化

根据数据手册,LCD的初始化需要完成下面7步:
1 写指令38H
2 延时15ms
3 写指令38H(检测忙)
4 写指令08H(检测忙)
5 写指令01H(检测忙)
6 写指令06H(检测忙)
7 写指令0CH(检测忙)
因LCD1602相较于单片机来说是慢速设备,所以在发送下一条指令时需要检测LCD是否处理完上一条指令,也就是检测BF位(可以发送完整指令集中的9.读BF及AC值),而根据上图lcd指令执行时间可以得知每条指令的执行时间为us级,因此我们此处可以将每条指令维持时间给到2ms(详见下面代码部分),就不需要检测BF位了。
注:此设计通过延时来避免读BF位的操作是根据这篇博客基于FPGA的LCD1602显示屏驱动中4、LCD1602指令(9)的解释。

②LCD写数据

此设计初始化需要发送6条指令,每条指令可以定义为1个状态用于赋值,或者整个初始化过程可以定义为1个状态再用循环来写入指令,完成后写入第一行的地址0X80(此时RS仍为0)。之后开始写入数据RS拉高等于1,不断写入再判断是否写到第一行末尾字符(这个可以自行设定),如果到了我们再写入第二行的地址0XC4(为了显示效果,我们从第二行中间位置开始显示),写完之后我们进入stop状态,也即显示静态字符。动态字符的显示与静态字符的显示有些许差别,需要再添加一个状态我们命名为dongtai(写入0X18指令),每进入一次该状态整屏字符就会往左平移一次,之后跳回stop状态,如果此时接着跳回dongtai状态则会因为两次平移间隔的时间太短,人眼难以观察,显示的效果不好。因此在滚动字符模式下,当我们进入stop状态,开始计时400ms(大于400ms也行,可以自己调节),计时结束后再跳入dongtai状态,这样两次平移之间的间隔就为400ms,人眼可以观察清楚。
(1)
基于Verilog HDL LCD1602显示器的设计,fpga开发
(2)
基于Verilog HDL LCD1602显示器的设计,fpga开发
(3)
基于Verilog HDL LCD1602显示器的设计,fpga开发

2.仿真结果及分析

基于Verilog HDL LCD1602显示器的设计,fpga开发
移动字符模式(mode=1),由仿真图可以知道,lcd_rs在①阶段完成了LCD的初始化配置,并在最后写入第一行首个字符的地址,在②阶段写入所要显示的字符。当第一行显示完成时,给它写入第二行首个字符的地址,待所有字符写入完成,在③阶段写入指令进入stop(0x38)状态并维持一段时间再跳入dongtai(0x18)状态,使字符整屏移动,两个状态相互跳转实现字符移动效果。
基于Verilog HDL LCD1602显示器的设计,fpga开发
静态字符模式(mode=0),静态字符模式与移动字符模式①②阶段相同,只是在③阶段一直维持stop状态,即一直写入0x38指令。

3.硬件调试

此次设计使用DE2-115开发板进行验证
(1)器件选择
基于Verilog HDL LCD1602显示器的设计,fpga开发

具体器件名可看开发板芯片
(2)引脚绑定

基于Verilog HDL LCD1602显示器的设计,fpga开发
基于Verilog HDL LCD1602显示器的设计,fpga开发
板子上50MHZ时钟可通过PIN_Y2输入
根据DE2-115使用手册进行LCD引脚配置,模式控制信号(此处我们与下图最后一排开关从左往右数第二个开关进行绑定)和刷新信号(此处我们与下图最后一排开关从左往右数第一个开关进行绑定)可自定义引脚。
(3)连接电脑
基于Verilog HDL LCD1602显示器的设计,fpga开发

白线为USB线,黑线为电源线。
(4)正确导入管脚分配表,无错误后,重新综合整个工程
基于Verilog HDL LCD1602显示器的设计,fpga开发

(5)综合完毕,无错误后,连接好开发板,开启电源,准备下载。
基于Verilog HDL LCD1602显示器的设计,fpga开发
基于Verilog HDL LCD1602显示器的设计,fpga开发

(6)配置完成后下载
基于Verilog HDL LCD1602显示器的设计,fpga开发

注意,下载时要将板子上的开关SW19拨到RUN
(7)固化文件下载
上述烧录的程序为非固化文件,每次重新上电后需要重新烧录,因此我们可以将文件转换为固化文件,再将其烧录,避免程序掉电丢失。
基于Verilog HDL LCD1602显示器的设计,fpga开发
基于Verilog HDL LCD1602显示器的设计,fpga开发
基于Verilog HDL LCD1602显示器的设计,fpga开发
基于Verilog HDL LCD1602显示器的设计,fpga开发

一般会显示文件超过了最大容量,因此在这里将文件压缩
基于Verilog HDL LCD1602显示器的设计,fpga开发
成功生成固化文件(.jic)
下载固化文件,首先将原先的文件删除掉,再点击添加文件、Start,烧录完成之后重新上电,即可观察到相应现象。
基于Verilog HDL LCD1602显示器的设计,fpga开发

如果想要擦除文件,将原先的√取消掉,再勾选Erase,最后点击Start即可。
烧录程序和固化文件下载可以参考这两篇博客
Quartus Prime硬件实验开发(DE2-115板)实验一CPU指令运算器设计
【工具教程】FPGA程序掉电保存,jic模式烧写EPCS,代替AS方式


4.完整代码

module lcd1602(
	input				clk,//50MHZ
	input				rst_n,
	input    wire  mode,//模式设置,静态字符(0),移动字符(1)
	output   wire  lcd_on,//开lcd      
	output	reg	lcd_rs,//数据/命令
	output	wire	lcd_rw,//写/读
	output	reg	lcd_en,
	output	reg	[7:0]	lcd_data	
	);
	
	reg	[17:0]	cnt;
	reg	[3:0]	state_c;//现态
	reg	[3:0]	state_n;//次态
	reg	[4:0]	char_cnt;//字符序号
	reg	[7:0]	data_display;//显示字符
	
	localparam
		IDLE			= 4'd0,//设置显示模式并延时15ms
		INIT 			= 4'd1,//再次设置显示模式
		S0				= 4'd2,//关闭显示
		S1				= 4'd3,//清屏
		S2				= 4'd4,//光标移动设置(写入新数据后光标右移)
		S3				= 4'd5,//显示开,不显示光标,光标不闪烁
		ROW1_ADDR	= 4'd6,//第一行地址
		WRITE			= 4'd7,
		ROW2_ADDR	= 4'd8,//第二行地址
		stop			= 4'd9,
		dongtai     = 4'd10;//移位
	assign lcd_rw = 1'b0;//写操作
   assign lcd_on = 1'b1;//lcd上电
	
	
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			cnt <= 18'd0;
		end
		else begin
			if (cnt==18'd100_000 - 1) begin//2ms清零
//			if (cnt==17'd4 - 1) begin//仿真
				cnt <= 18'd0;
			end
			else begin
				cnt <= cnt + 1'b1;
			end
		end
	end
 
   always @(posedge clk or negedge rst_n) begin//lcd_en周期为2ms,高电平1ms
		if (!rst_n) begin
			lcd_en <= 0;
		end
		else if (cnt==18'd50_000 - 1) begin
//		else if (cnt==17'd2 - 1) begin//仿真
			lcd_en <= 1;
		end
		else if (cnt==18'd100_000 - 1) begin
//		else if (cnt==17'd4 - 1) begin//仿真
			lcd_en <= 0;
		end
	end
 
	always @(posedge clk or negedge rst_n) begin//cnt=100_000时清零,所以每写一个字符间隔2ms
		if (!rst_n) begin
			char_cnt <= 0;
		end
		else if (state_c==WRITE && cnt==18'd50_000 - 1) begin
//		else if (state_c==WRITE && cnt==17'd2 - 1) begin//仿真
			if (char_cnt==5'd22) begin
				char_cnt <= 5'd0;
			end
			else begin
				char_cnt <= char_cnt + 1'b1;
			end
		end
	end
 
	always @(*) begin
		case(char_cnt)
			5'd0: data_display   = "H";
			5'd1: data_display   = "A";
			5'd2: data_display   = "P";
			5'd3: data_display   = "P";
			5'd4: data_display   = "Y";
			5'd5: data_display   = "-";
			5'd6: data_display   = "N";
			5'd7: data_display   = "E";
			5'd8: data_display   = "W";
			5'd9: data_display   = "-";
			5'd10: data_display  = "Y";
			5'd11: data_display  = "E";
			5'd12: data_display  = "A";
			5'd13: data_display  = "R";
			5'd14: data_display  = "!";
			5'd15: data_display  = "-";
			5'd16: data_display  = "L";
			5'd17: data_display  = "C";
			5'd18: data_display  = "D";
			5'd19: data_display  = "1";
			5'd20: data_display  = "6";
			5'd21: data_display  = "0";
			5'd22: data_display  = "2";
			default:data_display = "H";
		endcase
	end
	
	always @(posedge clk or negedge rst_n) begin//状态持续时间2ms,保证每个状态下发送的指令被lcd接收
		if (!rst_n) begin
			state_c <= IDLE;
		end
		else if(cnt==18'd50_000 - 1) begin//因cnt到100_000被清零,所以次态赋给现态时刻为50_000-150_000-250_000,也就是每2ms更新一次状态
//		else if(cnt==17'd2 - 1) begin//仿真
			state_c <= state_n;
		end
	end
 
   reg	[19:0]	cnt_15ms;//上电延时等待
	reg		flag;
	always@(posedge clk or negedge rst_n)begin
		if (!rst_n) begin
			cnt_15ms <= 0;
		end
		else if (state_c == IDLE) begin
			cnt_15ms <= cnt_15ms + 1'b1;
		end
	end
	always@(posedge clk or negedge rst_n)begin//判断延时是否完成
		if (!rst_n) begin
			flag <= 0;
		end
		else if (state_c==IDLE && cnt_15ms==20'd750_000) begin//15ms
//		else if (state_c==IDLE && cnt_15ms==20'd3-1) begin//仿真
			flag <= 1;
		end
	end
	
	
	reg	[24:0]	cnt_400ms;//每发送一条移动指令间隔时间
	reg		flag_1;
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			cnt_400ms <= 25'd0;
		end
		else begin
			if(state_c==stop) begin
			   if(cnt_400ms==25'd20_000_000 - 1)begin
//			   if(cnt_400ms==25'd6 - 1)begin//仿真
				  cnt_400ms <= 25'd0;
				end
				else begin
				  cnt_400ms <= cnt_400ms + 1'b1;				
				end
			end
		end
	end
	always@(posedge clk or negedge rst_n)begin//判断延时是否完成
		if (!rst_n) begin
			flag_1 <= 0;
		end
		else if (state_c==stop && cnt_400ms==25'd20_000_000 - 1) begin//开发板主频50MHZ,也即每20ns记一次,计20_000_000次总共就400ms。
//		else if (state_c==stop && cnt_400ms==25'd6 - 1) begin//仿真
			flag_1 <= 1;
		end
		else if (state_c==dongtai)begin
		  flag_1 <= 0;
		end
	end	
	
	always @(*) begin//状态转换及模式选择
	if(mode==0)begin
		case(state_c)
			IDLE		:
				begin
					if (flag) begin
						state_n = INIT;
					end
					else begin
						state_n = state_c;
					end
				end
			INIT 	:
				begin
					state_n = S0;
				end
			S0  	:
				begin
					state_n = S1;
				end
			S1  	:
				begin
					state_n = S2;
				end
			S2  	:
				begin
					state_n = S3;
				end
			S3  	:
				begin
					state_n = ROW1_ADDR;
				end
			ROW1_ADDR:
				begin
					state_n = WRITE;
				end
			WRITE		:
				begin
					if (char_cnt==5'd15) begin//第一行最后一个字符
						state_n = ROW2_ADDR;
					end
					else if (char_cnt==5'd22) begin//第二行最后一个字符
						state_n = stop;
					end
					else begin
						state_n = state_c;
					end
				end
			ROW2_ADDR:
				begin
					state_n = WRITE;
				end
			stop		:
				begin
					state_n = stop;
				end
		endcase
		end
		else if(mode==1) begin
				case(state_c)
			IDLE		:
				begin
					if (flag) begin
						state_n = INIT;
					end
					else begin
						state_n = state_c;
					end
				end
			INIT 	:
				begin
					state_n = S0;
				end
			S0  	:
				begin
					state_n = S1;
				end
			S1  	:
				begin
					state_n = S2;
				end
			S2  	:
				begin
					state_n = S3;
				end
			S3  	:
				begin
					state_n = ROW1_ADDR;
				end
			ROW1_ADDR:
				begin
					state_n = WRITE;
				end
			WRITE		:
				begin
					if (char_cnt==5'd15) begin
						state_n = ROW2_ADDR;
					end
					else if (char_cnt==5'd22) begin
						state_n = stop;
					end
					else begin
						state_n = state_c;
					end
				end
			ROW2_ADDR:
				begin
					state_n = WRITE;
				end
			stop	 :
				begin
				if (flag_1) begin
					state_n = dongtai;
				end
				else begin
				   state_n = state_c;
				end
				end
			dongtai :begin
					state_n = stop;
			   end
		endcase
		end
	end
 
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			lcd_data <= 8'd0;
		end
		else begin
			case(state_c)
				IDLE		:begin lcd_data <= 8'h38; lcd_rs <= 0;end
				INIT 		:begin lcd_data <= 8'h38; lcd_rs <= 0;end
				S0			:begin lcd_data <= 8'h08; lcd_rs <= 0;end
				S1			:begin lcd_data <= 8'h01; lcd_rs <= 0;end
				S2			:begin lcd_data <= 8'h06; lcd_rs <= 0;end
				S3			:begin lcd_data <= 8'h0c; lcd_rs <= 0;end
				ROW1_ADDR	:begin lcd_data <= 8'h80; lcd_rs <= 0;end
				WRITE		:begin lcd_data <= data_display; lcd_rs <= 1;end
				ROW2_ADDR	:begin lcd_data <= 8'hc4; lcd_rs <= 0;end//第二行中间显示
				stop        :begin lcd_data <= 8'h38; lcd_rs <= 0;end
				dongtai     :begin lcd_data <= 8'h18; lcd_rs <= 0;end//移位
				default:;
			endcase
		end
	end
endmodule

注:新建VWF文件进行仿真时,将注释了仿真的语句取消注释,将该语句的上一行注释掉。

三、功能演示

功能演示

总结

第一次写博客难免有地方没有考虑仔细,欢迎在评论区进行讨论,如有错误也欢迎指正。文章来源地址https://www.toymoban.com/news/detail-789325.html

到了这里,关于基于Verilog HDL LCD1602显示器的设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 字符型液晶显示器LCD 1602的显示控制(Keil+Proteus)

    趁机把LCD 1602的实验完成了,那个电路图有几个地方没弄懂,但是去掉也没有报错,就没管了。 LCD1602_百度百科 (baidu.com) https://baike.baidu.com/item/LCD1602/6014393?fr=ge_ala LCD1602液晶显示屏通过电压来改变填充在两块平行板之间的液晶材料内部分子的排列状况,以达到遮光和透光的目

    2024年02月06日
    浏览(37)
  • 【TB作品】MSP430单片机 Proteus仿真 DS18B20温度 LCD1602显示器 温度读取与显示

    效果图如下: 首先,让我们先来说说DS18B20集成电路。 • DS18B20是一款采用OneWire通讯协议的集成电路,因此只需要一条线就可以与微控制器通讯。它不需要额外的电源,但是也有外部电源输入端口。 • OneWire设备具有64位的ROM代码。如我们之前所说,这64位的前8位是家族代码,中间

    2024年02月08日
    浏览(50)
  • LCD液晶显示器 ----------- 原理篇

    1. 背景 ​ 在做嵌入式开发时,经常用到 LCD 显示屏,因为用户只能看得懂显示屏中的画面。因此,熟练掌握显示屏原理是非常有必要的,所以让我们来揭开LCD 显示屏的神秘面纱,进而达到完全掌控的目的。本章之讲解 LCD 显示原理以及显示时序。 2. 基本概念 2.1 LCD 是什么 ​

    2024年02月05日
    浏览(39)
  • LCD—STM32液晶显示(1.显示器简介及LCD显示原理)(6000字详细介绍)

    目录 显示器简介 液晶显示器 液晶 像素 液晶屏缺点 LED显示器 OLED显示器 显示器的基本参数 STM32板载液晶控制原理(不带微控制器) 液晶控制原理 控制信号线(不带液晶控制器) 液晶数据传输时序 显存 总结 3.2寸液晶屏介绍(搭载液晶控制器) 3.2寸电阻触摸屏实物 ILI9341液晶

    2024年02月17日
    浏览(32)
  • 基于51单片机带显示器的音乐盒设计

    点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87512938?spm=1001.2014.3001.5503 源码获取 摘 要 单片微型计算机室大规模集成电路技术发展的产物,属于第四代电子计算机它具有高性能、高速度、体积小、价格低廉、稳定可靠、应用广泛的特点。他的

    2024年02月06日
    浏览(31)
  • 毕业设计 基于51单片机控制LED点阵显示器的设计

    序 🔥 毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的

    2024年02月02日
    浏览(53)
  • 36、基于51单片机频率计 LCD 1602显示系统设计

    数字频率计是一种基本的测量仪器。它被广泛应用于航天、电子、测控等领域,还被应用在计算机及各种数学仪表中。一般采用的是十进制数字,显示被测信号频率。基本功能是测量正弦信号,方波信号以及其他各种单位时间内变坏的物理量。由于其使用十进制数显示,测量

    2024年02月03日
    浏览(36)
  • 【FPGA显示驱动(Display)】- 使用Verilog实现8位数字显示器

    【FPGA显示驱动(Display)】- 使用Verilog实现8位数字显示器 FPGA是一种可编程的逻辑器件,它可以通过不同的配置来实现多种应用。在本文中,我们将探讨如何使用Verilog语言在FPGA上实现8位数字显示器。 硬件环境 Xilinx ISE Design Suite 14.7 FPGA开发板 八段数码管 设计实现 在Verilog代

    2024年02月04日
    浏览(34)
  • STM32单片机三线制PT100温度采集控制系统LCD12864显示器

    功能介绍:    

    2024年02月15日
    浏览(45)
  • 应用在LCD显示器电源插头里的氮化镓(GaN)MTC-65W1C

    LCD(Liquid Crystal Display)显示器是利用液晶显示技术来进行图像表现的显示装置,从液晶显示器的结构来看,无论是笔记本电脑还是桌面系统,采用的LCD显示屏都是由不同部分组成的分层结构。LCD显示器按照控制方式不同可分为被动矩阵式LCD及主动矩阵式LCD两种。 LCD显示器是

    2024年01月23日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包