FPGA数字式竞赛抢答器设计:状态机与Verilog实现
基于FPGA设计一个数字式竞赛抢答器
该设计实现了一个数字式竞赛抢答器,具有以下功能:
- 倒计时显示: 倒计时时间单位为0.1秒,保留一位小数。显示第一位抢答者ID号(从1开始,0表示无抢答者)。 * 抢答开始后,如倒计时未结束前有违规抢答者,则记录第一违规抢答者ID号。 * 倒计时为0结束后有抢答者,则记录第一抢答者ID号,抢答有效。2. 抢答启动/重启: S2按键用于启动/重启抢答器,每次按下后即启动抢答前的5秒倒计时。3. 抢答操作: S0/S1/S3/S4键由4位参赛者进行抢答操作,对应ID号为S0/S1/S3/S4。4. 违规抢答处理: 在启动抢答前的5秒倒计时期间,如果检测到S0/S1/S3/S4键中有抢答动作,则立即停止倒计时,并显示第一违规抢答者ID号,同时通过指示灯闪烁3秒进行提醒。5. 有效抢答显示: 倒计时结束后,如果检测到S0/S1/S3/S4键中有抢答动作,则立即显示第一抢答者ID号,同时点亮指示灯表示抢答有效,直到启动下一次抢答时熄灭。6. 时间查阅: 支持查阅功能,通过开关K0~K3显示某位参赛者从倒计时结束开始抢答到按下抢答键的时间,时间精度1ms。
状态机设计

抢答器共有6个状态,分别是:
- Idle: 空闲状态,等待S2按键触发抢答器启动。2. Countdown: 倒计时状态,开始倒计时5秒。3. InvalidPress: 违规抢答状态,显示第一违规抢答者ID号,并闪烁指示灯3秒。4. ValidPress: 有效抢答状态,显示第一抢答者ID号,并点亮指示灯,直到下一次抢答器启动。5. DisplayTime: 查阅时间状态,通过开关K0~K3显示某位参赛者从倒计时结束开始抢答到按下抢答键的时间,时间精度1ms。6. Restart: 重启状态,等待S2按键触发抢答器重新启动。
Verilog代码verilogmodule quiz_buzzer( input clk, // 时钟信号 input rst, // 复位信号 input s2, // 抢答启动/重启键 input [3:0] s, // 抢答参赛者按键 input [3:0] k, // 时间查阅参赛者开关 output reg [3:0] id, // 显示抢答者ID号 output reg [3:0] time[9:0], // 显示时间 output reg led, // 指示灯 output reg [3:0] invalid_id, // 第一违规抢答者ID号 output reg [3:0] valid_id // 第一有效抢答者ID号);
// 状态定义localparam IDLE = 2'b00; // 空闲状态localparam COUNTDOWN = 2'b01; // 倒计时状态localparam INVALID_PRESS = 2'b10; // 违规抢答状态localparam VALID_PRESS = 2'b11; // 有效抢答状态localparam DISPLAY_TIME = 2'b100; // 查阅时间状态localparam RESTART = 2'b101; // 重启状态
// 状态寄存器reg [2:0] state_reg, state_next;
// 倒计时计数器reg [19:0] count_reg, count_next;
// 抢答者ID和时间计数器reg [3:0] id_reg, id_next;reg [39:0] time_reg[9:0], time_next[9:0];
// 第一违规抢答者ID号和第一有效抢答者ID号reg [3:0] invalid_id_reg, invalid_id_next;reg [3:0] valid_id_reg, valid_id_next;
// 指示灯计数器reg [19:0] led_count_reg, led_count_next;
// 初始状态为Idleinitialbegin state_reg = IDLE;end
// 状态机操作always @(posedge clk or negedge rst)begin if (~rst) begin state_reg <= IDLE; count_reg <= 0; id_reg <= 0; invalid_id_reg <= 0; valid_id_reg <= 0; led_count_reg <= 0; time_reg <= 0; end else begin state_reg <= state_next; count_reg <= count_next; id_reg <= id_next; invalid_id_reg <= invalid_id_next; valid_id_reg <= valid_id_next; led_count_reg <= led_count_next; time_reg <= time_next; endend
// 状态转移和操作always @*begin // 状态转移 case (state_reg) IDLE: if (s2) begin state_next = COUNTDOWN; count_next = 50000000; // 倒计时5秒 end else state_next = IDLE; COUNTDOWN: if (count_reg == 0) begin if (id_reg == 0) state_next = INVALID_PRESS; else begin state_next = VALID_PRESS; led_count_next = 5000000; // 指示灯闪烁5秒 end end else if (s != 4'b0000) state_next = INVALID_PRESS; else state_next = COUNTDOWN; INVALID_PRESS: if (led_count_reg == 0) begin invalid_id_next = id_reg; state_next = RESTART; end else if (s != 4'b0000) begin invalid_id_next = id_reg; state_next = RESTART; end else state_next = INVALID_PRESS; VALID_PRESS: if (led_count_reg == 0) begin valid_id_next = id_reg; led_next = 1; state_next = RESTART; end else if (s != 4'b0000) begin valid_id_next = id_reg; led_next = 1; state_next = RESTART; end else state_next = VALID_PRESS; DISPLAY_TIME: state_next = DISPLAY_TIME; RESTART: if (s2) state_next = COUNTDOWN; else state_next = RESTART; default: state_next = IDLE; endcase // 状态操作 case (state_reg) IDLE: id_next = 0; time_next = 0; COUNTDOWN: id_next = s; time_next = {40{count_reg > 0}}; count_next = count_reg - 1; INVALID_PRESS: id_next = 0; time_next = 0; VALID_PRESS: id_next = s; time_next = {40{count_reg == 0}}; led_count_next = led_count_reg - 1; DISPLAY_TIME: id_next = k; time_next = time_reg + 1; RESTART: id_next = 0; time_next = 0; led_next = 0; default: id_next = 0; time_next = 0; led_next = 0; endcase // 记录第一违规抢答者ID号和第一有效抢答者ID号 if (state_reg == INVALID_PRESS && invalid_id_next != 0) invalid_id <= invalid_id_next; if (state_reg == VALID_PRESS && valid_id_next != 0) valid_id <= valid_id_next;end
// 输出信号assign id = id_reg;assign time = time_reg;assign led = led_reg;assign invalid_id = invalid_id_reg;assign valid_id = valid_id_reg;endmodu
原文地址: https://www.cveoy.top/t/topic/nvsE 著作权归作者所有。请勿转载和采集!