当前位置:   article > 正文

基于FPGA的双通道DDS信号发生器

基于fpga的双通道dds

硬件FPGA开发板 ,AD9767双通道DA转换器

软件ISE,Matlab,Modelsim

最终效果:输出方波,正弦波,三角波以及锯齿波,可以通过按键改变输出波形的频率,频率在1Hz-1MHz可调,输出波形的电压通过旋钮可调

 

一、生成波形数据

        第一步,通过Matlab生成波形数据文件,数据最终存储在FPGA的ROM中,以.coe结尾。这里以生成正弦信号为例,由于AD9767是14位的DA转换芯片,所以生成的数据位宽也是14位。

  1. clear;
  2. clc;
  3. radix=2; %进制的格式
  4. width=14; %数据的位宽
  5. depth=1024; %数据的深度
  6. fid =fopen ('sin.coe','w');
  7. fprintf(fid,'MEMORY_INITIALIZATION_RADIX=%d;\n',radix);
  8. fprintf(fid,'MEMORY_INITIALIZATION_VECTOR=\n');
  9. for i=0:depth-1
  10. sin_data=floor((sin(2*pi*i/depth)+1)*0.5*(2^width-1));
  11. data=dec2bin(sin_data,width); %十进制到二进制的转换
  12. if (i~=depth-1)
  13. fprintf(fid,'%s,\n',data);
  14. else
  15. fprintf(fid,'%s;',data); %最后一行以分号结尾
  16. end
  17. end
  18. fclose (fid);

生成的.coe文件需要符合语法规范,将生成的coe文件保存到生成的Rom IP核中。

二、FPGA程序部分设计

        系统的整体框架分为FPGA和AD9767模块两部分,FPGA产生两路数字信号,AD9767模块将数字信号数模转换后经过滤波后输出。

        FPGA部分中,输出信号的频率由频率控制字决定。每经过一个时钟周期,加法器就将频率控制字与相位累加寄存器相加,最后得到的结果作为波形数据表的读取地址。同时相加结果反馈到加法器的输入端,使得每次相加都可以实现读取地址的递增,当累加次数足够多相位累加寄存器溢出,读取地址“归零”,如此循环往复输出正弦波信号。

