FPGA综合实验.docx
《FPGA综合实验.docx》由会员分享,可在线阅读,更多相关《FPGA综合实验.docx(36页珍藏版)》请在冰点文库上搜索。
LCD(12864)显示实验
1、 实验简介
在这个实验中,将学习液晶显示模块LCD的基本知识以及如何使用EDA实验箱上的FPGA芯片控制板上的12864LCD来实现其显示功能。
12864LCD是很常用的图片字符型显示器件,熟练使用它降会带给学生很大的益处。
2、 学习目标
完成这个实验后,你将能够:
✧ 了解LCD模块的结构,存储及显示方式;
✧ 学会如何用LCD显示图片、文字、数字等符号信息;
✧ 学会使用FPGA芯片实现控制LCD显示功能。
3、 LCD的基本知识
3.1 LCD的结构
带中文字库的128X64是一种具有4位/8位并行、2线或3线串行多种接口方式,内部含有国标一级、二级简体中文字库的点阵图形液晶显示模块;其显示分辨率为128×64, 内置8192个16*16点汉字,和128个16*8点ASCII字符集.利用该模块灵活的接口方式和简单、方便的操作指令,可构成全中文人机交互图形界面。
可以显示8×4行16×16点阵的汉字. 也可完成图形显示.低电压低功耗是其又一显著特点。
由该模块构成的液晶显示方案与同类型的图形点阵液晶显示模块相比,不论硬件电路结构或显示程序都要简洁得多,且该模块的价格也略低于相同点阵的图形液晶模块。
模块接口的说明:
管脚号
管脚名称
电平
管脚功能描述
1
VSS
0V
电源地
2
VCC
3.0+5V
电源正
3
V0
-
对比度(亮度)调整
4
RS(CS)
H/L
RS=“H”,表示DB7——DB0为显示数据
RS=“L”,表示DB7——DB0为显示指令数据
5
R/W(SID)
H/L
R/W=“H”,E=“H”,数据被读到DB7——DB0
R/W=“L”,E=“H→L”,DB7——DB0的数据被写到IR或DR
6
E(SCLK)
H/L
使能信号
7
DB0
H/L
三态数据线
8
DB1
H/L
三态数据线
9
DB2
H/L
三态数据线
10
DB3
H/L
三态数据线
11
DB4
H/L
三态数据线
12
DB5
H/L
三态数据线
13
DB6
H/L
三态数据线
14
DB7
H/L
三态数据线
15
PSB
H/L
H:
8位或4位并口方式,L:
串口方式(见注释1)
16
NC
-
空脚
17
/RESET
H/L
复位端,低电平有效(见注释2)
18
VOUT
-
LCD驱动电压输出端
19
A
VDD
背光源正端(+5V)(见注释3)
20
K
VSS
背光源负端(见注释3)
3.2 指令的说明
模块控制芯片提供两套控制命令,基本指令和扩充指令如下:
指令表1:
(RE=0:
基本指令)
指令
指 令 码
功 能
RS
R/W
D7
D6
D5
D4
D3
D2
D1
D0
清除
显示
0
0
0
0
0
0
0
0
0
1
将DDRAM填满"20H",并且设定DDRAM的地址计数器(AC)到"00H"
地址
归位
0
0
0
0
0
0
0
0
1
X
设定DDRAM的地址计数器(AC)到"00H",并且将游标移到开头原点位置;
这个指令不改变DDRAM 的内容
显示状态开/关
0
0
0
0
0
0
1
D
C
B
D=1:
整体显示 ONC=1:
游标ON
B=1:
游标位置反白允许
进入点
设定
0
0
0
0
0
0
0
1
I/D
S
指定在数据的读取与写入时,设定游标的移动方向及指定显示的移位
游标或显示移位控制
0
0
0
0
0
1
S/C
R/L
X
X
设定游标的移动与显示的移位控制位;这个指令不改变DDRAM 的内容
功能
设定
0
0
0
0
1
DL
X
RE
X
X
DL=0/1:
4/8位数据
RE=1:
扩充指令操作
RE=0:
基本指令操作
设定CGRAM
地址
0
0
0
1
AC5
AC4
AC3
AC2
AC1
AC0
设定CGRAM 地址
设定DDRAM
地址
0
0
1
0
AC5
AC4
AC3
AC2
AC1
AC0
设定DDRAM 地址(显示位址)
第一行:
80H-87H
第二行:
90H-97H
读取忙标志和地址
0
1
BF
AC6
AC5
AC4
AC3
AC2
AC1
AC0
读取忙标志(BF)可以确认内部动作是否完成,同时可以读出地址计数器(AC)的值
写数据到RAM
1
0
数据
将数据D7——D0写入到内部的RAM(DDRAM/CGRAM/IRAM/GRAM)
读出RAM的值
1
1
数据
从内部RAM读取数据D7——D0
(DDRAM/CGRAM/IRAM/GRAM)
指令表2:
(RE=1:
扩充指令)
指令
指 令 码
功 能
RS
R/W
D7
D6
D5
D4
D3
D2
D1
D0
待命
模式
0
0
0
0
0
0
0
0
0
1
进入待命模式,执行其他指令都棵终止
待命模式
卷动地址开关开启
0
0
0
0
0
0
0
0
1
SR
SR=1:
允许输入垂直卷动地址
SR=0:
允许输入IRAM和CGRAM地址
反白
选择
0
0
0
0
0
0
0
1
R1
R0
选择2行中的任一行作反白显示,并可决定反白与否。
初始值R1R0=00,第一次设定为反白显示,再次设定变回正常
睡眠
模式
0
0
0
0
0
0
1
SL
X
X
SL=0:
进入睡眠模式
SL=1:
脱离睡眠模式
扩充
功能
设定
0
0
0
0
1
CL
X
RE
G
0
CL=0/1:
4/8位数据
RE=1:
扩充指令操作
RE=0:
基本指令操作
G=1/0:
绘图开关
设定绘图RAM
地址
0
0
1
0
AC6
0
AC5
0
AC4
AC3
AC3
AC2
AC2
AC1
AC1
AC0
AC0
设定绘图RAM
先设定垂直(列)地址AC6AC5…AC0
再设定水平(行)地址AC3AC2AC1AC0
将以上16位地址连续写入即可
备注:
当IC1在接受指令前,微处理器必须先确认其内部处于非忙碌状态,即读取BF标志时,BF需为零,方可接受新的指令;如果在送出一个指令前并不检查BF标志,那么在前一个指令和这个指令中间必须延长一段较长的时间,即是等待前一个指令确实执行完成。
4、实验内容
本次实验我们的内容是在LCD上显示“欢迎来到电子科大成都学院微电子技术系”的字符。
本次实验共3个模块:
ROM模块,LCD控制模块,分频模块。
在这里贴上LCD控制模块的代码:
moduleLCD(
CLK_IN,
LCD_E,
LCD_DI,
LCD_RW,
LCD_CS_0,
LCD_CS_1,
LCD_RESET,
LCD_DATA,/
BUSY_DATA,
write
);
input CLK_IN;
output LCD_E;
output LCD_DI;
output LCD_RW;
output LCD_CS_0;
output LCD_CS_1;
output LCD_RESET;
output [7:
0] BUSY_DATA;
output write;
inout [7:
0] LCD_DATA;
reg LCD_E;
reg LCD_RW;
reg LCD_DI;
reg LCD_CS_0=1;
reg LCD_CS_1=0;
reg LCD_RESET;
reg [7:
0] LCD_DATA_r;
reg [7:
0] BUSY_DATA_r;
reg [9:
0] addr_picture;
reg [7:
0] line_addr;
reg [23:
0] count_init='b0; //初始化延时计数器
reg [4:
0] count_rd_state='b0;//读状态计数器
reg [4:
0] count_wr_order='b0;//写命令计数器
reg [4:
0] count_wr_data='b0; //写数据计数器
reg [7:
0] wr_count='b0; //写数据计数器
reg [7:
0] page_addr='b0; //页地址偏移量
reg [7:
0] clr_wr_count='b0; //清零计数器
wire [7:
0] wr_data;
//reg [7:
0] line_addr='b0;
//reg [255:
0] wr_data=256'h0040604242424242_7e42424242424040_000c14c464241404_0605342444840c10;//“空”字模
/*
0x10,0x0C,0x84,0x44,0x24,0x34,0x05,0x06,
0x04,0x14,0x24,0x64,0xC4,0x14,0x0C,0x00,
0x40,0x40,0x42,0x42,0x42,0x42,0x42,0x7E,
0x42,0x42,0x42,0x42,0x42,0x60,0x40,0x00
*/
////////////////////////////////////////////////////
reg busy_flag=0;
reg order_flag1=0;
reg order_flag2=0;
reg order_flag3=0;
reg order_flag4=0;
//reg order_flag5=0;
reg read_state=0;//初始化读状态标志,0有效
reg write_data=0;//初始化写数据标志,1有效
reg delay=0;//初始化延时标志,0有效
//reg cs_flag=0;//片选初始化标志
reg clr_flag=0;//清屏标志
reg flag=0;
reg flag1=0;
wire write;
parameter F_IN=20_000_000,
F_OUT=1,
COUNT_FULL=F_IN/2/F_OUT-1;
assign write=LCD_E&(!
LCD_RW);
assign LCD_DATA=write?
LCD_DATA_r:
8'bz;//将数据写到LCD总线上
assign BUSY_DATA=BUSY_DATA_r;
always@(posedgeCLK_IN)
begin
if(read_state==0&&delay==1)
begin
if(count_rd_state==5'd31)
count_rd_state<=0;
else
count_rd_state<=count_rd_state+5'b1;
end
else
count_rd_state<=0;
end
always@(posedgeCLK_IN)
begin
if(busy_flag==1&&((order_flag1==0)|(order_flag2==1)|(order_flag3==1)|(order_flag4==1)));
begin
if(count_wr_order==5'd31)
count_wr_order<=0;
else
count_wr_order<=count_wr_order+5'b1;
end
end
//写LCD数据计数器
always@(posedgeCLK_IN)
begin
if((busy_flag==1&&write_data==1)|(busy_flag==1&&clr_flag==1))
begin
if(count_wr_data==5'd31)
count_wr_data<=0;
else
count_wr_data<=count_wr_data+5'b1;
end
else
count_wr_data<=0;
end
ROM_picture ROM_picture_0(
.address(addr_picture),
.clock(CLK_IN),
.q(wr_data)
);
always@(negedgeCLK_IN)
begin
//LCD_CS_0<=1;//开左半屏
//LCD_CS_1<=0;
//写显示数据
if(write_data&busy_flag)
begin
LCD_DATA_r<=wr_data;
end
//写清屏数据
if(clr_flag&busy_flag)
begin
LCD_DATA_r<=8'h00;
end
//延时标志有效
if(delay==0)
begin
LCD_RESET<=0;//复位
if(count_init==COUNT_FULL)
begin
delay<=1;//延时结束
LCD_RESET<=1;
end
else
count_init<=count_init+24'b1;
end
//延时1s完成
if(delay==1)
begin
//LCD_RESET<=1;
//读状态标志有效
if(read_state==0)
begin
case(count_rd_state)//读状态的时序
5'd0:
//初始状态50ns
begin
LCD_E<=0;
LCD_DI<=1;
LCD_RW<=0;
end
5'd1:
//地址建立时间200ns
begin
LCD_DI<=0;
LCD_RW<=1;
end
5'd5:
//E脉冲宽度600ns
begin
LCD_E<=1;
end
5'd17:
//将E置0
begin
LCD_E<=0;
BUSY_DATA_r<=LCD_DATA;
end
5'd18:
//地址和数据保持时间50ns
begin
LCD_DI<=1;
LCD_RW<=0;
end
5'd31:
//E信号周期大于1us
begin
if(BUSY_DATA_r[7]==0)//如果不忙
begin
busy_flag<=1;//不忙标志
read_state<=1;//读状态标志无效
end
else
busy_flag<=0; //忙
delay<=0;
end
endcase
end
//写第一个命令标志有效
if(order_flag1==0)
begin //如果不忙
if(busy_flag==1)
begin //开始写命令的时序
case(count_wr_order)
5'd0:
//初始状态50ns
begin
LCD_E<=0;
LCD_DI<=1;
LCD_RW<=1;
end
5'd1:
//地址建立时间200ns
begin
LCD_DI<=0;
LCD_RW<=0;
end
5'd5:
//E脉冲宽度600ns
begin
LCD_E<=1;
LCD_DATA_r<=8'h3F;
end
5'd17:
begin
LCD_E<=0;
end
5'd18:
//地址和数据保持时间50ns
begin
LCD_DI<=1;
LCD_RW<=1;
end
5'd31:
begin
read_state<=0;//读状态标志有效
order_flag1<=1;//写第一个命令标志无效
order_flag2<=1;//写第二个命令标志有效
busy_flag<=0; //需要重新读忙
end
endcase
end
end
if(order_flag2==1)
begin //如果不忙
if(busy_flag==1)
begin
case(count_wr_order)
5'd0:
//初始状态50ns
begin
LCD_E<=0;
LCD_DI<=1;
LCD_RW<=1;
end
5'd1:
//地址建立时间200ns
begin
LCD_DI<=0;
LCD_RW<=0;
end
5'd5:
//E脉冲宽度600ns
begin
LCD_E<=1;
LCD_DATA_r<=8'hC0;
end
5'd17:
begin
LCD_E<=0;
end
5'd18:
//地址和数据保持时间50ns
begin
LCD_DI<=1;
LCD_RW<=1;
end
5'd31:
begin
read_state<=0;//读状态标志有效
order_flag2<=0;//写第二个命令标志无效
order_flag3<=1;//写第三个命令标志有效
busy_flag<=0; //需要读忙
end
endcase
end
end
if(order_flag3==1)
begin //如果不忙
if(busy_flag==1)
begin
case(count_wr_order)
5'd0:
//初始状态50ns
begin
LCD_E<=0;
LCD_DI<=1;
LCD_RW<=1;
end
5'd1:
//地址建立时间200ns
begin
LCD_DI<=0;
LCD_RW<=0;
end
5'd5:
//E脉冲宽度600ns
begin
LCD_E<=1;
LCD_DATA_r<=page_addr+8'hb8;
end
5'd17:
begin
LCD_E<=0;
end
5'd18:
//地址和数据保持时间50ns
begin
LCD_DI<=1;
LCD_RW<=1;
end
5'd31:
begin
read_state<=0;//读状态标志有效
order_flag3<=0;//写第三个命令标志无效
order_flag4<=1;//写第四个命令标志无效
busy_flag<=0; //需要读忙
end
endcase
end
end
if(order_flag4==1)
begin //如果不忙
if(busy_flag==1)
begin
case(count_wr_order)
5'd0:
//初始状态50ns
begin
LCD_E<=0;
LCD_DI<=1;
LCD_RW<=1;
end
5'd1:
//地址建立时间200ns
begin
LCD_DI<=0;
LCD_RW<=0;
end
5'd5:
//E脉冲宽度600ns
begin
LCD_E<=1;
LCD_DATA_r<=8'h40;
end
5'd17:
begin
LCD_E<=0;
end
5'd18:
//地址和数据保持时间50ns
begin
LCD_DI<=1;
LCD_RW<=1;
end
5'd31:
begin
read_state<=0;//读状态标志有效
order_flag4<=0;//写第三个命令标志无效
busy_flag<=0; //需要读忙
//order_flag5<=1;
//write_data<=1;
if(flag1==1)
write_data<=1;
else
clr_flag<=1;
end
endcase
end
end
if(clr_flag==1)
begin
//if(write_data==1)
//begin //如果不忙
if(busy_flag==1)
begin //开始写数据的时序
case(count_wr_data)
5'd0:
//初始状态50ns
begin
LCD_E<=0;
LCD_DI<=0;
LCD_RW<=1;
end
5'd1:
//地址建立时间200ns
begin
LCD_DI<=1;
LCD_RW<=0;
end
5'd5:
//E脉冲宽度600ns
begin
LCD_E<=1;
//LCD_DATA_r<=8'h00;
end
5'd17:
//地址和数据保持时间50ns
LCD_E<=0;
5'd18:
begin
LCD_DI<=0;
LCD_RW<=1;
end
5'd31:
//等待下一个写数据,判忙开始
begin
read_state<=0;//读状态标志有效
busy_flag<=0;
if(clr_wr_count==63)
begin
clr_flag<=0;
clr_wr_count<=0;
if(page_addr==7)
begin
page_addr<=0;
//write_data<=1;
LCD_CS_0<=0;//开左半屏
LCD_CS_1<=1;
flag<=1;
if(flag==1)
flag1<=1;
order_flag2<=1;
end
else
begin
page_addr<=page_addr+8'b1;
order_flag2<=1;
end
end
else
clr_wr_count<=clr_wr_count+8'b1;
end
endcase
end
//end
end
if(write_data==1)
begin //如果不忙
if(busy_flag==1)
begin //开始写数据的时序
case(count_wr_data)
5'd0:
//初始状态50ns
begin
LCD_E<=0;
LCD_DI<=0;
LCD_RW<=1;
end
5'd1:
//地址建立时间200ns
begin
LCD_DI<=1;
LCD_RW<=0;
end
5'd5:
//E脉冲宽度600ns
begin
LCD_E<=1;
addr_picture<=addr_picture+10'b1;
if(addr_picture==1023)
begin
addr_picture<=10'd0;
end
end
5'd17:
//地址和数据保持时间50ns
LCD_E<=0;
5'd18:
begin
LCD_DI<=0;
LCD_RW<=1;
end
5