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

  话不多说先上图

前言

       在做这个实验的时候在网上找了许多资料,都是关于使用单片机驱动LCD显示,确实用单片机驱动是要简单不少,记得在FPGA学习交流群里问问题的时候,被前辈指教,说给我最好的指教便是别在玩这个了,多看看关于FPGA方面的书籍,比做这个单片机做的东西价值强多了。现在想来确实,自从学习FPGA以来,看过的书没有多少,只是想做个什么了,就在网上找找例程,照抄下来,把算法推理一遍,下个板子实现了,便以为自己会了懂了,要是自己在写一个便问题百出。那么菜鸟始终是菜鸟。自己根本没有掌握FPGA的设计思想和优势,用着FPGA做着单片机的东西,这种东西练练手就足够了,所以,做完这个实验LCD就到此为止了,我也要好好想想该如何进行下面的学习了。在这个上面我还是花费了不少时间,所以写一篇博文总结记录想,回头看的时候,以警醒自己。

摘要                                                                 

         做完这个实验我最大的感触是一定要会读数据手册。拿到元件(LCD12864),上面有16个的引脚,先要搞清楚每个引脚的功能,这时候就一定要会读数据手册。我个qc12864b的中文手册琢磨了很久才看懂了一些简单的操作指令,包括在手册上要提取出来,元件的驱动时序,扫描时钟,引脚定义,操作指令,功能描述等。

LCD显示原理

扫描时钟

  从手册上可以读出,qc12864b(这是我所使用LCD12864的型号)的扫描时钟介于470—590khz之间,最适为530khz,为了方便分频,所以取500khz。

引脚说明

  VSS        电源地,0v ­——接开发板GND就行

  VDD      电源正极(5v,3.3v)

  VO        液晶显示偏压,调整对比度

  RS         RS=1时,当mpu进行读模块操作,指向地址寄存器

          当mpu进行写模块操作,指向指令寄存器

               RS=0时,无论进行读/写操作,都指向数据寄存器

  R/W             高电平读操作,低电平写操作

  E           使能信号,高电平读取信息,下降沿执行命令

  DB0      数据总线第0位

  DB1      数据总线第1位

  DB2      数据总线第2位

  DB3      数据总线第3位

  DB4      数据总线第4位

  DB5      数据总线第5位

  DB6      数据总线第6位

  DB7      数据总线第7位

  PSB      串并控制端口,H为并行,L为串行,直接接5v

  RST       液晶复位端口,低电平有效

  A           背光正极输入,调整电压大小可以调整亮度

  K           背光负极输入,一般直接接地

指令说明

  具体指令说明,这里就不一一列出,可查阅qc12864b中文数据手册,我在阅读这一部分的时候耗费了很长时间,这些指令是驱动LCD显示文字与图片的核心,所以必须要掌握。

并口显示时序

Verilog实现方案

  从时序图可以看出在使能信号的一个周期内可以完成写入数据操作,扫描时钟与使能信号周期相同即可。

状态转移图

  状态机初始化位置          IDLE                            lcd_data = 8’hzz

  功能设计                          SETFUNCTION            lcd_data = 8’h36//扩充指令绘图显示

  显示设置                          SWITCHMODE           lcd_data = 8’h0c//显示状态

  清屏                                  CLEAR                       lcd_data = 8’h01//清零指令

  设置Y坐标                      SETDDRAM_Y

  设置X坐标                      SETDDRAM_X

  写入数据                          WRITERAM

  结束                                  STOP

绘图指令下的坐标图

