本次就来讲讲上篇文章中载波发生器实现的原理、方法以及创建IP核的步骤。
目录
**********************************************************************************************************************************************************
前言
载波,通信领域中作为被调制信号的波形,通常形式为正弦波。
其作用包括:
1.减小传输信号在传输中的噪声。
2.实现频分复用,即同一信道中传输多路信号。
3.将信号调制后,传播距离更远,更有利于接收。
因此载波在通信领域起着重要作用,今天我们就来聊聊如何实现载波的产生。
一、DDS核心原理
1.如何获取正弦波信号并储存数据
由于IP核里只能存储离散的数值,因此我们需要对连续正弦波进行采样并量化。根据奈奎斯特采样定理,采样频率f≥2f0。因此,采样频率越高,采样间隔越小,则采样后的波形更接近原波形。因此我们只需要选择合适的采样间隔进行采样,并且对采样的模拟幅度进行量化,量化后的幅值采用相应的二进制数据编码。这样就把一个周期的正弦波连续信号转换成为一系列离散的二进制数字量,然后通过一定的手段固化在只读存储器ROM中,每个存储单元的地址即相位取样地址,存储单元的内容是已经量化的正弦波幅值。这样的一个只读存储器就构成了一个与2Pi周期内相位取样相对应的正弦函数表,因它存储的是一个周期的正弦波波形幅值,因此称其为正弦波形存储器,又称作查找表。
2.如何改变正弦信号的频率
因为一个周期的正弦波采样值已经存入IP核,我们只需对表内的数值依次读取输出,此过程就是寻址过程。读完一次就形成一个周期的正弦波,不断重复读取表内数据就会输出连续的正弦波了。那不难知道,我们读完一次表所用的时间就是正弦波的一个周期。假设采样点有256个,设频率控制字为K,我们用32位寄存器作为地址变量,其高8位作为地址位(因为有256个地址,所以需要8位),低24位作为频率控制位。以系统时钟(50MHz)进行计数的话,只有当低24位向前进位后地址位才+1,因此正弦波频率就和时钟频率形成了成倍的关系。即,
3.如何改变初始相位
这个问题比较简单,只需要改变初始地址即可。例如,我们想要余弦波,那只需将正弦波的初始相位改为pi/2,即从波的1/4处开始读取数值;假如我们采样值有256个,那我们只需将初始地址改为256/4=64即可。
二、实现流程
1.正弦波的采样与存储
利用MATLAB进行采样,我选取的是200个采样点,幅值为128。
代码如下:
clc; %清除命令行命令
clear all; %清除工作区变量,释放内存空间
F1=1; %信号频率
Fs=199; %采样频率
P1=0; %信号初始相位
N=200; %采样点数
t=[0:1/Fs:(N-1)/Fs]; %采样时刻
ADC=0; %直流分量
A=2^7; %信号幅度
%生成正弦信号
s=A*sin(2*pi*F1*t + pi*P1/180) + ADC;
plot(s); %绘制图形
grid;
%创建 mif 文件
fild = fopen('sin_rom_256x8.mif','wt');
%写入 mif 文件头
fprintf(fild, '%s\n','WIDTH=8;'); %位宽
fprintf(fild, '%s\n\n','DEPTH=256;'); %深度
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) = fix(s(i)); %对小数四舍五入以取整
if s0(i) <0
s0(i) = s0(i)+2^8;%负数转换为补码
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文件如下,可以看出采样的200个数值正确
2.添加IP核(存入数据)
①创建工程目录
此处步骤省略。
②添加IP核
请提前把mif文件放到工程目录下
点击Tools→Mega Wizard Plug-In Manager
点击next
左边选择ROM:1-PORT,右边名字自定义取。完成后点击next。
这里的规格意思是:8位,第一位是符号位,也就是存储数据的取值范围是-2^7~2^7
256指的是IP核的地址个数,也就是能存256个。
因为我们有200个数据,则需要至少256个地址才足够存储,而最大值是128,最小值为-128,故需要8bits。
完成后点击next
点击next
点击Browse
下面文件类型选择MIF Files,再选取跳出来的mif文件。
点击next
只勾选倒数第二个,点击finish。
IP核的创建就完成了。
3.寻址读取数据(相位累加器)
话不多说,直接上代码:
module DDS_wave(
input clk,
input rst,
output wire [7:0] sin_wave,
output wire [7:0] cos_wave
);
wire [7:0] addr_1;
wire [7:0] addr_2;
reg [31:0] F_cnt1;
reg [31:0] F_cnt2;
parameter F_WODR=335544;//频率控制字=(fsin*2^24*200)/50M 此处我选择输出5KHz的正弦波
parameter P_WODR=50;//相位控制字
always@(posedge clk or posedge rst)
begin
if(rst)
F_cnt1 <= 32'd0;
else if(F_cnt1[31:24]==8'd199) //循环寻址
F_cnt1 <= 32'd0;
else
F_cnt1 <= F_cnt1 + F_WODR;//频率控制
end
always @(posedge clk or posedge rst)
begin
if(rst)
F_cnt2 <= {P_WODR, 24'd0};//设置初始相位
else if(F_cnt2[31:24]==8'd199)
F_cnt2 <= 32'd0;
else
F_cnt2 <= F_cnt2 + F_WODR;//频率控制
end
assign addr_1 = F_cnt1[31:24];
assign addr_2 = F_cnt2[31:24];
wave_rom wave_rom_inst( //例化IP核
.address (addr_1),
.clock (clk),
.q (sin_wave)
);
wave_rom wave_rom_inst2(
.address (addr_2),
.clock (clk),
.q (cos_wave)
);
endmodule
也就是利用计数来寻址,很容易看懂,例化IP核的模块名就是之前自定义取的得一样。
三.仿真验证
测试代码:
`timescale 1 ns/ 1 ns
module DDS_wave_vlg_tst();
reg clk;
reg rst;
wire [7:0] cos_wave;
wire [7:0] sin_wave;
DDS_wave i1 (
.clk(clk),
.cos_wave(cos_wave),
.rst(rst),
.sin_wave(sin_wave)
);
initial
begin
#0 clk=0;rst=1;
#25 rst=0;
#10000000 $stop();
end
always #10 clk=~clk; //50MHz
endmodule
仿真结果:
频率验证:
测得T=200088ns,则f=1/T=4998Hz,比较接近预想的5KHz。
总结
以上就是今天的DDS信号发生器内容,本文仅仅简单介绍了正余弦信号的产生,同时也可以按照同样的方法改变mif文件来改变输出信号,例如方波、三角波等常用信号。