一.PWM原理
- 在介绍PWM之前,先介绍几个基本概念
1.1PWM频率
- 是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期);
也就是说一秒钟PWM有多少个周期
1.2PWM周期
-
T=1/f
周期=1/频率
1.3脉宽周期
- 高电平的时间
1.4占空比
- 是一个脉冲周期内,高电平的时间与整个周期时间的比例
1.5PWM原理
-
PWM就是在合适的信号频率下,通过一个周期里改变占空比的方式来改变输出的有效电压
-
PWM实现呼吸灯原理:
频率很高时,看不到闪烁,占空比越大,LED越亮;
频率很低时,可看到闪烁,占空比越大,LED越亮。
二.设计思路
- 设计三个计数器,cnt_us、cnt_ms、cnt_s。其关系为cnt_us计满时cnt_ms自增1;cnt_ms计满时cnt_s自增1。如下图
- 设计标志信号flag:cnt_s计满时flag反转,以实现渐亮后渐灭。
- 比较cnt_ms和cnt_s:当cnt_s > cnt_ms时,led灯的值为4’b1111;当cnt_s < cnt_s时,led灯的值为4’b0000;
三.代码实现
module pwm_led(
input clk ,
input rst_n ,
output reg [3:0] led
);
parameter TIME_US = 6'd49;
parameter TIME_MS = 10'd999;
parameter TIME_S = 10'd999;
reg [5:0] cnt_us;
reg [9:0] cnt_ms;
reg [9:0] cnt_s;
reg flag;
//us计数器
wire add_cnt_us;//us计数器开始计数的标志
wire end_cnt_us;//us计数器结束计数的标志
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_us <= 6'd0;
end
else if (add_cnt_us) begin
if (end_cnt_us) begin
cnt_us <= 6'd0;
end
else begin
cnt_us <= cnt_us + 1'd1;
end
end
else begin
cnt_us <= cnt_us;
end
end
assign add_cnt_us = 1'b1;
assign end_cnt_us = add_cnt_us && cnt_us == TIME_US;
//ms计数器
wire add_cnt_ms;//ms计数器开始计数的标志
wire end_cnt_ms;//ms计数器结束计数的标志
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_ms <= 10'd0;
end
else if (add_cnt_ms) begin
if (end_cnt_ms) begin
cnt_ms <= 10'd0;
end
else begin
cnt_ms <= cnt_ms + 1'd1;
end
end
else begin
cnt_ms <= cnt_ms;
end
end
assign add_cnt_ms = end_cnt_us;//cnt_us计满时cnt_ms加一
assign end_cnt_ms = add_cnt_ms && cnt_ms == TIME_MS;
//s计数器
wire add_cnt_s;//s计数器开始计数的标志
wire end_cnt_s;//s计数器结束计数的标志
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_s <= 10'd0;
end
else if (add_cnt_s) begin
if (end_cnt_s) begin
cnt_s <= 10'd0;
end
else begin
cnt_s <= cnt_s + 1'd1;
end
end
else begin
cnt_s <= cnt_s;
end
end
assign add_cnt_s = end_cnt_ms;//cnt_ms计满时cnt_s加一
assign end_cnt_s = add_cnt_s && cnt_s == TIME_S;
//flag信号当cnt_s计满时反转以实现渐亮后渐灭
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
flag <= 1'b0;
end
else if (end_cnt_s) begin
flag <= ~flag;
end
else begin
flag <= flag;
end
end
//比较cnt_s和cnt_ms
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led <= 4'b0000;
end
else if(!flag) begin
led <= (cnt_s > cnt_ms)?4'b1111:4'b0000;
end
else if (flag) begin
led <= (cnt_s > cnt_ms)?4'b0000:4'b1111;
end
else begin
led <= led;
end
end
endmodule
四.仿真
`timescale 1ns/1ns //单位and精度
module pwm_led_tb();
//激励信号
reg clk;
reg rst_n;
//输出信号
wire [3:0] led;
//重定义parameter参数,减少仿真时间,便于观察效果
parameter CYCLE = 20;
parameter TIME_US = 5;
parameter TIME_MS = 10;
parameter TIME_S = 10;
//生成clk信号,半个周期反转
always #(CYCLE/2) clk = ~clk;
//设置激励信号数值
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(CYCLE);
rst_n = 1'b1;
#(2 * (TIME_US + 1) * (TIME_MS + 1) * (TIME_S + 1) * CYCLE);
$STOP;
end
//模块例化
pwm_led #(
.TIME_US(TIME_US),
.TIME_MS(TIME_MS),
.TIME_S(TIME_S)
) u_pwm_led(
.clk(clk),
.rst_n(rst_n),
.led(led)
);
endmodule