ZYNQ学习笔记(一):基于ZYNQ7020、AN108的DDS实验(VIO可控频率字)

2023-10-28 18:50

本文主要是介绍ZYNQ学习笔记(一):基于ZYNQ7020、AN108的DDS实验(VIO可控频率字),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、设计需求、

二、DDS 基本原理、

三、Vivado工程建立、

四、添加设计源文件、模块例化、

3.1 添加设计源文件

3.2 模块例化

五、程序设计、

六、分析与综合、约束输入、

6.1 分析与综合过程

6.2  约束输入

七、连接测试、

八、数据导出与matlab分析、

九、实验改进、

9.1、模块例化

9.2、程序改进

9.3、约束输入

9.4、连接测试

总结、


实验环境:ALINX7020开发板,AN108模块,vivado2018,示波器。

一、设计需求、

使用VIO设定频率字,生成某频率的正弦波形
在ILA中观察波形,观察设定的波形的周期,来验证频率是否设定正确
把ILA波形导出到CSV文件
在Matlab里分析波形的频谱,验证生成波形的正确性。

二、DDS 基本原理、

        DDS 的基本结构主要由相位累加器、相位调制器、波形数据表 ROM、D/A 转换器等四大结构组成,DDS 基本结构图如下所示。

      如图可以看出主要有四部分:

      相位累加器(Phase Accumulator):相位累加器是DDS的核心组件之一。它由N位加法器和N位寄存器构成。在每个时钟脉冲到达时,相位累加器将频率控制字(或相位增量值)与累加寄存器中的相位数据相加。累加的结果反馈回累加寄存器的数据输入,以便在下一个时钟脉冲到达时继续相加。这种过程导致了线性相位累加,也就是相位随时间线性增加。当相位累加器的值达到最大值时,会产生一次溢出,这表示完成了一个完整的信号周期。

      相位调制器(Phase Modulator):相位调制器允许你控制输出信号的相位参数。通过改变相位控制字(P_WORD),可以引入相位调制,从而产生相移的效果。用相位调制器输出的数据,作为波形存储器的相位采样地址,这样就可以把存储在波形存储器里的波形采样值经查表找出,完后相位到幅度的转换。

      波形数据表(Waveform Lookup Table):波形数据表存储了DDS输出波形的样本点。每个样本点对应于相位累加器的不同值。通过查找表,DDS可以获取相应相位的幅度信息。这允许DDS生成不同波形,包括正弦波、锯齿波、方波等。

      D/A转换器(Digital-to-Analog Converter):DDS输出的数据是数字形式的,D/A转换器将数字信号转换为模拟信号,以便在外部电路中使用。DDS的输出信号随着时间变化,D/A转换器将其转换为模拟波形,供其他设备或电路使用。 

       此外,如图可以看出有三个输入端:系统时钟CLOCK;频率字输入(后面用F_WORD表示);相位字输入(后面用P_WORD表示)。一个输出端:信号输出。

       在相位调制器之前的两个累加寄存器的作用是,使频率字和相位字输入的时候不会影响到相位累加器和相位调制器的运行。相位调制器的位数与ROM地址线位数相同,即输出时按照相位调制器输出的值对照ROM地址,输出该地址对应数据。假设相位累加器位数为N,相位调制器位数为M。N一般大于M,所以N使用时,将高M位传入相位调制器,低N-M位用于F_WORD值计数。

       DDS系统运行流程为:F_WORD值输入暂存于累加寄存器,在相位累加器中不断累加直到计满低N-M位后溢出,将高M位传入相位调制器,相位调制器接到P_WORD值再在此基础上加上P_WORD值(P值为定值的时候输出波形的形状不变,相位平移P值),此时根据相位调制器输出的M位值查找对应ROM地址,输出地址对应数据。

      举个例子,假设输入F_WORD=1,N=32,M=12,ROM地址线12位,ROM数据线8位,P_WORD值暂不考虑。

      那么数据传输如下:

  (1)相位累加器:N-M=20,即N低20位用来F_WORD值自加,直至累加到2^20后溢出到高12位。共需要累加2^{20}/1次。即得到一个进位所需时间

(T_CLOCK为系统时钟周期)

   (2)相位调制器:接到相位累加器高12位数据,与P_WORD相加后寻址。12位计满(相位调制器计满)需要2^{12}次进位。经过2^{12}次进位后将储存在ROM内的波形完全读出,所以:

        输出周期:

        输出频率:

 (F_CLOCK为系统时钟频率)

       至此,我们得到了输出频率的公式,根据公式我们可以配置一些相应的固定参数,进而可以通过VIO模拟按键来控制DDS的频率控制字,也就是F_WORD的值,从而得到不同频率的正弦波。

三、Vivado工程建立、

点击“Create Project ”来创建一个新的工程

点击

弹出的页面点击Nest
下面输入工程的名称和路径

工程类型选择“RTL Project”

之后的两个页面均直接点击Nest ,因为使用的是黑金7020 开发板,可以在搜索栏直接输入“xc7z020clg400-2”,单击选中“Part”一栏的芯片型号,然后点击“Next”按钮。

在弹出的页面直接点击“ Finish ”按钮完成工程的创建。

四、添加设计源文件、模块例化、

3.1 添加设计源文件

工程创建完成后,就进入了 Vivado 的工程主界面,点击“Sources ”窗口中的“ +”号,选择添加设计源文件,然后点击“Next”按钮。
对创建的设计文件进行命名,然后点击“OK”按钮
后面弹出的页面依次点击Finish — OK — Yes,这时工程主界面的“ Sources ”窗口中就出现了我们刚刚创建的源文件,按相同的步骤再创建一个频率控制模块的设计文件,创建完成后如图所示

3.2 模块例化

添加DDS IP核,搜索后发现DDS IP核有很多个,这里先对几个IP核注释一下:

  1. Modulation下的DDS Compiler: 这种IP核通常用于生成带调制的信号。如果你的应用需要生成调制后的信号,比如AM(幅度调制)或FM(频率调制)信号,那么选择这个选项可能是合适的。

  2. Trig Functions下的DDS Compiler: 这个选项可能更适合需要执行三角函数运算(如正弦、余弦、正切等)的应用。如果你需要在DDS中执行特定的三角函数计算,可以选择这个选项。

  3. Waveform Synthesis下的DDS Compiler: 如果你只需生成基本的波形信号,如正弦波、方波、锯齿波等,而不需要调制或复杂的三角函数运算,那么选择这个选项可能更简单和适用

这里我们先选择第三个,如图所示

双击所选中的IP核,如图所示进行修改

首先是使用板载PL 50MHz时钟,对应第二节中的CLOCK。

这里的"Phase Width"选项允许你设置DDS输出信号中相位累加器的位数或相位分辨率,相位累加器的位数决定了DDS输出信号的相位精度,更高的位数会提供更高的相位分辨率,允许DDS生成更精确的输出信号。它所对应的也就是公式中的N,这里我们设置N=20。

经过配置输出的是16位宽的正弦信号。

接下来弹出的页面依次点击 OK—Generate—OK

下面添加VIO IP核,同理先搜索再双击选中,

如图修改

接下来弹出的页面依次点击 OK—Generate—OK

我们还要添加一个ILA核来观察波形

在这里设置了三个探针,至于采集哪些信号程序设计那一小结再讲~

接下来弹出的页面依次点击 OK—Generate—OK

此时窗口如图所示:

五、程序设计、

顶层模块vio_dds程序如下:

程序主要设置了四个模块

      首先是vio_0模块:key_PING是一个2位宽度的输入信号,表示通过外部VIO模块模拟的按键状态。根据 key_PING的值,不同的频率配置会被应用到 Fword上。这个模块通过输入的时钟sys_clk,控制按键并将按键状态输出到key_PING,按键状态可以用于选择要合成的频率。

       vio_control模块:在代码中定义的一个名为Fword的 24位宽度的信号线(或者说是一个 24位的寄存器),用于存储频率控制字F_WORD。这个信号被用作频率控制模块vio_contral_lg的输出,并且在 DDS 模块中被用作输入。这个模块通过接收key_PING 和 Fword信号,用于配置 DDS 模块以产生所需的频率。它通过控制 Fword 信号来实现频率选择。

       dds_compiler_0模块:这个模块是 DDS 模块的实例化,接收一些信号,以产生输出信号,它的输出m_axis_data_tdata包含合成信号的主体部分,即DDS生成的数据信号。     

       ila_0模块:用于采集和分析信号,以便调试和验证系统的功能。以key_PING、Fword 和 m_axis_data_tdata等信号作为采样点。

