差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
dds_verilog [2020/03/26 14:31] gongyu |
dds_verilog [2022/06/23 21:44] (当前版本) gongyusu [7. 系统构成示例] |
||
---|---|---|---|
行 2: | 行 2: | ||
[[DDS]]是一种用于通过单个固定频率的参考时钟信号生成任意波形的频率合成器,被广泛用于测试测量仪表和通信系统中。也是学习[[FPGA]]、[[大学生电子设计竞赛]]备赛必学的原理性技能,在这里我们通过FPGA [[Verilog]]代码编程示例一步步让大家理解一下DDS的基本概念、构成以及各项指标的含义。 | [[DDS]]是一种用于通过单个固定频率的参考时钟信号生成任意波形的频率合成器,被广泛用于测试测量仪表和通信系统中。也是学习[[FPGA]]、[[大学生电子设计竞赛]]备赛必学的原理性技能,在这里我们通过FPGA [[Verilog]]代码编程示例一步步让大家理解一下DDS的基本概念、构成以及各项指标的含义。 | ||
- | ### 用以学习和体验DDS的开源平台 | + | ### 1. 用以学习和体验DDS的开源平台 |
- | 硬禾学堂专门设计了两款用以学习DDS的平台: | + | 硬禾学堂专门设计了一款通过小脚丫FPGA来学习数字系统应用的平台: |
+ | * [[stepfpga_training_board|小脚丫FPGA综合技能训练平台]] | ||
+ | {{ :fpgatrainingboard.png |}} | ||
+ | {{ :howtouse_traingingboard.png?800 |}}<WRAP centeralign> 基于小脚丫FPGA的综合训练口袋实验系统沟通 </WRAP> | ||
- | - [[dds_awg_open_platform|任意波形信号源/可编程直流电源的开源学习平台]] | + | 以及一款针对高校电子设计竞赛的训练平台: |
- | - [[stepfpga_training_board|小脚丫FPGA综合技能训练平台]] | + | * [[contest_training_board|小脚丫FPGA综合技能训练平台]] |
+ | {{ :contest_training_board1.png |}} | ||
+ | {{ :fpga_contest_training_block.png |}} | ||
+ | <WRAP centeralign> 基于小脚丫FPGA的电赛训练平台 </WRAP> | ||
- | {{ :dds_boards.png?800 |}}<WRAP centeralign> 两款用于DDS学习和体验的FPGA平台 </WRAP> | + | 这两款平台可以支持任意一款的[[stepfpga|小脚丫FPGA核心板]],第一款平台的[[DAC]]部分都是由R-2R网络构成的,第二款平台上使用通用的高速DAC IC,它们的代码基本上是一致的,只是通用的高速DAC IC会需要转换时钟进行同步,而R-2R只需要数据线,无需时钟。 |
- | + | ||
- | 这两款平台可以支持任意一款的[[stepfpga|小脚丫FPGA核心板]],它们的[[DAC]]部分都是由R-2R网络构成的,DDS部分的代码也是通用的,不同的是输出电平的调节方式以及对应于每个电阻节点的管脚顺序。 | + | |
它们都能通过FPGA中的Verilog逻辑编程实现以下的功能并能达到相应的性能: | 它们都能通过FPGA中的Verilog逻辑编程实现以下的功能并能达到相应的性能: | ||
* DDS的主时钟选择为12M(案例中前面的例子)和120MHz(案例中后面的例子,由小脚丫外部的12MHz输入时钟,通过内部[[PLL]]倍频到120MHz),使用120MHz的时钟能够生成0 - 15MHz(用8个点构成一个周期波形),甚至更高频率的正弦波波形,输出的信号波形可以是正弦波、三角波、锯齿波、方波等; | * DDS的主时钟选择为12M(案例中前面的例子)和120MHz(案例中后面的例子,由小脚丫外部的12MHz输入时钟,通过内部[[PLL]]倍频到120MHz),使用120MHz的时钟能够生成0 - 15MHz(用8个点构成一个周期波形),甚至更高频率的正弦波波形,输出的信号波形可以是正弦波、三角波、锯齿波、方波等; | ||
- | * 由于R-2R后面的运算放大器的性能限制,为保证在不同的频率上实现恒定的信号幅度 ,这两款板子的最高输出频率做了一定的限制,其中: | + | * 由于R-2R后面的运算放大器的性能限制,为保证在不同的频率上实现恒定的信号幅度 ,这两款板子的最高输出频率做了一定的限制 - 第一款平台的输出信号带宽保证到2MHz,信号幅度为0.5-3V,电赛训练板的输出信号带宽可以达到15MHz,信号幅度为2Vpp。 |
- | * DDS任意信号发生器/可调直流电压开源平台的输出信号带宽保证到5MHz,信号幅度+/-1.5V可调,直流偏置+/-1.5V可调; | + | * 第一款板子都可以通过[[UART]]同PC连接,通过PC上的软件(比如LabView或用QT等自编)对FPGA中的参数进行设置,进而调节DDS输出信号的波形、频率、幅度和直流偏移等。 |
- | * 小脚丫FPGA综合技能训练平台的输出信号带宽保证到2MHz,信号幅度为0.5-3V。 | + | |
- | * 两款板子都可以通过[[UART]]同PC连接,通过PC上的软件(比如LabView或用QT等自编)对FPGA中的参数进行设置,进而调节DDS输出信号的波形、频率、幅度和直流偏移等,第一款板子还可以调节双通道直流电压的输出。 | + | |
下面我们来看看在这两款平台上通过DDS能够实现的任意波形及实现方法、相关的Verilog代码。 | 下面我们来看看在这两款平台上通过DDS能够实现的任意波形及实现方法、相关的Verilog代码。 | ||
行 23: | 行 25: | ||
--- | --- | ||
- | ### DAC的选用说明 | + | ### 2. DAC的选用说明 |
{{ ::DDS_Training_DAC.png?800 |}}<WRAP centeralign> 将数字波形表转换为模拟电信号需要数模转换器 - DAC </WRAP> | {{ ::DDS_Training_DAC.png?800 |}}<WRAP centeralign> 将数字波形表转换为模拟电信号需要数模转换器 - DAC </WRAP> | ||
行 52: | 行 54: | ||
--- | --- | ||
- | ### 一个简单的DDS | + | ### 3. 一个简单的DDS |
- | ##### 1 生成方波 | + | ##### 3.1 生成方波 |
{{ ::DDS_Training_Square.png?800 |}} | {{ ::DDS_Training_Square.png?800 |}} | ||
DDS常被用来产生周期性的信号,第一步我们先看看如何产生一个方波信号,下面是代码: | DDS常被用来产生周期性的信号,第一步我们先看看如何产生一个方波信号,下面是代码: | ||
行 71: | 行 73: | ||
wire cnt_tap = cnt[7]; // 取出计数器的其中1位(bit 7 = 第8位) | wire cnt_tap = cnt[7]; // 取出计数器的其中1位(bit 7 = 第8位) | ||
assign dac_data = {10{cnt_tap}}; // 重复10次作为10位DAC的值 | assign dac_data = {10{cnt_tap}}; // 重复10次作为10位DAC的值 | ||
+ | assign dac_clk= clk; | ||
endmodule | endmodule | ||
行 80: | 行 83: | ||
- | ##### 2 生成锯齿波 | + | ##### 3.2 生成锯齿波 |
{{ ::DDS_Training_Sawtooth.png?800 |}} | {{ ::DDS_Training_Sawtooth.png?800 |}} | ||
行 92: | 行 95: | ||
- | ##### 3 生成三角波 | + | ##### 3.3 生成三角波 |
根据下面的图示方法实现三角波也不难: | 根据下面的图示方法实现三角波也不难: | ||
行 109: | 行 112: | ||
--- | --- | ||
- | ### 任意形状且可以任意频率的信号生成 | + | ### 4. “任意”信号生成 |
为了产生任意波形,DDS依赖两个技巧: | 为了产生任意波形,DDS依赖两个技巧: | ||
- | #### 1 LUT - Lookup Table(查找表) | + | #### 4.1 LUT - Lookup Table(查找表) |
第一个技巧就是把“任意形状”的波形样点保存在一个LUT(查找表,类似组织有序的仓库)中,通过查找表格的方式来实现。 | 第一个技巧就是把“任意形状”的波形样点保存在一个LUT(查找表,类似组织有序的仓库)中,通过查找表格的方式来实现。 | ||
行 282: | 行 285: | ||
- | #### 2 相位累加器 | + | #### 4.2 相位累加器 |
上面讲述了任意波形的实现,那如何实现“任意频率”?这就体会到用“长”相位累加器这个神器的作用了,它使得DDS输出的信号的频率非常灵活。我们还是用上面生成正弦波的例子来看一下: | 上面讲述了任意波形的实现,那如何实现“任意频率”?这就体会到用“长”相位累加器这个神器的作用了,它使得DDS输出的信号的频率非常灵活。我们还是用上面生成正弦波的例子来看一下: | ||
行 324: | 行 327: | ||
{{ ::DDS_Training_PhaseWheel.png?800 |}} | {{ ::DDS_Training_PhaseWheel.png?800 |}} | ||
- | 比如,使用24位相位累加器和12MHz时钟,输出的频率分辨率可以达到0.7Hz!如果使用28位、32位的相位累加器,输出信号的频率分辨率可以达到更高,下面是一个24位相位累加器,用于从12MHz的时钟生成440Hz信号。 | + | 比如,使用24位相位累加器和12MHz时钟,输出的频率分辨率可以达到0.7Hz!如果使用28位、32位的相位累加器,输出信号的频率分辨率可以达到更高,下面是一个24位相位累加器,用于从12MHz的时钟生成20KHz信号。 |
<code verilog> | <code verilog> | ||
reg [23:0] phase_acc; // 24位相位累加器 | reg [23:0] phase_acc; // 24位相位累加器 | ||
- | always @(posedge clk) phase_acc <= phase_acc + 615; //在12MHz的主时钟时输出440Hz频率的波形 | + | always @(posedge clk) phase_acc <= phase_acc + 27962; //在12MHz的主时钟时输出20KHz频率的波形 |
lookup_tables u_lookup_table(.phase(phase_acc[23:16]), .sin_out(dac_data)); | lookup_tables u_lookup_table(.phase(phase_acc[23:16]), .sin_out(dac_data)); | ||
</code> | </code> | ||
- | 虽然这个例子中的440Hz是比较低,但DDS的输出频率可以达到主时钟的一半~6MHz(或接近6MHz),只需修改上面代码中的相位累加器的值就可以。 | + | 虽然这个例子中的20KHz是比较低,但DDS的输出频率可以达到主时钟的一半~6MHz(或接近6MHz),只需修改上面代码中的相位累加器的值就可以。 |
在主时钟为12MHz,采用24bit的相位累加器,可以生成频率范围在0.7Hz ~ 6MHz的信号,一般为使波形好看,频率的上限设定为一个周期~至少5个以上采样点,也就是12MHz的时钟,生成的模拟信号的上限频率最好在2MHz。 | 在主时钟为12MHz,采用24bit的相位累加器,可以生成频率范围在0.7Hz ~ 6MHz的信号,一般为使波形好看,频率的上限设定为一个周期~至少5个以上采样点,也就是12MHz的时钟,生成的模拟信号的上限频率最好在2MHz。 | ||
行 343: | 行 346: | ||
reg [23:0] phase_acc; // 24位相位累加器 | reg [23:0] phase_acc; // 24位相位累加器 | ||
- | always @(posedge clk_120m) phase_acc <= phase_acc + 6152; //在120MHz的主时钟时输出440Hz频率的波形 | + | always @(posedge clk_120m) phase_acc <= phase_acc + 2796; //在120MHz的主时钟时输出20KHz频率的波形 |
lookup_tables u_lookup_table(.phase(phase_acc[23:16]), .sin_out(dac_data)); | lookup_tables u_lookup_table(.phase(phase_acc[23:16]), .sin_out(dac_data)); | ||
</code> | </code> | ||
行 355: | 行 358: | ||
--- | --- | ||
- | ### 线性插值 | + | ### 5. 线性插值 |
虽然相位累加器可以实现非常高的精度,但其输出却受到查找表地址位数(内部存储资源决定)的限制,当从一个地址跳转到下一个地址时,输出值会发生“跳跃”,输出信号的频率低的时候这还是可以接受的,但对于较高的输出频率,就会在其输出频谱中引入不必要的频率。 | 虽然相位累加器可以实现非常高的精度,但其输出却受到查找表地址位数(内部存储资源决定)的限制,当从一个地址跳转到下一个地址时,输出值会发生“跳跃”,输出信号的频率低的时候这还是可以接受的,但对于较高的输出频率,就会在其输出频谱中引入不必要的频率。 | ||
行 412: | 行 415: | ||
上面由浅入深讲述了用DDS生成任意波形的工作原理及实现方式,最后看一个完整的[[波形信号发生器设计|基于DDS的任意波形、信号发生器设计]]的小系统设计实验,它是基于[[step-baseboard|小脚丫FPGA实验系统底板]]在此系统中我们使用了[[SPI]]接口的串行[[DAC]] - DAC081S10,并有旋转编码器来控制参数的调整。 | 上面由浅入深讲述了用DDS生成任意波形的工作原理及实现方式,最后看一个完整的[[波形信号发生器设计|基于DDS的任意波形、信号发生器设计]]的小系统设计实验,它是基于[[step-baseboard|小脚丫FPGA实验系统底板]]在此系统中我们使用了[[SPI]]接口的串行[[DAC]] - DAC081S10,并有旋转编码器来控制参数的调整。 | ||
{{ :step_baseboard_v3.0框图.png?800 |}} | {{ :step_baseboard_v3.0框图.png?800 |}} | ||
+ | |||
+ | ### 6. 幅值调节 | ||
+ | |||
+ | 信号发生器通常采用“DAC参考电压”配合“模拟通道信号调理”进行信号幅值的调节,市面上大多数信号发生器产品都是采用这种调幅方案。这样在调节的过程中信号的分辨率不会受影响,最大程度保证信号的性能指标,同时也需要额外的DAC等电路控制参考电压。 | ||
+ | |||
+ | 另外我们也可以在FPGA中增加对信号幅值调节的设计,例如我们将信号的数据乘以一个8bit的因数,然后再将结果右移8位(相当于除以256),然后再把结果输出给DAC电路。将8bit的因数作为变量可调,最后就实现了在FPGA中调节幅值的功能。这种方法不需要额外的DAC改变参考电压,即可实现对幅值的调节,但是由于采用量化后的数据进行,会造成信号数据分辨率的降低。 | ||
+ | |||
+ | <code verilog> | ||
+ | wire [9:0] signal_dat; //未调幅的波形数据 | ||
+ | wire [7:0] a_ver; //用于调幅的因数 | ||
+ | |||
+ | reg [17:0] amp_dat; //调幅后的波形数据 | ||
+ | always @(posedge clk) amp_dat = signal_dat * a_ver; //波形数据乘以调幅因数 | ||
+ | |||
+ | wire [9:0] dac_dat; //输出给DAC电路的数据端口 | ||
+ | assign dac_dat = amp_dat[17:8]; //取高十位输出,相当于右移8位 | ||
+ | </code> | ||
+ | |||
+ | |||