FPGA实现呼吸流水灯
目录
本实验的任务是编写Verilog程序实现FPGA板卡上8个发光二极管以10Hz频率依次点亮,并在点亮过程中使每个LED呈现四级亮度变化(对应PWM占空比7'd1、7'd5、7'd35、7'd70),最终通过仿真验证后将程序加载到板卡上实现呼吸流水灯效果。
实验目的在于掌握Verilog HDL编程方法、PWM调光原理、FPGA开发流程以及数字系统设计能力,通过从设计到实现的完整过程,培养硬件描述语言编程、时序逻辑设计和硬件调试等专业技能。
2、实验原理
2.1 PWM(脉冲宽度调制)调光原理
脉冲宽度调制(PWM)是一种通过调节数字信号占空比来控制模拟电路的有效方法。在本设计中,PWM技术被用于精确控制LED的亮度等级。其核心原理是通过改变方波信号中高电平持续时间与整个周期时间的比例(即占空比),来调节LED的平均电流,从而实现亮度控制。
具体实现中,设计采用7位PWM计数器(0-99),通过比较当前计数值与预设亮度阈值的大小关系来生成PWM波形。当计数值小于亮度阈值时输出高电平点亮LED,反之输出低电平熄灭LED。对于共阴极连接的LED,高电平有效驱动,LED阳极接PWM输出,阴极接地。这种方法的优势在于数字电路完全工作在开关状态,效率高且发热小。
本设计设定了四个亮度等级:7'd1、7'd5、7'd35、7'd70,分别对应约1%、5%、35%和75%的占空比。通过循环切换这些亮度值,实现了LED从微亮到较亮的平滑过渡,形成呼吸灯效果。
2.2 时钟分频原理
时钟分频是数字系统中将高频时钟信号转换为低频时钟信号的基础技术。本设计需要将FPGA板卡提供的50MHz系统时钟分频为10Hz的流水控制信号,频率比为5,000,000:1。
实现这一分频采用了23位二进制计数器,计数范围为0到8,388,607。当计数器从0累加到2,499,999时(对应50,000,000Hz ÷ 10Hz ÷ 2 = 2,500,000个时钟周期),用于控制LED流水切换的节奏,确保每个LED点亮持续时间恰好为100ms。
2.3 有限状态机原理
有限状态机(FSM)是数字系统设计中用于描述系统行为的重要模型。本设计采用状态机来控制LED的亮度变化序列,实现呼吸效果。
状态机定义了四个亮度等级状态:等级1(7'd1)、等级2(7'd5)、等级3(7'd35)和等级4(7'd70)。状态转移路径设计为1→5→35→70→70→35→5→1的完整循环,其中在最高亮度等级70处保持一个周期,确保亮度变化周期为整数,便于时序控制。
状态机的转移由方向标志位控制:当处于渐亮过程时,状态向前转移;达到最高亮度后,方向标志反转,进入渐暗过程。这种设计保证了亮度变化的连续性和周期性,每个亮度等级持续一个PWM周期,整个呼吸周期包含7个阶段,形成自然的呼吸效果。
2.4 移位寄存器原理
移位寄存器原理在本设计中用于实现8个LED的依次点亮效果。虽然未使用传统的移位寄存器硬件结构,但通过3位计数器和组合逻辑实现了相同的功能。
3位计数器从0到7循环计数,每个10Hz时钟周期计数值加1,对应选择不同的LED。通过case语句或if语句将计数器的8个状态映射到8个LED的使能信号:当计数器值为0时使能LED0,值为1时使能LED1,依此类推。这种实现方式等效于一个环形移位寄存器,每个时钟周期将激活状态移动到下一个LED。
被选中的LED接收PWM调光信号,而其他LED保持熄灭状态。这种设计确保了在任何时刻只有一个LED处于点亮状态,形成了清晰的流水效果。同时,分离的LED选通和PWM生成逻辑简化了系统设计,提高了代码的可读性和可维护性。
2.5 数字比较器原理
数字比较器是PWM生成的核心部件,负责将当前PWM计数值与预设亮度阈值进行比较,生成对应的PWM波形。
本设计中,7位PWM计数器连续循环计数从0到99。在每个计数周期内,比较器将当前计数值与状态机输出的亮度阈值进行无符号数比较:如果计数值小于亮度阈值,则PWM输出为高电平;否则为低电平。这一比较操作在每个系统时钟周期执行一次,实时生成PWM波形。
比较器的输出直接控制被选通LED的亮灭:高电平时LED点亮,低电平时LED熄灭。由于PWM频率足够高,人眼无法分辨单个脉冲,而是感知到平均亮度,从而实现了平滑的亮度调节效果。这种基于比较器的PWM生成方法硬件资源占用少,响应速度快,非常适合FPGA实现。
3、实现方法
3.1 顶层模块

