【正点原子FPGA连载】第二十三章 DDS信号发生器实验摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0

news2024/11/27 11:43:21

1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html

第二十三章 DDS信号发生器实验

DDS(Direct Digital Synthesizer)即直接数字式频率合成器,是一种新型的频率合成技术。与传统的频率合成器相比,DDS具有相对带宽大,频率转换时间短,稳定性好,分辨率高,可灵活产生多种信号等优点。较容易实现频率、相位及幅度的数控调制,因此,在现代电子系统及设备的频率源设计中,尤其在通信领域,直接数字频率合成器的应用越来越广泛。作为设计人员,我们习惯称它为信号发生器,一般用它产生正弦、锯齿、方波等不同波形或不同频率的信号波形,在电子设计和测试中得到广泛应用。本章我们将利用高速ad/da模块去设计实现一个简易的DDS信号发生器。
本章包括以下几个部分:
2323.1简介
23.2实验任务
23.3硬件设计
23.4软件设计
23.5下载验证

23.1简介
DDS(Direct Digital Synthesizer)即数字合成器,是一种新型的频率合成技术,具有低成本、低功耗、高分辨率、频率转换时间短、相位连续性好等优点,对数字信号处理及其硬件实现有着很重要的作用。DDS的基本结构主要由相位累加器、相位调制器、波形数据表ROM、D/A转换器等四大结构组成,其中较多设计还会在数模转换器之后增加一个低通滤波器(LPF)。DDS基本结构图如图 23.1.1所示。
在这里插入图片描述

图 23.1.1 DDS基本结构图
由上图可以看出,DDS主要由相位累加器、相位调制器、波形数据表以及D/A转换器构成。其中相位累加器由N位加法器与N位寄存器构成。每来一个时钟,加法器就将频率控制字与累加寄存器输出的相位数据相加,相加的结果又反馈至累加寄存器的数据输入端,以使加法器在下一个时钟脉冲的作用下继续与频率控制字相加。这样,相位累加器在时钟的作用下,不断对频率控制字进行线性相位累加。即在每一个时钟脉冲输入时,相位累加器便把频率控制字累加一次。当相位累加器累加满量时就会产生一次溢出,完成一个周期的动作。相位累加器输出的数据就是合成信号的相位。相位累加器的溢出频率,就是DDS输出的信号频率。
通过改变相位控制字P_WORD可以控制输出信号的相位参数。令相位加法器的字长为M,当相位控制字由0跃变为P_WORD时,波形存储器(ROM)的输入为相位累加器的输出与相位控制字P_WORD之和,因而其输出的幅度编码相位会增加P_WORD/2M,从而使输出的信号产生相移。
用相位调制器输出的数据,作为波形存储器的相位采样地址,这样就可以把存储在波形存储器里的波形采样值经查表找出,完后相位到幅度的转换。N位的寻址ROM相当于把0°-360°的正弦信号离散成具有2N个样值的序列。若波形存储器中有D位数据位,则2N个样值的幅值以D位二进制数值固化在波形存储器当中。按照地址的不同可以输出相应相位的正弦信号幅值。相位—幅度变换原理图如下图所示:
在这里插入图片描述

图 23.1.2 相位-幅度变换原理图
数模转换器(D/A)的作用是把合成的正弦波数字量转化为模拟量。正弦幅度量化序列经数模转换器转换后变成了包络为正弦波的阶梯波。频率合成器对数模转换器的分辨率有一定的要求,其分辨率越高,合成的正弦波台阶数就越多,输出的波形精度也就越高。DDS信号流程图如下图所示:

在这里插入图片描述

图 23.1.3 DDS信号流程图
23.2实验任务
本节实验任务是使用DFZU2EG/4EV MPSoC开发板及高速AD-DA扩展模块(ATK_HS_AD_DA模块)实现数模及模数的转换。首先ZYNQ PL端产生正弦波、方波、三角波、锯齿波变化的数字信号,经过DA芯片后转换成模拟信号,将DA的模拟电压输出端连接至AD的模拟电压输入端,AD芯片将模拟信号转换成数字信号,通过按下按键PL_KEY1和PL_KEY2可以实现波形种类和波形频率的切换,然后通过示波器观察DA端模拟信号的波形是否出现了相应变化,也可以通过Ila界面去进行观察。
23.3硬件设计
本章节中硬件设计与“高速AD/DA实验”完全相同,此处不在赘述。本实验中,各端口信号的管脚分配如下表所示。
表 23.3.1 DDS实验管脚分配
在这里插入图片描述

对应的XDC约束语句如下所示:

