该模块设计的的是一个数字信号发生器,实现的功能有:
波形选择:可以通过按键进行正弦波,方波,三角波,锯齿波进行切换;
波的频率切换:通过按键控制波的频率加减,范围是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
仿真波形如下所示: