先上传三张图片在说

                           

由于串口传输速度较慢,故此实验是在“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图片显示实验的更多相关文章

  1. 基于basys2驱动LCDQC12864B的verilog设计图片显示

    话不多说先上图 前言 在做这个实验的时候在网上找了许多资料,都是关于使用单片机驱动LCD显示,确实用单片机驱动是要简单不少,记得在FPGA学习交流群里问问题的时候,被前辈指教,说给我最好的指教便是别在 ...

  2. 在ubuntu中配置深度学习python图片分类实验环境

    1 安装numpy,scipy, matplotlib, sudo apt-get install python-numpy sudo apt-get install python-scipy sud ...

  3. SQL Server的镜像是基于物理块变化的复制 镜像Failover之后数据的预热问题

    SQL Server的镜像是基于物理块变化的复制 镜像Failover之后数据的预热问题 基于物理块变化的复制,没有并行也是很快的. 逻辑复制的日志是按事务结束的时间排序的,而物理复制是与事务无关的, ...

  4. easyconf——基于AugularJS的配置管理系统开发框架

    目录 1 easyconf的诞生2 easyconf的设计理念 2.1 总体设计 2.2 细节设计 2.2.1 CRUD操作 2.2.2 即时校验 2.2.3 下拉框设计3 easyconf使用指南 ...

  5. Spring框架bean的配置(3):基于注解的配置

    1.基于注解的配置: @Component: 基本注解, 标识了一个受 Spring 管理的组件 @Respository: 标识持久层组件 @Service: 标识服务层(业务层)组件 @Contr ...

  6. (spring-第4回【IoC基础篇】)spring基于注解的配置

    基于XML的bean属性配置:bean的定义信息与bean的实现类是分离的. 基于注解的配置:bean的定义信息是通过在bean实现类上标注注解实现. 也就是说,加了注解,相当于在XML中配置了,一样 ...

  7. (spring-第2回【IoC基础篇】)Spring的Schema,基于XML的配置

    要深入了解Spring机制,首先需要知道Spring是怎样在IoC容器中装配Bean的.而了解这一点的前提是,要搞清楚Spring基于Schema的Xml配置方案. 在深入了解之前,必须要先明白几个标 ...

  8. 基于jQuery向下弹出遮罩图片相册

    今天给大家分享一款基于jQuery向下弹出遮罩图片相册.单击相册图片时,一个遮罩层从上到下动画出现.然后弹出显示图片.这款插件适用浏览器:IE8.360.FireFox.Chrome.Safari.O ...

  9. Spring IoC — 基于注解的配置

    基于XML的配置,Bean定义信息和Bean实现类本身是分离的,而采用基于注解的配置方式时,Bean定义信息即通过在Bean实现类上标注注解实现. @Component:对类进行标注,Spring容器 ...

随机推荐

  1. split a string in C++

    This is my favourite way to iterate through a string. You can do what you want per word. string line ...

  2. how computer boot up?

    The power button activates the power supply in the PC, sending power to the motherboard and other co ...

  3. 理解本真的REST架构风格

       http://kb.cnblogs.com/page/186516/ 引子 在移动互联网.云计算迅猛发展的今天,作为一名Web开发者,如果您还没听说过“REST”这个buzzword,显然已经落 ...

  4. xib storyboard

    initWithNibName加载xib或者storyboard BLEViewController *controller = [[BLEViewController alloc] initWith ...

  5. vi 使用教程

    编辑一个文本文件是经常使用到的计算机操作.我们想做的大多数事情都需要使用某种文件编辑.文本编辑器会方便文件的创建和修改.编辑一个文本文件是经常使用到的计算机操作.我们想做的大多数事情都需要使用某种文件 ...

  6. [QML] Connections元素介绍

    一个Connections对象创建一个了一个QML信号的连接.在QML中,我们连接信号通常是用使用"on<Signal>"来处理的,如下所示: MouseArea { ...

  7. Mysql找回root密码

    1.停止数据库服务 service mysql stop 2.修改my.cnf 添加skip-grant-tables 3.重启数据库 service mysql restart 4.修改root密码 ...

  8. c_select 调用参数说明

    c_select 调用 1. select系统调用select系统调用是用来让我们的程序监视多个文件描述符的状态变化的.程序会停在select这里等待,直到被监视的文件描述符有某一个或多个发生了状态改 ...

  9. 有indexPath获取到cell对象

    //SHSecretaryTipsCell *cell=(SHSecretaryTipsCell *)[tableView cellForRowAtIndexPath:indexPath];

  10. c++11 对象池的实现

    ; template <typename T> class ObjectPool { template <typename... Args> using Constructor ...