fpga实训
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
信息工程学院电子信息系专业实训报告题目:基于verilog语言的LCD字符显示
专业班级:电子信息1班
学号:1068100205
姓名:那钦
指导教师:侯海鹏
基于verilog语言的LCD字符显示
实训目的:(小四号字)
1.掌握verilog语言的使用, Verilog HDL是一种硬件描述语言,是一种以文本形式来描述数字系统硬件的结构和行为的语言。
Verilog HDL就是在用途最广泛的C语言的基础上发展起来的一种件描述语言,Verilog HDL的最大特点就是易学易用,如果有C语言的编程经验,可以在一个较短的时间内很快的学习和掌握,
2.掌握FPGA的使用流程, 了解在EDA平台上进行数字电路集成设计的整个流程,熟悉QuartusⅡ软件环境下简单VHDL文本等输入设计方法,掌握利用Quqrtus Ⅱ的波形仿真工具验证设计的过程。
3.掌握spartan 3e开发板的使用,和各个模块的使用, 理解RS触发器的设计原理,采用QuartusII集成开发环境,利用VHDL硬件描述语言进行设计。
4.掌握简单的FPGA项目开发的流程。
实训时间、地点:
1.时间:2014年6月16日——2014年6月27日
2.地点:内蒙古科技大学FPGA应用技术实验室
实训内容:
1.设计原理
入门实验板显著的特征是2线16字符液晶显示器LCD。
尽管LCD支持8位的数据接口,为了与其它的XILINX的开发板保持兼容并且尽可能减少针脚数,FPGA仅通过4位的数据接口线控制LCD,如图5.1所示。
LCD通过使用ASCII标准和自定义字符可以有效地显示多种信息。
但是,这些显示速度并不是很快。
每半秒扫描一次以测试实际清晰度的界限。
与50MHz时钟频率相比,这样的显示速度是慢的。
PicoBlaze 处理器可以有效地控制显示时间和显示内容。
LCD接口特性
表5.1给出了字符LCD接口的接口信号。
字符LCD的供电电压是+5V。
FPGA的I/O口信号的电压是3.3V。
但是,FPGA的输出电平是通过LCD来识别是有效的低电平还是高电平。
LCD控制器接收5V TTL信号电平,FPGA
输出3.3V的LCMOS以满足5V TTL电压要求。
数据线上的390欧串联电阻,当LCD驱动一个逻辑高电平时,其用来防止了FPGA和SrtataFlsah I/O管脚的超负载。
当LCD_RW为高时,LCD驱动数据线。
在绝大多数应用中,LCD 作为只读外围设备,几乎没有从显示器读数据。
LCD数据线与StrataFlash 数据线SF_D<11:8>复用
如图5.1所示,4根LCD数据线与StrataFlash 数据线SF_D<11:8>复用。
正如表5.2所示,LCD/StrataFlash 存储器依赖于设计而交互使用。
当存储器失能时(SF_CE0=1),FPGA用作全读/写通道给LCD。
相反,当LCD读失能时(LCD_RW=0),FPGA用作全读/写通道给存储器。
要是StrataFlash 存储器为字节宽度×8模式(SF_BYTE=0),FPGA作为全同步读/写通道给LCD 和存储器。
在字节模式下,存储器没有用到SF_D<15:8>数据线。
UCF位置约束
该2×16字符LCD内嵌一个Sitronix ST7066U图形控制器。
其功能与下面几个一样:Samsung S6A0069X or KS0066U、 Hitachi HD44780和SMOS SED1278。
寻址地址
该控制器有三个内部存储空间,每个都有专门用途。
送数据给这些空间之前必须初始化。
1)DD RAM
显示数据RAM(DD RAM)存储字符编码。
绝大多数应用中,都是与DD RAM相结合的。
存储在DD RAM中的字符编码所涉及的特定的字符位图要么存在CG ROM字符设置中,要么存在用户自定义的CG RAM的字符设置中。
图5.3给出了显示器32位字符位置的默认地址。
字符的最上行存储在地址0X00与0X0F之间。
第二行的字符存储在地址0X40与0X4F之间。
从物理上讲,DD RAM一共有80个字符位置,每行有40个字符。
位置0X10到0X27和0X50到0X67之间的地址用来存储其它非显示数据。
此外,这些位置也可以存储只有使用控制器的显示移位功能才能显示的字符。
往DD RAM读或写之前,DD RAM 地址命令得初始化地址计数器。
写DD RAM数据使用写数据到CG RAM 或DD RAM命令,读DD RAM 使用从CG RAM或DD RAM命令读数据。
DD RAM地址计数器要么在读或写之后保持常数,要么自动增加1或自动减1。
2) CG ROM
字符产生器ROM(CG ROM)包括每个事先定好的字符的字体位图,这样LCD屏才能显示,如图5.4。
字符编码存储在DD RAM 中,每个字符的位置与CG ROM的位置按顺序对应。
例如,0X53的一个16进制的字符编码存储在DD RAM中的位置显示字符是“S”。
0X53最上面的轻咬位(高半位)等同于DB[7:4]=0101和最低的轻咬位(低半位)等同于DB[3:0]=0011。
如图5.4所示,字符“S”就显示在屏幕上了。
英语/罗马字符存储在CG ROM相应的ASCII编码地址中。
字符ROM存储ASCII英文字符和日本字符。
控制器同样提供了8位自定义字符位图,存储在CG RAM中。
这些8位的自定义字符编码显示时存储在DD RAM 的0X00与0X07之间。
3)CG RAM
字符产生器RAM(CG RAM)提供空间给8位的自定义字符位图。
每个自定义字符位由8行位图的5个点组成,如图5.5所示。
往CG RAM读或写之前CG RAM 地址命令得初始化地址计数器。
写CG RAM数据使用写数据到CG RAM 或DD RAM命令,读CG RAM 使用从CG RAM 或DD RAM命令读数据。
CG RAM地址计数器要么在读或写之后保持常数,要么自动增加1或自动减1。
图5.5举了个例子,产生一个特殊的西洋跳棋盘字符。
自定义字符存储在第四CG RAM 字符位置中,当DD RAM的位置是0x03时,其显示。
写自定义字符时,使用设置CG RAM 地址命令初始化CG RAM地址。
前三行的地址位对应自定义字符位。
后三行位对应字符地址的行地址。
写数据到CG RAM 或DD RAM命令用来写每个字符位行。
“1”表示点亮。
“0”表示熄灭。
只有低5位的数据被用到。
高三位的数据与位置无关。
第8行的数据位一般为0以适于指针之用。
控制命令和位的定义
表5.3简要的说明了LCD控制器的命令和位的定义。
由于该显示屏是4位操作,每8位命令被送到2个4位的轻咬位(2个半位)。
高半位先送,低半位后送。
1)失能
如果LCD_E使能信号为低,所有其它输入LCD信号全被忽视。
2)清屏
清屏后指针返回到原始位置——最左上角。
该命令写一个空白内容(ASCII/ANSI字符编码为0x20)给所有的DD RAM 地址。
DD RAM的0X00地址计数器置0。
清除所有的选择设置。
I/D 控制位置1(地址自动增加模式)。
执行时间:82us~1.64ms
3)返回指针原始位
指针返回原始位——最左上角。
DD RAM的内容不受影响。
所有的显示被移到原始位,如图5.3所示。
DD RAM的0X00地址计数器置0。
如果移位,显示返回原始状态。
指针或光标移到字符位的最左上角。
执行时间:40us~1.6ms
4)进入模式设置
设置指针移动的方向,并规定是否移动显示。
在读和写数据时,这些操作就完成了。
执行时间:40us
在每次写数据给CG RAM或DD RAM或从CG RAM 或DD RAM 读数据后,该位的DD RAM和CG RAM地址计数器要么自动增加1要么自动减少1.指针或光标的位置随之移动。
1)显示关/断
显示关或断,控制所有的字符、指针和指针位置的字符光标。
执行时间:40us
指针使用字符最底行的5个点。
指针出现在显示字符的下面。
2)指针和显示移动
移动指针和显示并不改变DD RAM 的内容。
移动指针位置或显示往左或往右时并不需要写或读显示数据。
指针的位置功能是为了修改个别的字符,或向左或右滚动窗口来显示存储在DD RAM 中的额外数据,可以移到每行的第16个符。
当它移到第一行的第40个字符之处时,指针自
动移到第二行。
两行的显示移动在同一时间进行。
当显示数据重复移动时,两行水平移动。
第二行不会移到第一行。
执行时间:40us
3)功能设置
设置接口数据的长度,每行显示的个数,字符的字体。
入门实验板支持单功能设置,其值为0X28。
执行时间:40us
7)设置CG RAM地址
设置CG RAM的初始地址。
该命令后,以后所有往显示屏的读或写操作的数据来自或去往CG RAM。
执行时间:40us
8)设置DD RAM 地址
设置DD RAM的初始地址。
该命令后,以后所有往显示屏的读或写操作的数据来自或去往DD RAM。
执行时间:40us
9)读忙标志和地址
读忙标志(BF)用来判断内部操作是否在进行,并读当前地址计数器的内容。
BF=1说明内部操作在进行。
下个指令不被接收直到BF被清0或直到当前指令达到最大的执行时间。
该命令返回当前地址计数器的值。
地址计数器为CG RAM和DD RAM所用。
具体内容取决于最新公布的设置CG RAM地址或设置DD RAM设置命令。
执行时间:1us
10)写数据给CG RAM或DD RAM
要是该命令在设置DD RAM 地址命令之后,则写数据给DD RAM;或是该命令在设置CGRAM 地址命令之后,则写数据给CGRAM。
根据进入设置模式,在写操作之后,地址自动加1或自动减1。
进入设置模式同样决定显示移动。
执行时间:40us
11)从CG RAM或DD RAM读数据
要是该命令在设置DD RAM 地址命令之后,则从DD RAM读数据;或是该命令在设置CGRAM 地址命令之后,从CG RAM读数据。
根据进入设置模式,在写操作之后,地址自动加1或自动减1。
进入设置模式同样决定显示移动。
执行时间:40us
Four-Bit 数据接口
该板使用4位数据接口给字符LCD。
图5.6说明了向LCD写操作建立、保持允许的最小时间以及使能脉冲对时钟(50MHz 或T=20ns)的偏移时间。
DF_D<11:8>的数据值,寄存器选择信号(LCD_RS)以及读/写(LCD_RW)控制信号必须建立并在使能信号LCD_E转向高电平之前至少稳定40ns。
使能信号必须保留高电平230ns 或更长时间——等于或超过12时钟周期(50MHz)。
在许多应用中,LCD_RW信号可以永远置低,因为FPGA一般不会从显示屏读取数据。
8位数据传输4比特接口
在初始化显示屏和建立通信之后,所有的命令和数据以8位形式传送给字符显示屏——用2个连续的4位传送。
每个8位传送必须分为2个4位,其间隔时间至少1us,如图5.6所示。
先传高半位,再传低半位。
一个8位的写操作在下个通信之前必须间隔至少40us。
在清屏命令之后,该延时必须增至1.64ms。
初始化
上电后,显示屏必须初始化建立所需的通信协议。
该初始化步骤简单,完全适合高效的8位PicoBlaze 嵌入式控制器。
初始化之后,PicoBlaze 控制器除了简单驱动显示屏外,还用来更为复杂的控制或计算。
1)上电初始化
初始化的第一步骤是建立FPGA与LCD的4位的数据接口,具体如下:
A:等待15ms或更长,尽管FPGA完成配置后显示屏一般处于准备就绪状态。
在50MHz 时,15ms时间等于750000时钟周期。
B:写SF_D<11:8>=0x3,LCD_E保持高电平12时钟周期。
C:等待4.1ms或更长,即在50MHz时,205000时钟周期。
D:写SF_D<11:8>=0x3,LCD_E保持高电平12时钟周期。
E:等待100us或更长,即在50MHz时,5000时钟周期。
F:写SF_D<11:8>=0x3,LCD_E保持高电平12时钟周期。
G:等待40us或更长,即在50MHz时,2000时钟周期。
H:写SF_D<11:8>=0x2,LCD_E保持高电平12时钟周期。
I:等待40us或更长,即在50MHz时,2000时钟周期。
2)显示屏配置
上电初始化完成后,4位的数据接口就建立了。
下一步就是配置显示屏了:
A:发一个功能设置命令,0x28,配置显示屏。
B:发一个进入模式命令,0X06,设置显示屏自动增地址指针。
C:发一个显示开/断命令,0x0c,开显示屏并失能指针和光标。
D:最后,发清屏命令,此后等待至少1.64ms(82000时钟周期)。
写入数据显示
写数据给显示屏,指定初始地址,紧接着是一个或多个数据值。
写任何数据之前,发送一个设置DD RAM地址命令给DD RAM中指定的初始7位地址。
见图5.3所示。
使用写数据给显示屏使用CG RAM 或DD RAM命令。
8位数据值通过查表地址送给CG ROM或CG RAM,如图5.4所示。
CG ROM 或CG RAM中存储的位图驱动5×8点阵给相应的字符。
如果地址计数器配置为自动增1,正如前面所说的,这种应用方法可以次序写多个字符编码,每个字符自动存储并显示在下个位置。
继续写字符,但是,在第一显示行的最后停止。
剩余的数据不会自动在第二行显示,因为DD RAM的映射从第一行到第二行并不是连续的。
如果FPGA的应用不使用LCD显示屏,置低LCD_E管脚,失能它。
同样,置低LCD_RW管脚,阻止LCD屏回传数据。
2.设计内容
本设计是基于FPGA的verilog语言设计的,主要完成在lcd1602上显示字符,包括时钟的设计,屏幕模块的设计和字符显示的设计。
时钟设计代码如下:
always@(posedge clk_50)
if(counter==25000)
begin
clk_1k<=~clk_1k;//分频模块
counter<=0; //分频至2000HZ(0.5ms)
end
else counter<=counter+1;
屏幕设置代码如下:
1:begin
lcd_data<=4'b0010; //0x28 四线输入
lcd_rs<=1'b0; //送2
lcd_en<=1'b1;
end
3:begin
lcd_data<=4'b1000; //送8
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
5:begin //0x0c
lcd_data<=4'b0000;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
7:begin
lcd_data<=4'b1100;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
9:begin //0x01
lcd_data<=4'b0000;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
11:begin //0x01
lcd_data<=4'b0001;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
15:begin //0x06
lcd_data<=4'b0000;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
17:begin //0x06
lcd_data<=4'b0110;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
字符显示部分,需要对第一行和第二行显示位置进行设置,此处给出的代码只显示第一行,和一个字符,代码如下:
19:begin
lcd_data<=4'h8;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
21:begin
lcd_data<=4'h0;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
23:begin
lcd_data<=4'h5;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
25:begin lcd_data<=4'h4;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
27:begin
lcd_data<=4'h6;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
29:begin lcd_data<=4'h8;
lcd_rs<=1'b1;
lcd_en<=1'b1;
End
31:begin
lcd_data<=4'h6;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
33:begin lcd_data<=4'h5;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
实训总结:
本次实训是两周,时间不算充裕,通过这次期末实验,更进一步认识了Verilog 语言的使用,详细了解了整个设计制作和仿真流程,独立思考并通过一步步的调试,逐步摸索和进一步优化程序最终完成实验,锻炼了逻辑思维能力。
实验综合性较强,在实验中涉及了本学期多个知识点:①always语句;②initial语句;
③if—else语句;④case语句;⑤传递函数assign;⑥function说明语句。
在测试和调整程序时发现一些容易犯错的问题:
第一、用case语句取代了定义ROM造成了数据开始并没有固化在ROM中而是从程序中写入再读出。
(解决办法:用function语句对ROM进行定义并装初值)
第二、应该注意传递函数中的输出端口应该是wire型的,如果定义为reg将会出错。
第三、开始时波形数据按照图放入初值时,有负数、小数等,当装入负数小数后在仿真中无法正确显示数字。
(解决办法:将波形平移扩大将小数和负数换算成整数装入即可显示波形数据)
第四、关键的问题是怎样实现寻址的操作,首先是定义一个从0—7的循环加法计数器作为段内基地址寻址,然后通过case语句choose波形选择相应波段的段地址。
个人觉得这样的实验比较贴近学习和工作,通过自己动手更加能够充分掌握所学知识点,将书本和实际结合起来,希望以后可以开展更多这样的实验。
附录
程序:
`timescale 1ns / 1ps
module lcd(clk_50,lcd_data,lcd_rw,lcd_rs,lcd_en);
input clk_50;
output [3:0] lcd_data;
output lcd_rw,lcd_rs,lcd_en;
reg [3:0] lcd_data;
reg lcd_rs;
reg lcd_en;
reg clk_1k=1'b0;
reg [20:0] counter=0;
reg [10:0] counter1=0;
reg [5:0]j=0;
reg [5:0]k=0;
reg [5:0]num=0;
assign lcd_rw=0;
//*************************************************
//模25000进行分频,分频至2000Hz
always@(posedge clk_50)
if(counter==25000)
begin
clk_1k<=~clk_1k;//分频模块
counter<=0; //分频至2000HZ(0.5ms)
end
else counter<=counter+1;
always@(posedge clk_1k)
//*******************************************************************
*********************************
begin
if(num<10)
begin
if(j<40)
begin
j<=j+1;
end
else begin
num<=num+1;
case(num)
1:begin
lcd_data<=4'h3;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
3:begin
if(k<10)
k<=k+1;
else
begin
lcd_data<=4'h3;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
end
5:begin
lcd_data<=4'h3;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
7:begin
lcd_data<=4'h2;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
default:lcd_en<=1'b0;
endcase
end
end
else if(counter1<150)
begin
counter1<=counter1+1;
case (counter1)
1:begin
lcd_data<=4'b0010; //0x28 四线输入 lcd_rs<=1'b0; //送2
lcd_en<=1'b1;
end
3:begin
lcd_data<=4'b1000; //送8
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
5:begin //0x0c
lcd_data<=4'b0000;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
7:begin
lcd_data<=4'b1100;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
9:begin //0x01
lcd_data<=4'b0000;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
11:begin //0x01
lcd_data<=4'b0001;
lcd_en<=1'b1;
end
15:begin //0x06 lcd_data<=4'b0000;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
17:begin //0x06 lcd_data<=4'b0110;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
//第一行地址设置0x80
19:begin lcd_data<=4'h8;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
21:begin lcd_data<=4'h0;
lcd_rs<=1'b0;
lcd_en<=1'b1;
end
23:begin
lcd_data<=4'h2;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
25:begin
lcd_data<=4'h0;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
27:begin
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
29:begin lcd_data<=4'h6;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
31:begin
lcd_data<=4'h5;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
33:begin lcd_data<=4'h0;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
35:begin
lcd_data<=4'h4;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
37:begin lcd_data<=4'h7;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
39:begin
lcd_data<=4'h4;
lcd_rs<=1'b1;
lcd_en<=1'b1;
end
41:begin lcd_data<=4'h1;
lcd_en<=1'b1;
end
default:lcd_en<=1'b0;//数据显示执行
endcase
end
else counter1<=86;//完成第一行后进行时钟不断更新
end
Endmodule
答辩记录。