一、原理
我们这里设计的是显示 1024 个波形数据点, 在绘制每一行的图像的时候, 比对每一个数据和 VS 的 Y 坐标是否相等, 如果相等就绘制这个波形点。 这样我们就能完成 1024 个波形点在整个屏幕的显示。

二、乒乓操作
可见FPGA实现双口RAM的乒乓操作——FPGA学习笔记2_ram乒乓存储-CSDN博客

三、测试程序设计

de:屏幕有效的画面显示
de_2:画中画波形绘制有效显示区域

`timescale 1ns / 1ps
module LCD(
input I_sysclk , //系统时钟
input I_vid_clk , //HDMIx1时钟
input I_vid_rstn , //系统复位输入
output O_vid_hs , //hs信号
output O_vid_vs , //vs信号
output O_vid_de , //视频数据有效信号
output [23:0] O_rgb // RGB
);
wire [7:0] O_wave1_data ;
wire [7:0] O_wave2_data ;
wire O_vtc2_de ;
wire data_en ;
//例化VTC模块
VTC#
(
.H_ActiveSize (1280 ) , //视频时间参数,行视频信号,一行有效(需要显示的部分)像素所占的时钟数,一个时钟对应一个有效像素,设置320个像素
.H_FrameSize (1280+88+44+239 ) , //视频时间参数,行视频信号,一行视频信号总计占用的时钟数
.H_SyncStart (1280+88 ) , //视频时间参数,行同步开始,即多少时钟数后开始产生行同步信号
.H_SyncEnd (1280+88+44 ) , //视频时间参数,行同步结束,即多少时钟数后停止产生行同步信号,之后就是行数据有效数据部分
.V_ActiveSize (720 ) , //视频时间参数,场视频信号,一帧图像所占用的有效(需要显示的部分)行数量,通常说的视频分辨率即H_ActiveSize*V_ActiveSize
.V_FrameSize (720+4+5+28 ) , //视频时间参数,场视频信号,一帧视频信号总计占用的行数量
.V_SyncStart (720+4 ) , //视频时间参数,场同步开始,即多少行数后开始产生场同步信号
.V_SyncEnd (720+4+5 ) , //视频时间参数,场同步结束,即多少行数后停止产生场同步信号,之后就是场有效数据部分
.H2_ActiveSize (1024 ) ,
.V2_ActiveSize (256 )
)
u_VTC
(
.I_vtc_clk (I_vid_clk ) , //系统时钟
.I_vtc_rstn (I_vid_rstn ) , //系统复位
.O_vtc_vs (O_vid_vs ) , //图像的vs信号
.O_vtc_hs (O_vid_hs ) , //图像的hs信号
.O_vtc_de (O_vid_de ) , //de数据有效信号
.I_vtc2_offset_x(128 ) , //相对屏幕原点,x轴偏移方向
.I_vtc2_offset_y(200 ) , //相对屏幕原点,y轴偏移方向
.O_vtc2_de (O_vtc2_de ) //
);
//
wave u_wave
(
.I_wave1_clk (I_sysclk ) , //波形一时钟
.I_wave1_data (O_wave1_data ) , //波形一数据
.I_wave1_data_de (data_en ) , //波形一数据有效
.I_wave2_clk (I_sysclk ) , //波形二时钟
.I_wave2_data (O_wave2_data ) , //波形二数据
.I_wave2_data_de (data_en ) , //波形二数据有效
//VTC时序输入
.I_vtc_rstn (I_vid_rstn ) , //
.I_vtc_clk (I_vid_clk ) , //
.I_vtc_vs (O_vid_vs ) , //
.I_vtc_de (O_vtc2_de ) , // de_2
//同步时序输出,以及像素输出
.O_vtc_vs ( ) , //
.O_vtc_de ( ) , //
.O_vtc_rgb (O_rgb ) //
);
//
TPG u_TPG(
.I_sysclk (I_sysclk ) ,
.I_rst_n (I_vid_rstn ) ,
.O_wave1_data (O_wave1_data ) ,
.O_wave2_data (O_wave2_data ) ,
.data_en (data_en )
);
endmodule
`timescale 1ns / 1ps
module VTC#(
parameter H_ActiveSize = 1280 , //视频时间参数,行视频信号,一行有效(需要显示的部分)像素所占的时钟数,一个时钟对应一个有效像素
parameter H_FrameSize = 1280+88+44+239 , //视频时间参数,行视频信号,一行视频信号总计占用的时钟数
parameter H_SyncStart = 1280+88 , //视频时间参数,行同步开始,即多少时钟数后开始产生行同步信号
parameter H_SyncEnd = 1280+88+44 , //视频时间参数,行同步结束,即多少时钟数后停止产生行同步信号,之后就是行有效数据部分
parameter V_ActiveSize = 720 , //视频时间参数,场视频信号,一帧图像所占用的有效(需要显示的部分)行数量,通常说的视频分辨率即H_ActiveSize*V_ActiveSize
parameter V_FrameSize = 720+4+5+28 , //视频时间参数,场视频信号,一帧视频信号总计占用的行数量
parameter V_SyncStart = 720+4 , //视频时间参数,场同步开始,即多少行数后开始产生场同步信号
parameter V_SyncEnd = 720+4+5 , //视频时间参数,场同步结束,即多少场数后停止产生场同步信号,之后就是场有效数据部分
parameter H2_ActiveSize = 1024 , //波形显示H区域
parameter V2_ActiveSize = 256 //波形显示V区域
)
(
input I_vtc_rstn , //系统复位
input I_vtc_clk , //系统时钟
output reg O_vtc_vs , //场同步输出
output reg O_vtc_hs , //行同步输出
output reg O_vtc_de , //视频数据有效
input [11:0] I_vtc2_offset_x , //相对屏幕原点,x轴偏移方向
input [11:0] I_vtc2_offset_y , //相对屏幕原点,y轴偏移方向
output reg O_vtc2_de //
);
reg [11:0] hcnt = 12'd0 ; //视频水平方向,列计数器,寄存器
reg [11:0] vcnt = 12'd0 ; //视频垂直方向,行计数器,寄存器
reg [2 :0] rst_cnt = 3'd0 ; //复位计数器,寄存器
wire rst_sync = rst_cnt[2] ; //同步复位
//同步复位
always @(posedge I_vtc_clk or negedge I_vtc_rstn ) begin
if (!I_vtc_rstn) begin
rst_cnt <= 3'd0;
end
else if(rst_cnt[2] == 1'b0)begin
rst_cnt <= rst_cnt + 1'b1 ;
end
end
//视频水平方向,列计数器
always @(posedge I_vtc_clk) begin
if (rst_sync == 1'b0) begin
hcnt <= 12'd0;
end
else if(hcnt < (H_FrameSize - 1'b1))begin
hcnt <= hcnt + 1'b1;
end
else begin
hcnt <= 12'd0;
end
end
//视频垂直方向,行计数器
always @(posedge I_vtc_clk) begin
if (rst_sync == 1'b0) begin
vcnt <= 12'd0;
end
else if(hcnt == (H_FrameSize - 1'b1))begin
if (vcnt == (V_FrameSize - 1'b1)) begin
vcnt <= 12'd0;
end
else begin
vcnt <= vcnt + 1'b1;
end
end
end
wire hs_valid = hcnt < H_ActiveSize ; //行信号有效像素部分
wire vs_valid = vcnt < V_ActiveSize ; //场信号有效像素部分
wire vtc_hs = (hcnt >= H_SyncStart && hcnt < H_SyncEnd) ; //产生hs,行同步信号
wire vtc_vs = (vcnt > V_SyncStart && vcnt <= V_SyncEnd) ; //产生vs,场同步信号
wire vtc_de = hs_valid && vs_valid ; //只有当视频水平方向,列有效和视频垂直方向,行同时有效,视频数据部分才是有效
wire hs2_valid = (hcnt >= I_vtc2_offset_x && hcnt < I_vtc2_offset_x + H2_ActiveSize) ;
wire vs2_valid = (vcnt >= I_vtc2_offset_y && vcnt < I_vtc2_offset_y + V2_ActiveSize) ;
wire vtc2_de = hs2_valid && vs2_valid ;
//完一次寄存打拍输出, 有利于改善时序, 尤其对于高分辨率, 高速的信号, 打拍可以改善内部时序, 以运行于更高速度
always @(posedge I_vtc_clk)begin
if(rst_sync == 1'b0)begin
O_vtc_vs <= 1'b0;
O_vtc_hs <= 1'b0;
O_vtc_de <= 1'b0;
O_vtc2_de <= 1'b0;
end
else begin
O_vtc_vs <= vtc_vs; //场同步信号打拍输出
O_vtc_hs <= vtc_hs; //行同步信号打拍输出
O_vtc_de <= vtc_de; //视频有效信号打拍输出
O_vtc2_de <= vtc2_de; //画中画, 数据有效绘制信号
end
end
endmodule
`timescale 1ns / 1ps
//测试数据生成
module wave(
input I_wave1_clk , //波形一时钟
input [7:0] I_wave1_data , //波形一数据
input I_wave1_data_de , //波形一数据有效
input I_wave2_clk , //波形二时钟
input [7:0] I_wave2_data , //波形二数据
input I_wave2_data_de , //波形二数据有效
//VTC时序输入
input I_vtc_rstn , //
input I_vtc_clk , //
input I_vtc_vs , //
input I_vtc_de , // de_2
//同步时序输出,以及像素输出
output O_vtc_vs , //
output O_vtc_de , //
output reg [23:0] O_vtc_rgb //
);
reg [1 :0] vtc_vs_r ;
reg [1 :0] vtc_de_r ;
reg [11:0] vcnt = 12'd0, hcnt = 12'd0 ;
reg grid_de ;
assign O_vtc_vs = vtc_vs_r[0] ;
assign O_vtc_de = vtc_de_r[0] ;
//寄存,同步
always @(posedge I_vtc_clk ) begin
vtc_vs_r <= {vtc_vs_r[0],I_vtc_vs} ;
vtc_de_r <= {vtc_de_r[0],I_vtc_de} ;
end
//hcnt计数器
always @(posedge I_vtc_clk) begin
if (hcnt == 12'd1023) begin
hcnt <= 12'd0;
end
else if(vtc_de_r[0] && (hcnt != 12'd1023))begin
hcnt <= hcnt + 1'b1;
end
end
//vcnt计数器
always @(posedge I_vtc_clk ) begin
if (vtc_vs_r == 2'b01) begin
vcnt <= 12'd0;
end
else if((vtc_de_r == 2'b10) && (vcnt != 12'd255))begin
vcnt <= vcnt + 1'b1;
end
end
//栅格绘制
always @(posedge I_vtc_clk ) begin
if ((hcnt[2:0]==7&&(vcnt[5:0]==63||vcnt == 0))||((hcnt[5:0]==63||hcnt==0)&&vcnt[2:0]==7)||(vcnt == 0&& hcnt==0)) begin
grid_de <= O_vtc_de;
end
else begin
grid_de <= 1'b0;
end
end
//1--绘制波形曲线 1, 绿色点
//2--绘制波形曲线 2, 黄色点
//3--绘制栅格虚线, 白色点
//4--绘制背景色, 黑色
always @(posedge I_vtc_clk)begin
casex({grid_de,wave2_pixel_en,wave1_pixel_en})
3'bxx1:
O_vtc_rgb <= {8'h00,8'hff,8'h00}; //wave1 信号显示像素颜色
3'bx10:
O_vtc_rgb <= {8'hff,8'hff,8'h00}; //wave2 信号显示像素颜色
3'b100:
O_vtc_rgb <= {8'h96,8'h96,8'h96}; //网格显示像素为白色点
default:
O_vtc_rgb <= {8'h00,8'h00,8'h00}; //黑色背景
endcase
end
wave_buf u_wave_buf1
(
.I_wave_clk (I_wave1_clk ), //写数据输入时钟, 和 ADC 采集时钟同步
.I_wave_data (I_wave1_data ), //写数据
.I_wave_data_de (I_wave1_data_de ), //写数据有效
.I_vtc_clk (I_vtc_clk ), //VTC 时序发生器时钟输入
.I_vtc_rstn (I_vtc_rstn ), //VTC 时序发生器复位
.I_vtc_vs (I_vtc_vs ), //VTC 时序发生器的 VS 同步信号输入
.I_vtc_de_r (vtc_de_r[0] ), //VTC 时序发生器的 de 有效区域输入
.I_vtc_vcnt (vcnt ), //vtc 的数据偏移, 主要对有符号数据进行调整
.O_pixel_en (wave1_pixel_en ) //输出输出使能
);
wave_buf u_wave_buf2
(
.I_wave_clk (I_wave2_clk ), //写数据输入时钟, 和 ADC 采集时钟同步
.I_wave_data (I_wave2_data ), //写数据
.I_wave_data_de (I_wave2_data_de ), //写数据有效
.I_vtc_clk (I_vtc_clk ), //VTC 时序发生器时钟输入
.I_vtc_rstn (I_vtc_rstn ), //VTC 时序发生器复位
.I_vtc_vs (I_vtc_vs ), //VTC 时序发生器的 VS 同步信号输入
.I_vtc_de_r (vtc_de_r[0] ), //VTC 时序发生器的 de 有效区域输入
.I_vtc_vcnt (vcnt ), //vtc 的数据偏移, 主要对有符号数据进行调整
.O_pixel_en (wave2_pixel_en ) //输出输出使能
);
endmodule
`timescale 1ns / 1ps
//实现乒乓
module wave_buf
(
input I_wave_clk , //写数据输入时钟, 和 ADC 采集时钟同步
input [7 :0] I_wave_data , //写数据
input I_wave_data_de , //写数据有效
input I_vtc_clk , //VTC 时序发生器时钟输入
input I_vtc_rstn , //VTC 时序发生器复位
input I_vtc_vs , //VTC 时序发生器的 VS 同步信号输入
input I_vtc_de_r , //VTC 时序发生器的 de 有效区域输入
input [7 :0] I_vtc_vcnt , //vtc 的数据偏移, 主要对有符号数据进行调整
output O_pixel_en //输出输出使能
);
//BRAM 简单双口 BRAM
reg [9 :0] addra = 0; //BRAM 通道 A 地址
//reg ena = 0; //BRAM 通道 A 使能
reg wea = 0 ; //BRAM 通道 A 写使能
reg [9 :0] addrb = 0 ; //BRAM 通道 B 地址
reg enb = 0 ; //BRAM 通道 B 读使能
reg [0 :0] WR_S,RD_S ; //写状态机, 读状态机
reg buf_flag ; //buf_flag 用于乒乓地址缓存切换
reg addr0_en ; //用于设置写第一个数据相对地址 0
wire [7 :0] wave_data ; //写波形数据到 BRAM
reg [3 :0] async_vtc_vs = 0 ; //同步信号
//异步信号I_vtc_vs转同步信号
always @(posedge I_wave_clk ) begin
async_vtc_vs <= {async_vtc_vs [2:0] , I_vtc_vs};
end
//绘制波形数据点使能, 绘制原理:
//当匹配到存储的 ADC 数据和正在扫描的 Y 坐标值一致就输出, 每个 X 坐标方向绘制 1 个波形点
assign O_pixel_en = I_vtc_de_r&(I_vtc_vcnt[7:0] == wave_data[7:0]);
//写RAM状态机
always @(posedge I_wave_clk or negedge I_vtc_rstn ) begin
if(!I_vtc_rstn)begin //复位重置所有寄存器
addra <= 10'd0 ; //写RAM地址
addr0_en <= 1'b1 ; //
wea <= 1'b0 ; //写信号 0:停止 1:开始
buf_flag <= 1'b0 ; //读写地址控制信号
WR_S <= 1'd0 ; //
end
else begin
case (WR_S)
0:begin
if (I_wave_data_de == 1'b1) begin
if (addra == 1023) begin //1024写完
wea <= 1'b0 ; //停止写
addra <= 10'd0 ;
addr0_en <= 1'b1 ;
WR_S <= 1'd1 ;
end
else begin
wea <= 1'b1 ; //写使能
addr0_en <= 1'b0 ;
addra <= (addr0_en == 1'b0) ? (addra + 1'b1) : 0 ; //相对地址递增
end
end
else begin
wea <= 1'b0;
end
end
1:begin //等待VTC时序通同步
if(async_vtc_vs[3:2] == 2'b10) begin
WR_S <= 1'd0;
buf_flag <= !buf_flag;
end
end
default: WR_S <= 1'd0 ;
endcase
end
end
//读状态机
always @(posedge I_vtc_clk or negedge I_vtc_rstn ) begin
if (!I_vtc_rstn) begin
addrb <= 10'd0 ;
RD_S <= 1'b0 ;
end
else begin
case (RD_S)
0:begin
if (I_vtc_de_r == 1'b1) begin
if (addrb == 1023) begin
addrb <= 0;
RD_S <= 1'd1;
end
else begin
addrb <= addrb + 1'b1;
end
end
end
1:begin
if (I_vtc_de_r == 1'b0) begin
RD_S <= 1'd0;
end
end
default: RD_S <=1'd0;
endcase
end
end
blk_mem_gen_0 u_blk_mem_gen_0 (
.clka (I_wave_clk ) , //写时钟
.wea (wea ) , // input wire [0 : 0]写使能
.addra ({buf_flag,addra} ) , // input wire [10 : 0] 写地址
.dina (I_wave_data ) , // input wire [7 : 0] 写数据
.clkb (I_vtc_clk ) , // input wire 读时钟
.addrb ({!buf_flag,addrb} ) , // input wire [10 : 0] 读地址
.doutb (wave_data ) // output wire [7 : 0] 读数据
);
endmodule
//使用buf_flag控制写入上半部分,还是下半部分
`timescale 1ns / 1ps
module TPG(
input I_sysclk ,
input I_rst_n ,
output [7:0] O_wave1_data ,
output [7:0] O_wave2_data ,
output data_en
);
// localparam SYSCLK = 100_000_000;
localparam t500ms_CNT = (50_000_000 - 1'b1);
reg [25:0] t500ms_cnt = 26'd0;
wire t500ms_en = (t500ms_cnt == t500ms_CNT) ;
always @(posedge I_sysclk ) begin
if (!I_rst_n) begin
t500ms_cnt <= 26'd0;
end
else if(t500ms_cnt == t500ms_CNT)begin
t500ms_cnt <= 26'd0;
end
else begin
t500ms_cnt <= t500ms_cnt + 1'b1;
end
end
reg [1:0] WAVE_S ; //写数据状态机
reg [9:0] test_data = 10'd0 ; //测试数据
assign O_wave1_data = 8'd255 - test_data[7:0];
assign O_wave2_data = test_data[7:0];
assign data_en = (WAVE_S == 0 || WAVE_S == 1);
always @(posedge I_sysclk) begin
if (!I_rst_n) begin
WAVE_S <= 2'd2;
test_data <= 10'd0;
end
else begin
case (WAVE_S)
0:begin
if (test_data == 10'd255) begin
WAVE_S <= 2'd1;
end
else begin
test_data <= test_data + 1'b1;
end
end
1:begin
if (test_data == 0) begin
WAVE_S <=2'd2;
end
else begin
test_data <= test_data - 1'b1;
end
end
2:begin
if (t500ms_en == 1'b1) begin
WAVE_S <= 2'd0;
end
else begin
WAVE_S <= WAVE_S;
end
end
default: WAVE_S <= 2'd0;
endcase
end
end
endmodule
四、下载验证

五、AD9248驱动的示波器
1、总体框图

2、AD9248芯片简介

3、采集模式
3.1非复合模式

3.2复合模式

3.3数据格式

4、引脚定义

367

被折叠的 条评论
为什么被折叠?



