1. 概述

    FPGA具有高度实时的特性。这里讨论基于FPGA设计一款简单的正弦信号发生器。


  2. DDS原理

    Direct Digital frequency Synthesis如下图所示:

    wKioL1faz36yECMGAABN7WNQ_2A773.jpg


3. DAC原理

这里DAC采用TLC5620,下面分别给出DAC的原理图和时序图。

wKiom1fa0YnjorWpAACq6sE1bq4849.jpg

wKiom1fa0cvBWpSuAABusQIHoeM399.jpg


4. ROM文件的生成

ROM波形可以通过MIF或HEX文件保存在FPGA的ram或rom模块中,也可以自己编写HDL文件存储。这里我们采用后者。

利用win-tcmatlab生产所需格式的函数数据,参考C代码如下:

#include "stdio.h"
#include "math.h"
#definePi 3.1416
#defineDEPTH 256
#defineLENTH DEPTH/2
intmain()
{
    FILE*fp;
    int j;
    unsigned char i= 0;
    unsigned char x= 0;
    if((fp=fopen("d:\\sin.txt","w"))==NULL)
    {
        printf("can't open this file..\n");
        exit(0);
    }
    for(j=0;j<DEPTH;j++)
    {
        x=(int)(LENTH+LENTH*sin(2*Pi*i/DEPTH - 0.5*Pi));
        fprintf(fp," 'd%d: data = 'h%x;\n",i,x);
        i++;
    }
    fprintf(fp,"\n");
    fclose(fp);
    printf("success\n");
    return 0;
}


5. DAC控制逻辑设计

module dac_ctrl(
		input clk,
		input rst_n,
		
		output reg dac_clk,
		output reg dac_load,
		output reg dac_ldac,
		output reg dac_dat,
		
		input [7:0] rom_dat,
		output reg [7:0] rom_addr,
		
		input [7:0] freq_ctrl
);

//=====================================
//The frequency of clk is divided by N
parameter bitsize = 4;
parameter N = 20;

reg [bitsize:0] cnt0;

always @(posedge clk or negedge rst_n)
begin 
	if(!rst_n)
	begin
		cnt0 <= 0;
		dac_clk <= 0;
	end
	else
	begin
		if(cnt0 < (N/2-1)) 
			cnt0 <= cnt0 + 1'b1;
		else
			cnt0 <= 0;
		if(cnt0==0 && cnt1 >=1 && cnt1 <= 4'hb) 
			dac_clk <= ~ dac_clk;
		else
			dac_clk <= dac_clk;
	end
end

wire clk_1M;

assign clk_1M = (cnt0 == 0)?1'b1:1'b0;

//======================================

reg [3:0] cnt1;

always @(posedge clk or negedge rst_n)
begin 
	if(!rst_n)
		cnt1 <= 0;
	else
		if(clk_1M)
			cnt1 <= cnt1 + 1'b1;
		else
			cnt1 <= cnt1;
end

reg [7:0] cnt2;

always @(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		cnt2 <= 0;
	else
		if(cnt1 == 4'hf)
			if(cnt2 != freq_ctrl)
				cnt2 <= cnt2 + 1'b1;
			else
				cnt2 <= 0;
		else
			cnt2 <= cnt2;
end
		
always @(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		rom_addr <= 0;
	else
		if(clk_1M && cnt1==4'hf && cnt2 == freq_ctrl)
			rom_addr <= rom_addr + 1'b1;
		else
			rom_addr <= rom_addr;
end

always @(cnt1)
begin
	case(cnt1)
	4'h1: begin
				dac_dat <= 1'b0;
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end
	4'h2: begin
				dac_dat <= 1'b0;
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end
	4'h3: begin
				dac_dat <= 1'b1;
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end
	4'h4: begin
				dac_dat <= rom_dat[7];
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end
	4'h5: begin
				dac_dat <= rom_dat[6];
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end
	4'h6: begin
				dac_dat <= rom_dat[5];
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end			
	4'h7: begin
				dac_dat <= rom_dat[4];
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end			
	4'h8: begin
				dac_dat <= rom_dat[3];
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end			
	4'h9: begin
				dac_dat <= rom_dat[2];
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end			
	4'ha: begin
				dac_dat <= rom_dat[1];
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end			
	4'hb: begin
				dac_dat <= rom_dat[0];
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end	
	4'hc: begin
				dac_dat <= 1'b0;
				dac_load <= 1'b0;
				dac_ldac <= 1'b1;
			end	
	4'hd: begin
				dac_dat <= 1'bx;
				dac_load <= 1'b1;
				dac_ldac <= 1'b0;
			end	
	default: 
			begin
				dac_dat <= 1'bx;
				dac_load <= 1'b1;
				dac_ldac <= 1'b1;
			end
	endcase
end
			
endmodule


6. 测试结果

wKiom1fa0xKRhCFDAADrC6APUzM357.jpg