代码如下

  1 module lcd_qc12864b(
  2             input mclk,
  3             input rst_n,
  4             output reg lcd_rs,//H读写数据,L指令
  5             output lcd_rw, //高电平读操作,低电平写操作
  6             output lcd_e,//使能信号高电平有效
  7             output reg [7:0]lcd_data,//八位数据总线  
  8         output psb,//串并控制端口,H为并行,L为串行,直接接5v  
  9         output lcd_rst//液晶的复位端口,低电平有效
 10     );
 11         
 12     reg lcd_clk;//需要500khz频率的时钟
 13     reg [7:0] state;//状态机寄存器
 14     reg [23:0] cnt;//分频驱动12864计数器
 15     reg flag = 1'b1;//显示完成标志 
 16     reg [9:0] char_cnt;// 
 17     wire [7:0] data_disp;//一个字节是八位,一个英文字符是一个字节,中文是俩个字节
 18     reg [5:0] cnt1;//行计数
 19     
 20     parameter T500KHZ=24'd49999;  
 21     
 22         //分频500khz时钟
 23         always @(posedge mclk or negedge rst_n)
 24         begin
 25             if(!rst_n)begin
 26                 lcd_clk <= 1'b0;
 27                 cnt <= 24'd0;
 28             end
 29             else if(cnt == T500KHZ)begin
 30                 cnt <= 24'd0;
 31                 lcd_clk <= ~lcd_clk;
 32             end
 33             else
 34                 cnt <= cnt + 1'd1;
 35         end        
 36         
 37         //状态机8个状态设置 
 38     parameter IDLE = 8'b00_000_000,//初始状态  
 39                         SETFUNCTION = 8'b00_000_001,//功能设置  
 40                           SWITCHMODE = 8'b00_000_010,//设置显示开和光标闪烁关闭  
 41                           CLEAR = 8'b00_000_100,//清屏操作  
 42                           SETDDRAM_Y = 8'b00_010_000,//设置y轴坐标
 43                             SETDDRAM_X = 8'b00_100_000,//设置x轴坐标
 44                           WRITERAM = 8'b01_000_000,//写设置写入寄存器  
 45                           STOP = 8'b10_000_000;//LCD操作停止,释放其控制
 46       
 47       //三段式书写状态机
 48       always @(posedge lcd_clk or negedge rst_n)
 49       begin
 50           if(!rst_n)
 51               lcd_rs <= 1'b0;
 52           else begin
 53               if(state == WRITERAM)
 54                   lcd_rs <= 1'b1;//写数据模式
 55               else
 56                   lcd_rs <= 1'b0;//写命令模式
 57           end
 58       end
 59       
 60       //lcd_rst始终为高电平,psb始终为低电平即可
 61     assign lcd_rst = 1'b1;  
 62     assign psb = 1'b1;  
 63     assign lcd_rw = 1'b0;//只是写操作,不需要读操作  
 64     assign lcd_e = (flag==1)?lcd_clk:1'b0;//使能信号与液晶时钟同步
 65       
 66       always @(posedge lcd_clk or negedge rst_n)
 67       begin
 68           if(!rst_n)begin
 69               cnt1<= 1'b0;
 70               state <= IDLE;     
 71               lcd_data <= 8'hzz;
 72               char_cnt <= 6'd0;
 73           end
 74           else begin
 75               case(state)
 76                   IDLE:begin
 77                       state <=  SETFUNCTION;//功能设置,8-bit+基本指令集0x30
 78                       lcd_data <= 8'h36;
 79                   end
 80                   
 81                   SETFUNCTION:begin
 82                       state <= SWITCHMODE;//设置显示开和光标闪烁关闭
 83                       lcd_data <= 8'h36;
 84                   end                  
 85         
 86                   SWITCHMODE:begin
 87                       state <= CLEAR;
 88                       lcd_data <= 8'h3e;//显示设置,全显示开,光标和闪烁关
 89                   end                  
 90                   CLEAR:begin//清屏操作
 91                       state <= SETDDRAM_Y;//点设置  
 92             lcd_data <= 8'h01;//清屏
 93           end           
 94             SETDDRAM_Y:begin
 95              if(cnt1< 32)
 96                  lcd_data <= 8'h80 + cnt1;//80H~9fH
 97              else
 98                  lcd_data <= 8'h80 + (cnt1- 32); //80H~9fH
 99                  
