基于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时(第31行),垂直地址(Y)得重新从80开始计数,水平地址(X)得从88开始。故程序中对add_cnt地址进行判断,每当加16次就重新设定垂直地址,i是对行计数。

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

代码1:

  1 module LCD12864(
  2                     //input 
  3                     sys_clk,
  4                     rst_n,
  5                     
  6                     //output 
  7                     lcd_rs,
  8                     lcd_rw,
  9                     lcd_en,
 10                     lcd_data,
 11                     lcd_psb
 12                 );
 13 input sys_clk;// 50MHZ
 14 input rst_n;
 15 
 16 output lcd_rs;//H:data    L:command
 17 output lcd_rw;//H:read module    L:write module
 18 output lcd_en;//H active
 19 output [7:0] lcd_data;
 20 output lcd_psb;//H:parallel    module    L:SPI module
 21 wire [7:0] dis_data;
 22 
 23 /***************************************************/
 24 parameter T3MS = 18'd149_999;
 25 parameter   NUM_64 = 6'd63;
 26 parameter    IDLE           = 4'd0,
 27             INIT_FUN_SET1 = 4'd1,
 28             INIT_FUN_SET2 = 4'd2,
 29             INIT_DISPLAY  = 4'd3,
 30             INIT_CLEAR       = 4'd4,
 31             SET_DDRAM_Y   = 4'd5,
 32             SET_DDRAM_X      = 4'd6,
 33             WRITE_DATA      = 4'd7,
 34             STOP          = 4'd8;
 35 /***************************************************/
 36 //产生周期为6MS的lcd_clk给LCD
 37 reg [17:0] cnt;
 38 reg lcd_clk;
 39 always @(posedge sys_clk or negedge rst_n)
 40 if(!rst_n) begin
 41     cnt <= 18'd0;
 42     lcd_clk <= 1'b0;
 43 end
 44 else if(cnt == T3MS)begin
 45     cnt <= 18'd0;
 46     lcd_clk <= ~lcd_clk;
 47 end
 48 else
 49     cnt <= cnt + 1'b1;
 50 /***************************************************/
 51 reg lcd_rs;
 52 reg [3:0] state;
 53 reg [9:0] add_cnt;
 54 always @(posedge lcd_clk or negedge rst_n)
 55 if(!rst_n)
 56     lcd_rs <= 1'b0;
 57 else if(state == WRITE_DATA)
 58     lcd_rs <= 1'b1;        //写数据模式
 59 else
 60     lcd_rs <= 1'b0;        //写命令模式
 61 /***************************************************/
 62 reg [7:0] lcd_data;
 63 reg en;
 64 reg [5:0] i;
 65 always @(posedge lcd_clk or negedge rst_n)
 66 if(!rst_n) begin
 67     state <= IDLE;
 68     lcd_data <= 8'hzz;
 69     en <= 1'b1;
 70     add_cnt <= 10'd0;
 71     i <= 5'd0;
 72 end    
 73 else
 74     case(state)
 75         IDLE: 
 76         begin
 77             state <= INIT_FUN_SET1;
 78             lcd_data <= 8'hzz;
 79             en <= 1'b1;
 80         end
 81         
 82         INIT_FUN_SET1: 
 83         begin
 84             lcd_data <= 8'h36;    //CL= 1--8位,扩充指令RE=1,绘图G=1
 85             state <= INIT_FUN_SET2;
 86         end 
 87         
 88         INIT_FUN_SET2:
 89         begin
 90             lcd_data <= 8'h36;    //CL= 1--8位,扩充指令RE=1,绘图G=1
 91             state <= INIT_DISPLAY;
 92         end
 93             
 94         INIT_DISPLAY:
 95         begin
 96             lcd_data <= 8'h3e;    //CL= 1--8位,扩充指令RE=1,绘图G=1
 97             state <= INIT_CLEAR;
 98         end
 99                                      //其实上面没必要写那么多次数,这是之前并口写的,懒得去掉,故多写几次36
