🎉欢迎来到FPGA专栏~产生非等占空比信号


一、效果演示
🥝等占空比信号

🥝非等占空比信号


二、等占空比信号的产生
我们通过LED的亮灭来展现等占空比信号:LED循环亮灭,亮1秒,灭1秒。
50MHz的晶振,要实现1秒的定时,需要计数49_999_999次,RTL视图展示如下:

Verilog HDL代码:
/
//模块作用:led亮一秒灭一秒
//作者:CSDN-小夏与酒
module led_plus1(
input Clk,
input Rst_n,
output reg led
);
reg [25:0]cnt;
parameter cnt_max = 26'd49_999_999;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt <= 26'd0;
else if(cnt == cnt_max)
cnt <= 26'd0;
else
cnt <= cnt + 1'b1;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
led <= 1'b1;
else if(cnt == cnt_max)
led <= ~led;
else
led <= led;
end
endmodule
测试激励文件代码:
`timescale 1ns/1ns
`define clock_period 20
module led_plus1_tb;
reg Clk;
reg Rst_n;
wire led;
led_plus Uled_plus1_0(
.Clk(Clk),
.Rst_n(Rst_n),
.led(led)
);
initial Clk = 1;
always #(`clock_period/2) Clk = ~Clk;
initial begin
Rst_n = 0;
#100 Rst_n = 1;
#(`clock_period*1000000);
$stop;
end
endmodule
三、非等占空比信号的产生
我们通过LED的亮灭来展现等占空比信号:LED循环亮灭,亮0.2秒,灭1.8秒。
实现的基本思路:当计时器从0开始计时到0.2秒的过程中,LED点亮;当计时器从0.2秒之后计时到2秒的过程中,LED熄灭;当计时器达到2秒时,计数清空。
50MHz的晶振,要实现0.2秒的定时,需要计数9_999_999次;要实现2秒的定时,需要计数99_999_999次,RTL视图展示如下:

Verilog HDL代码:
/
//模块作用:led亮0.2秒,灭1.8秒
//作者:CSDN-小夏与酒
module led_plus2(
input Clk,
input Rst_n,
output reg led
);
reg [26:0]cnt;
parameter cnt_max = 27'd99_999_999;
parameter cnt_ON = 27'd0;
parameter cnt_OFF = 27'd9_999_999;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt <= 27'd0;
else if(cnt == cnt_max)
cnt <= 27'd0;
else
cnt <= cnt + 1'b1;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
led <= 1'b1;
else if(cnt == cnt_ON)
led <= 1'b0;
else if(cnt == cnt_OFF)
led <= ~led;
else
led <= led;
end
endmodule
测试激励文件代码:
`timescale 1ns/1ns
`define clock_period 20
module led_plus2_tb;
reg Clk;
reg Rst_n;
wire led;
led_plus Uled_plus2_0(
.Clk(Clk),
.Rst_n(Rst_n),
.led(led)
);
initial Clk = 1;
always #(`clock_period/2) Clk = ~Clk;
initial begin
Rst_n = 0;
#100 Rst_n = 1;
#(`clock_period*1000000000);
$stop;
end
endmodule
产生非等占空比信号的思路:规定一个时间段,然后把这个时间段分成不同时间长度的小时间段,在各个小时间段里进行不同的操作。如上述代码,规定一个LED闪烁周期为2秒,前0.2秒点亮,后1.8秒熄灭。而在等占空比信号的产生中,规定一个LED闪烁周期为2秒,前1秒点亮,后1秒熄灭。
为了更直观地看到不同占空比信号的效果,将上述代码整理并加入到顶层文件中:
/
//模块作用:信号占空比顶层文件
//作者:CSDN-小夏与酒
module led_plus(
input Clk,
input Rst_n,
output led1,
output led2
);
led_plus1 Uled_plus1(
.Clk(Clk),
.Rst_n(Rst_n),
.led(led1)
);
led_plus2 Uled_plus2(
.Clk(Clk),
.Rst_n(Rst_n),
.led(led2)
);
endmodule
RTL视图:

其中,为了更好地理解本次项目的内容,led_plus1模块中也使用2秒为一个周期,前一秒LED亮,后一秒LED灭,写法思路和前述代码相同:
/
//模块作用:led亮一秒灭一秒
//作者:CSDN-小夏与酒
module led_plus1(
input Clk,
input Rst_n,
output reg led
);
reg [26:0]cnt;
parameter cnt_max = 27'd99_999_999;
parameter cnt_ON = 27'd0;
parameter cnt_OFF = 27'd49_999_999;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt <= 27'd0;
else if(cnt == cnt_max)
cnt <= 27'd0;
else
cnt <= cnt + 1'b1;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
led <= 1'b1;
else if(cnt == cnt_ON)
led <= 1'b0;
else if(cnt == cnt_OFF)
led <= ~led;
else
led <= led;
end
endmodule
使用上述代码编写led_plus1模块得到的RTL视图如下:

led_plus2模块的代码没有变化:
/
//模块作用:led亮0.2秒,灭1.8秒
//作者:CSDN-小夏与酒
module led_plus2(
input Clk,
input Rst_n,
output reg led
);
reg [26:0]cnt;
parameter cnt_max = 27'd99_999_999;
parameter cnt_ON = 27'd0;
parameter cnt_OFF = 27'd9_999_999;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt <= 27'd0;
else if(cnt == cnt_max)
cnt <= 27'd0;
else
cnt <= cnt + 1'b1;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
led <= 1'b1;
else if(cnt == cnt_ON)
led <= 1'b0;
else if(cnt == cnt_OFF)
led <= ~led;
else
led <= led;
end
endmodule
实现效果:

四、小项目
把握对定时器的理解和掌握非等占空比信号的产生方法。
在这部分加入两个小项目:使用产生非等占空比信号的思路实现两种类型的流水灯。
🔸项目一
具体要求:流水灯的循环周期为2.5秒,四个LED灯在最初时刻全部点亮,以从左到右或者从右到左的顺序,四个LED灯以0.5秒为间隔依次熄灭。
实现效果:

项目代码:
📜写法一:
/
//led_prj_01
//CSDN-小夏与酒
/
module led_prj_01(
input Clk,
input Rst_n,
output reg [3:0]led
);
reg [26:0]cnt;
parameter cnt_max = 27'd124_999_999;
parameter cnt_period = 27'd24_999_999;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt <= 27'd0;
else if(cnt == cnt_max)
cnt <= 27'd0;
else
cnt <= cnt + 1'b1;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
led <= 4'b0000;
else if(cnt == 1*cnt_period)
led <= 4'b1000;
else if(cnt == 2*cnt_period)
led <= 4'b1100;
else if(cnt == 3*cnt_period)
led <= 4'b1110;
else if(cnt == 4*cnt_period)
led <= 4'b1111;
else if(cnt == 5*cnt_period)
led <= 4'b0000;
else
led <= led;
end
endmodule
对应的RTL视图:

📜写法二:
/
//led_prj_01
//CSDN-小夏与酒
/
module led_prj_01(
input Clk,
input Rst_n,
output reg [3:0]led
);
reg [26:0]cnt;
parameter cnt_max = 27'd124_999_999;
parameter cnt_period = 27'd24_999_999;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt <= 27'd0;
else if(cnt == cnt_max)
cnt <= 27'd0;
else
cnt <= cnt + 1'b1;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
led <= 4'b0000;
else begin
case(cnt)
1*cnt_period:led <= 4'b1000;
2*cnt_period:led <= 4'b1100;
3*cnt_period:led <= 4'b1110;
4*cnt_period:led <= 4'b1111;
5*cnt_period:led <= 4'b0000;
default:led <= led;
endcase
end
end
endmodule
对应的RTL视图:

注意:
通过上述项目可以看出,if-else语句和case语句虽然可以实现相同的功能,但是它们的RTL视图却是不一样的,即if-else语句和case语句在底层逻辑的实现上是存在差异的,并且case语句的优化效果更好。
🔸项目二
具体要求:流水灯的循环周期为3秒,四个LED灯在最初时刻全部熄灭,以从左到右或者从右到左的顺序,四个LED灯分别以0.25秒、0.5秒、0.75秒、1秒为间隔依次点亮,全部点亮后持续0.25秒。
实现效果:

项目代码:
/
//led_prj_02
//CSDN-小夏与酒
/
module led_prj_02(
input Clk,
input Rst_n,
output reg [3:0]led
);
reg [27:0]cnt;
parameter cnt_max = 28'd149_999_999;
parameter cnt_period = 28'd12_499_999;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt <= 28'd0;
else if(cnt == cnt_max)
cnt <= 28'd0;
else
cnt <= cnt + 1'b1;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
led <= 4'b1111;
else begin
case(cnt)
1*cnt_period:led <= 4'b0111;
3*cnt_period:led <= 4'b0011;
6*cnt_period:led <= 4'b0001;
10*cnt_period:led <= 4'b0000;
11*cnt_period:led <= 4'b1111;
default:led <= led;
endcase
end
end
endmodule
对应的RTL视图:


🧸结尾
该文介绍了如何使用FPGA产生非等占空比信号,通过LED的亮灭展示了等占空比和非等占空比信号的区别。文中提供了VerilogHDL代码实现1秒和0.2秒定时器,并通过这两个定时器控制LED亮灭,实现了不同占空比的信号。此外,还设计了两个小项目,分别是不同间隔熄灭的流水灯,进一步巩固了定时器和非等占空比信号的应用。


3397

被折叠的 条评论
为什么被折叠?