1.顶层模块AD9767_DDS的设计

        通过按键key1实现频率控制字的控制,按键key2实现波形的选择,led灯用来指示现在输出的波形。

  1. module AD9767_DDS(
  2. input sclk,
  3. input rst_n,
  4. input key1,
  5. input key2,
  6. output [1:0]led,
  7. output DACA_CLK, //通道1时钟信号
  8. output DACB_CLK, //通道2时钟信号
  9. output DACA_WRT, //通道1使能信号
  10. output DACB_WRT, //通道2使能信号
  11. output [13:0]DAC_DATA1, //通道1输出数据
  12. output [13:0]DAC_DATA2 //通道2输出数据
  13. );
  14. assign D_CLK = sclk;
  15. assign DACA_CLK = D_CLK;
  16. assign DACB_CLK = D_CLK;
  17. assign DACA_WRT = D_CLK;
  18. assign DACB_WRT = D_CLK;
  19. wire [31:0]Fword;
  20. wire [1:0]wave_sel; //控制输出波形形状
  21. wire [2:0]Fword_sel;//改变频率控制字
  22. DDS_Module DDS_Module0(
  23. .clk(D_CLK),
  24. .rst_n(rst_n),
  25. .EN(1'b1),
  26. .key(key2),
  27. .Fword(Fword),
  28. .Pword(10'd0),
  29. .wave_sel(wave_sel),
  30. .DA_Clk(),
  31. .DA_Data(DAC_DATA1)
  32. );
  33. DDS_Module DDS_Module1(
  34. .clk(D_CLK),
  35. .rst_n(rst_n),
  36. .EN(1'b1),
  37. .key(key2),
  38. .Fword(Fword),
  39. .Pword(10'd512), //相对于通道1180度的偏移
  40. .DA_Clk(),
  41. .DA_Data(DAC_DATA2)
  42. );
  43. Fword_Set Fword_Set_inst(
  44. .clk(sclk),
  45. .rst_n(rst_n),
  46. .key(key1),
  47. .Fword(Fword),
  48. .Fword_sel(Fword_sel)
  49. );
  50. assign led = ~wave_sel;
  51. endmodule

2.信号产生模块DDS_Module的设计

        累加器是32位的,而数据深度是1024也就是十位的,取累加器的高十位作为读取地址。例化四个ROM模块分别存储不同的波形数据,通过按键按下控制输出的波形。

  1. module DDS_Module(
  2. input clk,
  3. input rst_n,
  4. input EN,
  5. input key,
  6. input [31:0] Fword,/*频率控制字*/
  7. input [9:0] Pword,/*相位控制字*/
  8. output reg [1:0] wave_sel,/*波形选择字*/
  9. output DA_Clk,/*DA数据输出时钟*/
  10. output reg [13:0] DA_Data/*D输出输出A*/
  11. );
  12. wire key_neg, key_out;
  13. wire [13:0] DA_Data1,DA_Data2,DA_Data3,DA_Data4;
  14. key_detect key_detect2(
  15. .sclk(clk),
  16. .rst_n(rst_n),
  17. .key_in(key),
  18. .key_neg(key_neg),
  19. .key_out(key_out)
  20. );
  21. always@(posedge clk or negedge rst_n)
  22. if(!rst_n)
  23. wave_sel <= 2'd0;
  24. else if(key_neg == 1 && !key_out)
  25. wave_sel <= wave_sel + 1'b1;
  26. else
  27. wave_sel <= wave_sel;
  28. always@(*)begin
  29. case(wave_sel)
  30. 0: DA_Data <= DA_Data1; //选择正弦波
  31. 1: DA_Data <= DA_Data2; //选择方波
  32. 2: DA_Data <= DA_Data3; //选择三角波
  33. 3: DA_Data <= DA_Data4; //选择锯齿波
  34. default: DA_Data <= DA_Data1;
  35. endcase
  36. end
  37. reg [31:0] Fre_acc;
  38. reg [9:0] Rom_Addr;/*rom深度1024*/
  39. /*---------------相位累加器------------------*/
  40. always @(posedge clk or negedge rst_n)
  41. if(!rst_n)
  42. Fre_acc <= 32'd0;
  43. else if(!EN)
  44. Fre_acc <= 32'd0;
  45. else
  46. Fre_acc <= Fre_acc + Fword;
  47. /*----------生成查找表地址---------------------*/
  48. always @(posedge clk or negedge rst_n)
  49. if(!rst_n)
  50. Rom_Addr <= 10'd0;
  51. else if(!EN)
  52. Rom_Addr <= 10'd0;
  53. else
  54. Rom_Addr <= Fre_acc[31:22] + Pword;
  55. /*----------例化查找表ROM SIN-------*/
  56. ddsrom ddsrom_inst(
  57. .addra(Rom_Addr),
  58. .clka(clk),
  59. .douta(DA_Data1)
  60. );
  61. /*----------例化查找表ROM -------*/
  62. square square_inst(
  63. .addra(Rom_Addr),
  64. .clka(clk),
  65. .douta(DA_Data2)
  66. );
  67. /*----------例化查找表ROM sawtooth-------*/
  68. sawtooth sawtooth_inst(
  69. .addra(Rom_Addr),
  70. .clka(clk),
  71. .douta(DA_Data3)
  72. );
  73. /*----------例化查找表ROM sawtooth_juci-------*/
  74. sawtooth_juci sawtooth_juci_inst(
  75. .addra(Rom_Addr),
  76. .clka(clk),
  77. .douta(DA_Data4)
  78. );
  79. /*----------输出DA时钟----------*/
  80. assign DA_Clk = (EN)?clk:1'b1;
  81. endmodule

3.频率选择模块Fword_Set的设计

        通过按钮选择频率控制字Fword,信号频率=Fword*2^累加器数据位宽/时钟频率。本来打算加一个扫频的功能,但是效果还没实现。

  1. module Fword_Set(
  2. input clk,
  3. input rst_n,
  4. input key,
  5. output reg[31:0]Fword,
  6. output reg[2:0]Fword_sel,
  7. output reg[19:0]cnt_time
  8. );
  9. parameter HUN_HZ = 8600,
  10. TWOK_HZ = 171799,
  11. ONE_HZ = 86;
  12. parameter IDLE = 2'd0,
  13. INCREASE = 2'd1,
  14. PAUSE = 2'd2;
  15. wire key_neg, key_out;
  16. reg [1:0] state;
  17. reg [4:0] cnt_latency;
  18. reg flag;
  19. wire [19:0] divider;
  20. wire [19:0] word;
  21. assign word = (Fword>>13)*(Fword>>13);
  22. always @(posedge clk or negedge rst_n) begin
  23. if(!rst_n)
  24. cnt_latency <= 'd0;
  25. else if(cnt_latency == 5'd22)
  26. cnt_latency <= 'd0;
  27. else
  28. cnt_latency <= cnt_latency + 5'd1;
  29. end
  30. always @(posedge clk or negedge rst_n) begin
  31. if(!rst_n)
  32. flag <= 1'd0;
  33. else if(cnt_time >= divider && cnt_latency == 5'd18)
  34. flag <= 1'd1;
  35. else
  36. flag <= 1'd0;
  37. end
  38. key_detect key_detect1(
  39. .sclk(clk),
  40. .rst_n(rst_n),
  41. .key_in(key),
  42. .key_neg(key_neg),
  43. .key_out(key_out)
  44. );
  45. always @(posedge clk or negedge rst_n) begin
  46. if(!rst_n)
  47. cnt_time <= 'd0;
  48. else if((Fword_sel == 3'd0) && (flag == 1'b0))
  49. cnt_time <= cnt_time + 20'd1;
  50. else
  51. cnt_time <= 20'd0;
  52. end
  53. always@(posedge clk or negedge rst_n)
  54. if(!rst_n)
  55. Fword_sel <= 3'd0;
  56. else if(Fword_sel < 3'd5) begin
  57. if(key_neg == 1 && !key_out)
  58. Fword_sel <= Fword_sel + 1'b1;
  59. else
  60. Fword_sel <= Fword_sel;
  61. end
  62. else
  63. Fword_sel <= 3'd0;
  64. always@(posedge clk)begin
  65. case(Fword_sel)
  66. 0: begin
  67. case(state)
  68. IDLE: Fword <= HUN_HZ;
  69. INCREASE:Fword <= Fword + ONE_HZ;
  70. PAUSE: Fword <= Fword;
  71. default:Fword <= Fword;
  72. endcase
  73. end
  74. 1: Fword <= 86; //1Hz 85.89934592 2^32*20/10^9
  75. 2: Fword <= 16063; //187kHz
  76. 3: Fword <= 85999; //1kHz
  77. 4: Fword <= 85999346; //1MHz
  78. default:Fword <= Fword;
  79. endcase
  80. end
  81. always@(posedge clk or negedge rst_n)begin
  82. if(!rst_n)
  83. state <= IDLE;
  84. else if(Fword_sel == 3'd0 && flag == 1'b1)
  85. state <= INCREASE;
  86. else if(Fword_sel == 3'd0 && Fword <= TWOK_HZ)
  87. state <= PAUSE;
  88. else
  89. state <= IDLE;
  90. end
  91. div div_inst(//8599*8599*500000/(Fword^2)
  92. .clk (clk),
  93. .dividend (20'd550000),
  94. .divisor (word),
  95. .quotient (divider)
  96. );
  97. endmodule

三、AD9767模块

        参考了这篇文章,做了以下改进:

1.在调试的过程中,发现原本小的贴片电感在使用过程中发热,不适用于电流大的场景,改成大的绕线电感。

2.原来的小旋扭电位器调节幅值并不方便,改用大旋钮的电位器。

测试视频:b站链接

工程文件:gitee

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/974359
推荐阅读
相关标签
  

闽ICP备14008679号