100         INIT_CLEAR:
101         begin
102             lcd_data <= 8'h01;    //清屏
103             state <= SET_DDRAM_Y;
104         end
105                               
106         SET_DDRAM_Y:  // 先设置垂直地址  
107         begin
108             if(i <= 5'd31)
109                 lcd_data <= 8'h80 + i;//80H~9fH
110             else
111                 lcd_data <= 8'h80 + (i-5'd32); //80H~9fH */
112                 
113             state <= SET_DDRAM_X;
114         end
115         
116         SET_DDRAM_X: //后设置水平地址 
117         begin
118             if(i <= 5'd31)
119                 lcd_data <=  8'h80;            //80H
120             else
121                 lcd_data <=  8'h88;            //88H
122             
123             state <= WRITE_DATA;
124         end
125         
126         WRITE_DATA:
127         begin
128             lcd_data <= dis_data;
129             add_cnt <= add_cnt + 1'b1;
130 
131             if(add_cnt[3:0] == 4'hf/*(add_cnt + 1'b1) % 10'd16 == 10'd0*/)begin      //计算行
132                 i <= i + 1'b1;
133                 if(i == NUM_64)
134                     state <= STOP;
135                 else
136                     state <= SET_DDRAM_Y;
137             end
138             else
139                 state <= WRITE_DATA;
140         end
141         
142         STOP: 
143         begin
144             en <= 1'b0;//显示完了,lcd_e就一直拉为低
145             state <= STOP;
146         end
147         
148         default: state <= IDLE;
149     endcase
150 /***************************************************/
151 assign lcd_rw = 1'b0;//只有写模式
152 assign lcd_psb = 1'b1;//并口模式
153 assign lcd_en = en ?  lcd_clk : 1'b0;
154 /***************************************************/
155 //ROM
156 lcd_rom  u1(
157             .address(add_cnt),
158             .clock(sys_clk),
159             .q(dis_data)
160             );
161 /***************************************************/
162 endmodule
View Code

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

代码2:

  1 module LCD12864(
  2                     //input 
  3                     sys_clk,
  4                     rst_n,
  5                     
  6                     //output 
  7                     lcd_rs,
  8                     lcd_rw,
  9                     lcd_en,
 10                     lcd_data,
 11                     lcd_psb
 12                 );
 13 input sys_clk;// 50MHZ
 14 input rst_n;
 15 
 16 output lcd_rs;//H:data    L:command
 17 output lcd_rw;//H:read module    L:write module
 18 output lcd_en;//H active
 19 output [7:0] lcd_data;
 20 output lcd_psb;//H:parallel    module    L:SPI module
 21 wire [7:0] dis_data;
 22 
 23 /***************************************************/
 24 `define   const_32    6'd32 
 25 `define   const_63    6'd63
 26 /***************************************************/
 27 parameter   T3MS          = 18'd149_999;
 28 parameter    IDLE           = 4'd0,
 29             INIT_FUN_SET1 = 4'd1,
 30             INIT_FUN_SET2 = 4'd2,
 31             INIT_DISPLAY  = 4'd3,
 32             INIT_CLEAR       = 4'd4,
 33             SET_DDRAM_Y   = 4'd5,
 34             SET_DDRAM_X      = 4'd6,
 35             WRITE_DATA      = 4'd7,
 36             STOP          = 4'd8;
 37 /***************************************************/
 38 //产生周期为6MS的lcd_clk给LCD
 39 reg [17:0] cnt;
 40 reg lcd_clk;
 41 always @(posedge sys_clk or negedge rst_n)
 42 if(!rst_n) begin
 43     cnt <= 18'd0;
 44     lcd_clk <= 1'b0;
 45 end
 46 else if(cnt == T3MS)begin
 47     cnt <= 18'd0;
 48     lcd_clk <= ~lcd_clk;
 49 end
 50 else
 51     cnt <= cnt + 1'b1;
 52 /***************************************************/
 53 reg lcd_rs;
 54 reg [3:0] state;
 55 reg [9:0] add_cnt;
 56 always @(posedge lcd_clk or negedge rst_n)
 57 if(!rst_n)
 58     lcd_rs <= 1'b0;
 59 else if(state == WRITE_DATA)
 60     lcd_rs <= 1'b1;        //写数据模式
 61 else
 62     lcd_rs <= 1'b0;        //写命令模式
 63 /***************************************************/
 64 reg [7:0] lcd_data;
 65 reg en;
 66 reg [5:0] i;
 67 always @(posedge lcd_clk or negedge rst_n)
 68 if(!rst_n) begin
 69     state <= IDLE;
 70     lcd_data <= 8'hzz;
 71     en <= 1'b1;
 72     add_cnt <= 10'd0;
 73     i <= 6'd0;
 74 end    
 75 else
 76     case(state)
 77         IDLE: 
 78         begin
 79             state <= INIT_FUN_SET1;
 80             lcd_data <= 8'hzz;
 81             en <= 1'b1;
 82         end
 83         
 84         INIT_FUN_SET1: 
 85         begin
 86             lcd_data <= 8'h36;    //CL= 1--8位,扩充指令RE=1,绘图G=1
 87             state <= INIT_FUN_SET2;
 88         end 
 89         
 90         INIT_FUN_SET2:
 91         begin
 92             lcd_data <= 8'h36;    //CL= 1--8位,扩充指令RE=1,绘图G=1
 93             state <= INIT_DISPLAY;
 94         end
 95             
 96         INIT_DISPLAY:
 97         begin
 98             lcd_data <= 8'h3e;    //CL= 1--8位,扩充指令RE=1,绘图G=1
 99             state <= INIT_CLEAR;
