OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 604|回复: 0

[国产FPGA] 《ATK-DFPGL22G 之FPGA开发指南》第三十五章 DDS信号发生器实验

[复制链接]

1070

主题

1081

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4443
金钱
4443
注册时间
2019-5-8
在线时间
1199 小时
发表于 2023-12-14 17:46:03 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-12-14 17:45 编辑

第三十五章 DDS信号发生器实验
1)实验平台:正点原子 ATK-DFPGL22G开发板

2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-PGL22G.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)FPGA技术交流QQ群:435699340

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

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

1.1 DDS简介
DDS( DirectDigital Synthesizer)即数字合成器,是一种新型的频率合成技术,具有低成本、低功耗、高分辨率、频率转换时间短、相位连续性好等优点,对数字信号处理及其硬件实现有着很重要的作用。DDS的基本结构主要由相位累加器、相位调制器、波形数据表ROM、D/A转换器等四大结构组成,其中较多设计还会在数模转换器之后增加一个低通滤波器(LPF)。DDS基本结构图如图 35.1.1所示。                              
image001.png
图 35.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位二进制数值固化在波形存储器当中。按照地址的不同可以输出相应相位的正弦信号幅值。相位—幅度变换原理图如下图所示:
image003.png
图 35.1.2 相位-幅度变换原理图
数模转换器(D/A)的作用是把合成的正弦波数字量转化为模拟量。正弦幅度量化序列经数模转换器转换后变成了包络为正弦波的阶梯波。频率合成器对数模转换器的分辨率有一定的要求,其分辨率越高,合成的正弦波台阶数就越多,输出的波形精度也就越高。DDS信号流程图如下图所示:
image004.png
图 35.1.3 DDS信号流程图

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

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

