Verilog 利用伪随机,时序,按键消抖等,实现一个(打地鼠)游戏

简介

这里使用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 实现一个类似(打地鼠)游戏

相关推荐
奋斗的牛马4 小时前
OFDM理解
网络·数据库·单片机·嵌入式硬件·fpga开发·信息与通信
2501_940094025 小时前
PS1模拟器 DuckStation更新最新版整合 下载即玩 附PS1Bios/游戏/金手指 安卓版+电脑版
android·游戏·电脑
ThreeYear_s7 小时前
【FPGA+DSP系列】——PWM电平光耦转换电路实验分析----电路原理分析,器件选型
单片机·嵌入式硬件·fpga开发
wanhengidc10 小时前
云手机是真实手机吗
运维·服务器·游戏·智能手机·云计算
AnalogElectronic11 小时前
用AI写游戏4——Python实现飞机大战小游戏1
python·游戏·pygame
APP出海11 小时前
Google政策大更新:涉及金融(个人贷款),社交约会与游戏(未成年人相关),健康等所有类别App
android·游戏·金融·产品运营·产品经理
wanhengidc12 小时前
云手机的核心价值
运维·游戏·智能手机·云计算
GitCode官方12 小时前
创意无限·开源共赢|2025「卡赢杯」开源游戏开发大赛正式启动!
游戏·开源
wanhengidc13 小时前
云手机的网络架构
服务器·网络·游戏·智能手机·架构·云计算