0
点赞
收藏
分享

微信扫一扫

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章 DDS信号发生器实验

DDS信号发生器实验​

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

本章包括以下几个部分:

  1. 简介
  2. 实验任务
  3. 硬件设计
  4. 软件设计
  5. 下载验证



简介

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

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分


23.1.1基本结构图

由上图可以看出,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位二进制数值固化在波形存储器当中。按照地址的不同可以输出相应相位的正弦信号幅值。相位—幅度变换原理图如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_数据_02


23.1.2 相位-幅度变换原理图

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

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_03



23.1.3信号流程图

实验任务

本节实验任务是使用DFZU2EG/4EV MPSoC开发板及高速AD-DA扩展模块(ATK_HS_AD_DA模块)实现数模及模数的转换。首先ZYNQ PL端产生正弦波、方波、三角波、锯齿波变化的数字信号,经过DA芯片后转换成模拟信号,将DA的模拟电压输出端连接至AD模拟电压输入端,AD芯片模拟信号转换成数字信号,通过按下按键PL_KEY1PL_KEY2可以实现波形种类和波形频率的切换,然后通过示波器观察DA端模拟信号波形是否出现了相应变化,也可以通过Ila界面去进行观察

硬件设计

本章节中硬件设计与“高速AD/DA实验”完全相同,此处不在赘述。本实验中,各端口信号的管脚分配如下表所示

23.3.1实验管脚分配

信号名

方向

管脚

端口说明

电平标准

sys_clk_p

input

AE5

系统差分输入时钟

DIFF_HSTL_I_12

sys_clk_N

input

AF5

系统差分输入时钟

DIFF_HSTL_I_12

sys_rst_n

input

AH11

系统复位低有效

LVCMOS33

da_clk

output

A12

DA(AD9708)驱动时钟

LVCMOS33

da_data[0]

output

B13

输出给DA的数据

LVCMOS33

da_data[1]

output

C14

输出给DA的数据

LVCMOS33

da_data[2]

output

A13

输出给DA的数据

LVCMOS33

da_data[3]

output

C13

输出给DA的数据

LVCMOS33

da_data[4]

output

B15

输出给DA的数据

LVCMOS33

da_data[5]

output

B14

输出给DA的数据

LVCMOS33

da_data[6]

output

A15

输出给DA的数据

LVCMOS33

da_data[7]

output

A14

输出给DA的数据

LVCMOS33

ad_data[0]

input

D11

AD输入数据

LVCMOS33

ad_data[1]

input

D10

AD输入数据

LVCMOS33

ad_data[2]

input

E12

AD输入数据

LVCMOS33

ad_data[3]

input

E10

AD输入数据

LVCMOS33

ad_data[4]

input

A10

AD输入数据

LVCMOS33

ad_data[5]

input

B10

AD输入数据

LVCMOS33

ad_data[6]

input

B11

AD输入数据

LVCMOS33

ad_data[7]

input

C11

AD输入数据

LVCMOS33

ad_otr

input

C12

模拟电压超出量程标志

LVCMOS33

ad_clk

output

A11

AD(AD9280)驱动时钟

LVCMOS33

key0

input

AD11

PL按键1

LVCMOS33

key1

input

AD10

PL按键2

LVCMOS33

对应的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]

软件设计

根据本章的实验任务,ZYNQ PL需要连续输出正弦波、方波、三角波、锯齿波波形的数据,才能使AD9708连续输出相应波形的模拟电压,如果使用三角函数公式运算的方式来编写代码,然后输出正弦波、方波、三角波、锯齿波的波形数据,那么程序设计会变得非常复杂。在工程应用中一般波形数据存储在RAM或者ROM中,由于本次实验并不需要数据到RAM中,因此我们将波形数据存储在只读ROM中,直接读取ROM中的数据发送DA转换芯片即可。

23.4.1根据本章实验任务画出的系统框图。ROM里面事先存储好了波形的数据,DA数据发送模块从ROM中读取数据,将数据时钟送到AD9708的输入数据端口输入时钟端口;AD数据接收模块给AD9280输出驱动时钟信号和使能信号,并采集AD9280输出模数转换完成的数据

DDS实验的系统框图如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_正弦波_04


23.4.1系统框图

顶层模块的原理图如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_正弦波_05


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_sendDA数据发送模块输出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中读取数据的功能。

在顶层模块代码的第7074行例化ROM模块,由Block Memory Generator IP核配置生成