对应的fdc约束语句如下所示:
  1. define_attribute{p:da_data[7]} {PAP_IO_DIRECTION} {OUTPUT}
  2. define_attribute{p:da_data[7]} {PAP_IO_LOC} {J17}
  3. define_attribute{p:da_data[7]} {PAP_IO_VCCIO} {3.3}
  4. define_attribute{p:da_data[7]} {PAP_IO_STANDARD} { LVCMOS33}
  5. define_attribute{p:da_data[7]} {PAP_IO_DRIVE} {2}
  6. define_attribute{p:da_data[7]} {PAP_IO_NONE} {TRUE}
  7. define_attribute{p:da_data[7]} {PAP_IO_SLEW} {SLOW}
  8. define_attribute{p:da_data[6]} {PAP_IO_DIRECTION} {OUTPUT}
  9. define_attribute{p:da_data[6]} {PAP_IO_LOC} {H17}
  10. define_attribute{p:da_data[6]} {PAP_IO_VCCIO} {3.3}
  11. define_attribute{p:da_data[6]} {PAP_IO_STANDARD} { LVCMOS33}
  12. define_attribute{p:da_data[6]} {PAP_IO_DRIVE} {2}
  13. define_attribute{p:da_data[6]} {PAP_IO_NONE} {TRUE}
  14. define_attribute{p:da_data[6]} {PAP_IO_SLEW} {SLOW}
  15. define_attribute{p:da_data[5]} {PAP_IO_DIRECTION} {OUTPUT}
  16. define_attribute{p:da_data[5]} {PAP_IO_LOC} {J18}
  17. define_attribute{p:da_data[5]} {PAP_IO_VCCIO} {3.3}
  18. define_attribute{p:da_data[5]} {PAP_IO_STANDARD} { LVCMOS33}
  19. define_attribute{p:da_data[5]} {PAP_IO_DRIVE} {2}
  20. define_attribute{p:da_data[5]} {PAP_IO_NONE} {TRUE}
  21. define_attribute{p:da_data[5]} {PAP_IO_SLEW} {SLOW}
  22. define_attribute{p:da_data[4]} {PAP_IO_DIRECTION} {OUTPUT}
  23. define_attribute{p:da_data[4]} {PAP_IO_LOC} {H18}
  24. define_attribute{p:da_data[4]} {PAP_IO_VCCIO} {3.3}
  25. define_attribute{p:da_data[4]} {PAP_IO_STANDARD} { LVCMOS33}
  26. define_attribute{p:da_data[4]} {PAP_IO_DRIVE} {2}
  27. define_attribute{p:da_data[4]} {PAP_IO_NONE} {TRUE}
  28. define_attribute{p:da_data[4]} {PAP_IO_SLEW} {SLOW}
  29. define_attribute{p:da_data[3]} {PAP_IO_DIRECTION} {OUTPUT}
  30. define_attribute{p:da_data[3]} {PAP_IO_LOC} {L17}
  31. define_attribute{p:da_data[3]} {PAP_IO_VCCIO} {3.3}
  32. define_attribute{p:da_data[3]} {PAP_IO_STANDARD} { LVCMOS33}
  33. define_attribute{p:da_data[3]} {PAP_IO_DRIVE} {2}
  34. define_attribute{p:da_data[3]} {PAP_IO_NONE} {TRUE}
  35. define_attribute{p:da_data[3]} {PAP_IO_SLEW} {SLOW}
  36. define_attribute{p:da_data[2]} {PAP_IO_DIRECTION} {OUTPUT}
  37. define_attribute{p:da_data[2]} {PAP_IO_LOC} {K17}
  38. define_attribute{p:da_data[2]} {PAP_IO_VCCIO} {3.3}
  39. define_attribute{p:da_data[2]} {PAP_IO_STANDARD} { LVCMOS33}
  40. define_attribute{p:da_data[2]} {PAP_IO_DRIVE} {2}
  41. define_attribute{p:da_data[2]} {PAP_IO_NONE} {TRUE}
  42. define_attribute{p:da_data[2]} {PAP_IO_SLEW} {SLOW}
  43. define_attribute{p:da_data[1]} {PAP_IO_DIRECTION} {OUTPUT}
  44. define_attribute{p:da_data[1]} {PAP_IO_LOC} {L18}
  45. define_attribute{p:da_data[1]} {PAP_IO_VCCIO} {3.3}
  46. define_attribute{p:da_data[1]} {PAP_IO_STANDARD} { LVCMOS33}
  47. define_attribute{p:da_data[1]} {PAP_IO_DRIVE} {2}
  48. define_attribute{p:da_data[1]} {PAP_IO_NONE} {TRUE}
  49. define_attribute{p:da_data[1]} {PAP_IO_SLEW} {SLOW}
  50. define_attribute{p:da_data[0]} {PAP_IO_DIRECTION} {OUTPUT}
  51. define_attribute{p:da_data[0]} {PAP_IO_LOC} {K18}
  52. define_attribute{p:da_data[0]} {PAP_IO_VCCIO} {3.3}
  53. define_attribute{p:da_data[0]} {PAP_IO_STANDARD} { LVCMOS33}
  54. define_attribute{p:da_data[0]} {PAP_IO_DRIVE} {2}
  55. define_attribute{p:da_data[0]} {PAP_IO_NONE} {TRUE}
  56. define_attribute{p:da_data[0]} {PAP_IO_SLEW} {SLOW}
  57. define_attribute{p:ad_data[7]} {PAP_IO_DIRECTION} {INPUT}
  58. define_attribute{p:ad_data[7]} {PAP_IO_LOC} {G18}
  59. define_attribute{p:ad_data[7]} {PAP_IO_VCCIO} {3.3}
  60. define_attribute{p:ad_data[7]} {PAP_IO_STANDARD} { LVCMOS33}
  61. define_attribute{p:ad_data[7]} {PAP_IO_NONE} {TRUE}
  62. define_attribute{p:ad_data[6]} {PAP_IO_DIRECTION} {INPUT}
  63. define_attribute{p:ad_data[6]} {PAP_IO_LOC} {J15}
  64. define_attribute{p:ad_data[6]} {PAP_IO_VCCIO} {3.3}
  65. define_attribute{p:ad_data[6]} {PAP_IO_STANDARD} { LVCMOS33}
  66. define_attribute{p:ad_data[6]} {PAP_IO_NONE} {TRUE}
  67. define_attribute{p:ad_data[5]} {PAP_IO_DIRECTION} {INPUT}
  68. define_attribute{p:ad_data[5]} {PAP_IO_LOC} {G17}
  69. define_attribute{p:ad_data[5]} {PAP_IO_VCCIO} {3.3}
  70. define_attribute{p:ad_data[5]} {PAP_IO_STANDARD} { LVCMOS33}
  71. define_attribute{p:ad_data[5]} {PAP_IO_NONE} {TRUE}
  72. define_attribute{p:ad_data[4]} {PAP_IO_DIRECTION} {INPUT}
  73. define_attribute{p:ad_data[4]} {PAP_IO_LOC} {J14}
  74. define_attribute{p:ad_data[4]} {PAP_IO_VCCIO} {3.3}
  75. define_attribute{p:ad_data[4]} {PAP_IO_STANDARD} { LVCMOS33}
  76. define_attribute{p:ad_data[4]} {PAP_IO_NONE} {TRUE}
  77. define_attribute{p:ad_data[3]} {PAP_IO_DIRECTION} {INPUT}
  78. define_attribute{p:ad_data[3]} {PAP_IO_LOC} {F18}
  79. define_attribute{p:ad_data[3]} {PAP_IO_VCCIO} {3.3}
  80. define_attribute{p:ad_data[3]} {PAP_IO_STANDARD} { LVCMOS33}
  81. define_attribute{p:ad_data[3]} {PAP_IO_NONE} {TRUE}
  82. define_attribute{p:ad_data[2]} {PAP_IO_DIRECTION} {INPUT}
  83. define_attribute{p:ad_data[2]} {PAP_IO_LOC} {H16}
  84. define_attribute{p:ad_data[2]} {PAP_IO_VCCIO} {3.3}
  85. define_attribute{p:ad_data[2]} {PAP_IO_STANDARD} { LVCMOS33}
  86. define_attribute{p:ad_data[2]} {PAP_IO_NONE} {TRUE}
  87. define_attribute{p:ad_data[1]} {PAP_IO_DIRECTION} {INPUT}
  88. define_attribute{p:ad_data[1]} {PAP_IO_LOC} {F17}
  89. define_attribute{p:ad_data[1]} {PAP_IO_VCCIO} {3.3}
  90. define_attribute{p:ad_data[1]} {PAP_IO_STANDARD} { LVCMOS33}
  91. define_attribute{p:ad_data[1]} {PAP_IO_NONE} {TRUE}
  92. define_attribute{p:ad_data[0]} {PAP_IO_DIRECTION} {INPUT}
  93. define_attribute{p:ad_data[0]} {PAP_IO_LOC} {J16}
  94. define_attribute{p:ad_data[0]} {PAP_IO_VCCIO} {3.3}
  95. define_attribute{p:ad_data[0]} {PAP_IO_STANDARD} { LVCMOS33}
  96. define_attribute{p:ad_data[0]} {PAP_IO_NONE} {TRUE}
  97. define_attribute{p:ad_clk} {PAP_IO_DIRECTION} {OUTPUT}
  98. define_attribute{p:ad_clk} {PAP_IO_LOC} {H13}
  99. define_attribute{p:ad_clk} {PAP_IO_VCCIO} {3.3}
  100. define_attribute{p:ad_clk} {PAP_IO_STANDARD} { LVCMOS33}
  101. define_attribute{p:ad_clk} {PAP_IO_DRIVE} {2}
  102. define_attribute{p:ad_clk} {PAP_IO_NONE} {TRUE}
  103. define_attribute{p:ad_clk} {PAP_IO_SLEW} {SLOW}
  104. define_attribute{p:da_clk} {PAP_IO_DIRECTION} {OUTPUT}
  105. define_attribute{p:da_clk} {PAP_IO_LOC} {H14}
  106. define_attribute{p:da_clk} {PAP_IO_VCCIO} {3.3}
  107. define_attribute{p:da_clk} {PAP_IO_STANDARD} { LVCMOS33}
  108. define_attribute{p:da_clk} {PAP_IO_DRIVE} {2}
  109. define_attribute{p:da_clk} {PAP_IO_NONE} {TRUE}
  110. define_attribute{p:da_clk} {PAP_IO_SLEW} {SLOW}
  111. define_attribute{p:ad_otr} {PAP_IO_DIRECTION} {INPUT}
  112. define_attribute{p:ad_otr} {PAP_IO_LOC} {K15}
  113. define_attribute{p:ad_otr} {PAP_IO_VCCIO} {3.3}
  114. define_attribute{p:ad_otr} {PAP_IO_STANDARD} { LVCMOS33}
  115. define_attribute{p:ad_otr} {PAP_IO_NONE} {TRUE}
  116. define_attribute{p:key0} {PAP_IO_DIRECTION} {INPUT}
  117. define_attribute{p:key0} {PAP_IO_LOC} {H5}
  118. define_attribute{p:key0} {PAP_IO_VCCIO} {3.3}
  119. define_attribute{p:key0} {PAP_IO_STANDARD} {LVCMOS33}
  120. define_attribute{p:key0} {PAP_IO_NONE} {TRUE}
  121. define_attribute{p:key1} {PAP_IO_DIRECTION} {INPUT}
  122. define_attribute{p:key1} {PAP_IO_LOC} {H6}
  123. define_attribute{p:key1} {PAP_IO_VCCIO} {3.3}
  124. define_attribute{p:key1} {PAP_IO_STANDARD} {LVCMOS33}
  125. define_attribute{p:key1} {PAP_IO_NONE} {TRUE}
  126. define_attribute{p:sys_clk} {PAP_IO_DIRECTION} {INPUT}
  127. define_attribute{p:sys_clk} {PAP_IO_LOC} {B5}
  128. define_attribute{p:sys_clk} {PAP_IO_VCCIO} {3.3}
  129. define_attribute{p:sys_clk} {PAP_IO_STANDARD} {LVCMOS33}
  130. define_attribute{p:sys_clk} {PAP_IO_NONE} {TRUE}
  131. define_attribute{p:sys_rst_n} {PAP_IO_DIRECTION} {INPUT}
  132. define_attribute{p:sys_rst_n} {PAP_IO_LOC} {G5}
  133. define_attribute{p:sys_rst_n} {PAP_IO_VCCIO} {1.5}
  134. define_attribute{p:sys_rst_n} {PAP_IO_STANDARD} {LVCMOS15}
  135. define_attribute{p:sys_rst_n} {PAP_IO_NONE} {TRUE}
