🌈以下是本人对LCD液晶显示模块的理解,是为了方便后续的学习,如有补充和错误,欢迎评论区留言!
1.实验设备及要求
实验设备:正点原子达芬奇开发板(Xilinx),LCD液晶显示屏。
实验要求:使用FPGA开发板在LCD液晶显示屏上显示4色彩条,要求兼容四种型号的LCD屏幕。
开发环境:Vivado_2020.2、Notepad++、Visio。
2.LCD液晶显示屏硬件知识汇总
2.1.LCD实物图及区域划分
图二中的VSYNC、HSYNC分别是场同步信号和行同步信号,当一帧图片显示完成,场同步信号拉低表示新的一帧图片显示的开始;当一行信号显示完成时,行同步信号拉低表示下一行数据显示的开始。VFP、VBP、HFP、HBP分别是场前延、场后延、行前延和行后延,这部分区域不显示有效信息,这部分所消耗的时间称为“消隐时间”。以下是以上信号的具体参数和消耗时间:
LCD(1024*600)刷新一帧的时间:
= (VSPW + VBP + LINE + VFP) * (HSPW + HBP + HOZVAL + HFP)
= (3 + 20 +600 +12) * (20 +140 +1024 +160)
= 635 * 1344
= 853440 ns
LCD显示一行的总时间 = (行后延时间 + 行前延时间 + 行同步信号维持时间 + 行有效数据时间),根据这个公式可知显示一行的时间就是1344ns,而分辨率为1024 * 600的LCD屏的总行数 = (600个有效行数 + 20行场前延 + 12行场后延),除此之外还有场同步信号的维持时间,它维持了3个场(帧)时序,即维持时间是3 * 1344 ns,故总时间就是853440 ns,这是一帧图像所需要的显示时间,如果我需要60HZ刷新率,即一秒钟60帧,则它的所需要的时钟约为51.2 Mhz。
2.1.LCD模块引脚分布及功能
引脚 |
作用 |
引脚 |
作用 |
LCD R[0:7] |
Red像素输入 |
RESET |
LCD复位 |
LCD G[0:7] |
Green像素输入 |
LCD BL |
LCD背光控制 |
LCD B[0:7] |
Blue像素输入 |
LCD DE |
LCD数据输入 |
LCD CLK |
LCD复位 |
LCD VSYNC |
LCD场同步信号 |
PIN 35~39 |
触摸相关 |
LCD HSYNC |
LCD行同步信号 |
注:R[7]、G[7]、B[7]的引脚为inout类型,FPGA通过读取这三个引脚的值,可以判断当前LCD的分辨率。
3.实验设计
3.1硬件模块的设计
- rd_id模块
管脚名称 |
引脚类型 |
作用 |
clk |
input |
时钟 |
rst_n |
input |
复位 |
lcd_rgb |
input |
判断LCD分辨率 |
lcd_id |
output |
输出LCD类型 |
rd_id模块作用:通过R[7]、G[7]、B[7]的信号判断当前的LCD屏id数据。
- clk_div模块
管脚名称 |
引脚类型 |
作用 |
clk |
input |
时钟 |
rst_n |
input |
复位 |
lcd_id |
input |
读取当前LCD类型 |
lcd_pclk |
output |
根据LCD类型输出对应时钟 |
clk_div模块作用:根据rd_id模块的lcd_id参数,输出对应的时钟频率,为后续模块提供时钟信号。
- lcd_display模块
管脚名称 |
引脚类型 |
作用 |
lcd_pclk |
input |
时钟 |
rst_n |
input |
复位 |
pixel_xpos |
input |
当前像素点横坐标 |
pixel_ypos |
input |
当前像素点纵坐标 |
h_disp |
input |
LCD屏水平分辨 |
v_disp |
input |
LCD屏垂直分辨 |
pixel_data |
output |
输出像素数据 |
lcd_display模块作用:lcd_drive模块提供当前像素坐标以及LCD分辨率,根据以上信息,输出当前位置需要显示的颜色数据。
- lcd_drive模块
管脚名称 |
引脚类型 |
作用 |
lcd_pclk |
input |
时钟 |
rst_n |
input |
复位 |
lcd_id |
input |
读取当前LCD类型 |
pixel_data |
input |
读取像素数据 |
pixel_xpos |
output |
当前像素点横坐标 |
pixel_ypos |
output |
当前像素点纵坐标 |
h_disp |
output |
LCD屏水平分辨 |
v_disp |
output |
LCD屏垂直分辨 |
lcd_de |
output |
LCD数据使能信号 |
lcd_hs |
output |
LCD行同步信号 |
lcd_vs |
output |
LCD场同步信号 |
lcd_bl |
output |
LCD背光控制信号 |
lcd_clk |
output |
LCD像素时钟 |
lcd_rst |
output |
LCD复位 |
lcd_rgb |
output |
LCD颜色数据 |
lcd_drive模块:读取LCD的id参数,并向lcd_drive模块中提供LCD的分辨率和当前像素位置坐标,根据内部的lcd_de信号,通过LCD显示颜色。
- 顶层模块框架
3.2程序设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| module rd_id( input clk , input rst_n , input [23:0] lcd_rgb, output reg [15:0] lcd_id );
reg rd_flag;
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin rd_flag <= 1'b0; lcd_id <= 16'd0; end else begin if(rd_flag == 1'b0) begin rd_flag <= 1'b1; case({lcd_rgb[7],lcd_rgb[15],lcd_rgb[23]}) 3'b000 : lcd_id <= 16'h4342; 3'b001 : lcd_id <= 16'h7084; 3'b010 : lcd_id <= 16'h7016; 3'b100 : lcd_id <= 16'h4384; 3'b101 : lcd_id <= 16'h1018; default : lcd_id <= 16'd0; endcase end end end endmodule
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| module clk_div( input clk, input rst_n, input [15:0] lcd_id, output reg lcd_pclk );
reg clk_25m; reg clk_12_5m; reg div_4_cnt;
always @(posedge clk or negedge rst_n) begin if(!rst_n) clk_25m <= 1'b0; else clk_25m <= ~clk_25m; end
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin div_4_cnt <= 1'b0; clk_12_5m <= 1'b0; end else begin div_4_cnt <= div_4_cnt + 1'b1; if(div_4_cnt == 1'b1) clk_12_5m <= ~clk_12_5m; else clk_12_5m <= clk_12_5m; end end
always @(*) begin case(lcd_id) 16'h4342 : lcd_pclk = clk_12_5m; 16'h7084 : lcd_pclk = clk_25m; 16'h7016 : lcd_pclk = clk; 16'h4384 : lcd_pclk = clk_25m; 16'h1018 : lcd_pclk = clk; default : lcd_pclk = 1'b0; endcase end
endmodule
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
| module lcd_display( input lcd_pclk, input rst_n, input [10:0] pixel_xpos, input [10:0] pixel_ypos, input [10:0] h_disp, input [10:0] v_disp, output reg [23:0] pixel_data );
parameter WHITE = 24'hFFFFFF; parameter BLACK = 24'h000000; parameter RED = 24'hFF0000; parameter GREEN = 24'h00FF00; parameter BLUE = 24'h0000FF;
always @(posedge lcd_pclk or negedge rst_n) begin if(!rst_n) pixel_data <= BLACK; else begin if((pixel_xpos >= 11'd0) && (pixel_xpos < h_disp/5*1)) pixel_data <= WHITE; else if((pixel_xpos >= h_disp/5*1) && (pixel_xpos < h_disp/5*2)) pixel_data <= BLACK; else if((pixel_xpos >= h_disp/5*2) && (pixel_xpos < h_disp/5*3)) pixel_data <= RED; else if((pixel_xpos >= h_disp/5*3) && (pixel_xpos < h_disp/5*4)) pixel_data <= GREEN; else pixel_data <= BLUE; end end endmodule
<!-- endtab -->
<!-- tab lcd_driver --> module lcd_driver( input lcd_pclk, input rst_n, input [15:0] lcd_id, input [23:0] pixel_data, output reg [10:0] pixel_xpos, output reg [10:0] pixel_ypos, output reg [10:0] h_disp, output reg [10:0] v_disp, output reg data_req, output reg lcd_de, output lcd_hs, output lcd_vs, output lcd_bl, output lcd_clk, output lcd_rst, output [23:0] lcd_rgb );
parameter H_SYNC_4342 = 11'd41; parameter H_BACK_4342 = 11'd2; parameter H_DISP_4342 = 11'd480; parameter H_FRONT_4342 = 11'd2; parameter H_TOTAL_4342 = 11'd525; parameter V_SYNC_4342 = 11'd10; parameter V_BACK_4342 = 11'd2; parameter V_DISP_4342 = 11'd272; parameter V_FRONT_4342 = 11'd2; parameter V_TOTAL_4342 = 11'd286;
parameter H_SYNC_7084 = 11'd128; parameter H_BACK_7084 = 11'd88; parameter H_DISP_7084 = 11'd800; parameter H_FRONT_7084 = 11'd40; parameter H_TOTAL_7084 = 11'd1056; parameter V_SYNC_7084 = 11'd2; parameter V_BACK_7084 = 11'd33; parameter V_DISP_7084 = 11'd480; parameter V_FRONT_7084 = 11'd10; parameter V_TOTAL_7084 = 11'd525;
parameter H_SYNC_7016 = 11'd20; parameter H_BACK_7016 = 11'd140; parameter H_DISP_7016 = 11'd1024; parameter H_FRONT_7016 = 11'd160; parameter H_TOTAL_7016 = 11'd1344; parameter V_SYNC_7016 = 11'd3; parameter V_BACK_7016 = 11'd20; parameter V_DISP_7016 = 11'd600; parameter V_FRONT_7016 = 11'd12; parameter V_TOTAL_7016 = 11'd635;
parameter H_SYNC_1018 = 11'd10; parameter H_BACK_1018 = 11'd80; parameter H_DISP_1018 = 11'd1280; parameter H_FRONT_1018 = 11'd70; parameter H_TOTAL_1018 = 11'd1440; parameter V_SYNC_1018 = 11'd3; parameter V_BACK_1018 = 11'd10; parameter V_DISP_1018 = 11'd800; parameter V_FRONT_1018 = 11'd10; parameter V_TOTAL_1018 = 11'd823;
parameter H_SYNC_4384 = 11'd128; parameter H_BACK_4384 = 11'd88; parameter H_DISP_4384 = 11'd800; parameter H_FRONT_4384 = 11'd40; parameter H_TOTAL_4384 = 11'd1056; parameter V_SYNC_4384 = 11'd2; parameter V_BACK_4384 = 11'd33; parameter V_DISP_4384 = 11'd480; parameter V_FRONT_4384 = 11'd10; parameter V_TOTAL_4384 = 11'd525;
reg [10:0] h_sync ; reg [10:0] h_back ; reg [10:0] h_total; reg [10:0] v_sync ; reg [10:0] v_back ; reg [10:0] v_total; reg [10:0] h_cnt ; reg [10:0] v_cnt ;
assign lcd_hs = 1'b1; assign lcd_vs = 1'b1;
assign lcd_bl = 1'b1; assign lcd_clk = lcd_pclk; assign lcd_rst= 1'b1;
assign lcd_rgb = lcd_de ? pixel_data : 24'd0;
always@ (posedge lcd_pclk or negedge rst_n) begin if(!rst_n) pixel_xpos <= 11'd0; else if(data_req) pixel_xpos <= h_cnt + 2'd2 - h_sync - h_back ; else pixel_xpos <= 11'd0; end
always@ (posedge lcd_pclk or negedge rst_n) begin if(!rst_n) pixel_ypos <= 11'd0; else if(v_cnt >= (v_sync + v_back)&&v_cnt < (v_sync + v_back + v_disp)) pixel_ypos <= v_cnt + 1'b1 - (v_sync + v_back) ; else pixel_ypos <= 11'd0; end
always @(*) begin case(lcd_id) 16'h4342 : begin h_sync = H_SYNC_4342; h_back = H_BACK_4342; h_disp = H_DISP_4342; h_total = H_TOTAL_4342; v_sync = V_SYNC_4342; v_back = V_BACK_4342; v_disp = V_DISP_4342; v_total = V_TOTAL_4342; end 16'h7084 : begin h_sync = H_SYNC_7084; h_back = H_BACK_7084; h_disp = H_DISP_7084; h_total = H_TOTAL_7084; v_sync = V_SYNC_7084; v_back = V_BACK_7084; v_disp = V_DISP_7084; v_total = V_TOTAL_7084; end 16'h7016 : begin h_sync = H_SYNC_7016; h_back = H_BACK_7016; h_disp = H_DISP_7016; h_total = H_TOTAL_7016; v_sync = V_SYNC_7016; v_back = V_BACK_7016; v_disp = V_DISP_7016; v_total = V_TOTAL_7016; end 16'h4384 : begin h_sync = H_SYNC_4384; h_back = H_BACK_4384; h_disp = H_DISP_4384; h_total = H_TOTAL_4384; v_sync = V_SYNC_4384; v_back = V_BACK_4384; v_disp = V_DISP_4384; v_total = V_TOTAL_4384; end 16'h1018 : begin h_sync = H_SYNC_1018; h_back = H_BACK_1018; h_disp = H_DISP_1018; h_total = H_TOTAL_1018; v_sync = V_SYNC_1018; v_back = V_BACK_1018; v_disp = V_DISP_1018; v_total = V_TOTAL_1018; end default : begin h_sync = H_SYNC_4342; h_back = H_BACK_4342; h_disp = H_DISP_4342; h_total = H_TOTAL_4342; v_sync = V_SYNC_4342; v_back = V_BACK_4342; v_disp = V_DISP_4342; v_total = V_TOTAL_4342; end endcase end
always@ (posedge lcd_pclk or negedge rst_n) begin if(!rst_n) lcd_de <= 1'b0; else lcd_de <= data_req; end
always@ (posedge lcd_pclk or negedge rst_n) begin if(!rst_n) data_req<=1'b0; else if((h_cnt >= h_sync + h_back - 2'd2) && (h_cnt < h_sync + h_back + h_disp - 2'd2) && (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp)) data_req <= 1'b1; else data_req <= 1'b0; end
always@ (posedge lcd_pclk or negedge rst_n) begin if(!rst_n) h_cnt <= 11'd0; else begin if(h_cnt == h_total - 1'b1) h_cnt <= 11'd0; else h_cnt <= h_cnt + 1'b1; end end
always@ (posedge lcd_pclk or negedge rst_n) begin if(!rst_n) v_cnt <= 11'd0; else begin if(h_cnt == h_total - 1'b1) begin if(v_cnt == v_total - 1'b1) v_cnt <= 11'd0; else v_cnt <= v_cnt + 1'b1; end end end
endmodule
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| module lcd_rgb_colorbar( input sys_clk, input sys_rst_n,
output lcd_de, output lcd_hs, output lcd_vs, output lcd_bl, output lcd_clk, output lcd_rst, inout [23:0] lcd_rgb );
wire [15:0] lcd_id ; wire lcd_pclk ; wire [10:0] pixel_xpos; wire [10:0] pixel_ypos; wire [10:0] h_disp ; wire [10:0] v_disp ; wire [23:0] pixel_data; wire [23:0] lcd_rgb_o ; wire [23:0] lcd_rgb_i ;
assign lcd_rgb = lcd_de ? lcd_rgb_o : {24{1'bz}}; assign lcd_rgb_i = lcd_rgb;
rd_id u_rd_id( .clk (sys_clk ), .rst_n (sys_rst_n), .lcd_rgb (lcd_rgb_i), .lcd_id (lcd_id ) );
clk_div u_clk_div( .clk (sys_clk ), .rst_n (sys_rst_n), .lcd_id (lcd_id ), .lcd_pclk (lcd_pclk ) );
lcd_display u_lcd_display( .lcd_pclk (lcd_pclk ), .rst_n (sys_rst_n ), .pixel_xpos (pixel_xpos), .pixel_ypos (pixel_ypos), .h_disp (h_disp ), .v_disp (v_disp ), .pixel_data (pixel_data) );
lcd_driver u_lcd_driver( .lcd_pclk (lcd_pclk ), .rst_n (sys_rst_n ), .lcd_id (lcd_id ), .pixel_data (pixel_data), .pixel_xpos (pixel_xpos), .pixel_ypos (pixel_ypos), .h_disp (h_disp ), .v_disp (v_disp ), .data_req ( ), .lcd_de (lcd_de ), .lcd_hs (lcd_hs ), .lcd_vs (lcd_vs ), .lcd_bl (lcd_bl ), .lcd_clk (lcd_clk ), .lcd_rst (lcd_rst ), .lcd_rgb (lcd_rgb_o ) );
endmodule
|
4.实验结果展示
5.实验扩展(LCD显示字符和图片)
扩展实验任务要求:在之前实验基础上,将显示彩条变为显示图片和字符。
5.1代码和操作步骤
对代码的修改主要是针对lcd_display模块,这个模块主要作用是控制LCD显示的字符和显示的图片。其可修改代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| module lcd_display( input lcd_pclk, input rst_n, input [10:0] pixel_xpos, input [10:0] pixel_ypos, output reg [23:0] pixel_data );
localparam PIC_X_START = 11'd10; localparam PIC_Y_START = 11'd10; localparam PIC_WIDTH = 11'd100; localparam PIC_HEIGHT = 11'd100; localparam CHAR_X_START= 11'd10; localparam CHAR_Y_START= 11'd120; localparam CHAR_WIDTH = 11'd216; localparam CHAR_HEIGHT = 11'd34; localparam BACK_COLOR = 24'hE0FFFF; localparam CHAR_COLOR = 24'hff0000;
reg [215:0] char[33:0]; reg [13:0] rom_addr ;
wire [10:0] x_cnt; wire [10:0] y_cnt; wire rom_rd_en ; wire [23:0] rom_rd_data ;
assign x_cnt = pixel_xpos + 1'b1 - CHAR_X_START; assign y_cnt = pixel_ypos - CHAR_Y_START; assign rom_rd_en = 1'b1;
always @(posedge lcd_pclk) begin char[0 ] <= 216'h000000000000000000000000000000000000000000000000000000; char[1 ] <= 216'h000000000000000000000000000000000000000000000000006000; char[2 ] <= 216'h0000000000000C0000000700000000000000000000000000003800; char[3 ] <= 216'h0000000000000E0000000380000000001800000380000000183000; char[4 ] <= 216'h00000F0000000E00000001C000000000FE000001C00000031C7000; char[5 ] <= 216'h0003FF8000000C00000000C00000003F0F000001C00000038C6000; char[6 ] <= 216'h00FFFE0000000C0000000000000020181C000001800000018C4000; char[7 ] <= 216'h007FF00000000DE00000001FE0003C00300000018000000184C000; char[8 ] <= 216'h001CF00000003FE0000007FFF0001C03C000000180000000808300; char[9 ] <= 216'h001EF0000001FE000007FE0000000C01800000018000000001FF80; char[10] <= 216'h001CF000000018000003800000000000BE000001800000207F01E0; char[11] <= 216'h001CF000000018060000103800000003CE0000018000003F8003E0; char[12] <= 216'h001CFFF8000010FF0000181E0000003D8600000183E00060000300; char[13] <= 216'h001FFFFC00003F03C0001C0F0000003186000001FFE000600F0400; char[14] <= 216'h1FFFE03E000FC703C000380300001831A60003FFE00000E3F38000; char[15] <= 216'h0FDCE03E007887018000703100007C13E60001F9800000C3838000; char[16] <= 216'h0F1CE03C0060C6018000C0380000D81186000001800000C0060000; char[17] <= 216'h071CF83C0020C40180018030000018118600000180000000040000; char[18] <= 216'h071CFE3C002047C10002386000001031F600000380000000180600; char[19] <= 216'h07387E3800207E0100000C6000001837E6000003400000000FFF80; char[20] <= 216'h073800380021F801000006C0000018318600000360000003FE0180; char[21] <= 216'h07F0007800201843000003C00000083186000006300001FF060000; char[22] <= 216'h0380007800201FE300000180000008318600000638000000060000; char[23] <= 216'h0383FE7800207E03000003E0000008213E00000E18000000060000; char[24] <= 216'h03FFFFF00027D803000006700000FE211E00000C0C000000060000; char[25] <= 216'h03F807F00020180300001C1C0001C1E10E0000180F000000060000; char[26] <= 216'h038003F0002018030000380F0000007C0400003807800000060000; char[27] <= 216'h000003E0002018370000E007C000000FE000006003E00000060000; char[28] <= 216'h000001C00020181F00078003FE000003FFF000C003F800000E0000; char[29] <= 216'h000001800020180F00000001FF000000FF80030001FF00007C0000; char[30] <= 216'h0000000000000006000000002000000018000000000000003C0000; char[31] <= 216'h000000000000000600000000000000000000000000000000180000; char[32] <= 216'h000000000000000000000000000000000000000000000000000000; char[33] <= 216'h000000000000000000000000000000000000000000000000000000; end
always @(posedge lcd_pclk or negedge rst_n) begin if (!rst_n) pixel_data <= BACK_COLOR; 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_HEIGHT) ) pixel_data <= rom_rd_data ; else if((pixel_xpos >= CHAR_X_START - 1'b1) && (pixel_xpos < CHAR_X_START + CHAR_WIDTH - 1'b1) && (pixel_ypos >= CHAR_Y_START) && (pixel_ypos < CHAR_Y_START + CHAR_HEIGHT)) begin if(char[y_cnt][CHAR_WIDTH -1'b1 - x_cnt]) pixel_data <= CHAR_COLOR; else pixel_data <= BACK_COLOR; end else pixel_data <= BACK_COLOR; end
always @(posedge lcd_pclk or negedge rst_n) begin if(!rst_n) rom_addr <= 14'd0; else if((pixel_ypos >= PIC_Y_START) && (pixel_ypos < PIC_Y_START + PIC_HEIGHT) && (pixel_xpos >= PIC_X_START - 2'd2) && (pixel_xpos < PIC_X_START + PIC_WIDTH - 2'd2)) rom_addr <= rom_addr + 1'b1; else if((pixel_ypos >= PIC_Y_START + PIC_HEIGHT)) rom_addr <= 14'd0; end
|
下面以显示字符为例,演示如何修改上述代码实现显示不同的字符。
- 首先我们要确定我们想要显示的字符以及字符的字宽和字高。这里以
正点原子
为例,字高和字宽都为32。
- 我们需要借助一款软件(PCtoLCD),它可以将字符点阵转化为16进制代码。软件获取方式:软件下载
- 打开软件后,接下来如下图所示进行操作:
- 点击左上角文件->另存为类型为.bmp的图片文件。接着在此处打开该文件,这时可以注意到软件已经从字符模式变为图片模式。
- 点击下方生成字模。并复制字模,对verilog进行修改,即可实现在LCD上显示
正点原子
。
5.2详细修改过程
这个修改过程其实是非常麻烦的,当时也是折磨了我很久我才想明白怎么回事。😭😭😭
- 显示的字符和Verilog程序的对应关系:
- 字高: 它决定了char这个数组的维数。当你的字高设置为32时,那么变量就要定义为char[31:0]。
- 字数和字宽:这两个参数共同决定了char这个变量的位数。例如字宽为32字数为4时,那么变量char位数就是
4*32=128
位,结合上面可以给出char变量的最终定义reg [127:0] char[31:0]
。另外它也决定了CHAR_WIDTH的赋值。它的赋值结果为localparam CHAR_WIDTH = 11'd128;
。
- 字符:它决定了char变量的赋值,不同的字符对应不同的字模。
- 显示的字符与PCtoLCD软件参数的设置之间的关系:
这里要注意的是点阵的显示单位是字节,也就是八位,所以在每一行要显示的位数一定是8的倍数,遵守这条规则的前提下,下面的设置就非常好理解了。
- 字数和字宽:由于一行显示的位数一定要是8的倍数,当我们确认了字数和字宽,那么一行显示的位数就确定了,位数 = 字数*字宽。如果次数的位数不满足是8的倍数,我们可以通过增加字符之间的间隔让其满足上述条件。例如字数为6,字宽为34的
西南交通大学
,通过计算发现6 * 34 = 204,这并不是8的整数倍,想要确保字符的字宽仍为34,我们就需要在字符之间加入2个间隔。这个间隔的个数可以通过下面的公式计算得出。
假设字数为num,字宽为font_w,字间隔为font_empty,那么就会有如下等式:(font_w + font_empty)*num %8 = 0
。
- 字数和字宽:同样的,这两个因素还决定了软件的
字模选项中->每行显示数据->点阵
中的值,在位数满足8的整数倍前提下,值 = (font_w + font_empty)*num /8
。