一、思路
与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彩条实验https://blog.csdn.net/weixin_48444982/article/details/133965228?spm=1001.2014.3001.5502