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

全部代码
len_on.v(顶层)
顶层模块,包含游戏正确,失败的部分处理。
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) beginif (!rst_n) begin// 复位时初始化counter <= 0;// 计数器初始化led <= 4'b1111;// 全灭now_state <= 2'b00;// 随时开始clear_led_enable <= 1'b0;random_enable<= 1'b0;endelse beginif (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; // 随机器使能end2'b01 :begin//重置完成led <= clear_out_led; // 输出按灯器信号clear_led_enable <= 1'b1; // 按灯器正常使能random_enable<= 1'b0; // 随机器不使能end2'b10 :begin//正常进行led <= clear_out_led; // 输出按灯器信号clear_led_enable <= 1'b1; // 按灯器正常使能random_enable<= 1'b0; // 随机器不使能end2'b11 :begin//失败led <= 4'b1111; // 全灭clear_led_enable <= 1'b0; // 按灯器不使能random_enable<= 1'b0; // 随机器不使能now_state <= 2'b01; // 进入闪灯状态counter <= 0;// 重置计时器enddefault : beginnow_state <= now_state;// 保持当前状态end// 没有内容,结束endcaseendelse beginif(counter>LED_CYCLES) beginnow_state <= now_state+1; // 进入下一状态counter <= 0;// 重置计时器endelsecounter <= counter + 1 ;if(now_state==2'b10)led <= 4'b0000; // 全亮elseled <= 4'b1111; // 全灭endend
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
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,000reg [31:0] counter;
reg but_in_prev;always @(posedge clk or negedge rst_n) beginif (!rst_n) beginbut_on <= 1;but_in_prev <= 1;counter <= 0;endelse beginbut_in_prev <= but_in;if (but_in != but_in_prev) begin // 检测到变化counter <= DEBOUNCE_CYCLES; // 重新开始计时endelse if (counter > 0) begin // 正在计时counter <= counter - 1;if (counter == 1) begin // 计时结束but_on <= but_in; // 更新输出endendend
endendmodule
lfsr_4bit.v (四位伪随机数生成器)
如果全0或者全1会重新随机。
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); // 检测全1always @(posedge clk or negedge rst_n) beginif (!rst_n) beginrandom_out <= 4'b0001; // 初始种子,不能全为0end else if (enable) begin// 如果下一个状态是全0或全1,则重新加载种子if ({random_out[2:0], feedback} == 4'b0000 || {random_out[2:0], feedback} == 4'b1111) beginrandom_out <= 4'b1001; // 重新加载种子end else beginrandom_out <= {random_out[2:0], feedback};endend
endendmodule
clear_led.v(游戏主逻辑)
由于按钮是低电平按下,所以检测下降沿。
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) beginif (!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;endelse 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;endelse beginbuf_old_4 <= buf_on_4; // 记录上次按键case (next_state)STATE_READY: beginif (clear_led_enable) begin// 如果刚从成功状态过来且延时未完成,保持READY状态if (!success_delay_done && counter < SUCCESS_DELAY_CYCLES) begincounter <= counter + 32'd1;led_out <= 4'b1111; // 保持全灭endelse begin// 延时完成或不需要延时,进入正常流程led_temp <= led_in;led_out <= led_in;counter <= 32'd1;next_state <= STATE_NORMAL;fail_detected <= 0;success_delay_done <= 0; // 重置延时标志endendendSTATE_NORMAL: beginif (counter >= OVER_CYCLES) beginnext_state <= STATE_FAIL;endelse begincounter <= counter + 32'd1;// 预先计算下一个LED状态led_next = led_temp;fail_detected = 0;// 检查每个按键的下降沿if (buf_falling_edge[0]) beginif (led_temp[0] == 1'b1) begin // 如果已经是灭的状态fail_detected = 1;endelse beginled_next[0] = 1'b1; // 灭掉LEDendendif (buf_falling_edge[1]) beginif (led_temp[1] == 1'b1) begin // 如果已经是灭的状态fail_detected = 1;endelse beginled_next[1] = 1'b1; // 灭掉LEDendendif (buf_falling_edge[2]) beginif (led_temp[2] == 1'b1) begin // 如果已经是灭的状态fail_detected = 1;endelse beginled_next[2] = 1'b1; // 灭掉LEDendendif (buf_falling_edge[3]) beginif (led_temp[3] == 1'b1) begin // 如果已经是灭的状态fail_detected = 1;endelse beginled_next[3] = 1'b1; // 灭掉LEDendend// 更新状态if (fail_detected) beginnext_state <= STATE_FAIL;endelse if (led_next != led_temp) beginled_temp <= led_next;led_out <= led_next;if (led_next == 4'b1111) begin // 所有LED都灭了next_state <= STATE_SUCCESS;counter <= 0;endendendendSTATE_SUCCESS: beginled_out <= 4'b1111; // 保持全灭// 设置成功延时标志,并重置计数器用于延时success_delay_done <= 0;counter <= 32'd1; // 开始计数next_state <= STATE_READY; // 立即转到READY状态,在READY中完成延时endSTATE_FAIL: begincounter <= 0;success_delay_done <= 0; // 失败时重置延时标志// 保持失败状态,等待外部重置if (!clear_led_enable) beginnext_state <= STATE_READY;endenddefault: beginnext_state <= STATE_READY;endendcaseend
endendmodule
结果视频
Verilog 实现一个类似(打地鼠)游戏
