从一定程度上讲,定时器就是计数器,计数器就是定时器。
定时器:核心单元本质也是一个计数器,设置一个定时值,启动定时器后,计数器开始计数,计数满后产生计数满标志信号,提示设定的定时时间到达。
计数器:对脉冲信号进行计数,统计一确定时间段内该脉冲信号出现的次数,或者等待指定次数的脉冲信号出现后,产生相应标志。
本节设计一个定时器,能够支持以下功能
1) 该定时器的定时时间参数可以通过该模块的一个端口输入,通过调节端口上输入数据的值,就能修改其定时时间。
2)设置一个计数模式控制信号,当该信号为1时,设置为循环定时模式,当该信号为0时,设置为单次定时模式。
3)设置一个计数启动信号,在循环定时模式下,该信号为高电平使能计时,为低电平则停止计时。在单次计数模式下,该信号的一个单基准时钟周期的脉冲使能一次定时。
4)输出计数器实时计数值,该值将用于产生特定占空比的方波。
蜂鸣器是一种产生声音的器件,广泛应用于报警器、电子玩具、汽车电子设备、定时器等电子产品中作发声器件。
蜂鸣器按照构造方式的不同,可分为压电式蜂鸣器和电磁式蜂鸣器两种类型。压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器、共鸣箱以及外壳等组成。电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。由于两种蜂鸣器发音原理不同,压电式结构简单耐用但音调单一音色差,适用于报警器等设备。而电磁式由于音色好,所以多用于语音、音乐等设备。
蜂鸣器按照驱动电路的不同可以分为有源蜂鸣器与无源蜂鸣器。有源蜂鸣器内部带震荡源,所以只要通电就会鸣叫;而无源蜂鸣器内部不带震荡源,因此如果用直流信号无法令其鸣叫,这就需要用200-5K的方波(声音频率)去驱动。
在如图电路中,电容C36用于提高电路抗干扰性能;D5起保护三极管的作用,当三极管突然截止时,无源蜂鸣器两端产生的瞬时感应电动势可以通过D1迅速释放掉,避免叠加到三极管集电极上从而击穿三极管;BEEP端口接FPGA输出管脚,使用时只需要在BEEP信号上输入2~5KHz的PWM波,就能驱动蜂鸣器按照既定的频率产生振动信号。
本次实验主要由以下步骤组成:
创建Quartus Prime工程
编写计数器HDL描述文件
编写计数器测试脚本(testbench)
对计数器进行功能仿真
在Quartus Prime中执行布局布线
对计数器进行时序仿真
分配引脚并编译得到FPGA配置文件.sof
配置FPGA并运行
module beep_top(
clk,
Rst_n,
beep,
CNT_GO//在循环定时模式下,该信号为高电平使能计时,为低电平则停止计时。在单次计数模式下,该信号的一个单基准时钟周期的脉冲使能一次定时。
);
input clk;
input Rst_n;
input CNT_GO;
output beep;
wire [31:0] CNT_NOW;
beep_test beep_test0(
.clk(clk),
.Rst_n(Rst_n),
.CNT_ACC(49999),
.MODE(1),
.CNT_GO(CNT_GO),
.CNT_NOW(CNT_NOW),
.FULL_Flag()
);
assign beep = (CNT_NOW >= 24999 )? 1'b1:1'b0;
endmodule
module beep_top(
clk,
Rst_n,
beep,
CNT_GO//在循环定时模式下,该信号为高电平使能计时,为低电平则停止计时。在单次计数模式下,该信号的一个单基准时钟周期的脉冲使能一次定时。
);
input clk;
input Rst_n;
input CNT_GO;
output beep;
wire [31:0] CNT_NOW;
beep_test beep_test0(
.clk(clk),
.Rst_n(Rst_n),
.CNT_ACC(49999),
.MODE(1),
.CNT_GO(CNT_GO),
.CNT_NOW(CNT_NOW),
.FULL_Flag()
);
assign beep = (CNT_NOW >= 24999 )? 1'b1:1'b0;
endmodule
testbench文件
`timescale 1ns/1ns
`define clk_period 20
module beep_test_tb;
reg clk;
reg Rst_n;
reg [31:0] CNT_ACC;
reg MODE;
reg CNT_GO;
wire [31:0]CNT_NOW;
wire FULL_Flag;
reg [31:0]cnt;
beep_test beep_test0(
.clk(clk),
.Rst_n(Rst_n),
.CNT_ACC(CNT_ACC),
.MODE(MODE),
.CNT_GO(CNT_GO),
.CNT_NOW(CNT_NOW),
.FULL_Flag(FULL_Flag)
);
initial clk = 1;
always #(`clk_period/2) clk=~clk;
initial begin
Rst_n = 0;
CNT_ACC = 0;
CNT_GO = 0;
#(`clk_period*20+1);//保证复位信号的变化不与clk信号边沿重合,方便观察异步复位
Rst_n = 1;
#(`clk_period*20);
//设置预设值为1000,模式为循环模式
CNT_ACC = 1000;
MODE = 1;
#(`clk_period*20);
CNT_GO = 1;
#(`clk_period*12000);
CNT_GO = 0;
#(`clk_period*20);
//设置预设值为600,模式为循环模式
CNT_ACC = 1000;
MODE = 1;
#(`clk_period*20);
CNT_GO = 1;
#(`clk_period*8000);
CNT_GO = 0;
#(`clk_period*20);
//设置预设值为1000,模式为单次定时模式
CNT_ACC = 1000;
MODE = 0;
#(`clk_period*20);
CNT_GO = 1;
#(`clk_period);
CNT_GO = 0;
#(`clk_period*1200);
//设置预设值为600,模式为单次定时模式
CNT_ACC = 600;
MODE = 0;
#(`clk_period*20);
CNT_GO = 1;
#(`clk_period);
CNT_GO = 0;
#(`clk_period*1200);
$stop;
end