数字设计项目一:直接信号发生器(DDS)产生爱心波
前言
DDS,即Direct Digital Synthesis ,直接数字频率合成器。我们常常用DDS配合DAC芯片来产生信号波形,其原理也十分简单:就是把目标波形上每个点的值逐个输出到DAC芯片上去以此产生模拟波形。
DDS主要有两种合成方式:查表法和计算法。打个比方就像用Oled显示一个圆圈一样,一种方法是把装在内存里的圆圈图片,一行一行地输出到显示屏上显示,这就是所谓查表法,计算法就是利用程序实时计算出每个点的数值,然后在Oled上打点显示。
下面我们直接来看DDS的基本结构:
图片来源网络,侵删。
初看有些迷糊,但是其实结构很简单,主要的部件就是虚线框里的相位累加器。把相位累加器当成一个黑盒,它的输入是频率控制字,相位控制字,输出是波形存储器地址。
频率控制字和相位控制字的作用是什么?举个例子就明白了:
值 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
地址 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
频率控制字是每个时钟周期反复累加的,即 K(x = t) = Kt(t为时间)假设相位控制字P为0,K=2,那么一个周期内,输出到波形存储器的地址的变换就为:0、2、4、6、8、10,输出的值为10、12、14、16、18、20。这样输出一个周期的波形就要6个时钟周期,很容易计算出实际输出的周期T和K的关系为: T = (12/K)*Tclk,同理当波形存储器有n个数据时,T = (n/K)*Tclk。
这时如果相位控制字为1的话,那么相应地址变换就为 0+1 = 1,2+1 = 3,4+1 =5 ,9,11。可以看到,相位控制字并不随时间增加而增加。因此可以得到总的地址索引数 = K*t + P,同时我们也可以理解,相位控制字并不会影响输出波形的频率(此时步进间距依然为Δ = K),但整体的波形就好像左移或者右移动了,这就是相位控制字控制相位的原理。
从上面的分析,可以得到几个重要的结论:
- 最小的频率Tmin = n *Tclk (n为查找表元素个数,Tclk为时钟周期,此时K = 1)
- 输出波形频率与K的关系: Tout = (n/K)*Tclk
实战
先利用matlab采集波形信号,保存成mif文件,再由quartus IV ROM IP 核读取,并由modelsim 仿真实现。
%matlab 爱心波产生程序
% 数据深度2^12
N=4069;
% 数据宽度2^7
P=128;
a=10;
a5(1:1:N)=0;
b5=1:1:N;
figure(5);
a5(b5) = real(round((P/4-4)*(abs((b5-N/2)/1000).^(2/3)+(0.9*sqrt((3.3-((b5-N/2)/1000).^2))).*sin(a*pi*((b5-N/2))/1000)+1.8)));
plot(b5,a5,'r','LineWidth',2);
axis([0, N, 0, P]);
%创建 mif 文件
fild = fopen('sin_wave_4096x8.mif','wt');
%写入 mif 文件头
fprintf(fild, '%s\n','WIDTH=8;'); %位宽
fprintf(fild, '%s\n\n','DEPTH=4096;'); %深度
fprintf(fild, '%s\n','ADDRESS_RADIX=UNS;'); %地址格式
fprintf(fild, '%s\n\n','DATA_RADIX=UNS;'); %数据格式
fprintf(fild, '%s\t','CONTENT'); %地址
fprintf(fild, '%s\n','BEGIN'); %开始
for i = 1:N
s0(i) = round(a5(i)); %对小数四舍五入以取整
if s0(i) <0 %负 1 强制置零
s0(i) = 0
end
fprintf(fild, '\t%g\t',i-1); %地址编码
fprintf(fild, '%s\t',':'); %冒号
fprintf(fild, '%d',s0(i)); %数据写入
fprintf(fild, '%s\n',';'); %分号,换行
end
fprintf(fild, '%s\n','END;'); %结束
fclose(fild);
运行之后可以看见产生如下图像,以及一个mif文件。
下面给出整体架构的Verilog代码
module sin_test(
input wire clk,
input wire reset,
input wire [31:0] Fword,
output wire [7:0] sin_out
);
wire [11:0] Pword;
assign Pword = 12'd0;
wire [7:0] wave_data;
reg [7:0] wave_data_r;
reg [31:0] Fcnt;
wire [11:0] rom_addr;
assign sin_out = wave_data_r;
always@(posedge clk or posedge reset )begin
if(reset == 1'b1)
Fcnt <= 32'd0;
else
Fcnt <= Fcnt + Fword;
end
assign rom_addr = Fcnt[31:20] + Pword;
love_rom rom(
.address(rom_addr),
.clock(clk),
.q(wave_data)
);
always@(posedge clk)begin
wave_data_r <= wave_data;
end
endmodule
其中的love_rom需要例化quartus的IP核,这一部分我会在下一篇文章中介绍,不懂的可以去网上查,这里不作详细展开。下面是仿真测试的代码
`timescale 1ns/1ps
module tb_sin_test();
reg clk;
reg reset;
reg [31:0] Fword;
wire [7:0] sin_out;
initial begin
clk = 1'b0;
reset = 1'b0;
Fword = 32'd8589933;
#20 reset = 1'b1;
#20 reset = 1'b0;
end
always begin
#10 clk = ~clk;
end
sin_test sin1(
.clk(clk),
.reset(reset),
.sin_out(sin_out),
.Fword(Fword)
);
endmodule
在modelsim内运行后,观察波形,将sin_out变量用图像显示,即可得到下面的波形输出:
至此,功能得以实现;
路漫漫其修远兮,吾将上下而求索。
相关文章
- 软件项目需求变更频繁,如何做好有效的需求管理和规划
- 把项目放到码云上,通过git 进行项目管理
- vue之cli脚手架安装和webpack-simple模板项目生成
- Hadoop项目实战-用户行为分析之分析与设计
- Kafka项目实战-用户日志上报实时统计之分析与设计
- Kafka项目实战-用户日志上报实时统计之分析与设计
- C#项目代码规范
- 项目的权限设计的小计
- Github 上优秀的 Java 项目推荐
- 路飞学城项目-登陆认证模块
- 【NLP】Python实例:基于文本相似度对申报项目进行查重设计
- IntelliJ IDEA教程之如何clean或者install Maven项目
- Atitit.mybatis的测试 以及spring与mybatis在本项目中的集成配置说明
- 【C语言项目设计】简单的猜拳游戏(与电脑对战)
- 【C语言项目设计】2048
- 【C语言项目设计】学生成绩管理系统设计
- 【C语言项目设计】趣味算术游戏设计
- 项目章程
- 开源之夏 2022 火热来袭 | 欢迎报名 OpenMLDB 社区项目~
- 【项目实战】WebFlux整合r2dbc-mysql实战
- 解决IDEA 运行项目出现的错误:找不到或无法加载主类
- 【2022世界杯开源项目实战】使用docker部署world-cup-2022-cli-dashboard数据看板工具