AD9248驱动的简易示波器设计——FPGA学习笔记21

一、原理

        我们这里设计的是显示 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、引脚定义

。。。。。。没做出来。。。。。。。。。。。,驱动不起来,,,,,有时间再写,学ZYNQ去了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值