`timescale 1ns / 1psmodule vio_dds(input       sys_clk,            input       rst_n              );wire [1 : 0]    key_PINC;vio_0 u_vio_0 (.clk(sys_clk),                // input wire clk.probe_out0(key_PINC)         // output wire [1 : 0] probe_out0
);wire [23 : 0]   Fword;         vio_contral  vio_contral_lg(.clk(sys_clk), .rst_n(rst_n), .key_PINC(key_PINC), .Fword(Fword)
);
wire [0:0]      fre_ctrl_word_en;    //output
wire [0 : 0]    m_axis_data_tvalid;
wire [31 : 0]   m_axis_data_tdata;
wire [0 : 0]    m_axis_phase_tvalid;
wire [23 : 0]   m_axis_phase_tdata;assign          fre_ctrl_word_en = 1'b1;dds_compiler_0 u_dds_compiler_0 (.aclk(sys_clk), .s_axis_config_tvalid(fre_ctrl_word_en), .s_axis_config_tdata(Fword),  .m_axis_data_tvalid(m_axis_data_tvalid),.m_axis_data_tdata(m_axis_data_tdata),.m_axis_phase_tvalid(m_axis_phase_tvalid),.m_axis_phase_tdata(m_axis_phase_tdata)  
);// ILA
ila_0 u_ila (.clk(sys_clk), // input wire clk.probe0(key_PINC), // input wire [1:0]  probe0  .probe1(Fword), // input wire [23:0]  probe1 .probe2(m_axis_data_tdata) // input wire [31:0]  probe2
);endmodule

  频率控制模块程序设计如下:

`timescale 1ns / 1psmodule vio_contral(input           clk,input           rst_n,input [1 : 0]   key_PINC,output reg [23 : 0] Fword);
always@(*)
begincase(key_PINC)0:  Fword <= 'had37;      //2Mhz 41943.04 取整419431:  Fword <= 'h147ae;     //4Mhz 83886.08 取整838862:  Fword <= 'h1eb85;     //6Mhz 125829.12 取整1258293:  Fword <= 'h28f5c;     //8Mhz 167772.16 取整167772endcase
endendmodule

 Fword的值是如何确定的呢?是由上文提到的公式:

 当我想设置能够生成频率为2Mhz的正弦波时,我们只需要确定F_WORD的值就可以,把之前设置DDS IP核时的各种数据带入:

                             

再转化为16进制,则为ad37,生成其他频率的正弦波同理~

保存文件后~此时窗口更新为:

六、分析与综合、约束输入、

6.1 分析与综合过程

代码输入完毕之后,就可以对设计进行分析(Elaborated)了。点击vivado页面最左侧的“Flow Navigator”窗口中的“Open Elaborated Design”按钮,此时,Vivado 会编译 RTL 源文件并进行全面语法检查,同时Vivado 会生成顶层原理图视图。

右击上图最顶部的蓝色区域,点击Close关闭分析过程。

接下来点击“Flow Navigator”窗口中的“Run Synthesis”按钮,来对代码进行综合。弹出的窗口依次

均点击“OK”按钮。

最后右击最顶部的蓝色区域,点击Close关闭分析。完成综合过程。

6.2  约束输入

弹出的窗口点击Greate File

输入文件名,点击“OK”,“Finish”。找到刚创建的约束文件,

打开输入:

create_clock -period 20.000 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN U18 [get_ports sys_clk]set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN K16 [get_ports rst_n]
约束输入完毕之后,就可以开始实现设计了。点击“ Flow Navigator ”窗口中的“ Run Implementation”按钮,在弹出的界面中直接点击 OK 即可,在等段一段时间实现设计完成后会弹出提示窗口,我们直接点击Cancel来关闭窗口。
实现设计完成后,我们可以看一下各模块资源的占用情况:点击Open Implemented Design,然后选择Report Utilization。之后会自动弹出资源报告窗口,使用默认配置,点击OK。

运行结果如下:

工程在逻辑单元(LUT)、LUTRAM资源、以及寄存器资源(FF)和BRAM资源的资源利用率相对较低,输入/输出资源(IO)的利用率相对较高,这可能是因为设计需要连接到外部设备或接口。总的来说,从资源利用率的角度来看,工程在几个方面都具有较低的资源消耗。这可能是一个良好的迹象,表明设计在资源利用方面有很大的余地,可以进一步优化或添加更多的功能,而不会达到FPGA的资源限制。
生成与 下载比特流过程不再细说~

七、连接测试、

将生成的比特流下载进入开发板后,Vivado 会自动打开 ILA 的调试窗口,点击运行触发的图标,如下图所示:

查看波形~

波形呈现为这种状态是因为原因是Vivado中大多数的IP核采用有符号二进制补码的编码方式,默认为十六进制数值不会进行补码计算。但如果不进行补码计算,那么所有的数都被当作正数处理,所以波形会出现这种形式,我们可以更改"Radix"选项用于指定在波形视图中显示信号时的数据格式。

以有符号十进制格式显示信号的值。这对于有符号数据类型的信号很有用。

可以看到生成的DDS正弦信号~

下面测试VIO模块更改频率是否生效,切换到hw_vios窗口,把key_PINC信号添加进去,更改key_PINC[1]与key_PINC[0]的值,修改完成后,再切换到hw_ila_1页面,点击运行重新采集信号,观察波形

  对应2Mhz。

对应4Mhz。

对应6Mhz。

对应8Mhz。

八、数据导出与matlab分析、

导出2Mhz的DDS信号数据

打开Matlab,新建脚本文件并写入如下代码

close all;
clc;
clear all;% 从CSV文件加载信号数据
data = xlsread('iladata.csv');x = data(:, 6);  % 提取信号向量
fs = 50000000; % 设置采样频率为50MHz% 调用 spectrum_plot 函数来绘制频谱图
tt_str = '频谱图'; % 设置图表标题
spectrum_plot(x, fs, tt_str);% 绘制DDS信号波形
figure;
plot(data(:, 1), data(:, 6));
title('DDS信号波形');
xlabel('Time');
ylabel('Amplitude');
xlim([0 200]);grid on;% spectrum_plot 函数定义
function spectrum_plot(x, fs, tt_str)vec_win = kaiser(length(x),10);y    = fftshift(fft(x .* vec_win));y_dB = 20*log10(abs(y));N_f = length(y);f_idx = [0:N_f-1].'/N_f * fs - (fs/2);figure;plot(f_idx/1E3, y_dB);xlabel('kHz');title(tt_str);
end

可以看到信号频率存在误差但基本一致,至此关于利用VIO可更改频率字的DDS信号生成实验就此结束了。

九、实验改进、

前面完成了DDS信号的生成,以及验证了通过VIO模拟按键来控制DDS的频率控制字,从而得到不同频率的正弦波的可行性,

那么我该如何使生成的DDS波形信号在示波器上显示呢?这就要用到AN108这个ADDA模块了,同时需要在再增加几个模块设计~

 9.1、模块例化

对DDS IP核进行修改:

因为AD108中AD9708是8位,125MSPS的DA转换芯片,AD9280是8位,32MSPS的AD转换芯片,所以我们把这里的输出DDS信号的输出波形位宽由原来的16位改为8位~

对ILA IP核进行修改:

这里我们再多添加一个探针,同时把PROBE2、3的采样深度改为8位,分别采集DA输出以及AD输入信号。

添加clk_wiz IP核:

在顶层模块代码例化了 clk_wiz的 ip核, 产生出25MHz的时钟,用于给ADC提供采样时钟。

9.2、程序改进

顶层文件vio_dds:

相较于之前的顶层程序,改进后的程序主要是添加了一个时钟模块与DA数据发送模块,分别用于产生一个输出时钟信号ad_clk这可以用于控制AD芯片的采样速率),以及用于将数据发送到DA芯片。此外更改了输出信号的位宽(适应AD108)以及多添加了一个ILA探点。

