基于M9K块配置ROM的LCD12864图片显示实验
先上传三张图片在说

由于串口传输速度较慢,故此实验是在“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的实验将结束。
基于M9K块配置ROM的LCD12864图片显示实验的更多相关文章
- 基于basys2驱动LCDQC12864B的verilog设计图片显示
话不多说先上图 前言 在做这个实验的时候在网上找了许多资料,都是关于使用单片机驱动LCD显示,确实用单片机驱动是要简单不少,记得在FPGA学习交流群里问问题的时候,被前辈指教,说给我最好的指教便是别在 ...
- 在ubuntu中配置深度学习python图片分类实验环境
1 安装numpy,scipy, matplotlib, sudo apt-get install python-numpy sudo apt-get install python-scipy sud ...
- SQL Server的镜像是基于物理块变化的复制 镜像Failover之后数据的预热问题
SQL Server的镜像是基于物理块变化的复制 镜像Failover之后数据的预热问题 基于物理块变化的复制,没有并行也是很快的. 逻辑复制的日志是按事务结束的时间排序的,而物理复制是与事务无关的, ...
- easyconf——基于AugularJS的配置管理系统开发框架
目录 1 easyconf的诞生2 easyconf的设计理念 2.1 总体设计 2.2 细节设计 2.2.1 CRUD操作 2.2.2 即时校验 2.2.3 下拉框设计3 easyconf使用指南 ...
- Spring框架bean的配置(3):基于注解的配置
1.基于注解的配置: @Component: 基本注解, 标识了一个受 Spring 管理的组件 @Respository: 标识持久层组件 @Service: 标识服务层(业务层)组件 @Contr ...
- (spring-第4回【IoC基础篇】)spring基于注解的配置
基于XML的bean属性配置:bean的定义信息与bean的实现类是分离的. 基于注解的配置:bean的定义信息是通过在bean实现类上标注注解实现. 也就是说,加了注解,相当于在XML中配置了,一样 ...
- (spring-第2回【IoC基础篇】)Spring的Schema,基于XML的配置
要深入了解Spring机制,首先需要知道Spring是怎样在IoC容器中装配Bean的.而了解这一点的前提是,要搞清楚Spring基于Schema的Xml配置方案. 在深入了解之前,必须要先明白几个标 ...
- 基于jQuery向下弹出遮罩图片相册
今天给大家分享一款基于jQuery向下弹出遮罩图片相册.单击相册图片时,一个遮罩层从上到下动画出现.然后弹出显示图片.这款插件适用浏览器:IE8.360.FireFox.Chrome.Safari.O ...
- Spring IoC — 基于注解的配置
基于XML的配置,Bean定义信息和Bean实现类本身是分离的,而采用基于注解的配置方式时,Bean定义信息即通过在Bean实现类上标注注解实现. @Component:对类进行标注,Spring容器 ...
随机推荐
- hdu_5761_Rower Bo(xjb猜公式)
题目链接:hdu_5761_Rower Bo 题意: 让你求一个物理问题 题解: xjb猜公式,由题目样例可得,答案为8/7,然后我们可以xjb猜出公式为v1*a/(v1*v1-v2*v2),然后特判 ...
- ios中的关键词retain release
内存分析 在函数中只要用new alloc copy 这样的分配空间时 则计算器retain就要为一 每调用一次就要加一 在.m文件中引用手动计数时 一定要调用[super dealloc]这 ...
- 查询MySQL中某个数据库中有多少张表
SELECT COUNT(*) TABLES, table_schema FROM information_schema.TABLES WHERE table_schema = '数据库' GRO ...
- 【我与一道水题的抗争之路】 哈理工2323 Emirp(反素数)
题目: http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=2323 1,打表的姿势不对. ...
- nginx 安装三方包重新编译
sudo -sapt-get source nginxapt-get build-dep nginxwget 'https://github.com/agentzh/chunkin-nginx-mod ...
- sql语句操作表
"create table mytable (m_id integer identity(1,1) primary key,m_class varchar(50) not null defa ...
- easyui-combobox select 设置不分行(只显示在一行)
使用easyui 1.4.4 <select id="hotalid" class="easyui-combobox" data-options=&quo ...
- Webdriver中实现区域截图的方式以及如何截取frame中的图片
import java.awt.Rectangle;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOE ...
- ReactiveCocoa / RxSwift 笔记一
原创:转载请注明出处 ReactiveCocoa / RxSwift Native app有很大一部分的时间是在等待事件发生,然后响应事件,比如 1.等待网络请求完成, 2.等待用户的操作, 3.等待 ...
- SSL交互过程
SSL交互过程 HTTPS将HTTP和SSL结合,即加了SSL隧道封装的HTTP,通过SSL对客户端身份和服务器进行验证,对传输的数据进行加密.不同情况下SSL的协商过程存在差异,本节以只验证服务器为 ...