• 基于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的实验将结束。

  • 相关阅读:
    第十三周学习进度
    第二次冲刺阶段每日任务02
    第二次冲刺阶段每日任务01
    构建之法阅读笔记03
    找水王续
    第十二周学习进度
    找水王
    第十一周学习进度
    博客园的用户体验
    找水王1
  • 原文地址:https://www.cnblogs.com/wen2376/p/3323451.html
Copyright © 2020-2023  润新知