呼吸灯设计


前言

PWM是一种利用微处理器或FPGA的数字输出来控制模拟电路的有效技术,广泛应用于多个领域。它通过高分辨率计数器调整方波的占空比来编码模拟信号的电平,无需数模转换。PWM信号是数字的,通过开关直流供电来控制模拟负载的电压或电流。实验中使用PWM实现LED呼吸灯效果,通过调整PWM的周期和占空比(高电平占周期的百分比)来控制LED灯的亮暗程度,产生逐渐变亮再逐渐变弱的效果。一般建议PWM周期设置为10毫秒。


一、程序设计

本次实验使用到两个计数器,cnt_10ms 用于计数 10ms,这是 PWM 方波的周期,cnt_pwn用来计数PWM的占空比,当cnt_10ms的值小于cnt_pwn的时候,pwm输出为 0,当 cnt_10ms 大于 cnt_pwn 的时候,pwm 输出为 1。
通过inc_flag信号来决定cnt_pwm的值是累加还是累减步进值OFFSET; inc_flag
为 1 的时候累加 OFFSET,inc_flag 为 0 的时候累减 OFFSET。

1.Verilog代码如下

// 定义呼吸灯模块,模拟呼吸灯效果
module breath_led #
(
    parameter CNT_10MS=10_000_000/20,  // 定义10毫秒的计数值,用于产生周期性信号
    parameter OFFSET=5000               // PWM调节的偏移量
)
(
    input clk,                         // 时钟信号输入
    input rst_n,                        // 复位信号输入,低电平有效
    output led                          // LED输出,表示呼吸灯状态
);

// 内部寄存器和信号定义
reg[31:0]cnt_10ms;  // 用于计数10毫秒的计数器
reg inc_flag;         // 增加标志,用于控制PWM占空比的增减
reg[31:0]cnt_pwm;    // PWM计数器,用于控制LED的亮度
wire pwm;             // PWM信号,用于控制LED

// 计数器cnt_10ms,用于产生周期性信号
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_10ms<='d0;            // 复位时清零计数器
    else if(cnt_10ms==CNT_10MS-1)
        cnt_10ms<='d0;            // 达到10毫秒时清零计数器
    else
        cnt_10ms<=cnt_10ms+1'b1;  // 否则计数器递增
end

