matlab与FPGA联合产生一个正弦信号发生器(DDS原理介绍)

3 篇文章 0 订阅
1 篇文章 0 订阅

正弦信号的产生流程

正弦信号离散化

1.首先使用matlab产生一个正弦信号,并将连续的正弦信号离散化
2.将离散化的正弦波信号一个整周期存储到RAM中
2.1 将matlab中的正弦波数据导出进行定点化,RAM的规格设置为256*8.数据规格为1位符号位,7位小数位。
负数放入到RAM中都是以补码的形式存放的
负数的补码计算简便方法:对于1个八位数的数据,怎么求他负数的补码呢?
假设附属为-127;则它对应的十进制的补码形式位256-127=129;则129就是-127的补码形式。
2.2 创建一个RAM,存放离散化的数据
创建一个单口的就可以了,因为我们只需要输出数据。
我们需要先创建一个mif文件,对这个ram进行初始化,建立好RAM之后,先随便修改几个值,然后打开这个文件,看看这个文件里面的格式是什么样的,然后再利用matlab去写这样的一个文件。创建的时候要使用fope与fprintf函数,如果不知道这两个函数的使用方法,则在matlab的命令窗口输入 help fope与help fprintf就可以跳出这两个函数的帮助文档。

matlab的程序

clc;
clear;
N=2^8;  %采样频率,1秒钟采多少个点
s_p = 0:255; %采样点数
t=s_p/N;    
sin_data=sin(2*pi*t);
% plot(t,sin_data);
%定点化(这个定点化小数位数是7位,符号位是1位;当然这个具体的根据自己的需要去设置,和采样频率没啥关系)
fix_sin_data=fix(sin_data*127);%这个的出来的是带正负号的定点数还要想办法把负数的副号去掉
for i=1:N
    if fix_sin_data(i)<0
        fix_sin_data(i) = N+fix_sin_data(i);
    else
        fix_sin_data(i) = fix_sin_data(i);
    end
    
end
fid=fopen('sp_ram_256_8.mif','w+');
%这些都是根据生成的初始化.mif的文件格式写出来的
fprintf(fid,'WIDTH=8;\n');
fprintf(fid,'DEPTH=256;\n');
fprintf(fid,'ADDRESS_RADIX=UNS;\n');
fprintf(fid,'DATA_RADIX=UNS;\n');
fprintf(fid,'CONTENT BEGIN\n');
for i=1:N
    fprintf(fid,'%d: %d;  \n',i-1,fix_sin_data(i));
end
fprintf(fid,'end; \n');
fclose(fid);

使用matlab生成的.mif文件来替换之前在quartus中生成的.mif文件,在使用这个文件对ram或rom的IP核进行初始化
然后建立顶层文件对IP核进行初始化。

Verilog程序

.v文件

module    ex_dds(
        input        wire        sclk,
        input         wire        rst_n,
        output        wire    [7:0]    o_wave        


);

reg    [7:0]    addr;

