ZYNQ-FPGA-LCD图片以及汉字显示

一、思路

        与LCD色块相比仅需调整LCD_display模块即可,因为lcd的显示内容取决于显示模块,与驱动、锁相环、顶层模块无关。

        显示字符:显示字符的过程其实与色块类似,就是点亮相应的像素点即可,但不可能人为的进行取模,因此需要软件取模。

        显示图片:显示图片比字符难一点,因为显示图片不仅需要确定显示的像素点,而且多了一个维度就是RGB888,需要先确定相应的像素点,再将色彩数据输入。

        本次实验显示16x16的字符与一张图片(280x265),由于图片的数据太大了,有280x265个点,每个点也是一个rgb888的数据,因此字符库可以直接在程序内,图片的库是不能也无法直接放在程序内,图片需要创建一个ROM进行存储,在需要显示的点再对ROM内容进行存储。(ROM的创建类似于前面的RAM创建)

二、ROM IP核创建及其参数

首先打开Block Memory Generator IP核,对Memory类型进行配置,选择单端口ROM

再配置这个端口A ,width是每个数据位的宽度选择24 因为RGB888每个数据位中要存放24bit颜色数据,再配置ROM深度,深度代表有多少个数据,即图片的分辨率如果是100x100即需要100000个深度。

我们需要提前把图片信息放在ROM 中即选取Load init file选择提前准备好的coe文件,(coe文件是通过图片转换软件得到的)

点击ok即可

三、display模块编写

首先接口不改变,只需要改变输出内容即可。

3.1字符显示

        首先对字符显示的区域进行宏定义,因此需要定义字符显示起点(x,y)、宽、高

        然后判断当扫描坐标pixel_xpos、pixel_ypos扫描到这个区域时进行判断,如果这个点在字符库中是1就显示红色、是0就显示背景色。因此我们还需定义一个x_cnt、y_cnt,代表相对于字符显示起点的坐标。