100              state <= SETDDRAM_X;
101           end               
102                     SETDDRAM_X:begin
103                       if(cnt1< 32)
104                           lcd_data <=  8'h80;            //80H
105                       else
106                           lcd_data <=  8'h88;            //88H
107                       state <= WRITERAM;
108                     end           
109                     WRITERAM:begin 
110                         lcd_data <= data_disp;
111                  char_cnt <= char_cnt + 1'b1;
112                  if(char_cnt[3:0] == 4'hf)begin      //计算行
113                      cnt1<= cnt1+ 1'b1;
114                 if(cnt1== 63)                         
115                     state <= STOP;
116                      else
117                   state <= SETDDRAM_Y;
118                        end 
119                        else
120                  state <= WRITERAM;
121             end                        
122           STOP:begin
123               flag <= 1'b0;
124               state <= STOP;//LCD操作停止,释放其控制
125             end
126           default: state <= IDLE;//回到初始状态
127         endcase  
128       end           
129      end  
130          
131          // ROM
132         rom U1_rom (
133     .clka(mclk), 
134     .addra(char_cnt), 
135     .douta(data_disp)
136     );
137 
138 endmodule

lcd_qc12864b

配置ROM

  有分布式ROM/ROM和块ROM/RAM,这里配置的是块ROM/RAM。这两种方式具体我也不是了解很深,以后再深入学习。

      该实验最让我头疼的是调用ROM,第一次接触IP核有许多地方都完全不懂。用LCD(带中文字库)显示文字的时候,可以直接输入文字的十六进制数值,设置显示地址坐标即可,12864显示原理点阵控制点的亮灭来实现,但是如果要显示图片的话一个个输入难免太过麻烦,这个时候调用ROM就方便许多。找一张或做一张像素为128x64的单色图片,使用取模软件,按c51的方式取模。取模出来的数据为十六进制,行8个,64个。

       配置块RAM/ROM时,要加载的是.coe文件,所以需要将取模的十六进制数据保存到.coe文件中。最开始我一直在找如何能直接将图片取模出来的数据转化成.coe文件,试了很多方法都失败了,最后发现完全可以自己按文件格式编辑一个即可,最终文件保存格式如下。

  将图片取模出来的每个数据前的ox去掉,这只是C语言中16进制的表示形式,文件第一行的MEMORY_INITIALIZATION_RADIX=16;表示数据全都为16进制数,这里的16可以换成8、2、10都行。

       打开ISE建立工程后,新建文件,选择IP(CORE Generator & Architecture Wizard),填写文件名,next

  找到Memories & Storage Elements单击,选择RAMs & ROMs单击,选择Block Memory Generator 7.3,next,finish。

  上面直接next,下面这里选Single Port ROM,next

  这里要填写数据位宽和深度,位宽是你所需要输出的数据位宽,LCD有8个数据位,故这里定义位宽为8,深度即有多少个这样的数据,从取模出来的数据看,易得共有1024个数据位。我使用的basys2开发板使用的是Xilinx Spantan3E—100TQ144 FPGA包含73728位的块RAM/ROM。因此,使用的块RAM的最大容量为9216字节或4608个16位字。从这里来看,是完全够用的。

  Next,加载.coe数据文件,勾选Load Init File 加载.coe文件,我这里是在桌面上保存着,直接找到选择就好。

  然后一直点击next直至最后一个界面,最后点击Generate,生成rom.xco。生成的IP核在工程目录下的ipcore_dir文件中。

  将rom.v文件打开端口定义如下,直接在顶层文件对其实例化,如代码所示

  最后可直接仿真,新建tb文件查看仿真传输数据是否正确。由波形图可得在功能设定指令传输完毕后,进入读数据状态,所读入的数据与文件中数据相符,一行数据传输完毕进入下一行,继续读入数据,仿真图结果正确。下板子也正确。

  最开始学习FPGA,最容易出现的就是两个问题,一个是把verilog当C语言来写,另一个就是把FPGA当单片机来用。前者我已经有了改善,后者我还需要继续努力。

转载请注明出处:NingHeChuan(宁河川)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

勒索之殇 | 从一个.NET病毒看透勒索三步曲

近三年的病毒走势大致可以总结为,2016年”流氓”,2017”勒索”,2018年”挖矿”,这篇文章我们就通过分析一个.Net的勒索软件,看看一个勒索软件到底是如...

1816
来自专栏XAI

企业支付宝账号开发接口教程--JAVA-UTF-8(实际操作------SpringMVC+JSP)

关于即时到账的开发。审核通过。简单测试如下。 希望看的可以收藏或者赞一下哦。 1:拥有自己的支付宝企业账号。去产品商店选择适合自己的方案。并签约合同。 ? 2:...

8459
来自专栏华仔的技术笔记

eos地址结构和公钥的关系

6433
来自专栏汇智网教程

以太坊智能合约示例

64110
来自专栏极客编程

以太坊智能合约开发DApp应用示例

智能合约开发是以太坊的核心,学习过程主要是搭建以太坊开发环境和solidity语言的编程。本文不用任何以太坊Dapp框架,直接在ganache下开发智能合约。

992
来自专栏SAP最佳业务实践

SAP最佳业务实践:FI–现金管理(160)-27现金集中-FF.D生成付款请求

4.8.2 FF.D生成付款请求 通过集中建议创建付款通知后,需要从付款通知中创建相应付款请求。付款请求在下一步通过付款程序读取并用于执行付款和创建付款文件。 ...

2717
来自专栏SAP最佳业务实践

SAP最佳业务实践:FI–应收帐款(157)-3 F-37预付款

4.3 F-37过帐预付款请求 预付款请求是一些不会影响余额表的备注项。在帐户分析、催款程序和付款程序中可将它们考虑在内。 使用备用统驭帐户19990501的特...

3714
来自专栏申龙斌的程序人生

同时给200多人发送比特币,程序员是这样做到的

在币圈中,使用自己的钱包给他人发币时,就跟平常的微信、支付宝扫码付款类似,输入BTC地址和金额,再输入支付密码,不管对方是否在地球的另一端,不需要任何银行的中介...

1302
来自专栏安富莱嵌入式技术分享

【RL-TCPnet网络教程】第5章 PHY芯片和STM32的MAC基础知识

本章节为大家讲解STM32自带的MAC和PHY芯片的基础知识,为下一章底层驱动的讲解做一个铺垫。

1383
来自专栏SAP最佳业务实践

SAP最佳业务实践:FI–应付账款(158)-5 F-54预付款清算

4.5 F-54预付款清算 现在您需要使用发票清算预付款,以便在以后的付款运行中仅对未清余额付款。 角色:应付会计 会计核算- 财务会计 - 应付帐款 -单据录...

3587

扫码关注云+社区