差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
dds_verilog [2020/04/02 12:19]
anran
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输出的信号的频率非常灵活。我们还是用上面生成正弦波的例子来看一下:
  
行 355: 行 358:
 --- ---
  
-### 线性插值+### 5. 线性插值
  
 虽然相位累加器可以实现非常高的精度,但其输出却受到查找表地址位数(内部存储资源决定)的限制,当从一个地址跳转到下一个地址时,输出值会发生“跳跃”,输出信号的频率低的时候这还是可以接受的,但对于较高的输出频率,就会在其输出频谱中引入不必要的频率。 虽然相位累加器可以实现非常高的精度,但其输出却受到查找表地址位数(内部存储资源决定)的限制,当从一个地址跳转到下一个地址时,输出值会发生“跳跃”,输出信号的频率低的时候这还是可以接受的,但对于较高的输出频率,就会在其输出频谱中引入不必要的频率。
行 413: 行 416:
 {{ :​step_baseboard_v3.0框图.png?​800 |}} {{ :​step_baseboard_v3.0框图.png?​800 |}}
  
-### 幅值调节+### 6. 幅值调节
  
-信号发生器通常采用“DAC参考电压”配合“模拟通道信号调理”进行信号幅值的调节,市面上大多数信号发生器产品都是采用这种调幅方案。这样在调节的过程中信号的分辨率不会受影响,最大程度保证信号的性能指标,同时也需要额外的DAC等电路控制参考电压 ​这在我们调节的过程中不会 +信号发生器通常采用“DAC参考电压”配合“模拟通道信号调理”进行信号幅值的调节,市面上大多数信号发生器产品都是采用这种调幅方案。这样在调节的过程中信号的分辨率不会受影响,最大程度保证信号的性能指标,同时也需要额外的DAC等电路控制参考电压。
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-// +
-虽然相位累加器可以实现非常高的精度,但其输出却受到查找表地址位数(内部存储资源决定)的限制,当从一个地址跳转到下一个地址时,输出值会发生“跳跃”,输出信号的频率低的时候这还是可以接受的,但对于较高的输出频率,就会在其输出频谱中引入不必要的频率 +
-{{ ::​DDS_Training_Phase_cut.png?​800 |}}+
  
-我们将解决此问题。 为了更容易理解我们回到12位相位累加器+另外我们也可以在FPGA中增加对信号幅值调节的设计例如我们将信号的数据乘以一个8bit的因数,然后再将结果右移8当于除以256),然后再把结果输出给DAC电路。将8bit的因数作为变量可调,最后就实现了在FPGA中调节幅值的功能。这种方法不需要额外的DAC改变参考电压,即可实现对幅值的调节,但是由于采用量化后的数据进行,会造成信号数据分辨率的降低
  
 <code verilog> <code verilog>
-// 采用线性内插正弦输出 +wire [9:0] signal_dat; ​//未调幅的波形数据 
-reg [11:0] phase_acc   //12位 +wire [7:0] a_ver; //用于调幅的因数
-always @(posedge clk) phase_acc <= phase_acc + 12'h1;+
  
-lookup_tables u_lookup_table(.phase(phase_acc[11:4]), .sin_out(dac_data)); +reg [17:0amp_dat; //​调幅后的波形数据 
 +always @(posedge clkamp_dat = signal_dat * a_ver; ​ //​波形数据乘以调幅因数 
 + 
 +wire [9:0] dac_dat; //​输出给DAC电路的数据端口 
 +assign dac_dat = amp_dat[17:​8]//​取高十位输出,相当于右移8位
 </​code>​ </​code>​
  
-上面的代码是每16个时钟从查找表的上一个地址移到下一个,这样使得输出每16个时钟“跳”一次。 
  
-一种有效改善的方式就是用相位累加器的最低4位(到现在还没有用的)来线性地对相邻的两个查找表的地址进行插值,实现起来也非常简单 - 使用两个查找表 
- 
-<code verilog> 
-//​使用线性内插的正弦波 
-reg [11:0] phase_acc; 
-always @(posedge clk) phase_acc <= phase_acc + 12'h1; 
- 
-//​使用两个查找表来获得两个连续的查找表的值 
-wire [13:0] sine1_lv, sine2_lv;  ​ 
-lookup_tables my_sine1(.phase(phase_acc[11:​4] ​      ), .sin_out(sine1_lv));​ 
-lookup_tables my_sine2(.phase(phase_acc[11:​4] + 8'h1), .sin_out(sine2_lv));​ 
- 
-//​现在相位累加器的最低4位需要做一下延迟,以匹配查找表带来的延迟 
-reg [3:0] phase_LSB_delay1; ​ always @(posedge clk) phase_LSB_delay1 <= phase_acc[3:​0];​ 
-reg [3:0] phase_LSB_delay2; ​ always @(posedge clk) phase_LSB_delay2 <= phase_LSB_delay1;​ 
-reg [3:0] phase_LSB_delay3; ​ always @(posedge clk) phase_LSB_delay3 <= phase_LSB_delay2;​ 
- 
-//​在使用它们做内插之前 
-wire [4:0] sine1_mf = 5'h10 - phase_LSB_delay3;​ 
-wire [3:0] sine2_mf = phase_LSB_delay3;​ 
-reg [20:0] sine_p; always @(posedge clk) sine_p <= sine1_lv*sine1_mf + sine2_lv*sine2_mf;​ 
- 
-assign dac_data = sine_p[20:​11];​ 
-</​code>​