verilog之定时计数--任意分频、任意宽度脉冲、复位信号产生、置位/清零、PWM波形
Timer的重要性
Timer是数字电路的基础,所有器件都是在时钟节拍下工作,由于时钟节拍速率高,FPGA内部可以达到100M,有些低速的设备就需要进行分频处理,这就产生了计数的需求。不同的功能模块速率不同,自然也就需要不同的计数器。
分频器
分频器的关键是计算出源时钟频率与分频后的时钟频率的倍数关系,倍数除以2 就是计数最大值cnt。
module counter(Clk,Rst_n,Low_clock);
input Clk; //系统时钟
input Rst_n; //全局复位,低电平复位
output reg Low_clock; //Low_clock输出
reg [14:0]cnt; //定义计数器寄存器
//计数器计数进程
always@(posedge Clk or negedge Rst_n)
if(Rst_n == 1'b0)
cnt <= 15'd0;
else if(cnt == 15'd24_999)
cnt <= 15'd0;
else
cnt <= cnt + 1'b1;
//Low_clock输出控制进程
always@(posedge Clk or negedge Rst_n)
if(Rst_n == 1'b0)
Low_clock<= 1'b1;
else if(cnt == 15'd24_999)
Low_clock<= ~Low_clock;
else
Low_clock<= Low_clock;
endmodule
任意时间长度复位信号的产生
在CPU系统中,有多个电压轨,主电源供电后,需要控制不同电压轨的上电时序,已确保系统正常启动。有时候为了保证供电的稳定,需要控制启动时CPU的复位时间。
设计目标就是在主电源上电后的多少ms内输出一个低电平,此后信号保持为高电平状态。
module rst_delay125ms(
cpld_16MHz,
rst_pld_n_delay
);
input cpld_16MHz;
output rst_pld_n_delay ;
reg [24:0]delay_125ms_cunt = 25'b0; //计数器上电赋初始值
reg delay_125ms = 1'b0;
//power_on 10ms delay generator
always@(posedge cpld_16MHz)
begin
if (delay_10ms_cunt == 25'd2_000_000) //改变cunt的值即可实现任意时间长度控制
begin
delay_125ms_cunt <= delay_125ms_cunt;
delay_125ms <= 1'b1;
end
else
begin
delay_125ms_cunt <= delay_125ms_cunt +1;
delay_125ms <= 1'b0;
end
end
assign rst_pld_n_delay = delay_125ms? 1'b1: 1'b0;
endmodule
任意宽度脉冲----展宽
该功能实现把单个时钟周期信号展宽为需要的时钟宽度信号,一般应用在片选、使能等信号上。
此例用到了参数定义语句,方便修改需要展宽的时间宽度,
仿真中为了便于观察,把此参数设为了16.
parameter CNT_MAX = 25’d8_000; //0.5ms
module rst_delay(Clk,Rst_n,Dog_rst_i,Dog_rst_delay_o);
input Clk; //系统时钟,50M
input Rst_n; //全局复位,低电平复位
input Dog_rst_i;
output Dog_rst_delay_o;
reg [24:0]cnt; //定义计数器寄存器
reg EN;
parameter CNT_MAX = 25'd8_000; //0.5ms
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
EN <= 1'b0;
else if(Dog_rst_i)
EN <= 1'b1;
else if(cnt == CNT_MAX)
EN <= 1'b0;
else
EN <= EN;
assign Dog_rst_delay_o = EN;
//计数器计数进程
always@(posedge Clk or negedge Rst_n )
if(Rst_n == 1'b0 )
cnt <= 25'd0;
else if(cnt == CNT_MAX)
cnt <= 25'd0;
else if(EN)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
endmodule
仿真图
任意宽度脉冲----压缩
脉宽展宽仅限于输入脉冲的宽度较窄的情况,如果输入信号是一个跳变,比如从低电平变到高电平,我们想通过此事件触发一个特定宽度的脉冲,那么只要把上面的程序稍作修改即可,计数器计数开始的条件改为输入脉冲,计到需要的脉宽后即停止,输出脉冲的起始时刻可以通过判断计数器的某一位来实现。
module rst_delay(Clk,Rst_n,Dog_rst_i,Dog_rst_delay_o);
input Clk; //系统时钟,50M
input Rst_n; //全局复位,低电平复位
input Dog_rst_i;
output Dog_rst_delay_o;
reg [24:0]cnt; //定义计数器寄存器
reg EN;
parameter CNT_MAX = 25'd16; //0.5ms
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
EN <= 1'b0;
else if(cnt[1:0]==2'b11) //计数器的某位作为条件触发输出跳变
EN <= 1'b1;
else if(cnt == CNT_MAX) //CNT_MAX 决定输出脉冲的宽度
EN <= 1'b0;
else
EN <= EN;
assign Dog_rst_delay_o = EN;
//计数器计数进程
always@(posedge Clk or negedge Rst_n )
if(Rst_n == 1'b0 )
cnt <= 25'd0;
else if(cnt == CNT_MAX)
// cnt <= 25'd0;
cnt <= cnt;
// else if(EN)
else if(Dog_rst_i) //输入跳变触发计数
cnt <= cnt + 1'b1;
// else
// cnt <= cnt;
endmodule
仿真图如下:
置位/清零 SET/RESET
该功能主要实现两个信号控制另外一个信号,比如看门狗的打开、关闭,set/reset功能。
module rst_delay(
Clk,
Rst_n,
SFTDog_open,
SFTDog_close,
SFTDog_en
);
input Clk;
input Rst_n; //全局复位,低电平复位
input SFTDog_open; // 打开
input SFTDog_close; //关闭
output reg SFTDog_en;//功能输出
always@(posedge Clk )
begin
if(!Rst_n )
SFTDog_en<= 1'b0;
else if(SFTDog_open)
SFTDog_en<= 1'b1;
else if(!SFTDog_close)
SFTDog_en<= 1'b0;
end
endmodule
仿真图
PWM
PWM波形在很多领域都可以用得到,比如屏幕背光亮度控制、运动控制、开关电源设计等等领域。在下面的代码中首先对输入时钟进行分频,产生所需要的pwm频率;其次对分频后的时钟进行上升沿采样,采样的原理就是利用两个寄存器,分别缓冲分频信号,通过
assign pos_ege = tmp0_div_clk & !tmp1_div_clk;
可以实现上升沿的判断。
上升沿抓到后,启动pwm脉宽计数,一直计到所需要的脉宽。
module PWM(Clk,Rst_n,pwm);
input Clk; //系统时钟 50MHz 20ns
input Rst_n; //全局复位,低电平复位
output reg pwm; //led输出
reg div_clk;
reg [24:0]cnt; //定义计数器寄存器
reg [24:0]pwm_cnt; //定义计数器寄存器
reg tmp0_div_clk,tmp1_div_clk;
wire pos_ege;
parameter CNT_MAX = 25'd2_499_999; // 0.05s 通过修改pwm_MAX,实现不同频率。
parameter pwm_MAX = 25'd100; // 0 < pwm_MAX < 2* CNT_MAX , 通过修改pwm_MAX,实现不同占空比。
//分频计数器计数进程
always@(posedge Clk or negedge Rst_n)
if(Rst_n == 1'b0)
cnt <= 25'd0;
else if(cnt == CNT_MAX)
cnt <= 25'd0;
else
cnt <= cnt + 1'b1;
//div clock 输出控制进程
always@(posedge Clk or negedge Rst_n)
if(Rst_n == 1'b0)
div_clk <= 1'b0;
else if(cnt == CNT_MAX)
div_clk <= ~div_clk;
//=========================================================
// posedge detect
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
tmp0_div_clk <= 1'b0;
tmp1_div_clk <= 1'b0;
end
else begin
tmp0_div_clk <= div_clk;
tmp1_div_clk <= tmp0_div_clk;
end
assign pos_ege = tmp0_div_clk & !tmp1_div_clk;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
pwm <= 1'b0;
else if(pos_ege)
pwm <= 1'b1;
else if(pwm_cnt == pwm_MAX)
pwm <= 1'b0;
//脉宽计数器计数进程
always@(posedge Clk or negedge Rst_n )
if(Rst_n == 1'b0 )
pwm_cnt <= 25'd0;
else if(pwm_cnt == pwm_MAX)
pwm_cnt <= 25'd0;
else if(pwm)
pwm_cnt <= pwm_cnt + 1'b1;
endmodule