#IO管脚约束
#时钟周期约束
create_clock -name sys_clk_p -period 10.000 [get_ports sys_clk_p]
#时钟
set_property IOSTANDARD DIFF_HSTL_I_12 [get_ports sys_clk_p]
set_property IOSTANDARD DIFF_HSTL_I_12 [get_ports sys_clk_n]
set_property PACKAGE_PIN AE5 [get_ports sys_clk_p]
set_property PACKAGE_PIN AF5 [get_ports sys_clk_n]
#复位
set_property -dict {PACKAGE_PIN AH11 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
#ad_da管脚约束
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN A14} [get_ports {da_data[7]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN A15} [get_ports {da_data[6]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN B14} [get_ports {da_data[5]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN B15} [get_ports {da_data[4]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN C13} [get_ports {da_data[3]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN A13} [get_ports {da_data[2]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN C14} [get_ports {da_data[1]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN B13} [get_ports {da_data[0]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN A12} [get_ports da_clk]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN C11} [get_ports {ad_data[7]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN B11} [get_ports {ad_data[6]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN B10} [get_ports {ad_data[5]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN A10} [get_ports {ad_data[4]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN E10} [get_ports {ad_data[3]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN E12} [get_ports {ad_data[2]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN D10} [get_ports {ad_data[1]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN D11} [get_ports {ad_data[0]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN A11} [get_ports ad_clk]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN C12} [get_ports ad_otr]
#按键
set_property -dict {PACKAGE_PIN AD11 IOSTANDARD LVCMOS33} [get_ports key0]
set_property -dict {PACKAGE_PIN AD10 IOSTANDARD LVCMOS33} [get_ports key1]

23.4软件设计
根据本章的实验任务,ZYNQ PL端需要连续输出正弦波、方波、三角波、锯齿波波形的数据,才能使AD9708连续输出相应波形的模拟电压,如果使用三角函数公式运算的方式来编写代码,然后输出正弦波、方波、三角波、锯齿波的波形数据,那么程序设计会变得非常复杂。在工程应用中,一般将波形数据存储在RAM或者ROM中,由于本次实验并不需要写数据到RAM中,因此我们将波形数据存储在只读的ROM中,直接读取ROM中的数据发送给DA转换芯片即可。
图 23.4.1是根据本章实验任务画出的系统框图。ROM里面事先存储好了波形的数据,DA数据发送模块从ROM中读取数据,将数据和时钟送到AD9708的输入数据端口和输入时钟端口;AD数据接收模块给AD9280输出驱动时钟信号和使能信号,并采集AD9280输出模数转换完成的数据。
DDS实验的系统框图如下图所示:
在这里插入图片描述

图 23.4.1 DDS系统框图
顶层模块的原理图如下图所示:
在这里插入图片描述

图 23.4.2 顶层模块原理图
FPGA顶层模块(dds)例化了以下五个模块:两个按键消抖模块(key_debounce)、DA数据发送模块(da_wave_send)、ROM波形存储模块(rom_400x8b)和AD数据接收模块(ad_wave_rec)。
按键消抖模块(key_debounce):对按键信号延时采样,将消抖后的按键信号和按键数据有效信号输出至da_wave_send模块。
DA数据发送模块(da_wave_send):DA数据发送模块输出读ROM地址,将输入的ROM数据发送至DA转换芯片的数据端口。
ROM波形存储模块(rom_400x8b):ROM波形存储模块由Vivado软件自带的Block Memory Generator IP核实现,其存储的波形数据可以使用matlab生成的.coe文件。
AD数据接收模块(ad_wave_rec):AD数据接收模块输出AD转换芯片的驱动时钟和使能信号,随后接收AD转换完成的数据。
顶层模块里的按键消抖模块可以参考按键控制蜂鸣器实验,AD数据接收模块可以参考高速AD/DA实验,这里我们重点讲解其余的几个模块。
顶层模块的代码如下:

1  module dds(
2      input                 sys_clk_p ,//系统差分输入时钟
3      input                 sys_clk_n ,//系统差分输入时钟
4      input                 sys_rst_n ,//系统复位,低电平有效
5      input                 key0      ,//按键key0
6      input                 key1      ,//按键key1
7      //DA芯片接口
8      output                da_clk    ,//DA(AD9708)驱动时钟,最大支持125Mhz时钟
9      output    [7:0]       da_data   ,//输出给DA的数据
10     //AD芯片接口
11     input     [7:0]       ad_data   ,//AD输入数据
12     //模拟输入电压超出量程标志(本次试验未用到)
13     input                 ad_otr    ,//0:在量程范围 1:超出量程
14     output                ad_clk     //AD(AD9280)驱动时钟,最大支持32Mhz时钟 
15 );
16 
17 //wire define 
18 wire      [8:0]    rd_addr;            //ROM读地址
19 wire      [7:0]    rd_data;            //ROM读出的数据
20 wire               key0_value;         //key0消抖后的按键值
21 wire               key0_flag;          //key0消抖后的按键值的效标志
22 wire               key1_value;         //key1消抖后的按键值
23 wire               key1_flag;          //key1消抖后的按键值的效标志
24 
25 //*****************************************************
26 //**                    main code
27 //*****************************************************
28 
29 //转换差分信号
30 IBUFDS diff_clock
31 (
32     .I (sys_clk_p),    //系统差分输入时钟
33     .IB(sys_clk_n),    //系统差分输入时钟
34     .O (sys_clk  )     //输出系统时钟
35 ); 
36 
37 //例化按键消抖模块
38 key_debounce  u_key0_debounce(
39     .sys_rst_n  (sys_rst_n ),
40     .clk        (sys_clk   ),
41     .key        (key0      ),
42     .key_value  (key0_value),
43     .key_flag   (key0_flag )    
44     );
45     
46 //例化按键消抖模块
47 key_debounce  u_key1_debounce(
48     .sys_rst_n  (sys_rst_n ),
49     .clk        (sys_clk   ),
50     .key        (key1      ),
51     .key_value  (key1_value),
52     .key_flag   (key1_flag )     
53     );  
54 
55 //DA数据发送
56 da_wave_send u_da_wave_send(
57     .clk         (sys_clk   ),
58     .rst_n       (sys_rst_n ),
59     .key0_value  (key0_value),
60     .key0_flag   (key0_flag ),
61     .key1_value  (key1_value),
62     .key1_flag   (key1_flag ),
63     .rd_data     (rd_data   ),
64     .rd_addr     (rd_addr   ),
65     .da_clk      (da_clk    ),  
66     .da_data     (da_data   )
67     );
68         
69 //ROM存储波形
70 rom_400x8b u_rom_400x8b (
71   .clka     (sys_clk),      // input wire clka
72   .addra    (rd_addr),      // input wire [8 : 0] addra
73   .douta    (rd_data)       // output wire [7 : 0] douta
74 );
75 
76 //AD数据接收
77 ad_wave_rec u_ad_wave_rec(
78     .clk         (sys_clk),
79     .rst_n       (sys_rst_n),
80     .ad_data     (ad_data),
81     .ad_otr      (ad_otr),
82     .ad_clk      (ad_clk)
83     );    
84 
85 //ILA采集AD数据
86 ila_0  ila_0 (
87     .clk         (ad_clk ), // input wire clk
88     .probe0      (ad_otr ), // input wire [0:0]  probe0  
89     .probe1      (ad_data)  // input wire [7:0]  probe0 
90 );
91 
92 endmodule

DA数据发送模块输出的读ROM地址(rd_addr)连接至ROM模块的地址输入端,ROM模块输出的数据(rd_data)连接至DA数据发送模块的数据输入端,从而完成了从ROM中读取数据的功能。
在顶层模块代码的第70至74行例化了ROM模块,由Block Memory Generator IP核配置生成。
在顶层模块代码的第86行例化了一个ILA的IP核,用于捕获ad_otr和ad_data的数据。需要注意的是,ILA的采样时钟必须使用ad_clk,否则数据可能采集错误。ILA IP核的配置如下图所示:
在这里插入图片描述

图 23.4.3 ILA IP核的Genaral Options配置
我们把探针数量设置为2,并且把采样深度设置为4096。探针宽度的设置如下图所示:
在这里插入图片描述

图 23.4.4 ILA IP核的Probe_Ports配置
我们将两个探针的位宽设置成1和8,分别对应ad_otr和ad_data的位宽,设置完成后点击“OK”按钮即可。
我们在前面说过,ROM中存储的波形数据可以使用MatLab软件生成,使用MatLab绘制4种信号波形,对波形进行等间隔采样,以采样次数作为ROM存储地址,将采集的波形幅值数据做为存储数据写入存储地址对应的存储空间。在本次实验中我们对4种信号波形进行分别采样,采样次数为100次,采集的波形幅值数据位宽为8bit,将采集数据保存为coe文件。
各波形参考代码如下。
正弦信号波形采集参考代码(sin_wave.m):

1  F1=1;                %信号频率
2  Fs=10^2;             %采样频率
3  P1=0;                %信号初始相位
4  N=10^2;              %采样点数
5  t=[0:1/Fs:(N-1)/Fs]; %采样时刻
6  ADC=2^7 - 1;         %直流分量
7  A=2^7;               %信号幅度
8  %生成正弦信号
9  s=A*sin(2*pi*F1*t + pi*P1/180) + ADC;
10 plot(s);             %绘制图形
11 %创建 coe 文件
12 fild = fopen('sin_wave_100x8.coe','wt');
13 %写入 coe 文件头
14 %固定写法,表示写入的数据是10进制表示
15 fprintf(fild, '%s\n','memory_initialization_radix=10;'); 
16 %固定写法,下面开始写入数据
17 fprintf(fild, '%s\n\n','memory_initialization_vector =');  
18 for i = 1:N
19 s2(i) = round(s(i));          %对小数四舍五入以取整
20 if s2(i) <0                   %1 强制置零
21 s2(i) = 0
22 end
23 fprintf(fild, '%d',s2(i));    %数据写入
24 if i==N
25    fprintf(fild, '%s\n',';'); %最后一个数据用;
26 else
27    fprintf(fild,',\n');       % 其他数据用,
28 end
29 end
30 fclose(fild);                 % 写完了,关闭文件

图 23.4.5 MatLab 生成的正弦信号波形
方波信号波形采集参考代码(square_wave.m):

1  F1=1;                %信号频率
2  Fs=10^2;             %采样频率
3  P1=0;                %信号初始相位
4  N=10^2;              %采样点数
5  t=[0:1/Fs:(N-1)/Fs]; %采样时刻
6  ADC=2^7 - 1;         %直流分量
7  A=2^7;               %信号幅度
8  %生成方波信号
9  s=A*square(2*pi*F1*t + pi*P1/180) + ADC;
10 plot(s);             %绘制图形
11 %创建 coe文件
12 fild = fopen('squ_wave_100x8.coe','wt');
13 %写入 coe文件头
14 %固定写法,表示写入的数据是10进制表示
15 fprintf(fild, '%s\n','memory_initialization_radix=10;'); 
16 %固定写法,下面开始写入数据  
17 fprintf(fild, '%s\n\n','memory_initialization_vector ='); 
18 for i = 1:N
19 s2(i) = round(s(i));          %对小数四舍五入以取整
20 if s2(i) <0                   %1 强制置零
21 s2(i) = 0
22 end
23 fprintf(fild, '%d',s2(i));    %数据写入
24 if i==N
25    fprintf(fild, '%s\n',';'); %最后一个数据用分号 
26 else
27    fprintf(fild,',\n');        % 其他数据用 ,
28 end
29 end
30 fclose(fild);                  % 写完了,关闭文件

图 23.4.6 MatLab 生成的方波信号波形
三角波信号波形采集参考代码(triangle_wave.m):

1  F1=1;                %信号频率
2  Fs=10^2;             %采样频率
3  P1=0;                %信号初始相位
4  N=10^2;              %采样点数
5  t=[0:1/Fs:(N-1)/Fs]; %采样时刻
6  ADC=2^7 - 1;         %直流分量
7  A=2^7;               %信号幅度
8  %生成三角波信号
9  s=A*sawtooth(2*pi*F1*t + pi*P1/180,0.5) + ADC;
10 plot(s);             %绘制图形
11 %创建 coe 文件
12 fild = fopen('tri_wave_100x8.coe','wt');
13 %写入coe文件头
14 %固定写法,表示写入的数据是10进制表示
15 fprintf(fild, '%s\n','memory_initialization_radix=10;');  
16 %固定写法,下面开始写入数据 
17 fprintf(fild, '%s\n\n','memory_initialization_vector ='); 
18 for i = 1:N
19 s2(i) = round(s(i));          %对小数四舍五入以取整
20 if s2(i) <0                   %1 强制置零
21 s2(i) = 0
22 end
23 fprintf(fild, '%d',s2(i));    %数据写入
24 if i==N
25    fprintf(fild, '%s\n',';'); %最后一个数据用分号 
26 else
27    fprintf(fild,',\n');       %其他数据用 ,
28 end
29 end
30 fclose(fild);                 % 写完了,关闭文件

在这里插入图片描述

图 23.4.7 MatLab 生成的三角波信号波形
锯齿波信号波形采集参考代码(sawtooth_wave.m):

1  F1=1;                %信号频率
2  Fs=10^2;             %采样频率
3  P1=0;                %信号初始相位
4  N=10^2;              %采样点数
5  t=[0:1/Fs:(N-1)/Fs]; %采样时刻
6  ADC=2^7 - 1;         %直流分量
7  A=2^7;               %信号幅度
8  %生成锯齿波信号
9  s=A*sawtooth(2*pi*F1*t + pi*P1/180) + ADC;
10 plot(s);             %绘制图形
11 %创建 coe 文件
12 fild = fopen('saw_wave_100x8.coe','wt');
13 %写入 coe 文件头
14 %固定写法,下面开始写入数据
15 fprintf(fild, '%s\n','memory_initialization_radix=10;');  
16 %固定写法,下面开始写入数据
17 fprintf(fild, '%s\n\n','memory_initialization_vector ='); 
18 for i = 1:N
19 s2(i) = round(s(i));          %对小数四舍五入以取整
20 if s2(i) <0                   %1 强制置零
21 s2(i) = 0
22 end
23 fprintf(fild, '%d',s2(i));    %数据写入
24 if i==N
25    fprintf(fild, '%s\n',';'); %最后一个数据用分号 
26 else
27    fprintf(fild,',\n');       % 其他数据用 ,
28 end
29 end
30 fclose(fild);                 % 写完了,关闭文件

在这里插入图片描述

图 23.4.8 MatLab 生成的锯齿波信号波形
使用MatLab对4种波形进行采样后,生成4个coe文件,分别对应4种波形,我们通过调用一个深度为100*4,位宽为8bit的ROM,将四个coe文件整合为一个coe文件。在配置rom的ip核时将整合的coe文件导入到rom中。
使用Notepad++代码编辑器打开生成的COE文件后如下图所示:
在这里插入图片描述

图 23.4.9 COE文件打开界面
工程中创建了一个单端口ROM,并命名为“rom_400x8b”,在调用Block Memory Generator IP核时,“Basic”选项也配置如下图所示:
在这里插入图片描述

图 23.4.10 Block Memory Generator IP核的Basic配置页面
我们将其接口类型设置为“Native”、Memory Type设置为“Single Port ROM”,即单端口ROM。
“Port A Options”选项页的配置页面如下图所示:

在这里插入图片描述

图 23.4.11 Block Memory Generator IP核的PortA Options配置页面
我们将PortA的位宽设置为8,深度设置为400,以存储matlab生成的400个数据。此外,将使能引脚的类型设置为“Always Enabled”,即ROM一直处于使能的状态。
接下来配置“Other Options”选项页,加载刚才生成的.coe文件,如下图所示:
在这里插入图片描述

图 23.4.12 Block Memory Generator IP核的Other Options配置页面
最后点击“OK”按钮完成IP核的配置。
DA数据发送模块的代码如下:

1   module da_wave_send(
2       input                 rst_n     ,   //复位信号,低电平有效
3       input                 clk       ,   //连接到clk_100M
4       input                 key0_value,   //消抖后的按键值
5       input                 key0_flag ,   //消抖后的按键值的有效标志
6       input                 key1_value,   //消抖后的按键值
7       input                 key1_flag ,   //消抖后的按键值的有效标志
8                                         
9       input        [7:0]    rd_data   ,   //ROM读出的数据
10      output  reg  [8:0]    rd_addr   ,   //读ROM地址
11      //DA芯片接口                        
12      output                da_clk    ,   //DA(AD9708)驱动时钟,最大支持125Mhz时钟
13      output       [7:0]    da_data       //输出给DA的数据  
14      );
15  
16  //parameter
17  //波形调节控制
18  parameter  sine_wave_addr     = 9'd0;   // 正弦波起始位置 
19  parameter  square_wave_addr   = 9'd100; // 方波起始位置  
20  parameter  triangle_wave_addr = 9'd200; // 三角波起始位置
21  parameter  sawtooth_wave_addr = 9'd300; // 锯齿波起始位置 
22  
23  //频率调节控制,FREQ_ADJ的越大,最终输出的频率越低,范围0~255
24  parameter  FREQ_ADJ0 = 8'd0;            //参数0对应输出1Mhz波形频率
25  parameter  FREQ_ADJ1 = 8'd1;            //参数1对应输出500khz波形频率
26  parameter  FREQ_ADJ2 = 8'd3;            //参数3对应输出250khz波形频率
27  parameter  FREQ_ADJ3 = 8'd7;            //参数7对应输出125khz波形频率
28                                          
29  //reg define                            
30  reg    [7:0]     freq_adj    ;          //频率调节参数寄存器
31  reg    [7:0]     freq_cnt    ;          //频率调节计数器
32  reg    [1:0]     wave_select ;          //切换波形地址寄存器
33  reg    [1:0]     freq_select ;          //切换波形频率寄存器
34  
35  //*****************************************************
36  //**                    main code
37  //*****************************************************
38  
39  //数据rd_data是在系统时钟的上升沿更新的,
40  //所以DA芯片在系统时钟的下降沿锁存数据是稳定的时刻。
41  //而DA实际上在da_clk的上升沿锁存数据,所以时钟取反,
42  //这样系统时钟的下降沿相当于da_clk的上升沿。           
43  assign  da_clk =~clk;       
44  assign  da_data = rd_data;  //将读到的ROM数据赋值给DA数据端口
45     
46  //切换波形种类
47  always @(posedge clk  or negedge rst_n) begin
48      if(rst_n == 1'b0)
49          wave_select <= 2'd0;
50      else if((key0_flag == 1) && (key0_value == 0)) begin //确保按键key0确实被有效按下
51             if(wave_select < 2'd3)
52                 wave_select <= wave_select+1'd1;
53             else  
54                 wave_select <= 0;
55            end
56           else 
57               wave_select <= wave_select;
58  end
59  
60  //切换波形频率
61  always @(posedge clk or negedge rst_n) begin
62      if(rst_n == 1'b0)
63          freq_select <= 2'd0;
64      else if((key1_flag ==1) && (key1_value ==0)) begin //确保按键key1确实被有效按下
65             if(freq_select < 2'd3)
66                freq_select <= freq_select+1'd1;
67             else  
68                 freq_select <= 0;
69            end
70           else 
71               freq_select <= freq_select;
72  end
73  
74  always @(posedge clk or negedge rst_n) begin
75      if(rst_n == 1'b0)
76        freq_adj <= 8'd0;
77      else case(freq_select)
78               2'd0:freq_adj <= FREQ_ADJ0;
79               2'd1:freq_adj <= FREQ_ADJ1;   
80               2'd2:freq_adj <= FREQ_ADJ2;
81               2'd3:freq_adj <= FREQ_ADJ3;
82              default:freq_adj <= FREQ_ADJ0;
83           endcase
84  end
85  
86  //频率调节计数器
87  always @(posedge clk or negedge rst_n) begin
88      if(rst_n == 1'b0)
89          freq_cnt <= 8'd0;
90      else if(freq_cnt == freq_adj)    
91          freq_cnt <= 8'd0;
92      else         
93          freq_cnt <= freq_cnt + 8'd1;
94  end
95  
96  //读ROM地址,按照100M的频率去读
97  always @(posedge clk or negedge rst_n) begin
98      if(rst_n == 1'b0)
99         rd_addr <= 9'd0;
100     else if(freq_cnt == freq_adj) begin
101         case(wave_select)
102             2'd0:
103                 if(rd_addr >= sine_wave_addr && rd_addr <= sine_wave_addr+9'd99)    
104                   if(rd_addr == sine_wave_addr+9'd99)  
105                      rd_addr <= sine_wave_addr;
106                   else 
107                       rd_addr <= rd_addr+9'd1; 
108                 else 
109                     rd_addr <= sine_wave_addr;                          
110             2'd1:
111                 if(rd_addr >=  square_wave_addr && rd_addr <= square_wave_addr+9'd99)   
112                   if(rd_addr == square_wave_addr+9'd99) 
113                       rd_addr <= square_wave_addr;
114                   else  
115                       rd_addr <= rd_addr+9'd1;
116                 else 
117                     rd_addr <= square_wave_addr; 
118             2'd2:
119                 if(rd_addr >= triangle_wave_addr && rd_addr <= triangle_wave_addr+9'd99) 
120                   if(rd_addr == triangle_wave_addr+9'd99) 
121                       rd_addr <= triangle_wave_addr; 
122                   else 
123                       rd_addr <= rd_addr+9'd1;  
124                 else  
125                     rd_addr <= triangle_wave_addr;                    
126             2'd3:
127                 if(rd_addr >= sawtooth_wave_addr && rd_addr <= sawtooth_wave_addr+9'd99)   
128                   if(rd_addr == sawtooth_wave_addr+9'd99)
129                       rd_addr <= sawtooth_wave_addr;
130                   else 
131                       rd_addr <= rd_addr+9'd1;    
132                 else  
133                     rd_addr <= sawtooth_wave_addr;   
134             default:
135                 if(rd_addr >= sine_wave_addr && rd_addr <= sine_wave_addr+9'd99)    
136                    if(rd_addr == sine_wave_addr+9'd99)  
137                        rd_addr <= sine_wave_addr;
138                    else 
139                        rd_addr <= rd_addr+9'd1; 
140                 else 
141                     rd_addr <= sine_wave_addr;            
142         endcase
143     end
144          else  rd_addr <= rd_addr;             
145 end
146 endmodule

在代码的第30行定义了一个参数寄存器freq_adj(频率调节),可以通过控制频率调节参数的大小来控制最终输出波形的频率大小,频率调节参数的值越小,波形频率越大。频率调节参数调节波形频率的方法是通过控制读ROM的速度实现的,频率调节参数越小,freq_cnt计数到频率调节参数值的时间越短,读ROM数据的速度越快,那么正弦波输出频率也就越高;反过来,频率调节参数越大,freq_cnt计数到频率调节参数值的时间越长,读ROM数据的速度越慢,那么正弦波输出频率也就越低。由于freq_cnt计数器的位宽为8位,计数范围是0255,所以频率调节参数freq_adj支持的调节范围是0255,可通过修改freq_cnt计数器的位宽来修改freq_adj支持的调节范围。
通过matlab生成的coe文件的数据深度为100,而输入的时钟100 Mhz作为da芯片的驱动时钟,那么一个完整的波形周期的长度为10010ns=1000ns,当freq_adj的值为0时,即波形的的最快输出频率为1s/1000ns(1s = 1000000000ns)=1Mhz,当我们把freq_adj的值设置为1时,一个完整的波形周期长度为1000ns(1+1) = 2000ns,频率为500Khz。当freq_adj的值设为3或7时,输出波形频率也相应的变为250khz和125khz。
23.5下载验证
将高速AD-DA模块插入DFZU2EG/4EV MPSoC开发板的J19扩展口,连接时注意扩展口电源引脚方向和开发板电源引脚方向一致,然后将下载器一端连接电脑,另一端与开发板上对应端口连接,最后连接电源线后拨动开关按键给开发板上电。
DFZU2EG/4EV MPSoC开发板硬件连接实物图如图 23.5.1所示:
在这里插入图片描述

图 23.5.1 DFZU2EG/4EV MPSoC开发板硬件连接实物图
将工程生成的比特流文件下载到开发板中后,然后使用示波器测量DA输出通道的波形。首先将示波器带夹子的一端连接到开发板的GND位置(可使用杜邦线连接至开发板上的任一的GND管脚),然后将另一端探针插入高速AD-DA模块的DA通道中间的金属圆圈内(注意将红色的保护套拿掉),如图 23.5.2
所示。或者也可以直接测试高速AD-DA模块的TP引脚,如图 23.5.3所示。
在这里插入图片描述

图 23.5.2 DA模拟电压测量孔位

在这里插入图片描述

图 23.5.3 DA模拟电压测量点(TP)
此时观察示波器可以看到模拟波的波形,如果观察不到波形,可查看示波器设置是否正确,可以尝试按下示波器的“AUTO”,再次观察示波器波形。示波器的显示界面如下所示:
通过按下按键PL_KEY1可实现波形的的切换:
在这里插入图片描述

图 23.5.4 正弦波测量图
在这里插入图片描述

图 23.5.5 方波测量图
在这里插入图片描述

图 23.5.6 三角波测量图
在这里插入图片描述

图 23.5.7 锯齿波测量图
通过按下按键PL_KEY2可实现频率的切换:
在这里插入图片描述

图 23.5.8 频率切换图(一)
在这里插入图片描述

图 23.5.9 频率切换图(二)
在示波器上观察到正确的波形后,说明DDS的功能已经实现了。关于生成的模拟波形除了可以在示波器上观察,也可以通过Ila去进行观察,在ILA中观察ad_data数据的具体的做法可以参照高速AD/DA实验里的步骤。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/47874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web网页设计期末课程大作业__公司官网 (曼谷科技 1页)带psd文件

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 家公司官网网站 | 企业官网 | 酒店官网 | 等网站的设计与制 | HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&#xff1a;结构 CSS&…

基于CCE Kubernetes网络与持久化存储实战

✅作者简介&#xff1a; CSDN内容合伙人&#xff0c;全栈领域新星创作者&#xff0c;阿里云专家博主&#xff0c;阿里云问答板块版主&#xff0c;华为云享专家博主&#xff0c;掘金后端评审团成员 &#x1f495;前言&#xff1a; 最近云原生领域热火朝天&#xff0c;那么云原生…

2022年NPDP新版教材知识集锦--【第四章节】(4)

【初始设计与规格阶段】(全部获取文末) 产品设计规格旨在&#xff1a; 明确产品设计并提供量化和客观性; 将产品设计要求传达给设计团队的其他成员; 推进产品从设计到制造的开发。 1、功能性设计(DesignforFunctionality,DFF) 功能设计决定了产品的最终性能&#xff0c;功…

小猴吃苹果-第12届蓝桥杯Scratch选拔赛真题精选

[导读]&#xff1a;超平老师计划推出Scratch蓝桥杯真题解析100讲&#xff0c;这是超平老师解读Scratch蓝桥真题系列的第90讲。 蓝桥杯选拔赛每一届都要举行4~5次&#xff0c;和省赛、国赛相比&#xff0c;题目要简单不少&#xff0c;再加上篇幅有限&#xff0c;因此我精挑细选…

JCTC:基于PWmat中的混合溶剂模型精确计算离子溶解自由能

溶液环境中溶质离子或中间体的自由能计算是电化学研究中最棘手的问题之一。目前单纯的实验手段并不能对发生在溶液中的化学反应过程/机理进行直接探测&#xff0c;许多信息仍主要依赖于理论模拟。对于这一问题&#xff0c;目前很多研究者采用经验势场的溶剂模型方法&#xff0c…

Casein-PEG-Rhodamine B 络蛋白-聚乙二醇-罗丹明B Casein-RB

产品名称&#xff1a;络蛋白-聚乙二醇-罗丹明B 英文名称&#xff1a;Casein-PEG-Rhodamine B 质量控制&#xff1a;95% 原料分散系数PDI&#xff1a;≤1.05 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途&#xff1a;仅供科研实验使用&#xff0c;不用于诊…

SAP 电商云 Spartacus UI Configurable Product 的页面设置

关键字 CPQ&#xff0c;Product Configuration&#xff0c;Product Configure&#xff0c;Product Variant 变体是在某些方面彼此不同但基于相同基本模型的产品。 变体的一个示例是 T 恤的颜色和尺寸。 在 Spartacus 中启用变体功能&#xff0c;并在 SAP Commerce Cloud 中配…

基于python的pulp库使用,从基础模型到复杂模型,从一维变量到二维变量

写在前面 学习笔记&#xff0c;仅作参考。 个人觉得配合步骤和建模&#xff0c;直接看代码就能入门pulp&#xff0c;所以没有啥解释&#xff0c;见谅。 参考 https://blog.csdn.net/youcans/article/details/116371416 步骤 1、安装PuLp &#xff08;pip install pulp) 2…

HRD特征及其检测方法简介

HRD特征及其检测方法简介1、HRD背景知识介绍1.1 HRR通路简介1.2 HRR基因突变可导致通路失活和HRD1.3 HRD高发癌种2、HRD的两类主要标志物2.1 致病基因2.2 基因组瘢痕3、HRD检测方法4、全景变异分析&#xff08;CGP&#xff09;5、关键信息6、参考文件1、HRD背景知识介绍 1.1 H…

(4)点云数据处理学习——其它官网例子

1、主要参考 &#xff08;1&#xff09;视频&#xff0c;大佬讲的就是好啊 【Open3D】三维点云python教程_哔哩哔哩_bilibili &#xff08;2&#xff09;官方的github地址 GitHub - isl-org/Open3D: Open3D: A Modern Library for 3D Data Processing &#xff08;3&#…

BUUCTF Misc 被偷走的文件 snake

被偷走的文件 下载文件 wireshark打开&#xff0c;搜索flag字符串 可以看到一个带有flag.rar的FTP包进行TCP流追踪 看来流量中有flag.rar&#xff0c;使用kali中的foremost进行文件分离 发现一个文件夹内有一个需要密码的压缩包 密码是5790&#xff0c;解压 得到flag …

Redis数据结构和类型

Redis 包含五种数据类型&#xff0c;分别为String、List、Hash、Set、ZSet 底层实现的数据结构包SDS、双向链表、压缩列表、哈希表、整数集合、跳表 redis结构图数据类型和数据结构的关系Redis六种数据结构 一、动态字符串(SDS) Redis 是用 C 语言实现的&#xff0c;但是它…

Kotlin高仿微信-第12篇-单聊-图片

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

STC 51单片机45——51单片机对脉冲计数 汇编 16位除法

部分代码&#xff1a; ORG 0000H LJMP INIT ORG 0003H //外部中断0 LJMP INT0SUB ORG 0013H //外部中断1 LJMP INT1SUB ORG 0100H INIT: CLR P1.0 //控制端复位 …

D-019 EEROM硬件电路设计

EEROM硬件电路设计1 简介1.1 存储器的分类1.2EEPROM的特性2 接口介绍2.1 IIC接口2.2 SPI接口2.3 MicroWire 接口3 EEPROM 和 FLASH4 电路设计实战5 电路设计要点1 简介 1.1 存储器的分类 按照掉电数据是否丢失的特性&#xff0c;存储器可划分为&#xff1a; 易失性存储器&…

快排图文详解:快速排序算法的实现 - 【双边循环法与单边循环法 递归与非递归(栈的方式)的实现】

1.基本介绍 同冒泡排序一样&#xff0c;快速排序&#xff08;Quicksort&#xff09;也属于交换排序&#xff0c;通过元素之间的比较和交换位置来达到排序的目的。但快速排序是对冒泡排序的一种改进。 2.基本思想 关于基本思想&#xff0c;我们在这里先不考虑是如何具体实现的…

nuxtjs生命周期、项目创建、声明式导航与编程式导航、动态路由、嵌套路由、配置式路由、定制默认应用模板、扩展默认布局

文章目录1. 介绍2. 生命周期3. 项目创建4. 声明式导航和编程式导航5. 动态路由参数和验证6. 嵌套路由7. 404页面8. 配置式路由9. 定制默认应用模板10. 扩展默认布局10.1 默认布局10.2 自定义布局10.3 显示错误的布局1. 介绍 Nuxt.js 是一个基于 Vue.js 的通用应用框架。通过对…

离线解锁 CodeCombat 全关卡教程 使用docker安装实现

背景 暂时还没收入&#xff0c;想玩顺便&#xff0c;但官方的有点贵&#xff08;是真的贵&#xff0c;扛不住&#xff09; 前期准备 下载安装docker desktop https://www.123pan.com/s/fmvUVv-HqApH&#xff0c; 这个安装不会的随便搜一个教程&#xff0c;挺多的。我随便找了一…

创建实例化新表格及新行

这期讲一下如何创建创建实例化新表格及行进行添加数据&#xff0c; 在上图可以看到先实例化DataTable表格&#xff0c;用于接收数据&#xff0c; Columns 获取此表的列的集合&#xff0c; Add 创建并添加DataColumn对象&#xff0c; Typeof 数据类型 创建并添加表头。 接下来…

Java定时器选择

java计时器和死循环哪个好&#xff1f;哪个建议使用&#xff1f; 计时器性能更好&#xff0c;但是写起来稍微复杂一点。如果是非常短暂的延迟&#xff0c;用死循环也未尝不可。一般来说能不用死循环的尽量不用死循环&#xff01;如果你使用的是JDK1.5以上的&#xff0c;可以使…