简介
这里使用Verilog实现了一个"打地鼠"游戏,规则如下:
1.灯的亮灭由伪随机数生成,按下对应按钮,可以将灯按灭
2.当所有灯灭掉后,视为胜利,等待0.3s进入下一关
3.当3s内没有完成或者按灭本身就是灭掉的灯,视为失败,所有灯会灭-亮-灭闪烁一次,然后进入下一关。
整个项目以len_on 为顶层,含有lfsr_4bit (伪随机生成),button_on (按键消抖),clear_led(游戏灭灯主逻辑)为子模块。
由于各个板子不同,可能部分代码需要修改
这里面,按钮和LED灯,以及复位,都是低电平 有效,按钮低电平表示按下,LED灯低电平点亮。时钟频率为50MHz。

全部代码
len_on.v(顶层)
顶层模块,包含游戏正确,失败的部分处理。
bash
module len_on(
input wire clk ,
input wire [3:0] buf_4 ,
input wire rst_n ,
output reg [3:0] led
);
parameter CLK_FREQ = 50_000_000; // 50MHz
parameter LED_TIME_MS = 300;// 300ms-0.3s 爆闪
parameter LED_CYCLES = (CLK_FREQ / 1000) * LED_TIME_MS;// 爆闪所经历周期
reg [1:0] now_state;// 当前状态,00正常 ,01灭10亮11灭 (失败闪灯)
reg [31:0] counter; // 闪灯时间计数器
wire [3:0] buf_on_4; // 消抖后的信号
reg random_enable; // 随机亮灯的使能信号
wire [3:0] random_out;// 随机4位数字信号
reg clear_led_enable; // 按灯器的使能信号
wire [3:0] clear_out_led; // 按灯器输出的led信号
wire [1:0] clear_out_state; // 按灯器状态
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// 复位时初始化
counter <= 0;// 计数器初始化
led <= 4'b1111;// 全灭
now_state <= 2'b00;// 随时开始
clear_led_enable <= 1'b0;
random_enable<= 1'b0;
end
else begin
if (now_state == 2'b00) begin// 正常开始状态
case (clear_out_state)//00开始重置,01重置完成,10正常进行,11失败
2'b00 :begin//开始重置
led <= 4'b1111; // 全灭
clear_led_enable <= 1'b0; // 按灯器不使能
random_enable<= 1'b1; // 随机器使能
end
2'b01 :begin//重置完成
led <= clear_out_led; // 输出按灯器信号
clear_led_enable <= 1'b1; // 按灯器正常使能
random_enable<= 1'b0; // 随机器不使能
end
2'b10 :begin//正常进行
led <= clear_out_led; // 输出按灯器信号
clear_led_enable <= 1'b1; // 按灯器正常使能
random_enable<= 1'b0; // 随机器不使能
end
2'b11 :begin//失败
led <= 4'b1111; // 全灭
clear_led_enable <= 1'b0; // 按灯器不使能
random_enable<= 1'b0; // 随机器不使能
now_state <= 2'b01; // 进入闪灯状态
counter <= 0;// 重置计时器
end
default : begin
now_state <= now_state;
// 保持当前状态
end// 没有内容,结束
endcase
end
else begin
if(counter>LED_CYCLES) begin
now_state <= now_state+1; // 进入下一状态
counter <= 0;// 重置计时器
end
else
counter <= counter + 1 ;
if(now_state==2'b10)
led <= 4'b0000; // 全亮
else
led <= 4'b1111; // 全灭
end
end
end
// 生成随机数
lfsr_4bit get_4(
.clk (clk),
.rst_n (rst_n),
.enable (random_enable) , // 使能信号
.random_out (random_out) // 4位随机数输出
);
// 按键消抖
button_on button_on_0(
.clk (clk) ,
.rst_n (rst_n) ,
.but_in (buf_4[0]) ,
.but_on (buf_on_4[0])
);
button_on button_on_1(
.clk (clk) ,
.rst_n (rst_n) ,
.but_in (buf_4[1]) ,
.but_on (buf_on_4[1])
);
button_on button_on_2(
.clk (clk) ,
.rst_n (rst_n) ,
.but_in (buf_4[2]) ,
.but_on (buf_on_4[2])
);
button_on button_on_3(
.clk (clk) ,
.rst_n (rst_n) ,
.but_in (buf_4[3]) ,
.but_on (buf_on_4[3])
);
clear_led clear_led(
.clk (clk),
.buf_on_4 (buf_on_4), // 消抖后的信号
.rst_n (rst_n),
.clear_led_enable (clear_led_enable), // 该模块使能信号
.led_in (random_out),
.led_out (clear_out_led),
.next_state (clear_out_state)//下次状态,0不变1重置,2失败
);
endmodule
button_on.v(按键消抖)
按键消抖部分,注意按键低电平有效,,消抖时间5ms
bash
module button_on(// 按键消抖
input wire clk,
input wire rst_n,
input wire but_in,
output reg but_on
);
parameter CLK_FREQ = 50_000_000; // 50MHz
parameter DEBOUNCE_TIME_MS = 5; // 5ms消抖时间
parameter DEBOUNCE_CYCLES = (CLK_FREQ / 1000) * DEBOUNCE_TIME_MS; // 100,000
reg [31:0] counter;
reg but_in_prev;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
but_on <= 1;
but_in_prev <= 1;
counter <= 0;
end
else begin
but_in_prev <= but_in;
if (but_in != but_in_prev) begin // 检测到变化
counter <= DEBOUNCE_CYCLES; // 重新开始计时
end
else if (counter > 0) begin // 正在计时
counter <= counter - 1;
if (counter == 1) begin // 计时结束
but_on <= but_in; // 更新输出
end
end
end
end
endmodule
lfsr_4bit.v (四位伪随机数生成器)
如果全0或者全1会重新随机。
bash
module lfsr_4bit (
input wire clk,
input wire rst_n,
input wire enable, // 使能信号
output reg [3:0] random_out // 4位随机数输出
);
// LFSR反馈多项式:x^4 + x^3 + 1
wire feedback = random_out[3] ^ random_out[2];
wire is_all_zero = (random_out == 4'b0000); // 检测全0
wire is_all_one = (random_out == 4'b1111); // 检测全1
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
random_out <= 4'b0001; // 初始种子,不能全为0
end else if (enable) begin
// 如果下一个状态是全0或全1,则重新加载种子
if ({random_out[2:0], feedback} == 4'b0000 ||
{random_out[2:0], feedback} == 4'b1111) begin
random_out <= 4'b1001; // 重新加载种子
end else begin
random_out <= {random_out[2:0], feedback};
end
end
end
endmodule
clear_led.v(游戏主逻辑)
由于按钮是低电平按下,所以检测下降沿。
bash
module clear_led(
input wire clk ,
input wire [3:0] buf_on_4 ,
input wire rst_n ,
input wire clear_led_enable,// 使能信号
input wire [3:0] led_in ,// 随机四位信号
output reg [3:0] led_out ,
output reg [1:0] next_state
);
parameter CLK_FREQ = 50_000_000;
parameter OVER_TIME_MS = 3000;// 失败时间3s
parameter OVER_CYCLES = (CLK_FREQ / 1000) * OVER_TIME_MS;
parameter SUCCESS_DELAY_MS = 300; // 成功后延时300ms
parameter SUCCESS_DELAY_CYCLES = (CLK_FREQ / 1000) * SUCCESS_DELAY_MS;
reg [31:0] counter;
reg [3:0] buf_old_4;
wire [3:0] buf_falling_edge;
assign buf_falling_edge = (~buf_on_4) & buf_old_4;
parameter STATE_SUCCESS = 2'b00; // 上一次成功,开始重置
parameter STATE_READY = 2'b01; // 重置完成
parameter STATE_NORMAL = 2'b10; // 正常运行
parameter STATE_FAIL = 2'b11; // 失败
// 注意:STATE_SUCCESS_DELAY 可以用 STATE_SUCCESS 的计数器实现,不需要新状态
reg [3:0] led_temp;
reg [3:0] led_next;
reg fail_detected;
reg success_delay_done; // 成功延时完成标志
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin// 复位
counter <= 0;
buf_old_4 <= 4'b1111;
led_out <= 4'b1111; // 全灭
next_state <= STATE_READY;
led_temp <= 4'b1111;
fail_detected <= 0;
success_delay_done <= 0;
end
else if (!clear_led_enable) begin// 不使能
led_out <= 4'b1111; // 全灭
next_state <= STATE_READY; //重置完成状态
counter <= 0;
led_temp <= 4'b1111;
fail_detected <= 0;
success_delay_done <= 0;
end
else begin
buf_old_4 <= buf_on_4; // 记录上次按键
case (next_state)
STATE_READY: begin
if (clear_led_enable) begin
// 如果刚从成功状态过来且延时未完成,保持READY状态
if (!success_delay_done && counter < SUCCESS_DELAY_CYCLES) begin
counter <= counter + 32'd1;
led_out <= 4'b1111; // 保持全灭
end
else begin
// 延时完成或不需要延时,进入正常流程
led_temp <= led_in;
led_out <= led_in;
counter <= 32'd1;
next_state <= STATE_NORMAL;
fail_detected <= 0;
success_delay_done <= 0; // 重置延时标志
end
end
end
STATE_NORMAL: begin
if (counter >= OVER_CYCLES) begin
next_state <= STATE_FAIL;
end
else begin
counter <= counter + 32'd1;
// 预先计算下一个LED状态
led_next = led_temp;
fail_detected = 0;
// 检查每个按键的下降沿
if (buf_falling_edge[0]) begin
if (led_temp[0] == 1'b1) begin // 如果已经是灭的状态
fail_detected = 1;
end
else begin
led_next[0] = 1'b1; // 灭掉LED
end
end
if (buf_falling_edge[1]) begin
if (led_temp[1] == 1'b1) begin // 如果已经是灭的状态
fail_detected = 1;
end
else begin
led_next[1] = 1'b1; // 灭掉LED
end
end
if (buf_falling_edge[2]) begin
if (led_temp[2] == 1'b1) begin // 如果已经是灭的状态
fail_detected = 1;
end
else begin
led_next[2] = 1'b1; // 灭掉LED
end
end
if (buf_falling_edge[3]) begin
if (led_temp[3] == 1'b1) begin // 如果已经是灭的状态
fail_detected = 1;
end
else begin
led_next[3] = 1'b1; // 灭掉LED
end
end
// 更新状态
if (fail_detected) begin
next_state <= STATE_FAIL;
end
else if (led_next != led_temp) begin
led_temp <= led_next;
led_out <= led_next;
if (led_next == 4'b1111) begin // 所有LED都灭了
next_state <= STATE_SUCCESS;
counter <= 0;
end
end
end
end
STATE_SUCCESS: begin
led_out <= 4'b1111; // 保持全灭
// 设置成功延时标志,并重置计数器用于延时
success_delay_done <= 0;
counter <= 32'd1; // 开始计数
next_state <= STATE_READY; // 立即转到READY状态,在READY中完成延时
end
STATE_FAIL: begin
counter <= 0;
success_delay_done <= 0; // 失败时重置延时标志
// 保持失败状态,等待外部重置
if (!clear_led_enable) begin
next_state <= STATE_READY;
end
end
default: begin
next_state <= STATE_READY;
end
endcase
end
end
endmodule
结果视频
Verilog 实现一个类似(打地鼠)游戏