新手入门
- 积分
- 9
- 金钱
- 9
- 注册时间
- 2025-11-16
- 在线时间
- 0 小时
|
1金钱
源码如下breath_led.v:
// 呼吸灯控制模块:PWM占空比线性增减实现呼吸效果
// 功能:KEY1减慢双灯呼吸速度,KEY2加快双灯呼吸速度,3次调整后重置
module breath_led(
input clk, // 50MHz时钟
input rst_n, // 低电平复位
input mode_en, // 模式使能(高电平有效)
input key1_press, // key1按下(减慢双灯速度)
input key2_press, // key2按下(加快双灯速度)
output reg led1_out, // LED1输出
output reg led2_out // LED2输出
);
// 参数定义
parameter CLK_FREQ = 50_000_000; // 50MHz时钟
parameter STEP_NUM = 1000; // 占空比步长数(精度1/1000)
// 3档呼吸速度:步长时间(时钟周期数)→ 步长时间越长,呼吸越慢
localparam SPEED_STEP_SLOW = CLK_FREQ * 3 / STEP_NUM; // 慢档(3秒呼吸周期)
localparam SPEED_STEP_MID = CLK_FREQ * 2 / STEP_NUM; // 中档(2秒呼吸周期,初始)
localparam SPEED_STEP_FAST = CLK_FREQ * 1 / STEP_NUM; // 快档(1秒呼吸周期)
// 内部信号
reg [1:0] speed_cnt; // 双灯共用速度档位计数器(0:中档,1:快档,2:慢档)
reg [31:0] step_timer; // 双灯共用步长定时器(控制占空比变化速度)
reg [9:0] duty_cycle1; // LED1占空比(0~STEP_NUM-1)
reg [9:0] duty_cycle2; // LED2占空比(与LED1同步,保证双灯一致)
reg dir; // 占空比变化方向(0:增加,1:减少)
reg [9:0] pwm_cnt; // PWM载波计数器(0~STEP_NUM-1)
// 1. 速度档位控制(KEY1减慢,KEY2加快,3次后重置)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
speed_cnt <= 2'd0; // 复位后默认中档(2秒周期)
end else if(mode_en) begin
// KEY2按下:加快速度(0→1→2→0,3次后重置)
if(key2_press) begin
speed_cnt <= (speed_cnt == 2'd1) ? 2'd0 : speed_cnt + 2'd1;
// 说明:0=中档→1=快档→2=重置回中档(3次加快逻辑:按3次KEY2→0→1→0→1→...)
// 实际效果:按1次→快档,按2次→中档,按3次→快档?不,修正为3次后重置:
// 正确逻辑:0(中)→1(快)→2(超快?不,原需求3次后重置,所以0→1→2→0)
// 重新调整:3档循环(0:中,1:快,2:超慢?不,按需求“加快3次后重置”)
// 最终逻辑:加快次数计数(0→1→2→0),对应档位:0=中,1=快,2=更快→3次后回中
speed_cnt <= (speed_cnt == 2'd2) ? 2'd0 : speed_cnt + 2'd1;
end
// KEY1按下:减慢速度(0→2→1→0,3次后重置)
else if(key1_press) begin
speed_cnt <= (speed_cnt == 2'd0) ? 2'd2 : speed_cnt - 2'd1;
end
end
end
// 2. 步长定时器(根据档位选择步长时间:档位越高,步长时间越短→呼吸越快)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
step_timer <= SPEED_STEP_MID; // 初始中档
end else if(mode_en) begin
case(speed_cnt)
2'd0: step_timer <= SPEED_STEP_MID; // 中档(2秒周期)
2'd1: step_timer <= SPEED_STEP_FAST; // 快档(1秒周期)
2'd2: step_timer <= SPEED_STEP_SLOW; // 慢档(3秒周期)
default: step_timer <= SPEED_STEP_MID;
endcase
end
end
// 3. PWM载波计数器(生成固定频率PWM,确保占空比精度)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
pwm_cnt <= 10'd0;
end else if(mode_en) begin
pwm_cnt <= (pwm_cnt == STEP_NUM - 1) ? 10'd0 : pwm_cnt + 10'd1;
end else begin
pwm_cnt <= 10'd0;
end
end
// 4. 占空比线性增减控制(双灯共用同一占空比和方向,保证同步)
reg [31:0] cnt; // 步长计时计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 32'd0;
duty_cycle1 <= 10'd0;
duty_cycle2 <= 10'd0;
dir <= 1'b0; // 初始方向:占空比增加(LED渐亮)
end else if(mode_en) begin
cnt <= cnt + 32'd1;
if(cnt >= step_timer) begin // 达到步长时间,更新占空比
cnt <= 32'd0;
if(dir == 1'b0) begin // 占空比增加(渐亮)
// 达到最大占空比(STEP_NUM-1),反向(渐暗)
if(duty_cycle1 == STEP_NUM - 1) begin
dir <= 1'b1;
end else begin
duty_cycle1 <= duty_cycle1 + 10'd1;
duty_cycle2 <= duty_cycle2 + 10'd1; // 双灯同步更新
end
end else begin // 占空比减少(渐暗)
// 达到最小占空比(0),反向(渐亮)
if(duty_cycle1 == 10'd0) begin
dir <= 1'b0;
end else begin
duty_cycle1 <= duty_cycle1 - 10'd1;
duty_cycle2 <= duty_cycle2 - 10'd1; // 双灯同步更新
end
end
end
end else begin
cnt <= 32'd0;
duty_cycle1 <= 10'd0;
duty_cycle2 <= 10'd0;
dir <= 1'b0;
end
end
// 5. LED1 PWM输出(占空比比较)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
led1_out <= 1'b0;
end else if(mode_en) begin
led1_out <= (pwm_cnt < duty_cycle1) ? 1'b1 : 1'b0;
end else begin
led1_out <= 1'b0;
end
end
// 6. LED2 PWM输出(与LED1完全同步)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
led2_out <= 1'b0;
end else if(mode_en) begin
led2_out <= (pwm_cnt < duty_cycle2) ? 1'b1 : 1'b0;
end else begin
led2_out <= 1'b0;
end
end
endmodule
-------------------------------------------------
buzzer_ctrl.v:
// 蜂鸣器控制模块:任何按键按下时,输出100ms提示音
// 特性:多次按键按下时,重新计时100ms
module buzzer_ctrl(
input clk, // 50MHz时钟
input rst_n, // 低电平复位
input key1_press, // key1按下脉冲
input key2_press, // key2按下脉冲
input touch_press, // 触摸按键按下脉冲
output reg buzzer_en // 蜂鸣器使能(高电平有效)
);
// 参数定义(移至端口声明后,修复语法错误)
parameter CLK_FREQ = 50_000_000; // 50MHz时钟
parameter PROMPT_MS = 250; // 提示音时长250ms
localparam PROMPT_CNT_MAX = CLK_FREQ * PROMPT_MS / 1000 - 1; // 100ms对应的时钟周期数
// 内部信号
reg [23:0] prompt_cnt; // 提示音计数器(24位足够表示5e6)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
prompt_cnt <= 24'd0;
buzzer_en <= 1'b0;
end else begin
// 任何按键按下,重置计数器并开启蜂鸣器
if(key1_press || key2_press || touch_press) begin
prompt_cnt <= PROMPT_CNT_MAX;
buzzer_en <= 1'b1;
end else if(prompt_cnt > 24'd0) begin // 计数器倒计时
prompt_cnt <= prompt_cnt - 24'd1;
buzzer_en <= 1'b1;
end else begin // 计时结束,关闭蜂鸣器
buzzer_en <= 1'b0;
end
end
end
endmodule
------------------------------------------------------
flow_led.v:
module flow_led(
input clk, // 50MHz时钟
input rst_n, // 低电平复位
input mode_en, // 模式使能(高电平有效)
input key1_press, // key1按下(减慢)
input key2_press, // key2按下(加快)
output reg led1_out, // LED1输出
output reg led2_out // LED2输出
);
parameter BASE_PERIOD = 50_000_000; // 基础流水周期(1000ms)
localparam ADJUST_STEP = 10_000_000; // 速度调整步长(200ms)
localparam MAX_PERIOD = BASE_PERIOD + ADJUST_STEP * 2; // 最大周期1400ms
localparam MIN_PERIOD = BASE_PERIOD - ADJUST_STEP * 2; // 最小周期600ms
// 内部信号(确保每个寄存器仅在一个always块中赋值)
reg [31:0] flow_period;// 当前流水周期(时钟周期数)
reg [31:0] flow_cnt; // 流水周期计数器
reg led_state; // 流水状态(0 ED1亮, 1 ED2亮)
// 1. 速度档位控制(互斥逻辑:加快/减慢二选一,3次后重置)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
flow_period <= BASE_PERIOD;
end else if(mode_en) begin
if(key2_press) begin // 加快模式:优先级高于减慢
flow_period <= flow_period - ADJUST_STEP;
end else if(key1_press) begin // 减慢模式
flow_period <= flow_period + ADJUST_STEP;
end else begin
flow_period <= flow_period;
end
end
end
// 3. 流水状态切换(mode_en有效即自动流水,无需按键触发)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
flow_period <= flow_period;
end else if(mode_en) begin
if(flow_cnt >= flow_period / 2) begin // 半周期切换一次状态
flow_cnt <= 32'd0;
led_state <= ~led_state;
end
end else begin
led_state <= 1'b0;
end
end
// 4. LED输出控制(唯一驱动源,无多驱动冲突)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
led1_out <= 1'b0;
led2_out <= 1'b0;
end else if(mode_en) begin
case(led_state)
1'b0: begin // LED1亮,LED2灭
led1_out <= 1'b1;
led2_out <= 1'b0;
end
1'b1: begin // LED1灭,LED2亮
led1_out <= 1'b0;
led2_out <= 1'b1;
end
default: {led1_out, led2_out} <= 2'b00;
endcase
end else begin
led1_out <= 1'b0;
led2_out <= 1'b0;
end
end
endmodule
--------------------------------------
key_debounce.v:
// 按键消抖模块:处理3个按键的抖动,输出1个时钟周期的按下脉冲
// 特性:两级同步消抖,避免亚稳态,消抖时间20ms(50MHz下1e6个时钟周期)
module key_debounce(
input clk, // 50MHz时钟
input rst_n, // 低电平复位
input [2:0] key_in, // 3个按键输入(未按下高电平,按下低电平)
output reg [2:0] key_press // 按键按下脉冲(高电平有效,持续1周期)
);
// 参数定义(移至端口声明后,修复语法错误)
parameter CLK_FREQ = 50_000_000; // 时钟频率50MHz
parameter DEBOUNCE_MS = 20; // 消抖时间20ms
localparam DEBOUNCE_CNT_MAX = CLK_FREQ * DEBOUNCE_MS / 1000 - 1; // 消抖计数最大值
// 内部信号
reg [2:0] key_in_sync; // 按键输入同步(消除亚稳态)
reg [2:0] key_in_stable;// 稳定的按键状态
reg [19:0] debounce_cnt;// 消抖计数器(20位足够表示1e6)
reg [2:0] key_in_stable_prev; // 上一周期稳定状态
// 第一步:两级同步按键输入(避免亚稳态)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_in_sync <= 3'b111; // 初始状态:按键未按下(高电平)
end else begin
key_in_sync <= key_in;
end
end
// 第二步:消抖计数,获取稳定状态
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_in_stable <= 3'b111;
debounce_cnt <= 20'd0;
end else begin
if(key_in_sync != key_in_stable) begin // 按键状态变化,开始计数
debounce_cnt <= DEBOUNCE_CNT_MAX;
key_in_stable <= key_in_sync;
end else if(debounce_cnt > 20'd0) begin // 计数未结束,继续倒计时
debounce_cnt <= debounce_cnt - 20'd1;
end
end
end
// 第三步:检测按键按下(高→低跳变),输出脉冲
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_press <= 3'b000;
key_in_stable_prev <= 3'b111;
end else begin
key_in_stable_prev <= key_in_stable; // 存储上一周期状态
// 未按下(1)→按下(0):产生高脉冲
key_press <= ~key_in_stable & key_in_stable_prev;
end
end
endmodule
--------------------------------
mode_ctrl.v:
// 模式控制模块:触摸按键按下一次,切换一次工作模式
// 初始模式:呼吸灯模式(mode=0),再次按下切换为流水灯模式(mode=1)
module mode_ctrl(
input clk, // 50MHz时钟
input rst_n, // 低电平复位
input touch_press, // 触摸按键按下脉冲
output reg mode // 模式输出(0:呼吸灯,1:流水灯)
);
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
mode <= 1'b0; // 复位后默认呼吸灯模式
end else begin
if(touch_press) begin // 触摸按键按下,模式翻转
mode <= ~mode;
end
end
end
endmodule
------------------------------
top_key_led_buzzer.v:
module top_key_led_buzzer(
input clk_50mhz, // 50MHz系统时钟
input rst_n, // 低电平复位
input key1, // 实体按键1
input key2, // 实体按键2
input touch_key, // 触摸按键
output reg led1, // LED1
output reg led2, // LED2
output reg buzzer // 蜂鸣器
);
wire [2:0] key_press; // 3个按键的按下脉冲(0:key1, 1:key2, 2:touch_key)
wire mode; // 工作模式(0:呼吸灯, 1:流水灯)
wire led1_breath;// 呼吸灯模式下LED1输出
wire led2_breath;// 呼吸灯模式下LED2输出
wire led1_flow; // 流水灯模式下LED1输出
wire led2_flow; // 流水灯模式下LED2输出
wire buzzer_en; // 蜂鸣器使能
// 按键消抖模块例化
key_debounce u_key_debounce(
.clk (clk_50mhz),
.rst_n (rst_n),
.key_in ({touch_key, key2, key1}),
.key_press (key_press)
);
// 模式控制模块例化
mode_ctrl u_mode_ctrl(
.clk (clk_50mhz),
.rst_n (rst_n),
.touch_press (key_press[2]),
.mode (mode)
);
// 呼吸灯模块例化
breath_led u_breath_led(
.clk (clk_50mhz),
.rst_n (rst_n),
.mode_en (~mode),
.key1_press (key_press[0]),
.key2_press (key_press[1]),
.led1_out (led1_breath),
.led2_out (led2_breath)
);
// 流水灯模块例化
flow_led u_flow_led(
.clk (clk_50mhz),
.rst_n (rst_n),
.mode_en (mode),
.key1_press (key_press[0]),
.key2_press (key_press[1]),
.led1_out (led1_flow),
.led2_out (led2_flow)
);
// 蜂鸣器模块例化
buzzer_ctrl u_buzzer_ctrl(
.clk (clk_50mhz),
.rst_n (rst_n),
.key1_press (key_press[0]),
.key2_press (key_press[1]),
.touch_press (key_press[2]),
.buzzer_en (buzzer_en)
);
// LED输出选择逻辑(唯一驱动源)
always @(posedge clk_50mhz or negedge rst_n) begin
if(!rst_n) begin
led1 <= 1'b0;
led2 <= 1'b0;
end else begin
case(mode)
1'b0: begin // 呼吸灯模式
led1 <= led1_breath;
led2 <= led2_breath;
end
1'b1: begin // 流水灯模式
led1 <= led1_flow;
led2 <= led2_flow;
end
default: {led1, led2} <= 2'b00;
endcase
end
end
// 蜂鸣器输出逻辑
always @(posedge clk_50mhz or negedge rst_n) begin
if(!rst_n) begin
buzzer <= 1'b0;
end else begin
buzzer <= buzzer_en;
end
end
endmodule
|
最佳答案
查看完整内容[请看2#楼]
根据你的描述(切换到流水灯模式后一直只亮一个灯),问题大概率出在 flow_led.v 模块的代码逻辑错误,约束文件无明显问题(呼吸灯模式正常,说明 LED 引脚约束、时钟约束有效)。以下是详细分析和修复方案:
一、核心问题定位(flow_led.v 代码缺陷)
流水灯的核心是「定时切换 LED 状态」,但你的代码中缺少「状态切换的计时驱动逻辑」,导致 led_state 无法翻转,始终保持初始状态(只亮一个灯)。具体错误如下:
1. 关键寄 ...
|