基于FPGA的DDS
好,讲一讲原理
先上经典图
这个图就基本讲出了DDS的工作原理
DDS主要由相位累加器、相位调制器、波形存储器(FPGA是使用的ROM)、数模(D/A)转换器四大结构组成,后面还可以接一个滤波器,之就属于模电的内容了,这里就不提及了。
首先定义几个专有名词,f_word表示频率字,p_word表示相位字,fout表示输出频率,其位宽为N,fclk表示系统系统工作频率 。同时根据采样定理,K的最大值应小于2N / 2。这样就可以计算出dds大概的输出频率的范围了。
先给出一个频率字的计算公式
f_woard = 2^N * fclk / fclk
举个栗子:如果N为32,系统频率为50MHz,如果要输出10KHz的信号,则K就应该是859000,具体关于这个公式的理解可以看这个视频,讲的非常详细
这个视频
相位字就很好理解了,说白了就是对波形数据表中要寻址的地址加一个偏移量,就可以实现移相操作了。
然后ROM表中的数据可以参考野火的教程用MATLAB导出,小梅哥也提供了一个exe,可以直接导出。文末附上链接
上RTL,代码主要是基于野火的教程写的,野火的教程阵地良心
//用的wave_16384x8.m生成的ROM文档
module dds
#(
parameter f_word = 32'd8590, //100Hz
p_word = 12'd0
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [3:0] wave_select ,
output wire [7:0] data_out
);
reg [31:0] fre_add;
reg [11:0] rom_addr_reg;
reg [13:0] rom_addr;
parameter sine_wave = 4'b0001,
square_wave = 4'b0010,
triangular_wave = 4'b0100,
sawtooth_wave = 4'b1000;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
fre_add <= 32'd0;
else
fre_add <= fre_add + f_word;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
rom_addr_reg <= 12'd0;
rom_addr <= 14'd0;
end
else
case(wave_select)
sine_wave:
begin
rom_addr_reg <= fre_add[31:20] + p_word;
rom_addr <= rom_addr_reg;
end
square_wave:
begin
rom_addr_reg <= fre_add[31:20] + p_word;
rom_addr <= rom_addr_reg + (15'd4096)*1;
end
triangular_wave:
begin
rom_addr_reg <= fre_add[31:20] + p_word;
rom_addr <= rom_addr_reg + (15'd4096)*2;
end
sawtooth_wave:
begin
rom_addr_reg <= fre_add[31:20] + p_word;
rom_addr <= rom_addr_reg + (15'd4096)*3;
end
default:
begin
rom_addr_reg <= fre_add[31:20] + p_word;
rom_addr <= rom_addr_reg;
end
endcase
rom_wave rom_wave_inst
(
.clka(sys_clk) , // input wire clka
.addra(rom_addr) , // input wire [13 : 0] addra
.douta(data_out) // output wire [7 : 0] douta
);
endmodule
tb仿真如下
`timescale 1ns / 1ns
module tb_dds();
reg sys_clk;
reg sys_rst_n;
reg [3:0] wave_select;
wire [7:0] data_out1;
wire [7:0] data_out2;
dds
#(
.f_word(32'd859000), //10KHz
.p_word(12'd0)
)
dds_inst0
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.wave_select (wave_select),
.data_out (data_out1 )
);
dds
#(
.f_word(32'd859000), //10KHz
.p_word(12'd2048)
)
dds_inst1
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.wave_select (wave_select),
.data_out (data_out2 )
);
always #10 sys_clk = ~sys_clk;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20;
sys_rst_n <= 1'b1;
wave_select <= 4'b0001;
#400000;
wave_select <= 4'b0010;
#400000;
wave_select <= 4'b0100;
#400000;
wave_select <= 4'b1000;
#400000;
wave_select <= 4'b0000;
end
endmodule
仿真结果如下
附上链接嗝:
链接:https://pan.baidu.com/s/1J0vQfQZ2SL_c1uI5qCRuvg
提取码:wssb
–来自百度网盘超级会员V111的分享)
小声BB
刚刚入门FPGA,本次项目的博客写的非常简略(主要是懒),具体可以参考上面贴的那个链接还有野火的视频。
如果有问题欢迎来交流鸭,老师让我用黑金的AX7Z035为基础进行练习,但是封校了快递进不来,心痛痛。
以后一定会继续更新关于FPGA相关的文章,以后一定会好好写的>o<