`timescale 1ns / 1ps
module vio_dds(input       sys_clk,            input       rst_n,//DA芯片接口output                da_clk,  output    [7:0]       da_data,  //输出给DA的数据//AD芯片接口input     [7:0]       ad_data,  //AD输入数据output                ad_clk                );wire [1 : 0]    key_PINC;
vio_0 u_vio_0 (.clk(sys_clk),                // input wire clk.probe_out0(key_PINC)         // output wire [1 : 0] probe_out0
);wire [23 : 0]   Fword;         
vio_contral  vio_contral_lg(.clk(sys_clk), .rst_n(rst_n), .key_PINC(key_PINC), .Fword(Fword)
);wire [0:0]      fre_ctrl_word_en;    
//output
wire [0 : 0]    m_axis_data_tvalid;
wire [7 : 0]   m_axis_data_tdata;
wire [0 : 0]    m_axis_phase_tvalid;
wire [23 : 0]   m_axis_phase_tdata;assign          fre_ctrl_word_en = 1'b1;
dds_compiler_0 u_dds_compiler_0 (.aclk(sys_clk),                                  // input wire aclk.s_axis_config_tvalid(fre_ctrl_word_en),        // input wire s_axis_config_tvalid.s_axis_config_tdata(Fword),                    // input wire [23 : 0] s_axis_config_tdata.m_axis_data_tvalid(m_axis_data_tvalid),        // output wire m_axis_data_tvalid.m_axis_data_tdata(m_axis_data_tdata),          // output wire [7 : 0] m_axis_data_tdata.m_axis_phase_tvalid(m_axis_phase_tvalid),      // output wire m_axis_phase_tvalid.m_axis_phase_tdata(m_axis_phase_tdata)         // output wire [23 : 0] m_axis_phase_tdata
);//例化PLL
clk_wiz_0 pll_inst(// Clock out ports.clk_out1(ad_clk),     // 给ad_clk 25MHz的频率// Status and control signals.reset(~rst_n), // input reset.locked(locked),       // output locked// Clock in ports.clk_in1(sys_clk)     // input clk_in1);//DA数据发送
da_wave_send u_da_wave_send(.clk         (sys_clk), .rst_n       (rst_n),.rd_data     (m_axis_data_tdata),.da_clk      (da_clk),  .da_data     (da_data));// ILA
ila_0 u_ila (.clk(ad_clk), // input wire clk.probe0(key_PINC), // input wire [1:0]  probe0  .probe1(Fword), // input wire [23:0]  probe1 .probe2(da_data), // input wire [7:0]  probe2.probe3(ad_data) // input wire [7:0]  probe3);endmodule

DA数据发送模块da_wave_send:

module da_wave_send(input                 clk    ,  //时钟input                 rst_n  ,  //复位信号,低电平有效    input        [7:0]    rd_data,  //读出的数据//DA芯片接口output                da_clk ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟output       [7:0]    da_data   //输出给DA的数据  
);//parameter 频率调节控制
parameter  FREQ_ADJ = 8'd5;  //频率调节,FREQ_ADJ的值越大,最终输出的频率越低,范围0~255//reg define
reg    [7:0]    freq_cnt  ;  //频率调节计数器assign  da_clk = ~clk;       
assign  da_data = rd_data;   //将读到的数据赋值给DA数据端口//频率调节计数器
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)freq_cnt <= 8'd0;else if(freq_cnt == FREQ_ADJ)    freq_cnt <= 8'd0;else         freq_cnt <= freq_cnt + 8'd1;
endendmodule

9.3、约束输入

在经过分析与综合过程后,进行约束输入:

############## clock and reset define##################
create_clock -period 20.000 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN U18 [get_ports sys_clk]set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN K16 [get_ports rst_n]########AN108 ON AX7020 and AX7010  J11##################
set_property PACKAGE_PIN F20 [get_ports da_clk]
set_property PACKAGE_PIN F19 [get_ports {da_data[7]}]
set_property PACKAGE_PIN G20 [get_ports {da_data[6]}]
set_property PACKAGE_PIN G19 [get_ports {da_data[5]}]
set_property PACKAGE_PIN H18 [get_ports {da_data[4]}]
set_property PACKAGE_PIN J18 [get_ports {da_data[3]}]
set_property PACKAGE_PIN L20 [get_ports {da_data[2]}]
set_property PACKAGE_PIN L19 [get_ports {da_data[1]}]set_property PACKAGE_PIN M20 [get_ports {da_data[0]}]
set_property PACKAGE_PIN L17 [get_ports {ad_data[0]}]
set_property PACKAGE_PIN L16 [get_ports {ad_data[1]}]
set_property PACKAGE_PIN M18 [get_ports {ad_data[2]}]
set_property PACKAGE_PIN M17 [get_ports {ad_data[3]}]
set_property PACKAGE_PIN D20 [get_ports {ad_data[4]}]
set_property PACKAGE_PIN D19 [get_ports {ad_data[5]}]
set_property PACKAGE_PIN E19 [get_ports {ad_data[6]}]
set_property PACKAGE_PIN E18 [get_ports {ad_data[7]}]
set_property PACKAGE_PIN G18 [get_ports ad_clk]set_property IOSTANDARD LVCMOS33 [get_ports da_clk]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports ad_clk]

9.4、连接测试

DA 输出通道与示波器相连。
将工程生成的比特流文件下载到 ZYNQ 中后,连接后在 ILA 中观察da_data 数据的变化,因为 DA 输出通道和 AD 输入通道并没有相连,所以ad_data数据的波形暂不观察。
观察到的da_data 数据波形如下图所示。
此刻我们更改"Radix"选项指定显示信号时的数据格式,固然可以在ILA波形视图中显示出正常的正弦波(如上面第六节)。
但是问题也随之出现~示波器上的波形形式并不会出现改变
对此我找了好久才发现~
原因是Vivado中大多数的IP核采用有符号二进制补码的编码方式,通过FPGA输出的有符号数与AD9708(AN108的DA芯片)数据格式不匹配。
因为DAC是无符号数编码,这就导致DA部分不认识有符号数,所以需要将有符号数平移,将其平移到无符号数范围,故解决方法是将8位有符号数-128~+127平移到0~255转为无符号数。
因此修改DA数据发送模块da_wave_send的代码:
module da_wave_send(input                 clk    ,  //时钟input                 rst_n  ,  //复位信号,低电平有效    input        [7:0]    rd_data,  //读出的数据//DA芯片接口output                da_clk ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟output       [7:0]    da_data   //输出给DA的数据  
);//parameter 频率调节控制
parameter  FREQ_ADJ = 8'd5;  //频率调节,FREQ_ADJ的值越大,最终输出的频率越低,范围0~255//reg define
reg    [7:0]    freq_cnt  ;  //频率调节计数器
reg    [7:0]    da_data;  // 修改为 reg 类型assign  da_clk = ~clk;// 进行偏移和缩放
always @(*) begin// 偏移:将最小的负数偏移到0if (rd_data < 8'h80)da_data = rd_data + 8'h80;elseda_data = rd_data - 8'h80;// 缩放:如果需要,将数据缩放到0到255的范围end//频率调节计数器
always @(posedge clk or negedge rst_n) beginif(rst_n == 1'b0)freq_cnt <= 8'd0;else if(freq_cnt == FREQ_ADJ)    freq_cnt <= 8'd0;else         freq_cnt <= freq_cnt + 8'd1;
endendmodule

修改完成后生成比特流下载

可以发现不再需要更改"Radix"就可以看到正弦波形~

生成6Mhz的DDS信号

连接观察示波器:

可以看到示波器显示6Mhz~
DA 输出通道和 AD 输入通道连接起来

将工程生成的比特流文件下载到 ZYNQ 中后,连接后在 ILA 中观察 ad_data、da_data  数据的变化,观察到的波形如下图所示。

可以看到DA输出与AD输入波形一致。

我们可以导出数据再用Matlab分析一下:

close all;
clc;
clear all;% 从CSV文件加载信号数据
data_da = xlsread('iladata_da_6Mhz.csv');
data_ad = xlsread('iladata_ad_6Mhz.csv');x_da = data_da(:, 6);  % 提取DA信号向量
x_ad = data_ad(:, 6);  % 提取AD信号向量
fs = 25000000; % 设置采样频率为25MHz% 调用 spectrum_plot 函数来绘制频谱图
tt_str_da = 'DA频谱图'; % 设置图表标题
tt_str_ad = 'AD频谱图'; % 设置图表标题
spectrum_plot(x_da, fs, tt_str_da);
spectrum_plot(x_ad, fs, tt_str_ad);% 绘制DDS信号波形
figure;
subplot(211);
plot(data_da(:, 1), data_da(:, 6));
title('DA信号波形');
xlabel('Time');
ylabel('Amplitude');
xlim([0 200]);subplot(212);
plot(data_ad(:, 1), data_ad(:, 6));
title('AD信号波形');
xlabel('Time');
ylabel('Amplitude');
xlim([0 200]);
grid on;% spectrum_plot 函数定义
function spectrum_plot(x, fs, tt_str)vec_win = kaiser(length(x), 8);y    = fftshift(fft(x .* vec_win));y_dB = 20*log10(abs(y));N_f = length(y);f_idx = [0:N_f-1].'/N_f * fs - (fs/2);figure;plot(f_idx/1E3, y_dB);xlabel('kHz');title(tt_str);
end

可以看到信号频率存在误差但基本一致~


总结、

       关于基于ALINX 7020、AN108的DDS实验(VIO可控频率字)就结束了,本实验的设计需求是使用VIO模块来设定频率字,生成特定频率的正弦波形,并通过多个步骤来验证生成波形的正确性,这个实验的设计需求也已基本完成,至此算是开始正式踏入了FPGA的学习~

这篇关于ZYNQ学习笔记(一):基于ZYNQ7020、AN108的DDS实验(VIO可控频率字)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/295219

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件