always    @(posedge    sclk or negedge rst_n)
        if(rst_n == 1'b0)
                addr <= 0;
        else
                addr <= addr+1'b1;    //这个地址的地增量和我们的频率是成正比的,也叫做频率控制字
                
sp_ram    sp_ram_inst (
    .address ( addr ),
    .clock ( sclk ),
    .data ( 8'd0 ),    //输入数据
    .wren ( 1'b0 ),    //写使能让他为0
    .q ( o_wave )
    );
endmodule

tb文件

`timescale    1ns/1ns


module    tb_ex_dds;


        reg        sclk;
        reg        rst_n;
        wire    [7:0]    o_wave;


initial    begin
        sclk=0;
        rst_n = 0;
        #100
        rst_n = 1;

end
always    #10 sclk=~sclk;

ex_dds        ex_dds_inst(
        .sclk(sclk),
        .rst_n(rst_n),
        .o_wave(o_wave)    

);

endmodule

在进行仿真时要注意的问题是对生成IP核的.v文件中的初始化.mif文件的位置进行修改。
在这里插入图片描述
因为在进行仿真时会默认根目录为sim文件夹下,如果不进行从修改的话可能会造成找不到文件,还有一种方法就是将mif文件拿到他原来默认的文件夹下
在这里插入图片描述

上面的这个程序生成的正弦信号的频率就是50M/256,如果地址每次累加的数据改变了,则正弦信号的频率也就跟着改变了

DDS的原理介绍

DDS的基本结构

在这里插入图片描述
M就相当于来一个时钟信号我的地址加M个数字,所以M是控制目标频率的一个数据。当M=2的时候就相当于1个时钟沿加了2个地址进去,那么时间周期就缩短了原来的一半
这个 n 指的就是相位累加器的位宽,在这次的实验中是8,当然可以根据需要去进行设置
频率分辨率和什么有关? 和我们相位累加器的位宽有关系。比如在这个系统里面,频率分辨率为50M/256,可以看到整个系统的频率分辨率的数值很高,效果很差。所以在正常使用的时候通过提高相位累加器的位宽来提高频率分辨率。
整个RAM的深度为256;那么整个RAM的深度和什么有关系呢? 存储正弦波的RAM深度或者叫正弦波一个周期量化的点数,量化的点数越多生成的正弦波的相位噪声越低

下面就把频率控制字加进去
设这次相位累加器的位宽为32位
接下来输出一个信号频率为1MHz的正弦信号
信号的频率 f0=M * Fc /2^32
从而可以反向求得M=1M*2^32/50M ~= 85,899,346

相位的设置

可以通过设置初始相位来实现
在这里插入图片描述
这个是通过控制相位控制字来控制信号的振荡频率
未加相位初始值
在这里插入图片描述

.v文件

module    ex_dds(
        input        wire        sclk,
        input         wire        rst_n,
        output        wire    [7:0]    o_wave        


);
parameter    FRQ_W = 32'd85899346;
parameter    FRQ_ADD = 32'd85899346/2;
parameter    INI_phase = 32'd2147483648;//设置这样一个初始相位,可以起到使相位发生移动的情况,这个是2^31,所以对应相位应该反转
reg        [31:0]    phase_sum;
wire        [7:0]    addr;
reg        [31:0]    frq_word;
reg        [6:0]    div_cnt;    //一般情况下都不拿计数器的值去作为某一个组合逻辑的判断条件,这比较容易出现竞争和冒险
reg                div_flag;    //这个就相当于只用一根线来作为判断条件,比较稳定,不易出错


/* always    @(posedge    sclk or negedge rst_n)
        if(rst_n == 1'b0)
                addr <= 0;
        else
                addr <= addr+1'b1; */


always @(posedge sclk or negedge rst_n)
        if(rst_n == 1'b0)
                div_cnt<='d0;
        else if(div_cnt == 7'd99)
                div_cnt <= 'd0;
        else
                div_cnt <= div_cnt + 1'b1;


always @(posedge sclk or negedge rst_n)
        if(rst_n == 1'b0)
                div_flag <= 1'b0;
        else if(div_cnt == 7'd99)
                div_flag <=1'b1;
        else
                div_flag<=1'b0;


always @(posedge sclk or negedge rst_n)
        if(rst_n == 1'b0)
                frq_word <= FRQ_W;
        else if(div_flag == 1'b1)
                frq_word <= frq_word + FRQ_ADD;
                
always @(posedge sclk or negedge rst_n)
                if(rst_n == 1'b0)
                //    phase_sum <= 'd0;
                    phase_sum <= INI_phase;    //通过改变初始相位来进行位移
                else
                    phase_sum <= phase_sum + frq_word;


assign    addr = phase_sum[31:24]; //成比例的将数据从RAM中拿出去                    
    
sp_ram    sp_ram_inst (
    .address ( addr ),
    .clock ( sclk ),
    .data ( 8'd0 ),    //输入数据
    .wren ( 1'b0 ),    //写使能让他为0
    .q ( o_wave )
    );


endmodule
  • 3
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值