100         end
101                                      //其实上面没必要写那么多次数,这是之前并口写的,懒得去掉,故多写几次36
102         INIT_CLEAR:
103         begin
104             lcd_data <= 8'h01;    //清屏
105             state <= SET_DDRAM_Y;
106         end
107                               
108         SET_DDRAM_Y:  // 先设置垂直地址  
109         begin
110             if(i < `const_32)
111                 lcd_data <= 8'h80 + i;//80H~9fH
112             else
113                 lcd_data <= 8'h80 + (i - `const_32); //80H~9fH/
114                 
115             state <= SET_DDRAM_X;
116         end
117         
118         SET_DDRAM_X: //后设置水平地址 
119         begin
120             if(i < `const_32)
121                 lcd_data <=  8'h80;            //80H
122             else
123                 lcd_data <=  8'h88;            //88H
124             
125             state <= WRITE_DATA;
126         end
127         
128         WRITE_DATA:
129         begin
130             lcd_data <= dis_data;
131             add_cnt <= add_cnt + 1'b1;
132 
133             if(add_cnt[3:0] == 4'hf/*(add_cnt + 1'b1) % 10'd16 == 10'd0*/)begin      //计算行
134                 i <= i + 1'b1;
135                 if(i == `const_63)
136                     state <= STOP;
137                 else
138                     state <= SET_DDRAM_Y;
139             end
140             else
141                 state <= WRITE_DATA;
142         end
143         
144         STOP: 
145         begin
146             en <= 1'b0;//显示完了,lcd_e就一直拉为低
147             state <= STOP;
148         end
149         
150         default: state <= IDLE;
151     endcase
152 /***************************************************/
153 assign lcd_rw = 1'b0;//只有写模式
154 assign lcd_psb = 1'b1;//并口模式
155 assign lcd_en = en ?  lcd_clk : 1'b0;
156 /***************************************************/
157 //ROM
158 lcd_rom  u1(
159             .address(add_cnt),
160             .clock(sys_clk),
161             .q(dis_data)
162             );
163 /***************************************************/
164 endmodule
View Code

代码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的实验将结束。

原文地址:https://www.cnblogs.com/wen2376/p/3323451.html