1、AD9708芯片解读和电路设计
AD9708 很简单,8 位分辨率,125MSPS 采样率,输入参考电压3~5V,内置 1.2V 参考电压,8bit数字信号输入,差分电流输出;芯片操作不需要软件配置,给个时钟信号就工作,简单得很,根据官方手册,内部结构如下:
SLEEP引脚提供芯片休眠功能,当不需要使用该芯片时可拉高SLEEP以降低电路板功耗,当不使用休眠功能时,官方建议该引脚悬空;
REFLO引脚接模拟地开启内置1.2V 参考电压功能,既然芯片都提供了这样的功能,当然要用啊,不用时要接AVDD;
芯片外围电路就这么简单,照着官方手册画就完事儿了,重要的是芯片输出的匹配电路设计,AD9708 是差分输出,若要用线缆输出连接其他设备,必然要实现差分转单端的功能,为了为了防止噪声干扰,保持输出波形的线条完美,参考了网上大佬,推荐使用7 阶巴特沃斯低通滤波器;原理图部分如下:
然后再使用差分转单端芯片输出模拟单端信号;
2、AD9280芯片解读和电路设计
AD9280也 很简单,8 位分辨率,32MSPS 采样率,输入参考电压2.7~5.5V,芯片操作不需要软件配置,给个时钟信号就工作,简单得很,根据官方手册,内部结构如下:
STBY引脚接高电平进入休眠模式,接低电平则一直工作;根据电路板功耗要求设计;
这里重点将一下输入电压AIN;作为模拟输入,它可以配置为多种模式,分别由不同引脚得上下拉决定,具体看手册,说得很清楚,这里只将我这里的配置,参考了官方给的设计如下:
根据官方给的公式:
REFBS引脚接地;REFTS引脚接地2V;
3、FPGA设计框架
可以看到:本设计哟如下功能:
1、上位机软件生成波形文件,通过AD9708发送出去;
2、AD9280接收AD9708发出的波形(回环);并采集;
3、HDMI显示器显示AD9280采集的波形;
4、AD9708发送的波形类型由串口配置;
5、HDMI显示波形的位置由串口配置;
4、AD9708波形生成并发送
采用上位机软件生成波形文件,并转化为.coe文件,vivado调用rom ip核,将.coe文件固化进去即可,波形数据直接给到AD9708输出接口即可,简单得很;
本设计设计了三种波形,正弦波,三角波,正弦波和三角波结合的自定义波形;
AD9708输出哪种波形由串口控制,串口发送不同数据,AD9708输出不同波形;
波形生成上位机软件附带在工程目录下,可直接转化为.coe文件,如下:
5、AD9280采集接收波形
AD9280采集接收AD9708发来波形;由于要适配显示器,所以AD9280采集需要设置时间间隔,不然看不到波形了;同时,AD9280采集数据后需要写入ram中,由视频时序生成器读出数据,达到跨时钟的作用;AD9280采集时钟为32M,视频时钟为65M;
6、HDMI波形显示算法
波形在屏幕上的显示效果如下:
显示分辨率设为1024X768;
对照上图,设置相关参数如下:
wire [23:0] GRID_BACK = 24'h000000 ; //波形显示区域背景颜色
wire [23:0] GRID_WAVE = 24'h00ff00 ; //波形格子条纹颜色
wire [10:0] GRID_LENGTH = 11'd1010 ; //波形显示区域长度
wire [10:0] GRID_WIDTH = 11'd300 ; //波形显示区域宽度
//parameter GRID_X_START = 11'd9 ; //波形显示区域背景x轴起始坐标
wire [10:0] GRID_X_END = i_grid_x_start+GRID_LENGTH-1'b1; //波形显示区域背景x轴终点坐标
//parameter GRID_Y_START = 11'd243 ; //波形显示区域背景y轴起始坐标
wire [10:0] GRID_Y_END = i_grid_y_start+GRID_WIDTH-1'b1 ; //波形显示区域背景y轴终点坐标
wire [10:0] GRID_CENTRE = i_grid_y_start+GRID_WIDTH/2 ; //波形显示区域背景中间线位置
wire [10:0] GRID_Y_WAVE_START = i_grid_y_start+11'd23 ; //波形格子条纹y轴起始坐标
wire [10:0] GRID_Y_WAVE_END = GRID_Y_END-11'd21 ; //波形格子条纹y轴终点坐标
其中波形显示区域背景x轴起始坐标和波形显示区域背景y轴起始坐标由输入接口决定,如下:
input [10:0] i_grid_x_start,
input [10:0] i_grid_y_start,
波形显示区模块的顶层接口如下:
module helai_grid_disp (
input pclk ,
input [10:0] i_grid_x_start,
input [10:0] i_grid_y_start,
input [10:0] i_pos_x ,
input [10:0] i_pos_y ,
input i_de ,
input [23:0] i_data ,
output [23:0] o_data
);
波形显示画线模块的顶层接口如下:
module helai_wav_disp (
input pclk ,
input [23:0] wave_color ,
input adc_clk ,
input adc_buf_wr ,
input [11:0] adc_buf_addr ,
input [7:0] adc_buf_data ,
input [10:0] i_grid_x_start,
input [10:0] i_grid_y_start,
input [10:0] i_pos_x ,
input [10:0] i_pos_y ,
input i_de ,
input [23:0] i_data ,
output [23:0] o_data
);
只要给出i_grid_x_start和i_grid_y_start两个参数,就能决定波形显示区的位置;
而这两个参数由串口输入决定;
7、串口协议帧控制波形显示
串口协议帧由帧头、数据、和校验、帧尾组成,其中数据4字节,有关串口协议帧,请参考我之前写的文章点击查看串口协议帧
FPGA解析串口协议帧,提取有效的4字节数据,控制波形输出和显示位置,具体协议如下:
//串口有效数据 Byte3 Byte2 Byte1 Byte0
//Byte3:定义波形显示区域背景x轴起始坐标
//Byte2和Byte1:波形显示区域背景y轴起始坐标
//Byte0=0-->输出正弦波
//Byte0=1-->输出三角波
//Byte0=2-->输出自定义波
代码层面如下:
always @(posedge clk_200m) begin
if(~rst_n) _rx_data<=32'h09_00_f3_00; //9 ,234 ,0
else if(o_rx_done) _rx_data<=o_rx_data;
end
reg [7:0] wave_data;
always @(*) begin
if(_rx_data[7:0]==8'd0) wave_data<=dac_sin;
else if(_rx_data[7:0]==8'd1) wave_data<=dac_triangular;
else if(_rx_data[7:0]==8'd2) wave_data<=dac_zihui;
else wave_data<=dac_sin;
end
上电后,波形默认显示在屏幕正中间位置,如下:
8、vivado工程
开发板:Xilinx Artix7开发板;
开发环境:vivado2019.1;
输入:串口、AD波形;
输出:AD数据、HDMI视频;
代码架构如下:
顶层代码如下:
module top(
input sys_clk_p ,
input sys_clk_n ,
input[7:0] ad9280_data,
output ad9280_clk ,
output[7:0] ad9708_data,
output ad9708_clk ,
inout hdmi_scl ,
inout hdmi_sda ,
output hdmi_nreset,
output vout_clk ,
output vout_hs ,
output vout_vs ,
output vout_de ,
output[23:0] vout_data ,
input i_uart_rx ,
output o_uart_tx
);
wire video_clk;
wire video_hs;
wire video_vs;
wire video_de;
wire adc_clk;
wire adc0_buf_wr;
wire[10:0] adc0_buf_addr;
wire[7:0] adc0_buf_data;
wire dac_clk;
wire [7:0] dac_sin ;
wire [7:0] dac_triangular;
wire [7:0] dac_zihui;
reg [8:0] rom_addr;
wire[9:0] hdmi_lut_index;
wire[31:0] hdmi_lut_data;
assign vout_clk = video_clk;
assign vout_hs = video_hs;
assign vout_vs = video_vs;
assign vout_de = video_de;
assign vout_data = wave0_rgb;
assign hdmi_nreset = rst_n;
assign ad9280_clk = adc_clk;
assign ad9708_clk = dac_clk;
assign ad9708_data = wave_data;
wire rst_n;
wire clk_200m;
wire o_rx_done;
wire [31:0] o_rx_data;
reg [31:0] _rx_data;
wire [23:0] video_rgb;
wire [10:0] x_pos;
wire [10:0] y_pos;
wire [23:0] grid_rgb;
wire [23:0] wave0_rgb;
clk_wiz_0 u_clk_wiz_0
(
// Clock out ports
.clk_200m(clk_200m), // output clk_200m
.clk_video(video_clk), // output clk_video
.clk_adc(adc_clk), // output clk_adc
.clk_dac(dac_clk), // output clk_dac
// Status and control signals
.locked(rst_n), // output locked
// Clock in ports
.clk_in1_p(sys_clk_p), // input clk_in1_p
.clk_in1_n(sys_clk_n)); // input clk_in1_n
//I2C master controller
i2c_config i2c_config_m2(
.rst (~rst_n ),
.clk (clk_200m ),
.clk_div_cnt (16'd500 ),
.i2c_addr_2byte(1'b0 ),
.lut_index (hdmi_lut_index ),
.lut_dev_addr (hdmi_lut_data[31:24]),
.lut_reg_addr (hdmi_lut_data[23:8] ),
.lut_reg_data (hdmi_lut_data[7:0] ),
.error ( ),
.done ( ),
.i2c_scl (hdmi_scl ),
.i2c_sda (hdmi_sda )
);
//configure look-up table
lut_hdmi lut_hdmi_m0(
.lut_index(hdmi_lut_index),
.lut_data (hdmi_lut_data )
);
//dac 125Mhz/512 = 244.14khz
always@(posedge dac_clk) rom_addr <= rom_addr + 9'd1;
always @(posedge clk_200m) begin
if(~rst_n) _rx_data<=32'h09_00_f3_00; //9 ,234 ,0
else if(o_rx_done) _rx_data<=o_rx_data;
end
reg [7:0] wave_data;
always @(*) begin
if(_rx_data[7:0]==8'd0) wave_data<=dac_sin;
else if(_rx_data[7:0]==8'd1) wave_data<=dac_triangular;
else if(_rx_data[7:0]==8'd2) wave_data<=dac_zihui;
else wave_data<=dac_sin;
end
sin_rom u_sin_rom (
.clka (dac_clk ),
.ena (1'b1 ),
.addra(rom_addr),
.douta(dac_sin )
);
triangular_rom u_triangular_rom (
.clka (dac_clk ), // input wire clka
.ena (1'b1 ), // input wire ena
.addra(rom_addr ), // input wire [8 : 0] addra
.douta(dac_triangular) // output wire [7 : 0] douta
);
zihui_rom u_zihui_rom (
.clka (dac_clk ), // input wire clka
.ena (1'b1 ), // input wire ena
.addra(rom_addr ), // input wire [8 : 0] addra
.douta(dac_zihui ) // output wire [7 : 0] douta
);
uart_rx_analysis_top #(
.CLK_FREQ(200_000_000), //系统时钟频率
.UART_BPS(115200 ) //串口波特率
)
u_uart_rx_analysis_top(
.clk (clk_200m ),
.rst_n (rst_n ),
.i_uart_rx (i_uart_rx),
.o_uart_tx (o_uart_tx),
.o_rx_done (o_rx_done),
.o_rx_data (o_rx_data)
);
video_timing_control u_video_timing_control(
.i_clk (video_clk ),
.i_rst_n(rst_n ),
.i_rgb (24'hffffff),
.o_hs (video_hs ),
.o_vs (video_vs ),
.o_de (video_de ),
.o_rgb (video_rgb ),
.o_x_pos(x_pos ),
.o_y_pos(y_pos )
);
helai_grid_disp u_helai_grid_disp(
.pclk (video_clk ),
.i_grid_x_start(_rx_data[31:24]),
.i_grid_y_start(_rx_data[23: 8]),
.i_pos_x (x_pos ),
.i_pos_y (y_pos ),
.i_de (video_de ),
.i_data (video_rgb ),
.o_data (grid_rgb )
);
ad9280_sample ad9280_sample_m0(
.adc_clk (adc_clk ),
.rstn (rst_n ),
.adc_data (ad9280_data ),
.adc_data_valid(1'b1 ),
.adc_buf_wr (adc0_buf_wr ),
.adc_buf_addr (adc0_buf_addr),
.adc_buf_data (adc0_buf_data)
);
helai_wav_disp u_helai_wav_disp(
.pclk (video_clk ),
.wave_color (24'h00ff00 ),
.adc_clk (adc_clk ),
.adc_buf_wr (adc0_buf_wr ),
.adc_buf_addr (adc0_buf_addr ),
.adc_buf_data (adc0_buf_data ),
.i_grid_x_start(_rx_data[31:24]),
.i_grid_y_start(_rx_data[23: 8]),
.i_pos_x (x_pos ),
.i_pos_y (y_pos ),
.i_de (video_de ),
.i_data (grid_rgb ),
.o_data (wave0_rgb )
);
endmodule
9、上板调试验证
演示视频如下:
FPGA实现AD9708和AD9280波形收发输出HDMI模文章来源:https://www.toymoban.com/news/detail-781514.html
10、福利:工程源码获取
福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
工程源码下载链接:https://download.csdn.net/download/qq_41667729/87421414
网盘资料如下
文章来源地址https://www.toymoban.com/news/detail-781514.html
到了这里,关于FPGA实现AD9708和AD9280波形收发输出HDMI模拟示波器,串口协议帧控制显示,提供工程源码和技术支持的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!