// 控制inc_flag的逻辑,用于决定cnt_pwm是增加还是减少
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        inc_flag<=1'b1;            // 复位时设置增加标志为1
    else if(cnt_10ms==CNT_10MS-1) begin
        if(inc_flag==1'b1) begin  // 如果当前是增加阶段
            if(cnt_pwm>=CNT_10MS-1)  // 如果已经达到最大值,则切换到减少阶段
                inc_flag<=1'b0;
            else
                inc_flag<=inc_flag;  // 否则保持当前状态
        end
        else if(inc_flag==1'b0) begin  // 如果当前是减少阶段
            if(cnt_pwm==OFFSET)  // 如果已经减少到最小值,则切换到增加阶段
                inc_flag<=1'b1;
            else
                inc_flag<=inc_flag;  // 否则保持当前状态
        end
        else
            inc_flag<=inc_flag;  // 如果inc_flag状态未知,则保持当前状态
    end
    else
        inc_flag<=inc_flag;  // 如果不是10毫秒计数器的触发时刻,则保持当前状态
end

// 控制PWM计数器cnt_pwm,实现LED亮度的调节
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_pwm<='d0;  // 复位时清零PWM计数器
    else if(cnt_10ms==CNT_10MS-1) begin
        if(inc_flag)  // 如果当前是增加阶段
            cnt_pwm<=cnt_pwm+OFFSET;  // PWM计数器增加
        else  // 如果当前是减少阶段
            cnt_pwm<=cnt_pwm-OFFSET;  // PWM计数器减少
    end
    else
        cnt_pwm<=cnt_pwm;  // 保持当前PWM计数器值
end

// 根据cnt_pwm和cnt_10ms的值,生成PWM信号
assign pwm=(cnt_10ms>cnt_pwm)?1'b1:1'b0;  // 当cnt_10ms大于cnt_pwm时,pwm为高电平

// 将PWM信号赋值给led引脚,实现LED的呼吸效果
assign led=pwm;

endmodule

2.仿真代码如下

// 定义仿真时间单位和精度,1纳秒每单位,1皮秒的精度
`timescale 1ns / 1ps

// 定义测试模块 tb_breath_led,用于测试 breath_led 模块
module tb_breath_led();
    // 定义测试模块中的信号
    reg clk;           // 定义时钟信号
    reg rst_n;         // 定义复位信号,低电平有效
    wire led;           // 定义LED信号,这里用作呼吸灯的输出

    // 实例化 breath_led 模块,并传递参数 CNT_10MS 和 OFFSET
    breath_led #(
        .CNT_10MS(1000),  // 设置呼吸周期为1000个时钟周期
        .OFFSET(50)       // 设置PWM偏移量为50
    )
    breath_led           // 实例化模块,未命名
    (
        .clk(clk),         // 连接时钟信号
        .rst_n(rst_n),     // 连接复位信号
        .led(led)          // 连接LED输出信号
    );
    
    // 第一个 initial 块,用于生成时钟信号
    initial begin
        clk = 1;          // 初始化时钟信号为高电平
        forever begin      // 创建一个永久循环,以产生连续的时钟信号
            #10 clk = ~clk;  // 每10纳秒翻转一次时钟信号,产生50%占空比的方波
        end
    end
    
    // 第二个 initial 块,用于初始化复位信号并触发复位序列
    initial begin
        rst_n = 0;        // 初始化复位信号为低电平,启动复位
        repeat(20) @(posedge clk);  // 等待20个时钟周期,即200纳秒
        rst_n = 1;        // 将复位信号置为高电平,结束复位
    end

endmodule

3.管脚分配

set_property PACKAGE_PIN J19 [get_ports clk]
set_property PACKAGE_PIN L18 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN N18 [get_ports led]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports led]
##############将 FPGA 的其他没使用到的管脚设置为高电平################
set_property BITSTREAM.CONFIG.UNUSEDPIN Pullup [current_design]
关于呼吸灯的C语言编程Options 1,0,0 // Target 'Target 1' Device (AT89C51) Vendor (Atmel) Cpu (IRAM(0-0x7F) IROM(0-0xFFF) CLOCK(24000000)) FlashUt () StupF ("LIB\STARTUP.A51" ("Standard 8051 Startup Code")) FlashDR () DevID (2976) Rgf (REGX51.H) Mem () C () A () RL () OH () DBC_IFX () DBC_CMS () DBC_AMS () DBC_LMS () UseEnv=0 EnvBin () EnvInc () EnvLib () EnvReg (Atmel\) OrgReg (Atmel\) TgStat=16 OutDir (.\) OutName (a9) GenApp=1 GenLib=0 GenHex=1 Debug=1 Browse=1 LstDir (.\) HexSel=0 MG32K=0 TGMORE=0 RunUsr 0 0 RunUsr 1 0 BrunUsr 0 0 BrunUsr 1 0 SVCSID MODEL5=0 RTOS5=0 ROMSZ5=2 DHOLD5=0 XHOLD5=0 T51FL=80 XT51FL=0 CBANKS5=0 XBANKS5=0 RCB51 { 0,0,0,0,0,0,0,1,0 } RXB51 { 0,0,0,0,0,0,0,0,0 } OCM51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } OCR51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } IRO51 { 1,0,0,0,0,0,16,0,0 } IRA51 { 0,0,0,0,0,128,0,0,0 } XRA51 { 0,0,0,0,0,0,0,0,0 } XRA512 { 0,0,0,0,0,0,0,0,0 } IROM512 { 0,0,0,0,0,0,0,0,0 } C51FL=21630224 C51VA=0 C51MSC () C51DEF () C51UDF () INCC5 () AX51FL=4 AX51MSC () AX51SET () AX51RST () INCA5 () PropFld { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } IncBld=1 AlwaysBuild=0 GenAsm=0 AsmAsm=0 PublicsOnly=0 StopCode=3 CustArgs () LibMods () BankNo=65535 LX51FL=292 LX51OVL () LX51MSC () LX51DWN () LX51LFI () LX51ASN () LX51RES () LX51CCL () LX51UCL () LX51CSC () LX51UCS () LX51COB () LX51XDB () LX51PDB () LX51BIB () LX51DAB () LX51IDB () LX51PRC () LX51STK () LX51COS () LX51XDS () LX51BIS () LX51DAS () LX51IDS () OPTDL (S8051.DLL)()(DP51.DLL)(-p51)(S8051.DLL)()(TP51.DLL)(-p51) OPTDBG 48125,-1,()()()()()()()()()() ()()()() FLASH1 { 0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0 } FLASH2 () FLASH3 () FLASH4 () EndOpt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值