数字信号发生器设计

该模块设计的的是一个数字信号发生器,实现的功能有:
波形选择:可以通过按键进行正弦波,方波,三角波,锯齿波进行切换;
波的频率切换:通过按键控制波的频率加减,范围是0~1MHZ;
波的频率显示:通过数码管对波的频率进行展示;

该模块分成四部分,其分别是:按键消抖模块、波形选择模块、信号发生模块、数码管显示模块。

其中的按键消抖模块是和之前的利用状态机消抖方法不一样,借用的是野火的消抖,比较粗暴,其代码如下:

*******************************************************************//

//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else    if(key_in == 1'b1)
        cnt_20ms <= 20'b0;
    else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 1'b1;

//key_flag:当计数满20ms后产生按键有效标志位
//且key_flag在999_999时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else    if(cnt_20ms == CNT_MAX - 1'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

然后就是通过按键进行波形的切换模块,该模块就是通过按下key0,不断对波形进行切换,按一次切换一次,其主要代码如下:

assign   add_flag = key1;
assign   sub_flag = key2;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//wave:按键0状态对应波形
reg  [2:0]    wave_flag;
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(sys_rst_n == 1'b0)begin
        wave_flag   <=  2'd0;
    end
    else if(key0 && wave_flag >= 2'd3)begin
        wave_flag <= 2'd0;
    end
    else if(key0)begin
        wave_flag <= wave_flag + 1'd1;
    end
    else begin
        wave_flag <= wave_flag;
    end
end

always@(posedge sys_clk or negedge sys_rst_n)begin
    if(sys_rst_n == 1'b0)begin
        wave_select <= 4'b0000;
    end
    else begin
        case (wave_flag)
            2'd0: wave_select <= sin_wave;
            2'd1: wave_select <= squ_wave;
            2'd2: wave_select <= tri_wave;
            2'd3: wave_select <= saw_wave; 
            default: wave_select <= sin_wave;
        endcase
    end
end

最主要的就是信号发生模块,DDS 模块有 5 路输入信号,2 路输出信号,其内部例化了前面生成的波形数据表 ROM;输入信号中有时钟 sys_clk、复位 sys_rst_n 和按键控制模块输入的波形选择信号 wave_select、控制波的频率加减的信号add_flag和sub_flag。输入的波形选择信号有 4 种状态,分别对应 4 中波形,根据输入的波形选择信号的不同,对 ROM 中波形选择信号对应波形的存储位置进行数据读取,将读出波形幅值数据通输出信号 data_out 输出到外部挂载 DA 板块,进行数模转换。 其主要代码如下:

module  dds
(
    input   wire            sys_clk     ,   //系统时钟,50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire    [3:0]   wave_select ,   //输出波形选择
    input   wire            add_flag    ,   //输出频率加1000Hz标志
    input   wire            sub_flag    ,   //输出频率减1000Hz标志
    output  wire    [7:0]   data_out    ,    //波形输出
    output  wire    [19:0]  f1_out        //输出数据频率
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   sin_wave    =   4'b0001     ,   //正弦波
            squ_wave    =   4'b0010     ,   //方波
            tri_wave    =   4'b0100     ,   //三角波
            saw_wave    =   4'b1000     ;   //锯齿波
parameter   PHASE_CTRL  =   12'd1024    ;   //相位偏移量

//reg   define
reg     [31:0]  fre_add     ;   //相位累加器
reg     [11:0]  rom_addr_reg;   //相位调制后的相位码
reg     [13:0]  rom_addr    ;   //ROM读地址

reg     [19:0]  f_out; //输出数据频率
reg     [39:0]  FREQ_CTRL ;//相位累加器单次累加值  计算方式 K = 2 ** 32(fre_add的宽度) * f(out) / f(clk) ,要实现频率可调,需要改变该值。
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

assign f1_out = f_out;
//f_out   :输出波形频率设置,起始值为0hz ,最大值为1Mhz
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)begin
        f_out <= 20'd0;
    end
    else if(add_flag && f_out >= 1000)begin  //当频率为1M时,再次按下增加,信号保持不变
        f_out <= f_out;  
    end
    else if(sub_flag && f_out == 0)begin  //当频率为1M时,再次按下减少,信号保持不变
        f_out <= f_out;  
    end
    else if(add_flag)begin
        f_out <= f_out + 20'd1;  
    end
    else if(sub_flag)begin
        f_out <= f_out - 20'd1;
    end
    else begin
        f_out <= f_out;
    end
end

reg [19:0]  f_out1;
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)begin
        f_out1 <= 20'd0;
    end
    else begin
        f_out1 <= f_out;
    end
end

//FREQ_CTRL: 通过按键增加或减少该值,来实现输出信号的频率控制。
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)begin
        FREQ_CTRL <= 40'd0;
    end
    else if(f_out != f_out1)begin
        FREQ_CTRL <= ((1 << 32) * f_out ) / 50000; //此处应该除以50000000,但是没按键增减都是1000,所以f_out同样除以1000,以减少计算量
    end
    else begin
        FREQ_CTRL <= FREQ_CTRL;
    end
end

//fre_add:相位累加器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        fre_add <=  32'd0;
    else
        fre_add <=  fre_add + FREQ_CTRL;

//rom_addr:ROM读地址
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            rom_addr        <=  14'd0;
            rom_addr_reg    <=  11'd0;
        end
    else
    case(wave_select)
        sin_wave:
            begin
                rom_addr_reg    <=  fre_add[31:20] + PHASE_CTRL;
                rom_addr        <=  rom_addr_reg;
            end     //正弦波
        squ_wave:
            begin
                rom_addr_reg    <=  fre_add[31:20] + PHASE_CTRL;
                rom_addr        <=  rom_addr_reg + 14'd4096;
            end     //方波
        tri_wave:
            begin
                rom_addr_reg    <=  fre_add[31:20] + PHASE_CTRL;
                rom_addr        <=  rom_addr_reg + 14'd8192;
            end     //三角波
        saw_wave:
        begin
                rom_addr_reg    <=  fre_add[31:20] + PHASE_CTRL;
                rom_addr        <=  rom_addr_reg + 14'd12288;
            end     //锯齿波
        default:
            begin
                rom_addr_reg    <=  fre_add[31:20] + PHASE_CTRL;
                rom_addr        <=  rom_addr_reg;
            end     //正弦波
    endcase

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------- rom_wave_inst ------------------------

rom_wave    rom_wave_inst
(
  .clka(sys_clk), // input clka
  .addra(rom_addr), // input [13 : 0] addra
  .douta(data_out) // output [7 : 0] douta
);

endmodule

然后就是数码管显示信号频率模块了,这个和其他的数码管没什么区别,同样是共阳极驱动,代码如下:

module seg_ctrl (
    input               clk  ,
    input               rst_n,
    input   wire[19:0]  f1_out,
    output    reg[7:0]  seg  ,
    output    reg[5:0]  sel
);
 
parameter MIN_NUM = 20'd15_0000; //3ms计数器
reg [19:0] cnt_3ms; //3ms计数器,用于位选信号切换计时
reg [3:0]flag; //位选信号状态设置。

wire [5:0] ones;
wire [5:0] tens;
wire [5:0] hundreds;
wire [5:0] thousands;
wire [5:0] ten_thousands;
wire [5:0] hundred_thousands;


assign    ones = (f1_out* 1000) % 10;
assign    tens = (f1_out * 100) % 10;
assign    hundreds = (f1_out* 10) % 10;
assign    thousands = (f1_out) % 10;
assign    ten_thousands = (f1_out / 10) % 10;
assign    hundred_thousands = f1_out  / 100;

//3ms计时模块,计满位选信号取反
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_3ms <= 20'd0;
        flag <= 4'd0;
    end
    else if(cnt_3ms == MIN_NUM - 1'd1 && flag <=5)begin
        cnt_3ms <= 20'd0;
        flag <= flag + 1'd1;
    end
    else if(cnt_3ms == MIN_NUM - 1'd1)begin
        cnt_3ms <= 20'd0;
        flag <= 4'd0;
    end
    else begin
        cnt_3ms <= cnt_3ms + 1'd1;
        flag <= flag;
    end
end
 
always @(*) begin //位选信号设置
    case (flag)
        4'd0: sel = 6'b011_111;
        4'd1: sel = 6'b101_111;
        4'd2: sel = 6'b110_111;
        4'd3: sel = 6'b111_011;
        4'd4: sel = 6'b111_101;
        4'd5: sel = 6'b111_110; 
        default: sel = 6'b111_111;
    endcase
end
reg [3:0] number; //寄存数字。
always @(*) begin //对每个数码管进行赋值。
   case (flag)
       4'd0:number  = ones;
       4'd1:number  = tens;
       4'd2:number  = hundreds;
       4'd3:number  = thousands;
       4'd4:number  = ten_thousands;
       4'd5:number  = hundred_thousands;
       default: ;
   endcase 
end
always @(*) begin
    if(!rst_n)begin
        seg = 8'b1100_0000;
    end
    else begin
         case(number)//匹配16进制数
			4'h0:    seg = 8'b1100_0000;//匹配到后参考共阳极真值表
	        4'h1:    seg = 8'b1111_1001;
	        4'h2:    seg = 8'b1010_0100;
	        4'h3:    seg = 8'b1011_0000;
	        4'h4:    seg = 8'b1001_1001;
	        4'h5:    seg = 8'b1001_0010;
	        4'h6:    seg = 8'b1000_0010;
	        4'h7:    seg = 8'b1111_1000;
	        4'h8:    seg = 8'b1000_0000;
	        4'h9:    seg = 8'b1001_0000;
	      	default : seg = 8'b1100_0000;
	    endcase
    end
end
endmodule

仿真代码:

`timescale  1ns/1ns
module  tb_top_dds();

//**************************************************************//
//*************** Parameter and Internal Signal ****************//
//**************************************************************//
parameter   CNT_1MS  = 20'd19000   ,
            CNT_11MS = 21'd69000   ,
            CNT_41MS = 22'd149000  ,
            CNT_51MS = 22'd199000  ,
            CNT_60MS = 22'd249000  ;

//wire  define
wire            dac_clk     ;
wire    [7:0]   dac_data    ;

//reg   define
reg             sys_clk     ;
reg             sys_rst_n   ;
reg     [21:0]  tb_cnt      ;
reg             key_in      ;
reg     [1:0]   cnt_key     ;
reg     [3:0]   key         ;

//defparam  define
defparam    top_dds_inst.key_control_inst.CNT_MAX = 24;

//**************************************************************//
//************************** Main Code *************************//
//**************************************************************//
//sys_rst_n,sys_clk,key
initial
    begin
        sys_clk     =   1'b0;
        sys_rst_n   <=   1'b0;
        key <= 4'b1111;
        #200;
        sys_rst_n   <=   1'b1;
        #1000;
        key  = 4'b1101;
        #1000 ;
        key = 4'b1111;
                #5000000;
        key  = 4'b1101;
        #1000 ;
        key = 4'b1111;
         #5000000;
        key  = 4'b1110;
        #1000 ;
        key = 4'b1111;
                 #5000000;
        key  = 4'b1110;
        #1000 ;
        key = 4'b1111;
                 #5000000;
        key  = 4'b1110;
        #1000 ;
        key = 4'b1111;
    end

always #10 sys_clk = ~sys_clk;

//**************************************************************//
//************************ Instantiation ***********************//
//**************************************************************//
//------------- top_dds_inst -------------
top_dds top_dds_inst
(
    .sys_clk    (sys_clk    ),
    .sys_rst_n  (sys_rst_n  ),
    .key        (key        ),

    .dac_clk    (dac_clk    ),
    .dac_data   (dac_data   )
);

endmodule

仿真波形如下所示:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值