• 09 呼吸灯


    呼吸灯简介

    呼吸灯实际展示的效果就是一个 LED 灯的亮度由亮到暗,再由暗到亮的变化过程,并且该过程是循环往复的,像呼吸一样那么有节奏。 呼吸灯通常是采用 PWM(Pulse Width Modulation,即脉冲宽度调制) 的方式实现,在 PWM 频率固定的情况下,通过调整其占空比来控制 LED 灯亮度的变化。
    在固定周期的 PWM 信号下,如果其占空比为 0,则 LED 灯不亮;如果其占空比为100%,则 LED 灯最亮。将占空比从 0 到 100%,再从 100%到 0 不断变化,就可以实现 LED 灯的“呼吸”效果,PWM 占空比调节示意图如下所示:
    在这里插入图片描述

    PWM信号的产生

    可以使用1个寄存器和一个比较值来控制 PWM 信号的产生,寄存器进行周期计数,控制 PWM 的周期,在计数器周期计数过程中同时将计数值与比较值比较,当周期计数器的值小于比较寄存器时输出低电平,否则输出高电平,此时通过调节比较值便可调节 PWM 占空比,调节周期计数器的最大计数值便可调节 PWM 的周期,PWM 信号产生原理如下图所示:
    在这里插入图片描述

    原理图

    LED0 到 LED3 这 4 个发光二极管的阴极分别连到 S8050(NPN 三极管)的集电极上,阳极都与 3.3V 电压相连,三极管的基极分别与 FPGA 相连,这是由于 FPGA 的 IO 口的电压只有 1.5V,电压较低,所以此处连接三极管是为了起到放大电压的作用。这样就可以通过改变三极管的状态来控制 LED 的亮灭。当 FPGA 输出到为高电平时,三极管导通,LED 灯亮;当 FPGA 输出到为低电平时,三极管截止,LED 灯灭。
    在这里插入图片描述
    在这里插入图片描述
    此实验中只用到了LED0

    系统框图

    系统框图如下,包括两部分,分别是产生PWM信号的PWM模块和控制PWM占空比的呼吸灯模块
    在这里插入图片描述

    编写代码

    PWM信号生成模块

    PWM信号生成模块包含一个周期计数器和一个比较值,比较值通过模块外部输入的占空比结合PWM周期转换得到(这里由呼吸灯模块进行占空比控制),模块代码如下:

    `timescale 1ns / 1ns
    
    module pwm_generate
    (
    	input sys_clk,							//系统时钟
    	input sys_rst_n,						//系统复位,低电平有效
    
    	input [31:0] period,					//PWM周期
    	input [31:0] duty_cycle,				//PWM占空比
    
    	output pwm_out							//输出的PWM信号
    );
    
    //PWM周期
    wire [31:0] pwm_period;
    //PWM比较值
    wire [31:0] pwm_compare;
    //周期计数器,用于控制PWM周期
    reg [31:0] count;
    
    //PWM周期,不能小于1
    assign pwm_period = (period < 1) ? 1 : period;
    //将PWM占空比转换为比较值
    assign pwm_compare = (duty_cycle < period) ? (period - duty_cycle) : 0;
    
    //周期计数器的值小于比较寄存器时输出低电平,否则输出高电平
    assign pwm_out = (count < pwm_compare) ? 0 : 1;
    
    //进行周期计数,用于控制PWM输出周期
    always @(posedge sys_clk) begin
    	if(!sys_rst_n)
    		count <= 0;
    	else if(count < (period - 1))
    		count = count + 1;
    	else
    		count <= 0;
    end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    PWM信号生成模块仿真激励代码

    PWM信号生成模块仿真激励代码很简单,就是在周期产生时钟信号的同时调整PWM占空比即可,代码如下:

    `timescale 1ns / 1ns	//仿真单位/仿真精度
    
    module tb_pwm_generate();
    
    reg sys_clk;					//时钟
    reg sys_rst_n;					//复位
    reg [31:0] duty_cycle;			//占空比
    wire pwm_out;					//PWWM信号
    
    initial begin
    	sys_clk = 1'b0;
    	sys_rst_n = 1'b0;
    	duty_cycle = 0;
    	#200
    	sys_rst_n = 1'b1;
    
    	//占空比为1
    	#1000
    	duty_cycle = 1;
    
    	//占空比为2
    	#1000
    	duty_cycle = 2;
    
    	//占空比为3
    	#1000
    	duty_cycle = 3;
    
    	//占空比为4
    	#1000
    	duty_cycle = 4;
    
    	//占空比为5
    	#1000
    	duty_cycle = 5;
    
    	//占空比为6
    	#1000
    	duty_cycle = 6;
    
    	//占空比为7
    	#1000
    	duty_cycle = 7;
    
    	//占空比为8
    	#1000
    	duty_cycle = 8;
    
    	//占空比为9
    	#1000
    	duty_cycle = 9;
    
    	//占空比为10
    	#1000
    	duty_cycle = 10;
    end
    
    //产生时钟
    always #10 sys_clk = ~sys_clk;
    
    pwm_generate u_tb_pwm_generate_inst (
    	.sys_clk(sys_clk),					//系统时钟
    	.sys_rst_n(sys_rst_n),				//系统复位,低电平有效
    
    	.period(10),						//PWM周期
    	.duty_cycle(duty_cycle),			//PWM占空比
    
    	.pwm_out(pwm_out)					//输出的PWM信号
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    呼吸灯模块

    呼吸灯模块用于向PWM模块输出一个占空比,内部包含以下几个部分:

    • 一个周期计数器,用于控制PWM占空比调节的间隔
    • 根据占空比调节方向调节占空比
    • 记录占空比调节次数
    • 当占空比连续递增(递减)到指定次数时说明占空比达到最大(最小),此时改变占空比调节方向标志
      完整的代码如下:
    `timescale 1ns / 1ns
    
    module breath_led #(
    	parameter PWM_PERIOD = 100_000,				//pwm信号的周期
    	parameter BREATH_PERIOD = 100_000_000,		//呼吸灯周期
    	parameter BRIGHT_LEVEL = 100				//亮度等级
    )
    (
    	input sys_clk,								//系统时钟
    	input sys_rst_n,							//系统复位,低电平有效
    
    	output reg [31:0] duty_cycle				//PWM占空比
    );
    
    //PWM占空比调节步进,分100个亮度等级进行占空比调节
    localparam DUTY_CYCLE_STEP = PWM_PERIOD / BRIGHT_LEVEL;
    //PWM占空比调节间隔,分100个亮度等级进行占空比调节
    localparam STEP_INTERVAL = BREATH_PERIOD / BRIGHT_LEVEL;
    
    //周期计数器,用于控制PWM占空比调节间隔
    reg [31:0] period_count;
    //占空比调节次数计数器,用于记录调节占空比的次数
    reg [31:0] step_count;
    //占空比调节方向控制,为0占空比加,为1占空比减
    reg inc_dec_flag;
    
    //进行周期计数,用于控制PWM占空比调节间隔
    always @(posedge sys_clk) begin
    	if(!sys_rst_n)
    		period_count <= 0;
    	else if(period_count < (STEP_INTERVAL - 1))
    		period_count <= period_count + 1;
    	else
    		period_count <= 0;
    end
    
    //根据占空比调节方向标志进行占空比调节,控制LED亮度
    always @(posedge sys_clk) begin
    	if(!sys_rst_n)
    		duty_cycle <= 0;
    	else if(period_count == (STEP_INTERVAL - 1)) begin
    		if((inc_dec_flag == 1'b0) && ((PWM_PERIOD - duty_cycle) >= DUTY_CYCLE_STEP))
    			duty_cycle <= duty_cycle + DUTY_CYCLE_STEP;
    		else if((inc_dec_flag == 1'b1) && (duty_cycle >= DUTY_CYCLE_STEP))
    			duty_cycle <= duty_cycle - DUTY_CYCLE_STEP;
    	end
    end
    
    //记录调节占空比的次数
    always @(posedge sys_clk) begin
    	if(!sys_rst_n)
    		step_count <= 0;
    	else if(period_count == (STEP_INTERVAL - 1)) begin
    		if(step_count < (BRIGHT_LEVEL - 1))
    			step_count <= step_count + 1;
    		else
    			step_count <= 0;
    	end
    end
    
    //占空比调节方向控制,为0占空比加,为1占空比减
    //当占空比连续递增(递减)到指定次数时说明占空比达到最大(最小),此时改变占空比调节方向标志
    always @(posedge sys_clk) begin
    	if(!sys_rst_n)
    		inc_dec_flag <= 0;
    	else if(period_count == (STEP_INTERVAL - 1)) begin
    		if(step_count == (BRIGHT_LEVEL - 1))
    			inc_dec_flag <= ~inc_dec_flag;
    	end
    end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    呼吸灯模块仿真激励代码

    呼吸灯模块仿真激励代码非常简单,只需要产生激励时钟即可,代码如下:

    `timescale 1ns / 1ns	//仿真单位/仿真精度
    
    module tb_breath_led();
    
    reg sys_clk;					//时钟
    reg sys_rst_n;					//复位
    wire [31:0] duty_cycle;			//占空比
    
    initial begin
    	sys_clk = 1'b0;
    	sys_rst_n = 1'b0;
    	#200
    	sys_rst_n = 1'b1;
    end
    
    //产生时钟
    always #10 sys_clk = ~sys_clk;
    
    breath_led #(
    	.PWM_PERIOD(100),				//pwm信号的周期
    	.BREATH_PERIOD(10_000),			//呼吸灯周期
    	.BRIGHT_LEVEL(10)				//亮度等级
    )
    u_tb_breath_led_inst(
    	.sys_clk(sys_clk),					//系统时钟
    	.sys_rst_n(sys_rst_n),				//系统复位,低电平有效
    
    	.duty_cycle(duty_cycle)				//PWM占空比
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    顶层模块

    顶层模块主要用于例化PWM生成模块和呼吸灯模块,并将两个模块进行关联,代码如下:

    `timescale 1ns / 1ns
    
    module top_breath_led #(
    	parameter PWM_PERIOD = 100_000,				//pwm信号的周期
    	parameter BREATH_PERIOD = 100_000_000,		//呼吸灯周期
    	parameter BRIGHT_LEVEL = 100				//亮度等级
    )
    (
    	input sys_clk,								//系统时钟
    	input sys_rst_n,							//系统复位,低电平有效
    
    	output led									//LED
    );
    
    //PWM占空比
    wire [31:0] duty_cycle;
    
    //例化呼吸灯模块
    breath_led #(
    	.PWM_PERIOD(PWM_PERIOD),				//pwm信号的周期
    	.BREATH_PERIOD(BREATH_PERIOD),			//呼吸灯周期
    	.BRIGHT_LEVEL(BRIGHT_LEVEL)				//亮度等级
    )
    u_breath_led_inst (
    	.sys_clk(sys_clk),						//系统时钟
    	.sys_rst_n(sys_rst_n),					//系统复位,低电平有效
    
    	.duty_cycle(duty_cycle)					//PWM占空比
    );
    
    //例化PWM发生模块
    pwm_generate u_pwm_generate_inst (
    	.sys_clk(sys_clk),						//系统时钟
    	.sys_rst_n(sys_rst_n),					//系统复位,低电平有效
    
    	.period(PWM_PERIOD),					//PWM周期
    	.duty_cycle(duty_cycle),				//PWM占空比
    
    	.pwm_out(led)							//PWM,用于控制LED
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    顶层模块仿真激励代码

    顶层模块仿真激励代码非常简单,只需要产生激励时钟即可,代码如下:

    `timescale 1ns / 1ns	//仿真单位/仿真精度
    
    module tb_top_breath_led();
    
    reg sys_clk;					//时钟
    reg sys_rst_n;					//复位
    wire led;						//LED
    
    initial begin
    	sys_clk = 1'b0;
    	sys_rst_n = 1'b0;
    	#200
    	sys_rst_n = 1'b1;
    end
    
    //产生时钟
    always #10 sys_clk = ~sys_clk;
    
    top_breath_led #(
    	.PWM_PERIOD(10),					//pwm信号的周期
    	.BREATH_PERIOD(1000),				//呼吸灯周期
    	.BRIGHT_LEVEL(10)					//亮度等级
    )
    u_tb_top_breath_led_inst (
    	.sys_clk(sys_clk),					//系统时钟
    	.sys_rst_n(sys_rst_n),				//系统复位,低电平有效
    
    	.led(led)							//LED
    );
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    约束输入

    管脚分配如下:
    在这里插入图片描述
    XDC 约束语句如下:

    #时序约束
    create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
    
    #IO 管脚约束
    set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
    set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
    set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS15} [get_ports led]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    2022杭电多校第四场题解
    Java poi 后台导出Excel
    PHP 自习室空位查询系统mysql数据库web结构apache计算机软件工程网页wamp计算机毕业设计
    Flowable 的使用
    Android图片圆角转换 RoundedImageView开源项目 小记(1)
    数据库主从恢复
    C4D遇到的动力学模拟问题怎么办?看完本文就知道
    java 迭代器 Iterator
    机器学习——PPO补充
    寒假训练——第二周(BFS)
  • 原文地址:https://blog.csdn.net/lf282481431/article/details/136220782