项目:串口接收—ram存储—TFT显示(完整设计)
一.功能:
1.使用uart串口接收模块接收待显示的串行数据(像素RGB值)。
2.把待显示的数据写入ram中。
3.从ram中读取像素值到TFT显示屏中显示,并对应输出该像素的坐标值。
4.在RAM的IP核中提前加载一幅图片并显示。
二.系统框图:
三.实现:使用之前设计的uart_tx模块发送串口数据,然后用uart_rx模块接收串口数据,每接收一个数据,就利用写入模块把数据写进RAM中,并调控地址变化。另一边,显示屏控制器VGA_CTL发出数据请求信号,经过读取数据模块连接到RAM,把uart_rx模块接收并存进RAM中的数据读取出来,作为一个像素点值输出。本次设计把之前的模块进行连线,并增加写入模块和读出模块。(为了相互适配,旧模块在本次设计中代码有所调整)
四.项目细节:
1.遵循uart协议的一个数据位宽为8,一个RGB565像素值位宽为16,所以接收两个再存入RAM中的一个存储单元。
2.由显示屏控制器发出读取数据的请求信号,再从ram中读出数据,传输到显示屏显示,总共慢了两拍,因此请求信号应该提前两拍发出。(由此可知,为了满足时序要求,对于子模块的个别信号是需要进行节拍调整的,调整时由于信号的约束关系,需要综合考虑)
3.在读取ram模块中可以控制在显示屏中显示的区域。用参数划定区域,然后区域内读取ram值并输出显示,区域外不读数据,输出0(黑屏)。
4.由于每个fpga芯片上的ram存储资源是有限的,只能存储定量的像素值,所以如果显示屏的分辨率太高,只能显示一部分区域。
5.ram资源的查找方法:①看fpga芯片有几个块ram资源
②到手册上查看该芯片上每个ram的存储容量。
③两个值相乘即是最大容量。
6.把两位低位宽数据存储成一位高位宽数据,可以利用计数器的奇偶,或者取它的高位。如:
五.制作RAM图片数据初始化文件
想要在TFT显示器上显示一副图片,需要先把一幅图片转化为后缀为.coe的文件,然后在RAM的IP核设置中先初始化这个文件。
1.显示普通的图片:BMP2Mif软件
①下载软件BMP2Mif:
②.找一张想要展示的图片,把它转化为8位/24位(即图片属性中的位深度)的BMP格式的图片。(可以用PS软件)
③.用BMP2Mif软件把BMP图片转化为coe文件,加载到RAM中即可。
2.显示字符点阵(静态):(显示英文字符或者中文字符均可)
1.下载软件:PCtoLCD
2.设置格式
3.输入想要显示的字符:
4.保存,打开txt文件。修改成如下格式:
列操作
然后修改后缀为.coe。
至于上面参数的修改以及完整的教程看《小梅哥Xilinx ACX720 FPGA开发板自学教程》21章。
3.显示字符点阵(动态):(显示英文字符或者中文字符均可)
思路:
①先用上面静态的方法生成以下点阵,并修改好文件。
②.把文件存储在一个ROM中
③利用动态扫描的方式更新显示的数据。具体方法是利用每个数字的起始地址的固定关系:
完整的教程看《小梅哥Xilinx ACX720 FPGA开发板自学教程》22章。
六.附代码
module uart_ram_TFT(//顶层连线模块
clk,//50MHz
reset,
uart_tx ,
RGB565_data,//输出的内容数据
vish_axis ,//显示的行坐标
visv_axis //显示的列坐标
);
input clk ;
input reset ;
input uart_tx ;
output [15:0]RGB565_data ;
output [15:0]vish_axis ;//显示的行坐标
output [15:0]visv_axis ;//显示的列坐标
//内部变量
wire [7:0]parallel_data ;//串口接收模块接收的并行数据8位
wire rx_done ;//串口接收模块接收8位数据结束信号
//uart接收模块
uart_receive_1 uart_receive(//串口接收模块
.clk(clk) ,
.reset(reset) ,
.baud_rate(5) ,
.uart_tx(uart_tx),
.data(parallel_data) ,
.rx_done(rx_done)
);
//内部变量
wire [15:0]addr_write ;//串口接收模块接收的并行数据8位
wire [15:0]data_write ;//串口接收模块接收8位数据结束信号
wire write_enable ;//写入使能信号
//uart接收的数据存入ram模块
uart_to_ram uart_to_ram(//将uart串口接收模块输出的数据写入ram中
.clk(clk) ,
.reset(reset) ,
.data(parallel_data),
.rx_done(rx_done),
.addr(addr_write) ,
.dout(data_write) ,
.wenable(write_enable )
);
//内部变量
wire clk_b ;//串口接收模块接收的并行数据8位
wire [15:0]addr_read ;
wire read_ram_request ;
wire [15:0]read_data ;
//ram存储模块
RAM_DISPLAY RAM (
.clka(clk), // input wire clka
.ena(1), // input wire ena
.wea(write_enable), // input wire [0 : 0] wea
.addra(addr_write), // input wire [15 : 0] addra
.dina(data_write), // input wire [15 : 0] dina
.clkb(clk_b), // input wire clkb
.enb(read_ram_request), // input wire enb
.addrb(addr_read), // input wire [15 : 0] addrb
.doutb(read_data) // output wire [15 : 0] doutb
);
//内部变量
wire disp_request ;
wire [15:0]disp_data ;
//从ram读数据模块
ram_to_display ram_to_display(//读ram数据到显示屏模块
.clka(clk),//输入时钟
.reset(reset),//复位
.data(read_data),//ram读出的数据16位
.data_request(disp_request),//显示屏控制的的数据请求信号
.h_axis(vish_axis) ,//行坐标
.v_axis(visv_axis) ,//列坐标
.clkb(clk_b),//显示屏控制器的输入时钟
.addr_read(addr_read),//读ram的地址
.read_ram_request(read_ram_request),//读ram请求信号
.display_data(disp_data)//显示屏控制器的数据输入信号
);
//屏幕显示器控制模块
VGA TFT_control(
.clk(clk_b),
.reset(reset),
.content_data_request(disp_request),//数据请求信号
.content_data(disp_data),//要显示的内容数据
.RGB_data(RGB565_data),//输出的内容数据
.vis_h_axis(vish_axis) ,//显示的行坐标
.vis_v_axis(visv_axis) ,//显示的列坐标
.vis_sig()//内容显示同步信号(高电平时显示)
);
endmodule
module uart_receive(//串口接收模块
clk ,
reset ,
baud_rate ,
uart_tx,
data ,
rx_done
);
input clk ;
input reset ;
input [2:0]baud_rate ;
input uart_tx ;
output reg [7:0]data ;
output reg rx_done ;
reg rx_done_sig ;
reg [2:0]r_data[7:0] ;//接收每一位数据
reg [2:0]sta_bit ;
reg [2:0]sto_bit ;
reg [17:0]bit_tim ;//每一位持续的时间(计数)
always@(baud_rate) //在这里一个 码元由一位组成,所以波特率=比特率
begin
case(baud_rate) //常见的串口传输波特率
3'd0 : bit_tim = 1000000000/300/20 ; //波特率为300
3'd1 : bit_tim = 1000000000/1200/20 ; //波特率为1200
3'd2 : bit_tim = 1000000000/2400/20 ; //波特率为2400
3'd3 : bit_tim = 1000000000/9600/20 ; //波特率为9600
3'd4 : bit_tim = 1000000000/19200/20 ; //波特率为19200
3'd5 : bit_tim = 1000000000/115200/20 ; //波特率为115200
default bit_tim = 1000000000/9600/20 ; //多余的寄存器位置放什么:默认速率
endcase
end
wire [17:0]bit_tim_16 ;//每1/16位的持续时间(计数)
assign bit_tim_16 = bit_tim / 16;
wire [8:0]bit16_mid ; //在中心点产生采样脉冲
assign bit16_mid = bit_tim_16 / 2 ;
//边沿检测
reg [1:0]edge_detect ;
always @( posedge clk or negedge reset )
begin
if (!reset )
edge_detect <= 2'd0 ;
else
begin
edge_detect[0] <= uart_tx ;
edge_detect[1] <= edge_detect[0] ;
end
end
wire byte_sta_neg ;
assign byte_sta_neg = ( edge_detect == 2'b10 ) ? 1 : 0 ;//输入的数据开始出现下降沿,说明出现了起始位(一直运行?)
reg receive_en ;//接收使能端
reg [17:0]div_cnt ;//每1/16bit内的计数
reg [7:0]bit16_cnt ;//计数到了第几个状态(10位,每位分成16份,总共160个状态)
always @( posedge clk or negedge reset )
begin
if (!reset )
receive_en <= 1'd0 ;
else if ( byte_sta_neg ) //检测到下降沿,使能段有效(只要有下降沿就使能?)
receive_en <= 1'd1 ;
else if ( (rx_done) || (sta_bit >= 3'd4 ))
receive_en <= 1'd0 ; //检测到结束信号,使能端无效
else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )//跑完159后re_en置零
receive_en <= 1'd0 ;
end
always@( posedge clk or negedge reset )
begin
if ( ! reset )
div_cnt <= 18'd0 ;
else if (receive_en)
begin
if ( div_cnt == bit_tim_16 - 1'd1 )//计数,每1/16bit清零
div_cnt <= 18'd0 ;
else
div_cnt <= div_cnt + 1'b1 ;
end
else
div_cnt <= 18'd0 ;
end
reg bit16_pulse ;//产生采样脉冲
always@( posedge clk or negedge reset )
begin
if ( ! reset )
bit16_pulse <= 18'd0 ;
else if (receive_en)
if ( div_cnt == bit16_mid )
bit16_pulse <= 1'd1 ;
else
bit16_pulse <= 1'd0 ;
else
bit16_pulse <= 1'd0 ;
end
always@( posedge clk or negedge reset )
begin
if ( ! reset )
bit16_cnt <= 8'd0 ;
else if (receive_en)
begin
if (( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ))
bit16_cnt <= 8'd0 ;
else if ( div_cnt == bit_tim_16 - 1'd1 )
bit16_cnt <= bit16_cnt + 1'b1 ;
end
end
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
sta_bit <= 3'd0 ;
r_data[0] <= 3'd0 ;
r_data[1] <= 3'd0 ;
r_data[2] <= 3'd0 ;
r_data[3] <= 3'd0 ;
r_data[4] <= 3'd0 ;
r_data[5] <= 3'd0 ;
r_data[6] <= 3'd0 ;
r_data[7] <= 3'd0 ;
sto_bit <= 3'd0 ;
end
else if (bit16_pulse)//舍弃前5后4取中7
case(bit16_cnt)
0:
begin
sta_bit <= 3'd0 ;
r_data[0] <= 3'd0 ;
r_data[1] <= 3'd0 ;
r_data[2] <= 3'd0 ;
r_data[3] <= 3'd0 ;
r_data[4] <= 3'd0 ;
r_data[5] <= 3'd0 ;
r_data[6] <= 3'd0 ;
r_data[7] <= 3'd0 ;
sto_bit <= 3'd0 ;
end
5,6,7,8,9,10,11 : sta_bit <= sta_bit + uart_tx ;
21,22,23,24,25,26,27 : r_data[0] <= r_data[0] + uart_tx ;
37,38,39,41,42,43,44 : r_data[1] <= r_data[1] + uart_tx ;
53,54,55,56,57,58,59 : r_data[2] <= r_data[2] + uart_tx ;
69,70,71,72,73,74,75 : r_data[3] <= r_data[3] + uart_tx ;
85,86,87,88,89,90,91 : r_data[4] <= r_data[4] + uart_tx ;
101,102,103,104,105,106,107 : r_data[5] <= r_data[5] + uart_tx ;
117,118,119,120,121,122,123 : r_data[6] <= r_data[6] + uart_tx ;
133,134,135,136,137,138,139 : r_data[7] <= r_data[7] + uart_tx ;
149,150,151,152,153,154,155 : sto_bit <= sto_bit + uart_tx ;
default ;
endcase
end
always@( posedge clk or negedge reset )
begin
if ( ! reset )
rx_done_sig <= 8'd0 ;
else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 2'd2 ) )//跑完159后产生一个rx_done信号
rx_done_sig <= 8'd1 ;
else if (rx_done_sig <= 8'd1 )
rx_done_sig <= 8'd0 ;
end
always@( posedge clk or negedge reset )//接收完数据发出rx_done
if(!reset )
rx_done <= 0 ;
else if (rx_done_sig)
rx_done <= 1 ;
else if (rx_done )
rx_done <= 0 ;
always@( posedge clk or negedge reset )//接收完数据发出rx_done后,把数据从r_data传递给data
begin
if ( ! reset )
data <= 8'd0 ;
else if ( rx_done_sig )
begin
data[0] = ( r_data[0] >3 ) ? 1 : 0 ;
data[1] = ( r_data[1] >3 ) ? 1 : 0 ;
data[2] = ( r_data[2] >3 ) ? 1 : 0 ;
data[3] = ( r_data[3] >3 ) ? 1 : 0 ;
data[4] = ( r_data[4] >3 ) ? 1 : 0 ;
data[5] = ( r_data[5] >3 ) ? 1 : 0 ;
data[6] = ( r_data[6] >3 ) ? 1 : 0 ;
data[7] = ( r_data[7] >3 ) ? 1 : 0 ;
end
// else if ( receive_en )
// data <= 8'd0 ;
end
endmodule
module uart_to_ram(//将uart串口接收模块输出的数据写入ram中
clk ,
reset ,
data,
rx_done,
addr ,
dout ,
wenable
);
input clk ;
input reset ;
input [7:0]data;
input rx_done;
output reg [15:0]addr ;
output reg [15:0]dout ;
output reg wenable ;
//ram的容量为256*256个像素,每个像素位宽16,共需256*256*2个8位的数据(131072)
//用计数器来计数
reg [16:0]pixel_cnt ;//131072
always@(posedge clk or negedge reset)
if(!reset)
pixel_cnt <= 17'd0 ;
else if ( ( pixel_cnt < 131071 ) && ( rx_done ) )
pixel_cnt <= pixel_cnt + 1 ;
else if (( pixel_cnt >= 131071 ) && ( rx_done ))
pixel_cnt <= 17'd0 ;
//需要一个寄存器,存两个8位,共16位
reg [15:0]data_register ;
always@(posedge clk or negedge reset)
if(!reset)
data_register <= 16'd0 ;
else if ( rx_done )
data_register <= { data_register[7:0] , data } ;
//添加一个变化条件信号
reg change_sig ;
always@(posedge clk or negedge reset)
if(!reset)
change_sig <= 1'd0 ;
else if ( rx_done && ( pixel_cnt[0] == 1 ) )
change_sig <= 1'd1 ;
else if ( change_sig == 1'd1 )
change_sig <= 1'd0 ;
//把每16位数据存进ram中,即输出16位数据
always@(posedge clk or negedge reset)
if(!reset)
dout <= 16'd0 ;
else if ( change_sig )
dout <= data_register ;
//产生一个写入ram的使能信号
always@(posedge clk or negedge reset)
if(!reset)
wenable <= 0 ;
else if ( change_sig )
wenable <= 1 ;
else if (wenable == 1)
wenable <= 0 ;
//产生写入ram的地址 //比weanbel和data提前变化,不然不满足下面除于2的条件,第0个地址将没有数据
always@(posedge clk or negedge reset)
if(!reset)
addr <= 16'd0 ;
else if ( rx_done && ( pixel_cnt[0] == 1 ) )
addr <= pixel_cnt[16:1] ; //除于2 即舍弃最后一位(右移一位)
endmodule
module ram_to_display(//读ram数据到显示屏模块
clka,//输入时钟
reset,//复位
data,//ram读出的数据16位
data_request,//显示屏控制的的数据请求信号
h_axis ,//行坐标
v_axis ,//列坐标
clkb,//显示屏控制器的输入时钟
addr_read,//读ram的地址
read_ram_request,//读ram请求信号
display_data//显示屏控制器的数据输入信号
);
input clka ;//输入时钟
input reset ;//复位
input [15:0]data;//ram读出的数据16位
input data_request;//显示屏控制的的数据请求信号
input [15:0]h_axis ;
input [15:0]v_axis ;
output clkb;//显示屏控制器的输入时钟
output reg [15:0]addr_read ;//ram的读地址 输入信号
output read_ram_request ;
output wire [15:0]display_data;//显示屏控制器的数据输入信号
my_PLL PLL//锁相环
(
.clk_out1(clkb), //40MHz
.clk_in1(clka)//50MHz
);
//接下来设置两个区域信号,之所以分开,是为了控制地址和显示刚刚好,因为从显示屏发送读数据请求到ram的数据送到显示屏这个过程有两个周期的延迟
//显示区域的地址
wire field_h_addr ;//要显示区域的行范围
assign field_h_addr = ( (h_axis >= 0 ) && ( h_axis < 255 ) ) ? 1 : 0 ;//到254
wire field_v_addr ;//要显示区域的行范围
assign field_v_addr = ( (v_axis >= 0 ) && ( v_axis <= 256 ) ) ? 1 : 0 ;
//显示区域的范围
wire my_field_addr ;
assign my_field_addr = ( field_h_addr && field_v_addr && ( data_request ) ) ? 1 : 0 ;
//显示区域的数据
wire field_h_data ;//要显示区域的行范围
assign field_h_data = ( (h_axis >= 0 ) && ( h_axis < 256 ) ) ? 1 : 0 ;//到255
wire field_v_data ;//要显示区域的行范围
assign field_v_data = ( (v_axis >= 0 ) && ( v_axis <= 256 ) ) ? 1 : 0 ;
//显示区域的范围
wire my_field_data ;
assign my_field_data = ( field_h_data && field_v_data && ( data_request ) ) ? 1 : 0 ;
//显示区域内读ram请求信号
wire field_h_display ;//要显示区域的行范围
assign field_h_display = ( (h_axis >= 0 ) && ( h_axis <= 256 ) ) ? 1 : 0 ;//到256
wire field_v_display ;//要显示区域的行范围
assign field_v_display = ( (v_axis >= 0 ) && ( v_axis <= 256 ) ) ? 1 : 0 ;
assign read_ram_request = ( field_h_display && field_v_display && ( data_request ) ) ? 1 : 0 ;//( data_request ) ? 1 : 0 ;
//读ram中的数据
always@(posedge clkb or negedge reset )
if(!reset )
addr_read <= 0 ;
else if ( my_field_addr )
addr_read <= addr_read + 1 ;
//显示屏控制器的数据输入信号
assign display_data = ( my_field_data ) ? ( data ) : 0 ;
endmodule
module VGA(
clk,
reset,
content_data_request,//数据请求信号
content_data,//要显示的内容数据
RGB_data,//输出的内容数据
vis_h_axis ,//显示的行坐标
vis_v_axis ,//显示的列坐标
vis_sig//内容显示同步信号(高电平时显示)
);
input clk ;
input reset ;
input [15:0]content_data ;
output content_data_request ;
reg adjust_sig ;//数据请求信号
output reg [15:0]vis_h_axis ;//显示的行坐标
output reg [15:0]vis_v_axis ;//显示的列坐标
reg horizontal_sig ;
reg vertical_sig ;
output reg [15:0]RGB_data ;
output reg vis_sig ;//内容显示同步信号
reg h_vis_sig ;//行内容显示同步信号
reg v_vis_sig ;//列内容显示同步信号
`include "VGA_resolution_parameter.v"
//定义时间节点参数
//行参数
parameter h_pulse_start = 0 ; //行起始脉冲开始信号
parameter h_pulse_end = `H_Sync_Time ;// 行起始脉冲结束信号
parameter h_content_start = `H_Sync_Time + `H_Back_Porch + `H_Left_Border ;//行内容开始信号
parameter h_content_end = `H_Sync_Time + `H_Back_Porch + `H_Left_Border + `H_Data_Time ;//行内容结束信号
parameter h_end = `H_Sync_Time + `H_Back_Porch + `H_Left_Border + `H_Data_Time + `H_Right_Border + `H_Front_Porch;//行结束信号
//列参数
parameter v_pulse_start = 0 ; //列起始脉冲开始信号
parameter v_pulse_end = `V_Sync_Time ;//列起始脉冲结束信号
parameter v_content_start = `V_Sync_Time + `V_Back_Porch + `V_Top_Border ;//列内容开始信号
parameter v_content_end = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time ;//列内容结束信号
parameter v_end = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time + `V_Bottom_Border + `V_Front_Porch;//列结束信号
//行与列 计数器
reg [11:0]h_cnt ;
reg [11:0]v_cnt ;
always@(posedge clk or negedge reset)//行计数
if(!reset)
h_cnt <= 0 ;
else if ( h_end - 1 <= h_cnt )//0-799
h_cnt <= 0 ;
else
h_cnt <= h_cnt + 1 ;
always@(posedge clk or negedge reset)//列计数
if(!reset)
v_cnt <= 0 ;
else if (( v_end - 1 <= v_cnt ) && ( h_end - 1 <= h_cnt ))//0-524
v_cnt <= 0 ;
else if (( h_end - 1 <= h_cnt ) && ( v_end - 1 > v_cnt ))
v_cnt <= v_cnt + 1 ;
//产生行起始脉冲
always@(posedge clk or negedge reset)//
if(!reset)
horizontal_sig <= 1 ;
else if( h_cnt == 0 )
horizontal_sig <= 0 ;//1-96 共96
else if ( h_cnt == h_pulse_end )
horizontal_sig <= 1 ;
//产生列起始脉冲
always@(posedge clk or negedge reset)//
if(!reset)
vertical_sig <= 1 ;
else if(( v_cnt == 0 )&& ( h_end - 1 <= h_cnt ) )
vertical_sig <= 0 ;//1-2 共2
else if (( v_cnt == v_pulse_end )&& ( h_end - 1 <= h_cnt ) )
vertical_sig <= 1 ;
//产生行显示同步信号
always@(posedge clk or negedge reset)//
if(!reset)
h_vis_sig <= 0 ;
else if (( h_content_start - 2 <= h_cnt ) && ( h_content_end - 1 - 2 >= h_cnt )) //提前两拍保证输出在第145拍开始
h_vis_sig <= 1 ;//146 - 785共
else
h_vis_sig <= 0 ;
//行坐标
always@(posedge clk or negedge reset)//
if(!reset)
vis_h_axis <= 0 ;
else if ( adjust_sig )begin
if ( h_content_start - 1 == h_cnt )
vis_h_axis <= 1 ;//146 - 785共
else
vis_h_axis <= vis_h_axis + 1 ;
end
else if ( h_content_end <= h_cnt )
vis_h_axis <= 0 ;
//产生列显示同步信号
always@(posedge clk or negedge reset)//
if(!reset)
v_vis_sig <= 0 ;
else if (( v_content_start <= v_cnt ) && ( v_content_end >= v_cnt ) && ( h_end - 1 <= h_cnt ) )
v_vis_sig <= 1 ;
else if (( v_content_start > v_cnt ) || ( v_content_end < v_cnt ) )
v_vis_sig <= 0 ;
//列坐标
always@(posedge clk or negedge reset)//
if(!reset)
vis_v_axis <= 0 ;
else if (( v_content_start == v_cnt ) && ( h_end - 1 <= h_cnt ) )
vis_v_axis <= 1 ;//
else if (( h_end - 1 <= h_cnt ) && ( v_content_end > v_cnt ) && ( v_content_start <= v_cnt ) )
vis_v_axis <= vis_v_axis + 1 ;
else if (( h_end - 1 <= h_cnt ) && (v_content_end <= v_cnt) )
vis_v_axis <= 0 ;
//调整信号
always@(posedge clk or negedge reset)
if(!reset)
adjust_sig <= 0 ;//
else if ( ( v_vis_sig ) && ( h_vis_sig ) )
adjust_sig <= 1 ;
else
adjust_sig <= 0 ;
//请求信号
assign content_data_request = ( ( v_vis_sig ) && ( h_vis_sig ) ) ? 1 : 0 ;
//显示同步信号
always@(posedge clk or negedge reset)//
if(!reset)
vis_sig <= 0 ;
else if ( adjust_sig )
vis_sig <= 1 ;
else
vis_sig <= 0 ;
//显示
always@(posedge clk or negedge reset)//
if(!reset)
RGB_data <= 0 ;
else if ( adjust_sig )
RGB_data <= content_data ;//
else
RGB_data <= 0 ;
endmodule
`define Resolution_480x272 1 //刷新率为60Hz其时钟频率为 9MHz
//`define Resolution_640x480 1 //刷新率为60Hz其时钟频率为 25.2MHz
//`define Resolution_800x480 1 //刷新率为60Hz其时钟频率为 33.264MHz
//`define Resolution_800x600 1 //刷新率为60Hz其时钟频率为 40MHz
//`define Resolution_1024x600 1 //刷新率为60Hz其时钟频率为 50.64MHz
//`define Resolution_1024x768 1 //刷新率为60Hz其时钟频率为 65MHz
//`define Resolution_1280x720 1 //刷新率为60Hz其时钟频率为 74.25MHz
//`define Resolution_1920x1080 1 //(1080p)刷新率为60Hz其时钟频率为 148.5MHz
//时钟频率计算公式:f = 1 / (( 1 / 60 ) / ( H_Total_Time * V_Total_Time ) )
`ifdef Resolution_480x272
`define H_Right_Border 0
`define H_Front_Porch 2
`define H_Sync_Time 41
`define H_Back_Porch 2
`define H_Left_Border 0
`define H_Data_Time 480
`define H_Total_Time 525
`define V_Bottom_Border 0
`define V_Front_Porch 2
`define V_Sync_Time 10
`define V_Back_Porch 2
`define V_Top_Border 0
`define V_Data_Time 272
`define V_Total_Time 286
`elsif Resolution_640x480
`define H_Right_Border 8
`define H_Front_Porch 8
`define H_Sync_Time 96
`define H_Back_Porch 40
`define H_Left_Border 8
`define H_Data_Time 640
`define H_Total_Time 800
`define V_Bottom_Border 8
`define V_Front_Porch 2
`define V_Sync_Time 2
`define V_Back_Porch 25
`define V_Top_Border 8
`define V_Data_Time 480
`define V_Total_Time 525
`elsif Resolution_800x480
`define H_Right_Border 0
`define H_Front_Porch 40
`define H_Sync_Time 128
`define H_Back_Porch 88
`define H_Left_Border 0
`define H_Data_Time 800
`define H_Total_Time 1056
`define V_Bottom_Border 8
`define V_Front_Porch 2
`define V_Sync_Time 2
`define V_Back_Porch 25
`define V_Top_Border 8
`define V_Data_Time 480
`define V_Total_Time 525
`elsif Resolution_800x600
`define H_Right_Border 0
`define H_Front_Porch 40
`define H_Sync_Time 128
`define H_Back_Porch 88
`define H_Left_Border 0
`define H_Data_Time 800
`define H_Total_Time 1056
`define V_Bottom_Border 0
`define V_Front_Porch 1
`define V_Sync_Time 4
`define V_Back_Porch 23
`define V_Top_Border 0
`define V_Data_Time 600
`define V_Total_Time 628
`elsif Resolution_1024x600
`define H_Right_Border 0
`define H_Front_Porch 24
`define H_Sync_Time 136
`define H_Back_Porch 160
`define H_Left_Border 0
`define H_Data_Time 1024
`define H_Total_Time 1344
`define V_Bottom_Border 0
`define V_Front_Porch 1
`define V_Sync_Time 4
`define V_Back_Porch 23
`define V_Top_Border 0
`define V_Data_Time 600
`define V_Total_Time 628
`elsif Resolution_1024x768
`define H_Right_Border 0
`define H_Front_Porch 24
`define H_Sync_Time 136
`define H_Back_Porch 160
`define H_Left_Border 0
`define H_Data_Time 1024
`define H_Total_Time 1344
`define V_Bottom_Border 0
`define V_Front_Porch 3
`define V_Sync_Time 6
`define V_Back_Porch 29
`define V_Top_Border 0
`define V_Data_Time 768
`define V_Total_Time 806
`elsif Resolution_1280x720
`define H_Right_Border 0
`define H_Front_Porch 110
`define H_Sync_Time 40
`define H_Back_Porch 220
`define H_Left_Border 0
`define H_Data_Time 1280
`define H_Total_Time 1650
`define V_Bottom_Border 0
`define V_Front_Porch 5
`define V_Sync_Time 5
`define V_Back_Porch 20
`define V_Top_Border 0
`define V_Data_Time 720
`define V_Total_Time 750
`elsif Resolution_1960x1080
`define H_Right_Border 0
`define H_Front_Porch 88
`define H_Sync_Time 44
`define H_Back_Porch 148
`define H_Left_Border 0
`define H_Data_Time 1920
`define H_Total_Time 2200
`define V_Bottom_Border 0
`define V_Front_Porch 4
`define V_Sync_Time 5
`define V_Back_Porch 36
`define V_Top_Border 0
`define V_Data_Time 1080
`define V_Total_Time 1125
`endif
测试:
在ram中存入一幅图片像素值的coe文件。
`timescale 1ns /1ns
module uart_ram_TFT_send_tb( );
reg clk ;
reg reset ;
reg uart_tx ;
wire [15:0]RGB565_data ;
wire [15:0]vish_axis ;//显示的行坐标
wire [15:0]visv_axis ;//显示的列坐标
reg [7:0] rand;
reg [16:0]input_cnt ;
uart_ram_TFT uart_ram_TFT_sim_send(//顶层连线
clk,//50MHz
reset,
uart_tx ,
RGB565_data,//输出的内容数据
vish_axis ,//显示的行坐标
visv_axis //显示的列坐标
);
initial clk = 1 ;
always #10 clk = ! clk ;
initial begin
reset = 0 ;
uart_tx = 1;
input_cnt = 0 ;
rand = 0 ;
input_cnt = 0 ;
#201 ;
reset = 1 ;
#800000;
repeat(65536)begin
#20 ;
rand = {$random} % 255;
#20 ;
uart_input(rand);
input_cnt = input_cnt + 1 ;
end
#1000 ;
$stop ;
end
task uart_input ;//设定一个任务uart_inpt,有一个输入端uart_tx_data_stm 。在这个task里可以对task外的变量进行赋值
input [7:0]uart_tx_data_stm ;//不返回值,所以不能用x=uart_input。而是直接uart_input。
begin //结构简单的begin-end
uart_tx = 1 ;
#20 ;
uart_tx = 0 ;
#8640 ;
uart_tx = uart_tx_data_stm[0] ;
#8640 ;
uart_tx = uart_tx_data_stm[1] ;
#8640 ;
uart_tx = uart_tx_data_stm[2] ;
#8640 ;
uart_tx = uart_tx_data_stm[3] ;
#8640 ;
uart_tx = uart_tx_data_stm[4] ;
#8640 ;
uart_tx = uart_tx_data_stm[5] ;
#8640 ;
uart_tx = uart_tx_data_stm[6] ;
#8640 ;
uart_tx = uart_tx_data_stm[7] ;
#8640 ;
uart_tx = 1 ;
#8640 ;
end
endtask
endmodule
七.结果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理