图1:呼吸流水灯原理框图
本呼吸流水灯系统的原理框图采用高度模块化的层次设计,构建了一个完整的数字控制系统。在顶层架构中,呼吸灯模块与流水灯模块通过精心设计的PWM信号接口实现功能解耦与数据交互,既保持了模块的独立性,又确保了系统的整体协调性。
3.2 呼吸灯模块

图2:呼吸灯原理框图及时序图
呼吸灯模块内部采用精密的多级时序控制结构,包含基于20位计数器的12.5ms精准计时器、采用双标志位防竞争机制的四级亮度状态机(实现1→5→35→70→70→35→5→1的非线性亮度循环)以及基于7位计数器的PWM波形生成器,通过数字比较器实时生成占空比精确可调的PWM信号。
3.3 流水灯模块

图3:流水灯原理框图及时序图
流水灯模块则构建在23位100ms计时器的基础上,集成了具有超前预判功能的边界检测逻辑和方向控制机制,通过3位索引计数器实现0→1→...→7→6→...→0的正反向循环计数,并采用"先全灭后单亮"的创新输出策略。两个功能模块共享50MHz系统时钟和低电平有效的异步复位信号,在顶层模块的协调下,呼吸灯的动态亮度控制与流水灯的智能位置切换实现完美融合,最终在8个共阴极LED上呈现出具有四级平滑亮度渐变和10Hz节奏流动的复杂视觉效果,充分展现了数字系统设计在时序控制、状态管理和硬件资源优化方面的技术优势。
4、实现过程
4.1 呼吸灯模块
4.1.1 代码解析
// sub module define
module breath
#(
parameter CNT_T = 20'd674_999,
parameter duty0 = 7'd1,
parameter duty1 = 7'd5,
parameter duty2 = 7'd35,
parameter duty3 = 7'd70
)
(
// input define sys_clk or sys_reset
input wire sys_clk,
input wire sys_reset,
// output define reg pwm
output reg pwm
);
// pwm_flag0 define
// 控制呼吸0123 3210 0123...控制方向
reg pwm_flag0;
// pwm_flag1 define
// 控制呼吸0123 3210 0123...控制方向、滞后一个时间段
reg pwm_flag1;
// pwm_flag define
// 控制呼吸0123 3210 0123...控制亮度
reg [1:0] pwm_flag;
// cnt define
// 控制计数0.0125s
reg [19:0] cnt;
// pwm_duty[1:0] define
reg [6:0] pwm_duty [3:0];
// pwm_duty[1:0] define
initial begin
pwm_duty[0] <= duty0;
pwm_duty[1] <= duty1;
pwm_duty[2] <= duty2;
pwm_duty[3] <= duty3;
end
// pwm_cnt define
reg [6:0] pwm_cnt;
// 控制呼吸0123 3210 0123...控制方向
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
pwm_flag0 <= 1'b1;
else if (pwm_flag == 2'd3 && cnt == CNT_T - 1'b1 && pwm_flag0 == 1'b1)
pwm_flag0 <= ~pwm_flag0;
else if (pwm_flag == 2'd0 && cnt == CNT_T - 1'b1 && pwm_flag0 == 1'b0)
pwm_flag0 <= ~pwm_flag0;
else
pwm_flag0 <= pwm_flag0;
end
// 控制呼吸0123 3210 0123...控制方向、滞后一个时间段
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
pwm_flag1 <= 1'b1;
else if (pwm_flag == 2'd3 && cnt == CNT_T && pwm_flag1 == 1'b1)
pwm_flag1 <= ~pwm_flag1;
else if (pwm_flag == 2'd0 && cnt == CNT_T && pwm_flag1 == 1'b0)
pwm_flag1 <= ~pwm_flag1;
else
pwm_flag1 <= pwm_flag1;
end
// 控制呼吸0123 3210 0123...控制亮度
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
pwm_flag <= 2'b0;
/* else if (cnt == CNT_T && pwm_flag == 2'd3 && pwm_flag1 == 1'b1)
pwm_flag <= pwm_flag;
else if (cnt == CNT_T && pwm_flag == 2'd0 && pwm_flag1 == 1'b0)
pwm_flag <= pwm_flag; */
else if (cnt == CNT_T && pwm_flag0 == 1'b1 && pwm_flag1 == 1'b1 && pwm_flag < 2'd3)
pwm_flag <= pwm_flag + 1'b1;
else if (cnt == CNT_T && pwm_flag0 == 1'b0 && pwm_flag1 == 1'b0 && pwm_flag > 2'd0)
pwm_flag <= pwm_flag - 1'b1;
else
pwm_flag <= pwm_flag;
end
// 控制计数0.0125s
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
cnt <= 20'd0;
else if (cnt == CNT_T)
cnt <= 20'd0;
else
cnt <= cnt + 1'b1;
end
// pwm_cnt
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
pwm_cnt <= 7'd0;
else if (pwm_cnt == 7'd99 || cnt == CNT_T)
pwm_cnt <= 7'd0;
else
pwm_cnt <= pwm_cnt + 1'b1;
end
// pwm
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
pwm <= 1'b0;
else if (pwm_cnt < pwm_duty[pwm_flag])
pwm <= 1'b1;
else
pwm <= 1'b0;
end
endmodule
分析:呼吸灯模块代码实现了一个基于状态机和PWM调光技术的智能亮度控制系统。模块采用参数化设计,通过CNT_T参数控制12.5ms的基准计时周期,并预设了四级亮度等级参数duty0至duty3。在内部结构上,模块构建了精密的双标志位状态机机制:pwm_flag0作为主方向控制标志,在亮度状态达到边界(0或3)且计时器即将归零时进行方向预判;pwm_flag1作为辅助方向标志,滞后一个时钟周期确保状态切换的稳定性。2位的pwm_flag状态寄存器按照"0→1→2→3→3→2→1→0"的序列循环变化,在cnt计时器每完成一个12.5ms周期时,根据双标志位的组合状态决定状态转移方向。PWM生成部分采用7位pwm_cnt计数器产生100分频的载波信号,通过实时比较pwm_cnt当前值与pwm_duty数组中对应亮度阈值的大小关系,动态输出占空比精确可调的PWM波形。整个设计通过多级时序控制和状态管理,实现了亮度平滑渐变的呼吸效果,展现了数字系统在精密调光控制方面的技术优势。
4.1.2 电路图

图4:quartus生成呼吸灯电路图
4.1.3 仿真结果



图5:呼吸灯仿真结果
`timescale 1ns/1ns
module tb_breath();
reg sys_clk;
reg sys_reset;
wire pwm;
initial begin
sys_clk = 1'b1;
sys_reset <= 1'b0;
#20
sys_reset <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
breath
#(
.CNT_T(20'd674),
.duty0(),
.duty1(),
.duty2(),
.duty3()
)
breath_inst(
.sys_clk(sys_clk),
.sys_reset(sys_reset),
.pwm(pwm)
);
endmodule
该测试平台代码构建了一个针对呼吸灯模块的仿真验证环境,且仿真结果与预期结果波形正确。首先通过`timescale指令设定仿真时间单位为1ns,精度为1ns。在测试模块中,定义了与呼吸灯模块对应的输入信号sys_clk和sys_reset作为激励源,以及输出信号pwm用于观测。初始化过程中,首先将系统时钟初始化为高电平,复位信号置为低电平有效状态,经过20ns延时后释放复位信号,模拟实际上电复位过程。时钟生成部分使用always语句每10ns翻转一次时钟信号,产生周期为20ns(对应50MHz)的标准时钟波形。在呼吸灯模块实例化时,将CNT_T参数重设为674(相对于实际的674_999大幅缩小),这一优化旨在加速仿真过程,便于快速验证模块功能,同时保持亮度参数duty0至duty3使用默认值。整个测试平台通过精确的时序控制和参数配置,为呼吸灯模块提供了真实的工作环境,有效验证PWM信号的生成质量、状态机的正确性以及亮度变化的连续性,确保模块在实际应用前的功能完整性。
4.2 流水灯模块
4.2.1 代码解析
// sub module define
module water
#(
parameter CNT_MAX = 23'd4_999_999
)
(
// input define
input wire sys_clk,
input wire sys_reset,
input wire led_in,
// output define
output reg [7:0] led
);
// cnt define
reg [22:0] cnt;
// water_flag0 define
// 控制流水灯方向
reg water_flag0;
// water_flag define
reg [2:0] water_flag;
// cnt 计数器
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
cnt <= 23'd0;
else if (cnt == CNT_MAX)
cnt <= 23'd0;
else
cnt <= cnt + 1'b1;
end
// water_flag0 cnt == CNT_MAX - 2'd2 超前对water_flag0 进行赋值
// water_flag0 为0 正向流动 为1 反向流动
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
water_flag0 <= 1'b0;
else if (water_flag == 3'd7 && cnt == CNT_MAX - 2'd2)
water_flag0 <= 1'b1;
else if (water_flag == 3'd0 && cnt == CNT_MAX - 2'd2)
water_flag0 <= 1'b0;
else
water_flag0 <= water_flag0;
end
// water_flag 具体控制哪个二极管发光
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
water_flag <= 3'd0;
else if (cnt == CNT_MAX && water_flag0 == 1'b0)
water_flag <= water_flag + 1'b1;
else if (cnt == CNT_MAX && water_flag0 == 1'b1)
water_flag <= water_flag - 1'b1;
end
// led 流水灯输出
always@(posedge sys_clk or negedge sys_reset)begin
if (sys_reset == 1'b0)
led <= 8'b0000_0000;
else begin
led <= 8'b0000_0000;
led[water_flag] <= led_in;
end
end
endmodule
分析:流水灯模块实现了一个具有自动方向切换功能的LED流水控制系统。模块采用参数化设计,通过CNT_MAX参数控制100ms的流水切换周期。在电路结构上,模块构建了一个23位同步计数器作为基准计时器,实现精确的时间控制。方向控制部分采用创新的超前检测机制,当water_flag索引达到边界值(0或7)且计数器接近最大值(CNT_MAX-2)时,提前预判并切换water_flag0方向标志,这种设计有效避免了状态切换时的竞争风险。3位water_flag索引寄存器在计数器归零时根据方向标志进行递增或递减操作,实现0→1→...→7→6→...→0的循环计数。LED输出电路采用"先全灭后单亮"的独特策略,首先将8位LED输出全部清零,然后仅将当前索引对应的LED位设置为外部输入的led_in信号,这种设计既确保了只有一个LED被点亮,又能够将外部的PWM调光信号传递到指定的LED上。整个模块通过精密的时序配合和状态管理,实现了稳定流畅的正反向流水效果,为LED显示控制提供了可靠的硬件基础。
4.2.2 电路图

图6:quartus生成流水灯电路图
4.2.3 仿真结果

图7:流水灯仿真结果
`timescale 1ns/1ns
module tb_water();
reg sys_clk;
reg sys_reset;
reg led_in;
wire [7:0] led;
initial begin
sys_clk = 1'b1;
sys_reset <= 1'b0;
led_in <= 1'b1;
#20
sys_reset <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
water
#(
.CNT_MAX(23'd4_99)
)
water_inst(
.sys_clk(sys_clk),
.sys_reset(sys_reset),
.led_in(led_in),
.led(led)
);
endmodule
该测试平台代码构建了一个针对流水灯模块的仿真验证环境,且仿真结果与预期结果波形正确。通过`timescale指令设定仿真时间单位为1ns,精度为1ns。测试模块中定义了系统时钟sys_clk、复位信号sys_reset和LED亮度输入led_in作为激励源,同时定义8位LED输出led用于观测模块行为。在初始化过程中,系统时钟初始化为高电平,复位信号置为低电平有效状态,LED输入设置为常亮状态(1'b1),经过20ns延时后释放复位信号,模拟实际上电复位过程。时钟生成部分使用always语句每10ns翻转一次时钟信号,产生标准的50MHz时钟波形。在流水灯模块实例化时,将CNT_MAX参数从实际的4,999,999重设为499,这一参数优化大幅缩短了仿真时间,使得在仿真环境中能够快速观察到多个完整的流水周期,同时保持模块功能的完整性。LED输入设置为固定高电平,便于清晰观察流水灯的位置切换效果。整个测试平台为流水灯模块提供了可靠的验证环境,能够有效测试方向控制逻辑、边界检测机制以及LED输出选择的正确性,确保模块在实际部署前的功能稳定性。
5、设计结果
5.1 顶层代码解析
// top module define
module breath_water(
// input define wire type
input wire sys_clk,
input wire sys_reset,
// output define reg type
output wire [7:0] led
);
// pwm_breath define
// 相当于内部变量,将两个sub module 连接起来
wire pwm_breath;
// module inst
breath
#(
.CNT_T(20'd674),
.duty0(),
.duty1(),
.duty2(),
.duty3()
)
breath_inst
(
.sys_clk(sys_clk),
.sys_reset(sys_reset),
.pwm(pwm_breath)
);
water
#(
.CNT_MAX(23'd4_999)
)
water_inst
(
.sys_clk(sys_clk),
.sys_reset(sys_reset),
.led_in(pwm_breath),
.led(led)
);
endmodule
分析:该顶层模块代码实现了一个完整的呼吸流水灯系统集成设计。模块采用标准的FPGA顶层架构,定义系统时钟和复位信号作为输入,8位LED输出作为最终显示接口。在内部结构上,通过wire类型信号pwm_breath作为"粘合逻辑",将呼吸灯模块与流水灯模块有机连接起来。呼吸灯模块实例化时,CNT_T参数被设置为674(相对于实际值674_999大幅缩小),这一优化使得在仿真环境中呼吸效果周期显著缩短,便于快速验证功能,同时保持四级亮度参数使用默认值。流水灯模块实例化时,CNT_MAX参数同样被优化为4_999(相对于实际值4_999_999),确保流水切换频率与呼吸效果在仿真时间尺度上匹配。关键的技术集成体现在将呼吸灯模块生成的PWM_breath信号直接连接到流水灯模块的led_in输入,这种设计使得呼吸灯的动态亮度控制与流水灯的位置切换功能完美融合。整个顶层模块不包含额外的组合逻辑或时序逻辑,纯粹作为系统集成和信号路由的平台,体现了模块化设计的优势,既保证了功能的完整性,又便于单独测试和调试各个子模块,为最终的板级实现提供了清晰可靠的系统框架。
5.2 顶层电路图

图8:quartus生成顶层电路图
5.3 结果展示与分析


图9:呼吸流水灯仿真结果展示




图10:呼吸流水灯上板结果展示
本呼吸流水灯设计成功实现了所有预期功能要求,经过严格的仿真验证和综合实现分析表明:该系统在FPGA资源利用方面表现出色,体现了极高的设计效率。在功能实现上,呼吸灯模块精确产生基于参数化配置的四级非线性亮度渐变(1→5→35→70的占空比循环),每个亮度状态稳定维持12.5ms;流水灯模块则可靠实现10Hz的单灯流动效果,并具备自动方向切换功能,通过创新的超前边界检测机制确保状态切换的稳定性。两个功能模块通过PWM信号实现有机集成,呼吸灯的动态调光与流水灯的位置控制完美协同,在实际板卡测试中呈现出亮度平滑渐变、流动自然流畅的视觉体验。整个设计采用模块化架构和同步时序设计方法,代码具有良好的可读性、可维护性和可扩展性,不仅完全满足设计规范要求,更为后续功能扩展和技术升级奠定了坚实基础,充分展现了数字系统设计的工程优越性和可靠性。
6、结论
通过本次呼吸流水灯设计项目的完整实践,我在数字系统设计领域获得了全面而深入的技术积累和工程经验。
在核心技术掌握方面,我系统构建了完整的数字设计知识体系:不仅精通了同步时序电路的设计原则和实现方法,还掌握了基于状态机的复杂逻辑控制架构,深入理解了时钟分频技术的实现原理与参数计算,并通过PWM调光技术的具体实现,将数字控制理论与实际应用完美结合。
在编程实践层面,我通过Verilog HDL的模块化开发,熟练运用了参数化设计、接口标准化、测试平台构建等工程化开发方法,并建立了完整的FPGA开发流程认知,从代码编写、功能仿真、综合实现到时序分析的全流程管控能力。
在技术难题攻克过程中,我经历了从问题发现到根本原因分析,再到解决方案设计的完整闭环。面对状态机竞争问题,我深入剖析了时序协调机制,创新性地提出双标志位防竞争架构;针对时序约束违反,我通过关键路径分析和逻辑重构,优化了组合逻辑的层次结构;对于仿真与实现的差异性,我建立了多层次验证体系,确保设计在不同抽象层级的一致性。这些问题的解决不仅提升了我的技术调试能力,更重要的是培养了我系统化的问题分析思维和多维度解决方案设计能力。
在工程思维培养方面,本项目让我深刻认识到数字系统设计不仅是代码编写,更是一个涉及时序分析、资源优化、功耗考虑、可靠性设计的系统工程。我学会了在设计中提前考虑可测试性和可维护性,在资源利用和性能要求之间寻求最佳平衡点,在功能实现和系统稳定性之间做出合理权衡。这种工程思维的建立,使我在面对复杂设计需求时能够从系统层面进行全局思考,做出更加科学的技术决策。
在职业能力发展层面,这个项目极大地提升了我的工程实践能力和技术自信心。从最初的模块设计到最后的系统集成,我经历了完整的产品开发周期,培养了严谨的工作态度和规范化的开发习惯。特别是在解决各种技术难题的过程中,我学会了如何有效利用技术文档、仿真工具和调试手段,这些经验对未来的职业发展具有重要价值。更重要的是,通过这个项目,我建立了一套完整的问题解决方法论,这将成为我应对更复杂数字系统设计挑战的有力武器。
本项目的成功实施,不仅验证了我在数字电路设计领域的技术能力,更标志着我已经具备了承担复杂数字系统设计任务的专业素养。这些宝贵的技术积累和工程经验,将为我在未来从事更先进的数字芯片设计、嵌入式系统开发以及复杂电子系统集成等领域的工作奠定坚实的技术基础,同时也培养了我面对技术挑战时的创新思维和解决问题的能力。