image.png

一、前置知识

脉冲宽度调制(英语:Pulse-width modulation,缩写:PWM),简称脉宽调制,是将模拟信号变换为脉冲的一种技术,一般变换后脉冲的周期固定,但脉冲的工作周期(占空比)会依模拟信号的大小而改变。[1]
占空比(英语:Duty Ratio,Duty Cycle),是频射、微波电路、低频交流和直流电流等多个领域的一个概念,表示在一个周期内,工作时间与总时间的比值,有多个具体定义方式。[2]下图表示占空比的变化情况:
PWM_duty_cycle_with_label.gif
呼吸灯采用 PWM 的方式,在固定的频率下,通过调整占空比的方式来控制 LED 灯亮度的变化。 在由计数器产生的固定周期的 PWM 信号下,如果其占空比为 0,则 LED 灯不亮;如果其占空比为 100%,则 LED 灯最亮。所以将占空比从 0 到 100%,再从 100%到 0 不断变化,就可以实现 LED 灯的“呼吸”效果。
下图展示了不同照度占空比下LED的亮度情况:
image.png

二、原理图分析

涉及到的四个LED的原理图及引脚分配情况如下所示。同样是阳极与FPGA端口相连,高电平点亮LED,低电平熄灭LED。
image.pngimage.png
正点原子提供的模块端口及结构框图如下所示:
image.png

  • 周期信号计数器:用来产生一个1KHz的周期信号。
  • 占空比调节器:用来在每个周期之后改变下个周期内的占空比大小。
  • 比较数值并输出PWM波形:根据周期信号和占空比大小,来控制LED状态。

    三、代码流程分析

    1. /*
    2. 用来计算实际占空比
    3. 如果按以下语句计算,则实际占空比是与duty_cycle相反
    4. 相当于 实际占空比 = (50_000 - duty_cycle) / 50_000
    5. */
    6. assign led[3:0] = (period_cnt >= duty_cycle) ? 4'b1111 : 4'b0000;
    第一个always语句块用来让period_cnt累加,以产生一个以50_000个clk为周期,即以1KHz为频率的周期。
    第二个always语句块用来让duty_cycle在每个1KHz周期结束后,改变占空比数值,使其自增25或自减25。
    image.png
    最终代码如下: ```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

``` 实验效果: breath_led.mp4 (14.86MB)

附录

[1] 脉冲宽度调制 - 维基百科
[2] 占空比 - 维基百科