一、前置知识
脉冲宽度调制(英语:Pulse-width modulation,缩写:PWM),简称脉宽调制,是将模拟信号变换为脉冲的一种技术,一般变换后脉冲的周期固定,但脉冲的工作周期(占空比)会依模拟信号的大小而改变。[1]
占空比(英语:Duty Ratio,Duty Cycle),是频射、微波电路、低频交流和直流电流等多个领域的一个概念,表示在一个周期内,工作时间与总时间的比值,有多个具体定义方式。[2]下图表示占空比的变化情况:
呼吸灯采用 PWM 的方式,在固定的频率下,通过调整占空比的方式来控制 LED 灯亮度的变化。 在由计数器产生的固定周期的 PWM 信号下,如果其占空比为 0,则 LED 灯不亮;如果其占空比为 100%,则 LED 灯最亮。所以将占空比从 0 到 100%,再从 100%到 0 不断变化,就可以实现 LED 灯的“呼吸”效果。
下图展示了不同照度占空比下LED的亮度情况:
二、原理图分析
涉及到的四个LED的原理图及引脚分配情况如下所示。同样是阳极与FPGA端口相连,高电平点亮LED,低电平熄灭LED。
正点原子提供的模块端口及结构框图如下所示:
- 周期信号计数器:用来产生一个1KHz的周期信号。
- 占空比调节器:用来在每个周期之后改变下个周期内的占空比大小。
- 比较数值并输出PWM波形:根据周期信号和占空比大小,来控制LED状态。
三、代码流程分析
第一个always语句块用来让period_cnt累加,以产生一个以50_000个clk为周期,即以1KHz为频率的周期。/*
用来计算实际占空比
如果按以下语句计算,则实际占空比是与duty_cycle相反
相当于 实际占空比 = (50_000 - duty_cycle) / 50_000
*/
assign led[3:0] = (period_cnt >= duty_cycle) ? 4'b1111 : 4'b0000;
第二个always语句块用来让duty_cycle在每个1KHz周期结束后,改变占空比数值,使其自增25或自减25。
最终代码如下: ```verilog //——————————— Copyright (c) ——————————— // Copyright(C) ZHJ0125 2022 // All rights reserved //—————————————————————————————- // File Name : touch_led.v // Created Date : 2022-04-18 15:06 // Modified Date : 2022-04-18 15:06 // Created By : ZHJ0125 // Last Version : V0.1 // Descriptions : 呼吸灯实验。 // 开发板上电后LED由亮变暗,再由暗变亮,以此循环。 //—————————————————————————————- module breath_led( input clk, input rst_n, output [3:0] led );
// 1KHz→1ms, 1ms/0.02us=50_000→16bit reg [15:0] period_cnt; // 周期计数器 reg [15:0] duty_cycle; // 占空比 reg up_down_flag; // 占空比增加或减少标志位 1:自增 0:自减
// duty_cycle增大时,实际占空比逐渐减小 assign led[3:0] = (period_cnt >= duty_cycle) ? 4’b1111 : 4’b0000;
// 周期计数器配置:计数至50_000后清零,以此循环 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin period_cnt <= 16’d0; end else begin if(period_cnt == 16’d50_000) period_cnt <= 16’d0; else period_cnt <= period_cnt + 1’d1; end end
// 占空比配置:每次周期结束后,重新计算占空比duty_cycle always @(posedge clk or negedge rst_n) begin if(!rst_n) begin duty_cycle <= 16’d0; up_down_flag <= 1’b1; end else begin if(period_cnt == 16’d50_000) begin if(up_down_flag == 1) begin if(duty_cycle >= 16’d50_000) up_down_flag <= 1’b0; else duty_cycle <= duty_cycle + 16’d25; end else begin if(duty_cycle <= 16’d0) up_down_flag <= 1’b1; else duty_cycle <= duty_cycle - 16’d25; end end end end
endmodule
附录
[1] 脉冲宽度调制 - 维基百科
[2] 占空比 - 维基百科