文章目錄
- 1螢幕
- 2行場掃描時序
- 3分析
- 4代碼
-
- 4-1産生9M驅動時鐘
- 4-2驅動部分
- 4-3顯示内容部分
- 4-3頂層
- 5結果
-
- 5-1
- 5-2
- 6Testbench
1螢幕

使用像素:480*272的一塊RGB565螢幕。
像素時鐘:9Mhz
接口:
lcd_bl:lcd背光
lcd_rgb[15:0]:色彩值
lcd_de:當計數器處于valid(有效)區域時應将其拉高,此時輸出lcd_rgb[15:0]的值到螢幕的對應像素點上。
lcd_hs:可一直将其拉高沒有影響
lcd_vs:可一直将其拉高沒有影響
lcd_pclk:9M時鐘
lcd_rst:lcd單獨複位低電平有效
2行場掃描時序
3分析
1、利用ip核倍分頻獲得9M時鐘。
2、使用子母兩個計數器對驅動時鐘進行計數。
3、子計數器的計數範圍為0-524,所代表一個行掃描周期,滿載時在下一個時鐘周期清零,并使母計數器+1。
4、母計數器的計數範圍為0-285,所代表一個場掃描周期,滿載時在下一個時鐘周期清零。
5、利用組合邏輯将子母倆個計數的計數值做範圍限定,确定真正valid的範圍。
6、當計數值處于真正valid的範圍時,需要回傳目前的坐标值,使用者根據回傳的坐标值設定RGB565的色彩值。
4代碼
4-1産生9M驅動時鐘
-其端口清單如下:
module pll_9m (
areset,//注意:其為高電平複位
inclk0,//輸入時鐘
c0, //輸出時鐘
locked //穩定鎖:當c0輸出穩定時locked會從低電平變為高電平
);
4-2驅動部分
module lcd_driver(
input lcd_clk, //lcd子產品驅動時鐘
input sys_rst_n, //複位信号
//下列信号與RGBLCD螢幕相連接配接
output lcd_hs, //LCD 行同步信号
output lcd_vs, //LCD 場同步信号
output lcd_de, //LCD 資料使能
output [15:0] lcd_rgb, //LCD RGB565顔色資料
output lcd_bl, //LCD 背光控制信号
output lcd_rst, //LCD 複位信号
output lcd_pclk, //LCD 采樣時鐘
//接口信号,此子產品推出valid坐标值,并接收使用者發來的RGB565的像素點顔色值
input [15:0] pixel_data, //像素點資料
output [10:0] pixel_xpos, //像素點橫坐标
output [10:0] pixel_ypos //像素點縱坐标
);
//參數清單
parameter H_SYNC = 11'd41; //行同步
parameter H_BACK = 11'd2; //行顯示後沿
parameter H_DISP = 11'd480; //行有效資料
parameter H_FRONT = 11'd2; //行顯示前沿
parameter H_TOTAL = 11'd525; //行掃描周期
parameter V_SYNC = 11'd10; //場同步
parameter V_BACK = 11'd2; //場顯示後沿
parameter V_DISP = 11'd272; //場有效資料
parameter V_FRONT = 11'd2; //場顯示前沿
parameter V_TOTAL = 11'd286; //場掃描周期
//子母兩個計數器
reg [10:0] cnt_h;
reg [10:0] cnt_v;
//...
wire lcd_en;
wire data_req;
//對lcd螢幕的某些信号加以固定
assign lcd_bl = 1'b1;
assign lcd_rst = 1'b1;
assign lcd_pclk = lcd_clk;
assign lcd_hs = 1'b1;
assign lcd_vs = 1'b1;
//當子母計數器的值處于valid範圍内時,将lcd_de拉高,此時輸出lcd_rgb[15:0]的值到螢幕的對應像素點上。
assign lcd_de = lcd_en;
assign lcd_en = (((cnt_h > H_SYNC+H_BACK) && (cnt_h <= H_SYNC+H_BACK+H_DISP))
&&((cnt_v > V_SYNC+V_BACK) && (cnt_v <= V_SYNC+V_BACK+V_DISP)))
? 1'b1 : 1'b0;
//lcd_rgb[15:0]的值由使用者(input)給予即為:pixel_data
assign lcd_rgb = lcd_en ? pixel_data : 16'd0;
//...此處做一個簡單的分析,lcd_de拉高,lcd_rgb[15:0]的值瞬間到達螢幕的對應像素點上。
// 我們最開始的分析是:cnt值->産生坐标->根據坐标确認有效範圍->再往範圍裡放顔色值
// 是以說我們在上面寫的那個就是有效範圍,由于觸發器的特性,是以要提前一個時鐘周期将坐标值送出,才來的及在lcd_en為1時候準确的送出顔色值
// 而一場是由很多行組成的,母計數器,要比子計數器慢。子計數器變很多次,母計數器才變一次。是以對子計數器需要提前一個時鐘周期發送出坐标。
// PS:如果沒懂的話也沒關系,因為我也比較暈。但隻要時序對,螢幕就能出現顔色然後進行simulation或者實物調試
assign data_req = (((cnt_h > H_SYNC+H_BACK-1'b1) && (cnt_h <= H_SYNC+H_BACK+H_DISP-1'b1)) //對子計數器需要提前一個時鐘周期發送出坐标。
&& ((cnt_v > V_SYNC+V_BACK) && (cnt_v <= V_SYNC+V_BACK+V_DISP))) //母計數器不變
? 1'b1 : 1'b0;
//目前像素點坐标
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 11'd0;
assign pixel_ypos = data_req ? ((cnt_v - (V_SYNC + V_BACK - 1'b1)) - 1'b1) : 11'd0;
//這塊為什麼有個-1 在最下面的測試圖檔中可以看到解釋
//行計數器對像素時鐘計數(子)
always @(posedge lcd_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
cnt_h <= 11'd0;
else begin
if(cnt_h < H_TOTAL - 1'b1)
cnt_h <= cnt_h + 1'b1;
else
cnt_h <= 11'd0;
end
end
//場計數器對行計數(母)
always @(posedge lcd_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
cnt_v <= 11'd0;
else if(cnt_h == H_TOTAL - 1'b1) begin
if(cnt_v < V_TOTAL - 1'b1)
cnt_v <= cnt_v + 1'b1;
else
cnt_v <= 11'd0;
end
end
endmodule
4-3顯示内容部分
module lcd_display(
input lcd_clk, //lcd驅動時鐘
input sys_rst_n, //複位信号
input [10:0] pixel_xpos, //像素點橫坐标
input [10:0] pixel_ypos, //像素點縱坐标
output reg [15:0] pixel_data //像素點資料
);
localparam WHITE = 16'b11111_111111_11111; //RGB565 白色
localparam BLACK = 16'b00000_000000_00000; //RGB565 黑色
localparam RED = 16'b11111_000000_00000; //RGB565 紅色
localparam GREEN = 16'b00000_111111_00000; //RGB565 綠色
localparam BLUE = 16'b00000_000000_11111; //RGB565 藍色
localparam BROWN = 16'h7800; //RGB565 棕色
//測試邊框
always @(posedge lcd_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
pixel_data <= 16'hffff;
else begin
if(pixel_xpos == 11'd1)
pixel_data <= GREEN;
else if(pixel_xpos == 11'd480)
pixel_data <= GREEN;
else if(pixel_xpos == 11'd240)
pixel_data <= GREEN;
else if(pixel_ypos == 11'd1)
pixel_data <= GREEN;
else if(pixel_ypos == 11'd272)
pixel_data <= GREEN;
else if(pixel_ypos == 11'd136)
pixel_data <= GREEN;
else
pixel_data <= BROWN;
end
end
//彩條
// always @(posedge lcd_clk or negedge sys_rst_n) begin
// if (!sys_rst_n)
// pixel_data <= 16'hffff;
// else begin
// if((pixel_xpos >= 1) && (pixel_xpos < (11'd480/5)*1))
// pixel_data <= GREEN;
// else if((pixel_xpos >= (11'd480/5)*1) && (pixel_xpos < (11'd480/5)*2))
// pixel_data <= BLACK;
// else if((pixel_xpos >= (11'd480/5)*2) && (pixel_xpos < (11'd480/5)*3))
// pixel_data <= RED;
// else if((pixel_xpos >= (11'd480/5)*3) && (pixel_xpos < (11'd480/5)*4))
// pixel_data <= BLUE;
// else
// pixel_data <= BROWN;
// end
// end
endmodule
4-3頂層
module lcd_rgb_colorbar(
input sys_clk,
input sys_rst_n,
output lcd_hs,
output lcd_vs,
output lcd_de,
output [15:0] lcd_rgb,
output lcd_bl,
output lcd_rst,
output lcd_pclk
);
wire lcd_clk_w;
wire locked_w;
wire rst_n_w;
wire [15:0] pixel_data_w;
wire [ 9:0] pixel_xpos_w;
wire [ 9:0] pixel_ypos_w;
//待PLL輸出穩定之後,停止複位
assign rst_n_w = sys_rst_n & locked_w;
pll_9m u_lcd_pll( //時鐘分頻子產品
.inclk0 (sys_clk),
.areset (~sys_rst_n),
.c0 (lcd_clk_w), //lcd驅動時鐘
.locked (locked_w)
);
lcd_driver u_lcd_driver( //lcd驅動子產品
.lcd_clk (lcd_clk_w),
.sys_rst_n (rst_n_w),
.lcd_hs (lcd_hs),
.lcd_vs (lcd_vs),
.lcd_de (lcd_de),
.lcd_rgb (lcd_rgb),
.lcd_bl (lcd_bl),
.lcd_rst (lcd_rst),
.lcd_pclk (lcd_pclk),
.pixel_data (pixel_data_w),
.pixel_xpos (pixel_xpos_w),
.pixel_ypos (pixel_ypos_w)
);
lcd_display u_lcd_display( //lcd顯示子產品
.lcd_clk (lcd_clk_w),
.sys_rst_n (rst_n_w),
.pixel_xpos (pixel_xpos_w),
.pixel_ypos (pixel_ypos_w),
.pixel_data (pixel_data_w)
);
endmodule
5結果
5-1
可以看到,測試中出現了列偏移(1-480)*(0~271),是以對代碼進行修整
見上面代碼中的這一句:
5-2
OK!(1-480)*(1~272)
6Testbench
給予時鐘激勵,觀察信号關系即可。