复制代码

1.4 程序设计
根据本章的实验任务,FPGA需要连续输出正弦波、方波、三角波、锯齿波波形的数据,才能使AD9708连续输出相应波形的模拟电压,如果使用三角函数公式运算的方式来编写代码,然后输出正弦波、方波、三角波、锯齿波的波形数据,那么程序设计会变得非常复杂。在工程应用中,一般将波形数据存储在RAM或者ROM中,由于本次实验并不需要写数据到RAM中,因此我们将波形数据存储在只读的ROM中,直接读取ROM中的数据发送给DA转换芯片即可。
图 35.4.1是根据本章实验任务画出的系统框图。ROM里面事先存储好了波形的数据,DA数据发送模块从ROM中读取数据,将数据和时钟送到AD9708的输入数据端口和输入时钟端口;AD数据接收模块给AD9280输出驱动时钟信号和使能信号,并采集AD9280输出模数转换完成的数据。
DDS实验的系统框图如下图所示:
image006.png
图 35.4.1 DDS系统框图
顶层模块的原理图如下图所示:
image008.png
图 35.4.2 顶层模块原理图
FPGA顶层模块(dds)例化了以下六个模块:时钟模块(clk_wiz_0)、两个按键消抖模块(key_debounce)、DA数据发送模块(da_wave_send)、ROM波形存储模块(rom_400x8b)和AD数据接收模块(ad_wave_rec)。
时钟模块(clk_wiz_0):为按键消抖模块、DA数据发送模块、ROM波形存储模块提供驱动时钟。
按键消抖模块(key_debounce):对按键信号延时采样,将消抖后的按键信号和按键数据有效信号输出至da_wave_send模块。
DA数据发送模块(da_wave_send):DA数据发送模块输出读ROM地址,将输入的ROM数据发送至DA转换芯片的数据端口。
ROM波形存储模块(rom_400x8b):ROM波形存储模块由软件自带的Distributed ROM IP核实现,其存储的波形数据可以使用matlab生成的.coe文件。
AD数据接收模块(ad_wave_rec):AD数据接收模块输出AD转换芯片的驱动时钟和使能信号,随后接收AD转换完成的数据。
顶层模块里的按键消抖模块可以参考按键控制蜂鸣器实验,AD数据接收模块可以参考高速AD/DA实验,这里我们重点讲解其余的几个模块。
顶层模块的代码如下:
  1. 1 module dds(
  2. 2      input                sys_clk     ,  //系统时钟
  3. 3      input                sys_rst_n   ,  //系统复位,低电平有效
  4. 4      input                key0        ,  //按键key0
  5. 5      input                key1        ,  //按键key1
  6. 6      //DA芯片接口
  7. 7      output               da_clk      ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟
  8. 8      output    [7:0       da_data     ,  //输出给DA的数据
  9. 9      //AD芯片接口
  10. 10     input     [7:0      ad_data     ,  //AD输入数据
  11. 11     //模拟输入电压超出量程标志(本次试验未用到)
  12. 12     input                ad_otr      ,  //0:在量程范围 1:超出量程
  13. 13     output               ad_clk         //AD(AD9280)驱动时钟,最大支持32Mhz时钟
  14. 14 );
  15. 15
  16. 16 //wire define
  17. 17 wire      [8:0     rd_addr;            //ROM读地址
  18. 18 wire      [7:0     rd_data;            //ROM读出的数据
  19. 19 wire              key0_value;         //key0消抖后的按键值
  20. 20 wire              key0_flag;          //key0消抖后的按键值的效标志
  21. 21 wire              key1_value;         //key1消抖后的按键值
  22. 22 wire              key1_flag;          //key1消抖后的按键值的效标志
  23. 23 wire              clk_100M;          //da芯片的驱动时钟
  24. 24 wire      [7:0    ad_data_out/* synthesis PAP_MARK_DEBUG="1" */;    //这里使用防优化语句,便于在线调试工具可以捕获到数据
  25. 25
  26. 26
  27. 27 //*****************************************************
  28. 28 //**                   main code
  29. 29 //*****************************************************
  30. 30
  31. 31 //例化按键消抖模块
  32. 32 key_debounce  u_key0_debounce(
  33. 33     .sys_rst_n  (sys_rst_n),
  34. 34     .clk        (clk_100M),
  35. 35     .key        (key0),
  36. 36     .key_value  (key0_value),
  37. 37     .key_flag   (key0_flag)   
  38. 38     );
  39. 39     
  40. 40 //例化按键消抖模块
  41. 41 key_debounce  u_key1_debounce(
  42. 42     .sys_rst_n  (sys_rst_n),
  43. 43     .clk        (clk_100M),
  44. 44     .key        (key1),
  45. 45     .key_value  (key1_value),
  46. 46     .key_flag   (key1_flag)     
  47. 47     );  
  48. 48
  49. 49 //DA数据发送
  50. 50 da_wave_send u_da_wave_send(
  51. 51     .clk         (clk_100M),
  52. 52     .rst_n       (sys_rst_n),
  53. 53     .key0_value  (key0_value),
  54. 54     .key0_flag   (key0_flag),
  55. 55     .key1_value  (key1_value),
  56. 56     .key1_flag   (key1_flag),
  57. 57     .rd_data     (rd_data),
  58. 58     .rd_addr     (rd_addr),
  59. 59     .da_clk      (da_clk),
  60. 60     .da_data     (da_data)
  61. 61     );
  62. 62 clk_wiz_0 u_clk_wiz_0
  63. 63   (
  64. 64     //Clock out ports
  65. 65     .clkout0(clk_100M),     // output clk_out0
  66. 66     //Status and control signals
  67. 67     .pll_rst(~sys_rst_n), // inputreset
  68. 68   // Clock in ports
  69. 69     .clkin1(sys_clk)      // input clk_in1
  70. 70     );      
  71. 71
  72. 72     
  73. 73 //ROM存储波形
  74. 74 rom_400x8b u_rom_400x8b (
  75. 75  .clk(clk_100M),      // input wire clka
  76. 76  .addr(rd_addr),     //input wire [8 : 0] addra
  77. 77  .rst(sys_rst_n),
  78. 78  .rd_data(rd_data)     //output wire [7 : 0] douta
  79. 79 );
  80. 80
  81. 81 //AD数据接收
  82. 82 ad_wave_rec u_ad_wave_rec(
  83. 83     .clk         (sys_clk),
  84. 84     .rst_n       (sys_rst_n),
  85. 85     .ad_data     (ad_data),
  86. 86     .ad_otr      (ad_otr),
  87. 87     .ad_data_out(ad_data_out),
  88. 88     .ad_clk      (ad_clk)
  89. 89     );   
  90. 90
  91. 91 endmodule
复制代码

DA数据发送模块输出的读ROM地址(rd_addr)连接至ROM模块的地址输入端,ROM模块输出的数据(rd_data)连接至DA数据发送模块的数据输入端,从而完成了从ROM中读取数据的功能。
在顶层模块代码的第62至70行例化了PLL的ip核,用于生成100M时钟作为读rom地址的时钟和AD模块的驱动时钟。注意,rom模块的端口时钟和按键消抖模块的时钟也是100M。PLL核的配置如下图所示:
image010.png
图 35.4.3 “Input Clock Information”选项卡的设置
接下来切换至“Basic Configuration”选项卡,在“Basic Configuration”选项卡中,勾选Enable clkout0时钟,并且将“Desired Frequency“设置为100Mhz,其他设置保持默认即可,如下图所示。
image012.png
图 35.4.4 “OutputClocks”选项卡的设置
其余的设置保持默认,最后直接点击“OK”按钮即可。
在顶层模块代码的第74至79行例化了ROM模块,由Distributed ROM IP核配置生成。
关于调试方式,这里我们直接采用PDS集成的Debugger进行在线调试,详细的使用方法可以参考软件使用篇的Debugger在线调试工具,具体Inserter端口设置如下图所示:
image014.png
图 35.4.5 Inserter的NetConnections配置
我们在前面说过,ROM中存储的波形数据可以使用MatLab软件生成,使用 MatLab绘制4种信号波形,对波形进行等间隔采样,以采样次数作为ROM存储地址,将采集的波形幅值数据做为存储数据写入存储地址对应的存储空间。在本次实验中我们对4种信号波形进行分别采样,采样次数为100次,采集的波形幅值数据位宽为8bit,将采集数据保存为coe文件。
各波形参考代码如下。
正弦信号波形采集参考代码(sin_wave.m):
  1. 1  F1=1;                %信号频率
  2. 2  Fs=10^2;             %采样频率
  3. 3  P1=0;                %信号初始相位
  4. 4  N=10^2;              %采样点数
  5. 5  t=[0:1/Fs:(N-1)/Fs]; %采样时刻
  6. 6  ADC=2^7 - 1;         %直流分量
  7. 7  A=2^7;               %信号幅度
  8. 8  %生成正弦信号
  9. 9  s=A*sin(2*pi*F1*t + pi*P1/180) + ADC;
  10. 10 plot(s);             %绘制图形
  11. 11 %创建 coe 文件
  12. 12 fild = fopen('sin_wave_100x8.coe','wt');
  13. 13 %写入 coe 文件头
  14. 14 %固定写法,表示写入的数据是10进制表示
  15. 15 fprintf(fild, '%s\n','memory_initialization_radix=10;');
  16. 16 %固定写法,下面开始写入数据
  17. 17 fprintf(fild, '%s\n\n','memory_initialization_vector=');  
  18. 18 for i = 1:N
  19. 19 s2(i) = round(s(i));          %对小数四舍五入以取整
  20. 20 if s2(i) <0                   %负 1强制置零
  21. 21 s2(i) = 0
  22. 22 end
  23. 23 fprintf(fild, '%d',s2(i));    %数据写入
  24. 24 if i==N
  25. 25    fprintf(fild, '%s\n',';'); %最后一个数据用;
  26. 26 else
  27. 27    fprintf(fild,',\n');       % 其他数据用,
  28. 28 end
  29. 29 end
  30. 30 fclose(fild);                 % 写完了,关闭文件
复制代码

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

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

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

image022.png
图 35.4.9 MatLab 生成的锯齿波信号波形
使用 MatLab对4种波形进行采样后,生成4个coe文件,分别对应4种波形,我们通过调用一个深度为100*4,位宽为8bit的 ROM,将四个coe文件提取修改整合为一个DAT文件。在配置rom的ip核时将整合的DAT文件导入到rom中。
使用Notepad++代码编辑器打开生成的COE文件,提取数据部分,并将其全部转换为16进制的数据,并另存为后缀为.dat的文件,如下图所示:
image024.png
图 35.4.10 dat文件打开界面
工程中创建一个单端口ROM,并命名为“rom_400x8b”,配置如下图所示:
image026.png
图 35.4.11 Distributed ROM IP核的基础配置页面
我们将AddressWidth的位宽设置为9,DataWidth数据位宽设置为8,以存储matlab生成的400个数据,其他保持默认。
接着点击InitFile加载数据文件,Initial Data Format Type选择HEX类型,最后点击“Generate”按钮完成IP核的配置。
DA数据发送模块的代码如下:
  1. 1  module da_wave_send(
  2. 2       input                rst_n  ,      //复位信号,低电平有效
  3. 3       input                clk,         
  4. 4       input key0_value           ,       //消抖后的按键值
  5. 5       input key0_flag            ,       //消抖后的按键值的有效标志
  6. 6       input key1_value           ,       //消抖后的按键值
  7. 7       input key1_flag            ,       //消抖后的按键值的有效标志
  8. 8                                          
  9. 9       input         [7:0    rd_data,     //ROM读出的数据
  10. 10      output  reg  [8:0    rd_addr,      //读ROM地址
  11. 11      //DA芯片接口                        
  12. 12      output               da_clk,
  13. 13      output       [7:0   da_data       //输出给DA的数据
  14. 14      );
  15. 15
  16. 16 //parameter
  17. 17 //波形调节控制
  18. 18 parameter  sine_wave_addr     = 9'd0;   // 正弦波起始位置
  19. 19 parameter  square_wave_addr   =9'd100; // 方波起始位置
  20. 20 parameter  triangle_wave_addr = 9'd200; // 三角波起始位置
  21. 21 parameter  sawtooth_wave_addr = 9'd300; // 锯齿波起始位置
  22. 22
  23. 23 //频率调节控制,FREQ_ADJ的越大,最终输出的频率越低,范围0~255
  24. 24 parameter  FREQ_ADJ0 = 8'd0;            //参数0对应输出1Mhz波形频率
  25. 25 parameter  FREQ_ADJ1 = 8'd1;            //参数1对应输出500khz波形频率
  26. 26 parameter  FREQ_ADJ2 = 8'd3;            //参数3对应输出250khz波形频率
  27. 27 parameter  FREQ_ADJ3 = 8'd7;            //参数7对应输出125khz波形频率
  28. 28                                          
  29. 29 //reg define                           
  30. 30 reg    [7:0    freq_adj    ;          //频率调节参数寄存器
  31. 31 reg    [7:0    freq_cnt    ;          //频率调节计数器
  32. 32 reg    [1:0    wave_select ;          //切换波形地址寄存器
  33. 33 reg    [1:0    freq_select ;          //切换波形频率寄存器
  34. 34
  35. 35 //*****************************************************
  36. 36 //**                    main code
  37. 37 //*****************************************************
  38. 38 assign  da_clk = clk;
  39. 39 assign  da_data = rd_data;  //将读到的ROM数据赋值给DA数据端口
  40. 40     
  41. 41 //切换波形种类
  42. 42 always @(posedge clk  or negedge rst_n) begin
  43. 43      if(rst_n == 1'b0)
  44. 44          wave_select <= 2'd0;
  45. 45      else if((key0_flag == 1) && (key0_value == 0)) begin //确保按键key0确实被有效按下
  46. 46             if(wave_select < 2'd3)
  47. 47                 wave_select <= wave_select+1'd1;
  48. 48             else  
  49. 49                 wave_select <= 0;
  50. 50            end
  51. 51           else
  52. 52               wave_select <= wave_select;
  53. 53 end
  54. 54
  55. 55 //切换波形频率
  56. 56 always @(posedge clk or negedge rst_n) begin
  57. 57      if(rst_n == 1'b0)
  58. 58          freq_select <= 2'd0;
  59. 59     else if((key1_flag ==1) && (key1_value ==0)) begin //确保按键key1确实被有效按下
  60. 60             if(freq_select < 2'd3)
  61. 61                freq_select <= freq_select+1'd1;
  62. 62             else  
  63. 63                 freq_select <= 0;
  64. 64            end
  65. 65           else
  66. 66               freq_select <= freq_select;
  67. 67 end
  68. 68 always @(posedge clk or negedge rst_n) begin
  69. 69      if(rst_n == 1'b0)
  70. 70        freq_adj <= 8'd0;
  71. 71      else case(freq_select)
  72. 72               2'd0:freq_adj <= FREQ_ADJ0;
  73. 73               2'd1:freq_adj <= FREQ_ADJ1;   
  74. 74               2'd2:freq_adj <= FREQ_ADJ2;
  75. 75               2'd3:freq_adj <= FREQ_ADJ3;
  76. 76              default:freq_adj <= FREQ_ADJ0;
  77. 77           endcase
  78. 78 end
  79. 79
  80. 80 //频率调节计数器
  81. 81 always @(posedge clk or negedge rst_n) begin
  82. 82      if(rst_n == 1'b0)
  83. 83          freq_cnt <= 8'd0;
  84. 84      else if(freq_cnt ==freq_adj)   
  85. 85          freq_cnt <= 8'd0;
  86. 86      else         
  87. 87          freq_cnt <= freq_cnt + 8'd1;
  88. 88 end
  89. 89
  90. 90 //读ROM地址,按照100M的频率去读
  91. 91 always @(posedge clk or negedge rst_n) begin
  92. 92      if(rst_n == 1'b0)
  93. 93         rd_addr <= 9'd0;
  94. 94      else if(freq_cnt ==freq_adj) begin
  95. 95          case(wave_select)
  96. 96              2'd0:
  97. 97                  if(rd_addr >= sine_wave_addr && rd_addr <= sine_wave_addr+9'd99)   
  98. 98                    if(rd_addr == sine_wave_addr+9'd99)  
  99. 99                       rd_addr <= sine_wave_addr;
  100. 100                   else
  101. 101                       rd_addr <= rd_addr+9'd1;
  102. 102                 else
  103. 103                     rd_addr <= sine_wave_addr;                          
  104. 104             2'd1:
  105. 105                 if(rd_addr >= square_wave_addr && rd_addr <= square_wave_addr+9'd99)   
  106. 106                   if(rd_addr == square_wave_addr+9'd99)
  107. 107                       rd_addr <= square_wave_addr;
  108. 108                   else  
  109. 109                       rd_addr <= rd_addr+9'd1;
  110. 110                 else
  111. 111                     rd_addr <= square_wave_addr;
  112. 112             2'd2:
  113. 113                 if(rd_addr >= triangle_wave_addr && rd_addr <= triangle_wave_addr+9'd99)
  114. 114                   if(rd_addr == triangle_wave_addr+9'd99)
  115. 115                       rd_addr <= triangle_wave_addr;
  116. 116                   else
  117. 117                       rd_addr <= rd_addr+9'd1;  
  118. 118                 else  
  119. 119                     rd_addr <= triangle_wave_addr;                  
  120. 120             2'd3:
  121. 121                 if(rd_addr >= sawtooth_wave_addr && rd_addr <= sawtooth_wave_addr+9'd99)   
  122. 122                   if(rd_addr == sawtooth_wave_addr+9'd99)
  123. 123                       rd_addr <= sawtooth_wave_addr;
  124. 124                   else
  125. 125                       rd_addr <= rd_addr+9'd1;   
  126. 126                 else  
  127. 127                     rd_addr <= sawtooth_wave_addr;   
  128. 128             default:
  129. 129                 if(rd_addr >= sine_wave_addr && rd_addr <= sine_wave_addr+9'd99)   
  130. 130                    if(rd_addr == sine_wave_addr+9'd99)  
  131. 131                        rd_addr <= sine_wave_addr;
  132. 132                    else
  133. 133                        rd_addr <= rd_addr+9'd1;
  134. 134                 else
  135. 135                     rd_addr <= sine_wave_addr;            
  136. 136         endcase
  137. 137     end
  138. 138          else  rd_addr <= rd_addr;            
  139. 139 end
  140. 140 endmodule
  141. 141
复制代码
在代码的第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,而输入时钟为50 Mhz,通过锁相环倍频可以得到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。

1.5 下载验证
将高速AD-DA模块插入开发板的扩展口,连接时注意扩展口电源引脚方向和开发板电源引脚方向一致,然后将下载器一端连接电脑,另一端与开发板上对应端口连接,最后连接电源线并打开电源开关。
硬件连接实物图如图 35.5.1所示:
image028.png
图 35.5.1 FPGA开发板硬件连接实物图
将工程生成的比特流文件下载到FPGA中后,然后使用示波器测量DA输出通道的波形。首先将示波器带夹子的一端连接到开发板的GND位置(可使用杜邦线连接至开发板上的任一的GND管脚),然后将另一端探针插入高速AD-DA模块的DA通道中间的金属圆圈内(注意将红色的保护套拿掉),如图 35.5.2
所示。或者也可以直接测试高速AD-DA模块的TP引脚,如图 35.5.3所示。
image031.png
图 35.5.2 DA模拟电压测量孔位

image032.png
图 35.5.3 DA模拟电压测量点(TP)
此时观察Debuger窗口的波形:
通过按下按键key0可实现波形的的切换:
image034.png
图 35.5.4 正弦波测量图
image036.png
图 35.5.5 方波测量图
image038.png
图 35.5.6 三角波测量图
image040.png
图 35.5.7 锯齿波测量图
通过按下按键key1可实现频率的切换:
image042.png
图 35.5.8 频率切换图(一)
image044.png
图 35.5.9 频率切换图(二)
在Debuger上观察到正确的波形后,说明DDS的功能已经实现了。关于生成的模拟波形除了可以在Debuger上观察,也可以通过示波器去进行观察。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-6-9 22:58

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表