先上传三张图片在说

                           

由于串口传输速度较慢,故此实验是在“LCD12864 液晶显示-汉字及自定义显示(并口)”基础上进一步修改而来。在写代码之前还是得先搞清楚每一步的动作,具体步骤如下:

一、先找到一张128*64大小的图片,自己也可以通过系统自带的“画图”工具进行调整,最终保存为"单色图.bmp"格式。最好找一张比较简单的图片。

二、图片通过“字模.EXE”软件提取出数据,总不能像之前那样把一个个数据赋值给dis_data,那工作量太大了,说不定中间还会弄错。可以用一个简单的办法把这些数据放置到FPGA内部自带的ROM中,通过调用在把数据从ROM中提取出来(其实FPGA内部并没有专用的ROM硬件资源,实现ROM的思路是对RAM赋予初值,并保持该初值)。

a、首先新建一个文本,命名为xx.c格式的文件这里是logo.c,打开,在该文件里面定义一个数组.

unsigned char code tab[] = {0x00,0x00.....0x00}; 这个数组大小我们可以算出是1024。

b、打开keli软件,新建一个工程,芯片随便选一个ATxx类型就可以,然后把该文件添加到该工程中,按"Alt + F7",弹出一个“options for target 'target 1'”,选择"output",勾上“Creat Hex file”,点OK,按F7进行编译,编译成功后,可在该工程目录下看到logo.hex文件。

c、用Quartus 软件打开建立的LCD12864工程,按照如下图所示进行操作建立一个ROM:

在工程目录下可看到多了“lcd_rom.v”和“lcd_rom_bb.v”,打开“lcd_rom.v”,复制“lcd_rom (address, clock,q);”到LCD12864.v中,并把相关接口传进去lcd_rom  u1(.address(add_cnt),   .clock(sys_clk),   .q(dis_data));  ,OK,ROM已经加载好了。

三、得知道LCD128*64显示图片的原理

a、图片初始化与汉字初始化有所不同,图片需要用到扩展指令。RE=1,并要把绘图开关打开G=1,这里要设置成0x36就可以了。

b、通过手册都知道,在显示之前,要设置垂直地址(Y)和水平地址(X),要连续发送给LCD的。由于绘图RAM 的地址计数器(AC)只会对水平地址(X )自动加一,当水平地址加0FH(16)次时,会重新设为00H 但并不会对垂直地址做进位自动加一,故当连续写入多个数据时,要判断垂直地址是否需重新设定。如下图,是一张图片所提取的数据(16*64 = 1024),一个数据占8bit,故一行有8*16= 128bit,一列有64行,共128*64。某一bit就是LCD128*64中的某一个点阵。而且对于液晶显示,只要把液晶哪个点阵接上高电平(相应bit置1),哪个点阵就点亮了。垂直地址就要注意了,当垂直地址(Y)加到9f时(第行),垂直地址(Y)得重新从80开始计数,水平地址(X)得从88开始。故程序中对add_cnt地址进行判断,每当加16次就重新设定垂直地址,i是对行计数。

说了那么多,不知道有没说清楚,就这样吧,代码实现如下:

代码1:

 module LCD12864(
//input
sys_clk,
rst_n, //output
lcd_rs,
lcd_rw,
lcd_en,
lcd_data,
lcd_psb
);
input sys_clk;// 50MHZ
input rst_n; output lcd_rs;//H:data L:command
output lcd_rw;//H:read module L:write module
output lcd_en;//H active
output [:] lcd_data;
output lcd_psb;//H:parallel module L:SPI module
wire [:] dis_data; /***************************************************/
parameter T3MS = 'd149_999;
parameter NUM_64 = 'd63;
parameter IDLE = 'd0,
INIT_FUN_SET1 = 'd1,
INIT_FUN_SET2 = 'd2,
INIT_DISPLAY = 'd3,
INIT_CLEAR = 'd4,
SET_DDRAM_Y = 'd5,
SET_DDRAM_X = 'd6,
WRITE_DATA = 'd7,
STOP = 'd8;
/***************************************************/
//产生周期为6MS的lcd_clk给LCD
reg [:] cnt;
reg lcd_clk;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
cnt <= 'd0;
lcd_clk <= 'b0;
end
else if(cnt == T3MS)begin
cnt <= 'd0;
lcd_clk <= ~lcd_clk;
end
else
cnt <= cnt + 'b1;
/***************************************************/
reg lcd_rs;
reg [:] state;
reg [:] add_cnt;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
lcd_rs <= 'b0;
else if(state == WRITE_DATA)
lcd_rs <= 'b1; //写数据模式
else
lcd_rs <= 'b0; //写命令模式
/***************************************************/
reg [:] lcd_data;
reg en;
reg [:] i;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n) begin
state <= IDLE;
lcd_data <= 'hzz;
en <= 'b1;
add_cnt <= 'd0;
i <= 'd0;
end
else
case(state)
IDLE:
begin
state <= INIT_FUN_SET1;
lcd_data <= 'hzz;
en <= 'b1;
end INIT_FUN_SET1:
begin
lcd_data <= 'h36; //CL= 1--8位,扩充指令RE=1,绘图G=1
state <= INIT_FUN_SET2;
end INIT_FUN_SET2:
begin
lcd_data <= 'h36; //CL= 1--8位,扩充指令RE=1,绘图G=1
state <= INIT_DISPLAY;
end INIT_DISPLAY:
begin
lcd_data <= 'h3e; //CL= 1--8位,扩充指令RE=1,绘图G=1
state <= INIT_CLEAR;
end
//其实上面没必要写那么多次数,这是之前并口写的,懒得去掉,故多写几次36
INIT_CLEAR:
begin
lcd_data <= 'h01; //清屏
state <= SET_DDRAM_Y;
end SET_DDRAM_Y: // 先设置垂直地址
begin
if(i <= 'd31)
lcd_data <= 'h80 + i;//80H~9fH
else
lcd_data <= 'h80 + (i-5'd32); //80H~9fH */ state <= SET_DDRAM_X;
end SET_DDRAM_X: //后设置水平地址
begin
if(i <= 'd31)
lcd_data <= 'h80; //80H
else
lcd_data <= 'h88; //88H state <= WRITE_DATA;
end WRITE_DATA:
begin
lcd_data <= dis_data;
add_cnt <= add_cnt + 'b1; if(add_cnt[:] == 'hf/*(add_cnt + 1'b1) % 'd16 == 10'd0*/)begin //计算行
i <= i + 'b1;
if(i == NUM_64)
state <= STOP;
else
state <= SET_DDRAM_Y;
end
else
state <= WRITE_DATA;
end STOP:
begin
en <= 'b0;//显示完了,lcd_e就一直拉为低
state <= STOP;
end default: state <= IDLE;
endcase
/***************************************************/
assign lcd_rw = 'b0;//只有写模式
assign lcd_psb = 'b1;//并口模式
assign lcd_en = en ? lcd_clk : 'b0;
/***************************************************/
//ROM
lcd_rom u1(
.address(add_cnt),
.clock(sys_clk),
.q(dis_data)
);
/***************************************************/
endmodule

其实代码1是有错误的,图片下半部分显示是乱码,检查程序好久都没发现(说明检查的还是不够仔细啊),也没有报错,最终通过仿真查看得知错误在哪,在仿真时要等好长时间后才能看得到哦。