else if ((pixel_xpos >=  CHAR_X_STRAT - 1'b1)
    && (pixel_xpos < CHAR_X_STRAT + CHAR_WIDTH - 1'b1)
    && (pixel_ypos >=CHAR_Y_STRAT)
    && (pixel_ypos < CHAR_Y_STRAT+CHAR_HIGH)) begin
        if (char[y_cnt][CHAR_WIDTH - x_cnt - 1'b1]) //字符显示
            pixel_data <= REG;
        else
            pixel_data <= WHITE;

        这里的char定义的是一个二阶寄存器

      reg         [127:0]    char[31:0]      ;  //横轴   纵轴

横着有128个数据位,竖着有32个分别代表五个字的横坐标核纵坐标
由于用软件取模得到的横轴是由高到低,即127 126……0,由于lcd的扫描点是从左向右扫描,字库又是从高位到低位,因此

        if (char[y_cnt][CHAR_WIDTH - x_cnt - 1'b1]) //字符显示

        中用了CHAR_WIDTH - x_cnt - 1'b1也恰巧是从左向右

3.2图片显示

        首先我们在二中以及调用ROM的IP核进行数据的存储,只需要把ROM的颜色数据在对应的点读出来即可。

blk_mem_gen_0 u_blk_mem_gen_0 (
  .clka              (lcd_pclk),    // input wire clka
  .ena               (rom_rd_en),      // input wire ena
  .addra             (rom_addr),  // input wire [16 : 0] addra
  .douta             (rom_rd_data)  // output wire [23 : 0] douta
);

        在例化的IP核中把ROM的使能信号连接到rom_rd_en再把rom_rd_en设置恒为1,这样ROM就处于一直读的状态,当我们需要这个颜色数据的时候再把他的ROM地址rom_addr放在正确的位置上。

always @(posedge lcd_pclk or negedge rst) begin
    if (rst == 1'b0)
        rom_addr <= 17'b0;
    else if ((pixel_xpos >=  PIC_X_START - 10'd2)
    && (pixel_xpos < PIC_X_START + PIC_WIDTH - 10'd2)
    && (pixel_ypos >=PIC_Y_START)
    && (pixel_ypos < PIC_Y_START+PIC_HIGH) )
        rom_addr <= rom_addr +1'b1;
    else if (pixel_ypos >= PIC_Y_START+PIC_HIGH)
        rom_addr <=  17'b0;      
end

当我们处于图片显示区域的时候让ROM的读取地址开始增加,当读到最后一行的时候赋值0。

图片读取的时候有一个细节:由于阻塞赋值的原因,地址提前一个周期给模块才能读取相应数据,而读取的数据又要提前一个周期给显示模块才能检测到,因此地址模块要比实际显示提前两个周期才能正确显示。这也是为什么图片读取要-10'd2,而pixel y的变化是很久的不受阻塞赋值影响。

整体显示模块代码:

module       lcd_display(
        input               lcd_pclk     ,
        input               rst          ,
        input       [9:0]   pixel_xpos   ,
        input       [9:0]   pixel_ypos   ,
        output reg  [23:0]  pixel_data
);
//480*272  
parameter       H_DISP  = 10'd480            ;              //行显示有效数据
parameter       V_DISP  = 10'd272            ;              //场显示有效数据
        
parameter       WHITE   = 24'hffffff         ;
parameter       BLACK   = 24'h000000         ;
parameter       REG     = 24'hff0000         ;


parameter       PIC_X_START = 10'd180        ;    //图片起始点坐标
parameter       PIC_Y_START = 10'd3          ;    //图片起始点坐标
parameter       PIC_WIDTH   = 10'd285        ;    //图片起始点坐标
parameter       PIC_HIGH    = 10'd250        ;    //图片起始点坐标
                                          
parameter       CHAR_X_STRAT  = 10'd10        ;    //图片起始点坐标
parameter       CHAR_Y_STRAT  = 10'd50       ;    //图片起始点坐标
parameter       CHAR_WIDTH    = 10'd80      ;    //图片起始点坐标
parameter       CHAR_HIGH     = 10'd16      ;    //图片起始点坐标

      reg         [127:0]    char[31:0]      ;  //横轴   纵轴
      wire        [9:0  ]    x_cnt           ;
      wire        [9:0  ]    y_cnt           ;
      wire                   rom_rd_en       ;
      wire        [23:0 ]    rom_rd_data     ;
      reg         [16:0 ]    rom_addr        ;
//x_cnt y_cnt 是字符相对坐标x 0-127 y 0-31      
assign   x_cnt = pixel_xpos + 1'b1 -CHAR_X_STRAT  ; //xpos存在阻塞赋值
assign   y_cnt = pixel_ypos - CHAR_Y_STRAT        ;
assign   rom_rd_en = 1'b1                         ;//rom设置为一直读模式,这样只需要该地址就能读到相应数据


//给二位寄存器赋值 32*32
always @(posedge lcd_pclk ) begin
            char[    0      ]         <=     80'h04400100000010100000;
            char[    1      ]         <=     80'h04407FFC47F808101000;
            char[    2      ]         <=     80'h3FF80100201008201000;
            char[    3      ]         <=     80'h04403FF821A000001000;
            char[    4      ]         <=     80'h0440000000403FF81000;
            char[    5      ]         <=     80'hFFFE3FF807FC01001000;
            char[    6      ]         <=     80'h04402008E44401001000;
            char[    7      ]         <=     80'h08203FF8244401001000;
            char[    8      ]         <=     80'h1210082027FCFFFE1000;
            char[    9      ]         <=     80'h2208FFFE244401001000;
            char[    10     ]         <=     80'hC2260000244402800000;
            char[    11     ]         <=     80'h12903FF827FC02800000;
            char[    12     ]         <=     80'h12482008244404401000;
            char[    13     ]         <=     80'h22482008245408201000;
            char[    14     ]         <=     80'h0A003FF8540830180000;
            char[    15     ]         <=     80'h040020088FFEC0060000;

end
//rom地址赋值
always @(posedge lcd_pclk or negedge rst) begin
    if (rst == 1'b0)
        rom_addr <= 17'b0;
    else if ((pixel_xpos >=  PIC_X_START - 10'd2)
    && (pixel_xpos < PIC_X_START + PIC_WIDTH - 10'd2)
    && (pixel_ypos >=PIC_Y_START)
    && (pixel_ypos < PIC_Y_START+PIC_HIGH) )
        rom_addr <= rom_addr +1'b1;
    else if (pixel_ypos >= PIC_Y_START+PIC_HIGH)
        rom_addr <=  17'b0;      
end


always @(posedge lcd_pclk or negedge rst) begin
    if (rst == 1'b0)
        pixel_data <= WHITE;
       
    else if ((pixel_xpos >=  PIC_X_START - 1'b1)
    && (pixel_xpos < PIC_X_START + PIC_WIDTH - 1'b1)
    && (pixel_ypos >=PIC_Y_START)
    && (pixel_ypos < PIC_Y_START+PIC_HIGH))
        pixel_data <= rom_rd_data ;// 图片 行减一是阻塞赋值的影响
      
    else if ((pixel_xpos >=  CHAR_X_STRAT - 1'b1)
    && (pixel_xpos < CHAR_X_STRAT + CHAR_WIDTH - 1'b1)
    && (pixel_ypos >=CHAR_Y_STRAT)
    && (pixel_ypos < CHAR_Y_STRAT+CHAR_HIGH)) begin
        if (char[y_cnt][CHAR_WIDTH - x_cnt - 1'b1]) //字符显示
            pixel_data <= REG;
        else
            pixel_data <= WHITE;
    
    end
    else
            pixel_data<= WHITE;
end   

blk_mem_gen_0 u_blk_mem_gen_0 (
  .clka              (lcd_pclk),    // input wire clka
  .ena               (rom_rd_en),      // input wire ena
  .addra             (rom_addr),  // input wire [16 : 0] addra
  .douta             (rom_rd_data)  // output wire [23 : 0] douta
);


endmodule

其余模块代码与上次LCD彩条显示实验一模一样

lcd彩条实验icon-default.png?t=N7T8https://blog.csdn.net/weixin_48444982/article/details/133965228?spm=1001.2014.3001.5502

四、结果

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值