在顶层模块代码的第86行例化了一个ILA的IP核,用于捕获ad_otr和ad_data的数据。需要注意的是,ILA的采样时钟必须使用ad_clk,否则数据可能采集错误。ILA IP核的配置如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_数据_06


23.4.3核的Genaral Options配置

我们把探针数量设置为2,并且把采样深度设置为4096。探针宽度的设置如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_数据_07


23.4.4核的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); % 写完了,关闭文件

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_正弦波_08


23.4.5生成的正弦信号波形

方波信号波形采集参考代码(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); % 写完了,关闭文件

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_正弦波_09


23.4.6生成的方波信号波形

三角波信号波形采集参考代码(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); % 写完了,关闭文件


《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_数据_10


23.4.7生成的三角波信号波形

锯齿波信号波形采集参考代码(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); % 写完了,关闭文件

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_11


23.4.8生成的锯齿波信号波形

使用MatLab对4种波形进行采样后,生成4个coe文件,分别对应4种波形,我们通过调用一个深度为100*4,位宽为8bit的ROM,将coe文件整合为一个coe文件。在配置rom的ip核时将整合的coe文件导入到rom中。

使用Notepad++代码编辑器打开生成COE文件后如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_12


23.4.9文件打开界面

工程中创建了一个单端口ROM,并命名为“rom_400x8b”,在调用Block Memory Generator IP核时,“Basic”选项也配置如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_数据_13


23.4.10核的Basic配置页面

我们将其接口类型设置为“Native”、Memory Type设置为“Single Port ROM”,即单端口ROM

“Port A Options”选项页的配置页面如下图所示:


《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_14


23.4.11核的PortA Options配置页面

我们将PortA的位宽设置为8,深度设置为400,以存储matlab生成的400个数据。此外,将使能引脚的类型设置为“Always Enabled”,即ROM一直处于使能的状态。

接下来配置“Other Options”选项页,加载刚才生成的.coe文件,如下图所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_15


23.4.12核的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位,计数范围是0~255所以频率调节参数freq_adj支持的调节范围是0~255可通过修改freq_cnt计数器的位宽修改freq_adj支持的调节范围

通过matlab生成的coe文件的数据深度为100,输入时钟100 Mhz作为da芯片的驱动时钟,那么一个完整的波形周期的长度为100*10ns=1000ns,freq_adj的值为0时,即波形的的最快输出频率为1s/1000ns(1s = 1000000000ns)=1Mhz,当我们freq_adj值设置为1一个完整的波形周期长度1000ns*(1+1) = 2000ns,频率为500Khz。freq_adj的值设为3或7时,输出波形频率也相应的变为250khz和125khz。

下载验证

将高速AD-DA模块插入DFZU2EG/4EV MPSoC开发板的J19扩展口连接时注意扩展口电源引脚方向和开发板电源引脚方向一致,然后将下载器一端连接电脑,另一端与开发板上对应端口连接,最后连接电源线后拨动开关按键给开发板上电。

DFZU2EG/4EV MPSoC开发板硬件连接实物图如23.5.1所示:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_正弦波_16


23.5.1 DFZU2EG/4EV MPSoC开发板硬件连接实物图

将工程生成的比特流文件下载到开发板中后,然后使用示波器测量DA输出通道的波形。首先将示波器带夹子的一端连接到开发板的GND位置使用杜邦线连接至开发板上的任一的GND管脚,然后另一端探针插入高速AD-DA模块DA通道中间的金属圆圈内(注意将红色的保护套拿掉23.5.2

所示或者也可以直接测试高速AD-DA模块的TP引脚,如23.5.3所示。

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_17


23.5.2模拟电压测量孔位


《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_数据_18


23.5.3模拟电压测量点(TP)

此时观察示波器可以看到模拟波的波形,如果观察不到波形,可查看示波器设置是否正确,可以尝试按下示波器的“AUTO”,再次观察示波器波形。示波器显示界面如下所示:

通过按下按键PL_KEY1可实现波形的的切换:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_19


23.5.4 正弦波测量图

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_20


23.5.5 方波测量图

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_正弦波_21


23.5.6 三角波测量图

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_正弦波_22


23.5.7 锯齿波测量图

通过按下按键PL_KEY2可实现频率的切换:

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_数据_23


23.5.8 频率切换图(一)

《DFZU2EG_4EV MPSoc之FPGA开发指南》 第二十三章  DDS信号发生器实验_差分_24


23.5.9 频率切换图(二)

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


举报

相关推荐

0 条评论