代码2:

 module LCD12864(
//input
sys_clk,
rst_n, //output
lcd_rs,
lcd_rw,
lcd_en,
lcd_data,
lcd_psb
);
input sys_clk;// 50MHZ
input rst_n; output lcd_rs;//H:data L:command
output lcd_rw;//H:read module L:write module
output lcd_en;//H active
output [:] lcd_data;
output lcd_psb;//H:parallel module L:SPI module
wire [:] dis_data; /***************************************************/
`define const_32 'd32
`define const_63 'd63
/***************************************************/
parameter T3MS = 'd149_999;
parameter IDLE = 'd0,
INIT_FUN_SET1 = 'd1,
INIT_FUN_SET2 = 'd2,
INIT_DISPLAY = 'd3,
INIT_CLEAR = 'd4,
SET_DDRAM_Y = 'd5,
SET_DDRAM_X = 'd6,
WRITE_DATA = 'd7,
STOP = 'd8;
/***************************************************/
//产生周期为6MS的lcd_clk给LCD
reg [:] cnt;
reg lcd_clk;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
cnt <= 'd0;
lcd_clk <= 'b0;
end
else if(cnt == T3MS)begin
cnt <= 'd0;
lcd_clk <= ~lcd_clk;
end
else
cnt <= cnt + 'b1;
/***************************************************/
reg lcd_rs;
reg [:] state;
reg [:] add_cnt;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
lcd_rs <= 'b0;
else if(state == WRITE_DATA)
lcd_rs <= 'b1; //写数据模式
else
lcd_rs <= 'b0; //写命令模式
/***************************************************/
reg [:] lcd_data;
reg en;
reg [:] i;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n) begin
state <= IDLE;
lcd_data <= 'hzz;
en <= 'b1;
add_cnt <= 'd0;
i <= 'd0;
end
else
case(state)
IDLE:
begin
state <= INIT_FUN_SET1;
lcd_data <= 'hzz;
en <= 'b1;
end INIT_FUN_SET1:
begin
lcd_data <= 'h36; //CL= 1--8位,扩充指令RE=1,绘图G=1
state <= INIT_FUN_SET2;
end INIT_FUN_SET2:
begin
lcd_data <= 'h36; //CL= 1--8位,扩充指令RE=1,绘图G=1
state <= INIT_DISPLAY;
end INIT_DISPLAY:
begin
lcd_data <= 'h3e; //CL= 1--8位,扩充指令RE=1,绘图G=1
state <= INIT_CLEAR;
end
//其实上面没必要写那么多次数,这是之前并口写的,懒得去掉,故多写几次36
INIT_CLEAR:
begin
lcd_data <= 'h01; //清屏
state <= SET_DDRAM_Y;
end SET_DDRAM_Y: // 先设置垂直地址
begin
if(i < `const_32)
lcd_data <= 'h80 + i;//80H~9fH
else
lcd_data <= 'h80 + (i - `const_32); //80H~9fH/ state <= SET_DDRAM_X;
end SET_DDRAM_X: //后设置水平地址
begin
if(i < `const_32)
lcd_data <= 'h80; //80H
else
lcd_data <= 'h88; //88H state <= WRITE_DATA;
end WRITE_DATA:
begin
lcd_data <= dis_data;
add_cnt <= add_cnt + 'b1; if(add_cnt[:] == 'hf/*(add_cnt + 1'b1) % 'd16 == 10'd0*/)begin //计算行
i <= i + 'b1;
if(i == `const_63)
state <= STOP;
else
state <= SET_DDRAM_Y;
end
else
state <= WRITE_DATA;
end STOP:
begin
en <= 'b0;//显示完了,lcd_e就一直拉为低
state <= STOP;
end default: state <= IDLE;
endcase
/***************************************************/
assign lcd_rw = 'b0;//只有写模式
assign lcd_psb = 'b1;//并口模式
assign lcd_en = en ? lcd_clk : 'b0;
/***************************************************/
//ROM
lcd_rom u1(
.address(add_cnt),
.clock(sys_clk),
.q(dis_data)
);
/***************************************************/
endmodule

代码2是正确的,代码1错在哪呢,就是i的位宽弄错了,代码2中用宏定义替代数字直接与i的比较。哎,不小心写错了,尤其是软件不提醒也不报警告的那种错误,势必要害了自己啊。。。。。。。

注意:if(add_cnt[3:0] == 4'hf/*(add_cnt + 1'b1) % 10'd16 == 10'd0*/),这两种写法下到板子验证都正确,后面“取于”方式,套用了C语言的方式,建议不采取,一不规范,二也很少看到别人这样写,在群里问别人,说经常写这样不规范的代码,日后势必给自己增加痛苦,面试时会被人家鄙视,果断采取add_cnt[3:0] == 4'hf这种方式。

到此,LCD12864的实验将结束。

最新文章

  1. Daily Scrum02 12.08
  2. C语言题库的上机题
  3. groovy-正则表达式
  4. C函数之memcpy()函数用法
  5. 常用的php数组排序函数
  6. HTML5-canvas实例:刮刮乐游戏
  7. UIApplication和delegate
  8. chrome浏览器打开网页,总是跳转到2345主页的解决方法 2345.com 绑架主页
  9. angular中设置$http的post请求的数据传递格式
  10. 【Scala】Scala之Classes and Properties
  11. ajax异步上传文件之data参数----小哈学js
  12. SharePoint Column Format
  13. .NET Core微服务系列基础文章索引(目录导航Draft版)
  14. js来判断设备类型
  15. angular2学习笔记3
  16. CornerStone使用跳坑总结(陆续更新)
  17. Winform中Picture控件图片的拖拽显示
  18. 解决AS gradle下载同步卡慢的问题
  19. Mysql优化系列之——优化器对子查询的处理
  20. SQL Server2012远程访问设置

热门文章

  1. ftp以及smb的配置
  2. Hadoop 中关于 map,reduce 数量设置
  3. Linux入门学习教程:虚拟机体验之KVM篇
  4. code.google.com
  5. Petit FatFs
  6. HTTPclient cookie的获取与设置
  7. 旋转图css3
  8. 【转】ThinkPHP 页面跳转
  9. div.2/Bellovin&lt;最长上升子序列&gt;
  10. 自动打开notepad 并写入数据