我之所以做了这个半透明字幕叠加,得谢谢CB哥给我分配的任务。当时,他给了我这样的一条公式:
其中A为图像源,B为字幕,C为字幕叠加后的图像,a%为透明度。
CB哥让我在48小时内用FPGA实现,最后我也按时完成任务了。接下来,我将介绍该设计的实现过程。当拿到这个任务后,我就用VC++验证了半透明字幕叠加的效果,如下所示:
用软件测试了之后,发现效果挺不错的。呵呵,接下来该把它移植到FPGA上了。首先,我们可以从公式中看出它其实进行了浮点数计算,如果在FPGA中也进行浮点数计算,结果你会发现实现起来真的很复杂、很吃力。然而,FPGA对整数的运算却是非常的简单。既然这样,我们何不一开始对把它进行适当的缩放,然后进行整数运算,最后对结果进行缩放以得到预期的数据。为了实现这个目的,我对公式的两边同时乘以256。例如,对于
公式两边乘以256后为
最后再将结果8’d256*C向右移8位以得到预期的图像数据C。大概的思路就是这样~~它的代码实现如下:
/******************************************************* Author : crazyBird Filename : lcd_driver.v Data : 2015-3-4 Description : driver of lcd ********************************************************/
`timescale 1ns/1ps
module lcd_driver (
//global clock
input clk, //system clock
input rst_n, //sync reset
//lcd interface
output lcd_dclk, //lcd pixel clock
output lcd_blank, //lcd blank
output lcd_sync, //lcd sync
output lcd_hs, //lcd horizontal sync
output lcd_vs, //lcd vertical sync
output lcd_en, //lcd display enable
output [23:0] lcd_rgb, //lcd display data
//user interface
output lcd_request, //lcd data request
output [10:0] lcd_xpos, //lcd horizontal coordinate
output [10:0] lcd_ypos, //lcd vertical coordinate
input [23:0] lcd_data, //lcd data
input [ 7:0] ret_data ); `include "lcd_para.v"
//------------------------------------------
//h_sync counter & generator
reg [10:0] hcnt;
always @(posedge clk or negedge rst_n)
begin
if (!rst_n) hcnt <= 11'd0;
else
begin
if(hcnt < `H_TOTAL - 1'b1)
hcnt <= hcnt + 1'b1;
else
hcnt <= 11'd0;
end
end
assign lcd_hs = (hcnt <= `H_SYNC - 1'b1) ? 1'b0 : 1'b1;
//------------------------------------------
//v_sync counter & generator
reg [10:0] vcnt;
always@(posedge clk or negedge rst_n)
begin
if (!rst_n) vcnt <= 11'b0;
else if(hcnt == `H_TOTAL - 1'b1)
begin
if(vcnt < `V_TOTAL - 1'b1)
vcnt <= vcnt + 1'b1;
else
vcnt <= 11'd0;
end
end
assign lcd_vs = (vcnt <= `V_SYNC - 1'b1) ? 1'b0 : 1'b1;
//------------------------------------------
assign lcd_dclk = ~clk;
assign lcd_blank = lcd_hs & lcd_vs;
assign lcd_sync = 1'b0;
//------------------------------------------
//ahead x clock
localparam H_AHEAD = 11'd1;
localparam ROM_ADDR_AHEAD = 11'd2;
assign lcd_request = (hcnt >= (`H_SYNC + `H_BACK - H_AHEAD) &&
hcnt < (`H_SYNC + `H_BACK + `H_DISP - H_AHEAD)) &&
(vcnt >= (`V_SYNC + `V_BACK) &&
vcnt < (`V_SYNC + `V_BACK + `V_DISP)) ? 1'b1 : 1'b0;
//lcd xpos & ypos
assign lcd_xpos = lcd_request ? (hcnt - (`H_SYNC+`H_BACK-H_AHEAD)) : 11'd0;
assign lcd_ypos = lcd_request ? (vcnt - (`V_SYNC + `V_BACK)) : 11'd0;
wire [10:0] rom_xpos = lcd_request ? (hcnt - (`H_SYNC + `H_BACK - ROM_ADDR_AHEAD)) : 11'd0;
wire [10:0] rom_ypos = lcd_request ? (vcnt - (`V_SYNC + `V_BACK)) : 11'd0;
//--------------------------------------------------------
wire [ 2:0] word_data;
wire area1 = (rom_ypos < 11'd160);
wire [16:0] rom_addr = area1?{6'b0,rom_xpos}+({6'b0,rom_ypos})*10'd640: 17'd0;
word u_word
(
.address(rom_addr ),
.clock (clk ),
.q (word_data)
);
reg [15:0] lcd_data_r = 16'b0;
reg [15:0] lcd_data_g = 16'b0;
reg [15:0] lcd_data_b = 16'b0;
always @(*)
begin
if(word_data == 3'b111)
begin
lcd_data_r = {lcd_data[23:16],8'b0};
lcd_data_g = {lcd_data[15: 8],8'b0};
lcd_data_b = {lcd_data[ 7: 0],8'b0};
end
else
begin
lcd_data_r = lcd_data[23:16] * ret_data + {8{word_data[2]}} *
(8'd255 - ret_data);
lcd_data_g = lcd_data[15: 8] * ret_data + {8{word_data[1]}} *
(8'd255 - ret_data);
lcd_data_b = lcd_data[ 7: 0] * ret_data + {8{word_data[0]}} *
(8'd255 - ret_data);
end
end
//-----------------------------------------
assign lcd_en = (hcnt >= (`H_SYNC + `H_BACK) &&
hcnt < (`H_SYNC + `H_BACK + `H_DISP)) &&
(vcnt >= (`V_SYNC + `V_BACK) &&
vcnt < (`V_SYNC + `V_BACK + `V_DISP)) ? 1'b1 : 1'b0;
assign lcd_rgb = lcd_en ? {lcd_data_r[15:8],lcd_data_g[15:8],lcd_data_b[15:8]} : 24'h0;
endmodule
上述代码中的ret_data是来自外部模块的,它的值由按键控制,即透明度可调。
FPGA测试结果如下:
先写到这吧,更详细地以后再补充~~~又是该睡觉的时候了,晚安,88*^_^*