0%

基于FPGA的两路信号发生器

基于FPGA的两路信号发生器

生产实习著blog2

这个的基本实现是一般新手就可以实现的了.整篇博客所需数学基础大概在初三水平

release : 2019-07-05 修正正弦波ROM裁剪问题
著有另一篇博客说明

[TOC]

DDS(Direct Digital Synthesizer)

DDS是直接数字式频率合成器的英文缩写。直接数字合成器除了具有最基本的频率合成(Frequency Synthesis)功能,还是一个灵活的多用途数字式信号调制与信号合成发生器,具有频率分辨率高、频率切换时间短、输出频率带宽大、可输出任意波形等特点,已经成为通信和雷达系统中的关键单元。 –百度百科

整体系统框图

先上一个dds理论图:
aaa

有几点需要注意:

  1. 整个系统框图是非常简单的,就是做一个查找表,然后去用时钟去查表就完事了
  2. 对特定波形,ROM表定制性会很高,比如正弦波只需要$\frac14$段就够用了,方波锯齿波可以不用ROM等
  3. 框图在DAC后面少了一个低通滤波器,其实相当于信号重建滤波器,十分重要!

这里做的指标是这样的:

  1. 频率相位定点可控,其实可以做连续可控,但是因为想了个新想法估计要做专利,就换掉了.
  2. 只是做了正弦,但是不同波形用matlab生成,然后fpga加个选通就完事了.
  3. 做了14位的8192点的,主要考虑到电赛用的DAC和fpga的ROM资源

所以整体的FPGA框图是这样的:
baa

MATLAB生成查找表

这次我们直接生成mif文件,免得产生格式问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
depth = 8192;
width = 14;
x = 0 : 2*pi/(depth-1) :2*pi;
y = sin(x);

y=(y+1)/2*(2^width -1 )/(2^width); %转正,防溢出
disp(y);
y_qua = round(y*2^width);

%编写mif文件
fid = fopen('/home/heweibao/project_matlab/fpga_dds/sin.mif','wt'); %将信号写入一个.mif文件中
fprintf(fid,'WIDTH=%d;\n',width);%写入存储位宽
fprintf(fid,'DEPTH=%d;\n',depth);%写入存储深度
fprintf(fid,'ADDRESS_RADIX=UNS;\n');%写入地址类型为无符号整型
fprintf(fid,'DATA_RADIX=UNS;\n');%写入数据类型为无符号整型
fprintf(fid,'CONTENT BEGIN\n');%起始内容
for num=0 : (depth-1)
fprintf(fid,'%d:%14.0f;\n',num,y_qua(num+1));
end
fclose(fid);
plot(x,y_qua);

简单,有一些小技巧大家可以看一看,我这里为了偷懒还是直接生成一个周期,没有节省空间

频率和相位计算

在介绍调频和调相之前,我们需要对相应的一些参数计算做点说明

最高频率$f_{max}$

设系统时钟为$f_{clk}$,总的点数为N,则最高输出频率为:
$$ f_{max} = \frac{f_{clk}}{N} $$
当然了,如果对一个给定的ROM表,而我们又想要一个高一点的输出频率的话,可以在相位累加器那里进行抽取,比如进行基2抽取后,8192点就相当于减半到4096点,即有:
$$ f_{max2} = \frac{f_{clk}}{N/2} = 2f_{max} $$

频率分辨率$\Delta f$

设系统时钟为1Hz,则频率分辨率为:
$$\Delta f = \frac1N (Hz)$$

最小步进相移$ P_{min} $

这个主要取决于相位累加器的位数,但是由于这里我取的是一个周期,所以总的相位就限制为$2\pi$:
$$ P_{min} = \frac{2\pi}{N} $$
我个人比较喜欢用角度制(信号发生器嘛):
$$ P_{min} = \frac{360}{N} $$

自然,这个视ROM表存储的不同而不同,这里仅仅介绍个最简单的应用

调相位

实际上,为了实现两路信号的相位差,我们只要设计两个相位累加器的初值不一样就完事了,所以大家会看见我的框图中存在一条soft_rst线用来进行调相之后的软复位.

  1. 比如假设我们想产生90度的相差,设总点数为8192点,则:
    $$ \Delta N = \frac{90}{\frac1{8192}*360} $$
  2. 180度:
    $$ \Delta N = \frac{180}{\frac1{8192}*360} $$

当然,在调相这方面,存在连续调相和定点调相两种方法,通过公式对比大家可以发现,连续调相相位差越调越大,定点调相相位越大误差越小,那怎么克服这一点呢?就在这里给大家留下一个博后习题了.

调频率

这个就特别简单了,在前面说过,我们调频的范围在$DC-f_{max}$,所以只要做一个程控分频器就完事了,程序中我为了测试FPGA的极限参数,使用PLL例化了450M的时钟.输出频率其实跟上面的最高频率计算方法一样

这个就没什么连续和定点的区别了,比较简单

FPGA实现简单DDS

这里由于做一个简单应用,所以调频调相模块我没有放上来,有兴趣的朋友可以私信博主获取源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
module read_data_rom(
input clk,rst_n,
input soft_rst,
input [12:0] phase_control,
output wire [13:0] data_out
);

reg [12:0] address_reg;
always @(posedge clk or negedge rst_n) //相位累加器
begin
if(!rst_n)
address_reg <= 13'b0;
else if (soft_rst)
address_reg <= phase_control;
else
address_reg <= address_reg + 1'b1;

end


sin_data sin_data_inst ( //调用的一个小ROM
.address ( address_reg ),
.clock ( clk ),
.q ( data_out )
);

endmodule

仿真结果

c

结语

其实这里还是有很多东西可以讲的,但是真的不慎研究了几天写了一个小算法,发现之前的人没有想过这个思路,就去找个弟弟写专利去了,在这里没法详述,等以后申请了之后再给大家细细聊聊怎么优化这个粗糙得一匹的信号发生器

欢迎大家有什么新想法(无论是DDS还是有什么算法想用FPGA实现的)都可以私信我大家聊一聊喔

下一篇应该就是用FPGA实现各种通信编码,大家无妨投一个关注

如果你觉得有丶收获的话