FPGA简单抢答器的实现

本文介绍了使用FPGA实现一个简单的抢答器,包括按键消抖处理和6位数码管显示。通过计数器消除按键抖动,实现5秒倒计时与蜂鸣提示。数码管采用静态显示方式,利用视觉暂留原理实现多位数码管同时显示。文章还提出了若干设计问题供读者思考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发环境

AX301黑金开发版,编译软件quartus

主要功能

第一个文件
主要功能:
1.被动
正向计时,每5秒进行一次蜂鸣
2.主动
KEY2和KEY3同时按下,开始计时
KEY1 KEY2 KEY3分别按下对应LED亮起 且他人的灯不能再点亮,同时计时暂停
RESET重置LED 数码管

第二个文件
1.KEY2和KEY3同时按下,开始9秒倒计时,点亮LED0
2.KEY1 KEY2 KEY3开始作答,一人答题后其他人不可作答
3.如果LED0未亮,有人作答,蜂鸣器响
4.RESET键 一切重置

基本原理

1.按键消抖

问题描述

一个按键按下的理论状态是,立马发生电位变化,瞬间由最高点到底最低点,但实际上不可能实现,而是有略微抖动的变换.
在这里插入图片描述
在这里插入图片描述
实验中设计了一个计数器,当按键输入有变化时,
计时器清零,否则就累加,直到加到一个预定值(例如 10ms),就认为按键稳定,输出按键值,
这样就得到以后没有抖动的按键值。

代码展示

always @(posedge clk  or negedge rst_n)
    if (!rst_n) key_rst <= 1'b1;
    else key_rst <= sw3_n&sw2_n&sw1_n;
 
reg key_rst_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中
 
always @ ( posedge clk  or negedge rst_n )
    if (!rst_n) key_rst_r <= 1'b1;
    else key_rst_r <= key_rst;
   
//当寄存器key_rst由1变为0时,led_an的值变为高,维持一个时钟周期 
wire key_an = key_rst_r & (~key_rst);
reg[19:0]  cnt;	//计数寄存器
 
always @ (posedge clk  or negedge rst_n)
    if (!rst_n) cnt <= 20'd0;	//异步复位
	else if(key_an) cnt <=20'd0;
    else cnt <= cnt + 1'b1;
  
reg[2:0] low_sw;
 
always @(posedge clk  or negedge rst_n)
    if (!rst_n) low_sw <= 3'b111;
    else if (cnt == 20'hfffff) 	//满20ms,将按键值锁存到寄存器low_sw中	 cnt == 20'hfffff
      low_sw <= {sw3_n,sw2_n,sw1_n};

reg  [2:0] low_sw_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中
always @ ( posedge clk  or negedge rst_n )
    if (!rst_n) low_sw_r <= 3'b111;
    else low_sw_r <= low_sw;
//当寄存器low_sw由1变为0时,led_ctrl的值变为高,维持一个时钟周期 
wire[2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);
reg d1;
reg d2;
reg d3;
always @ (posedge clk or negedge rst_n)
    if (!rst_n) begin
        d1 <= 1'b0;
        d2 <= 1'b0;
        d3 <= 1'b0;
      end
    else begin		//某个按键值变化时,LED将做亮灭翻转
        if ( led_ctrl[0]&led_d2==1'b0&led_d3==1'b0 ) d1 <= ~d1;	
        if ( led_ctrl[1]&led_d1==1'b0&led_d3==1'b0 ) d2 <= ~d2;
        if ( led_ctrl[2]&led_d1==1'b0&led_d2==1'b0 ) d3 <= ~d3;
      end
assign led_d1 = d1 ? 1'b1 : 1'b0;		//LED翻转输出
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d3 = d3 ? 1'b1 : 1'b0;		

2.6位数码管显示

使用方法

  1. 电路用 PNP 管来反向驱动并且控制列扫描信号(SEL0_T~SEL5_T)来选择哪个数码管。而且所有的 6 个数码管的“段选信号”(LEDA … LEDH)都共用驱动引脚(LED_A~LEDH)。数码管的所有驱动信号都是“低电平有效”。
  2. 单个数码管可以采用静态显示方式,如图所示,数码管被分成 a、b、c、d、e、f、g 和小数点,每段可以单独控制亮灭,通过点亮不同的段显示不同的数字和字符。
  3. 对于多位数码管,利用视觉暂留原理,快速交替显示,让眼睛看上去是多个数码管同时显示的。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

代码展示

译码模块
always@(*)
begin
	case(bin_data)
		4'd0:seg_data <= 7'b100_0000;
		4'd1:seg_data <= 7'b111_1001;
		4'd2:seg_data <= 7'b010_0100;
		4'd3:seg_data <= 7'b011_0000;
		… … … …
		default:seg_data <= 7'b111_1111;
	endcase
end
endmodule
//主要为了将二进制信号转化为段控制信号

在这里插入图片描述

计数器
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
	begin 
		scan_timer <= 32'd0;
		scan_sel <= 4'd0;
		end
	else if(scan_timer >= SCAN_COUNT)
	begin 
		scan_timer <= 32'd0;
		if(scan_sel == 4'd5)
			scan_sel <= 4'd0;
		else
			scan_sel <= scan_sel + 4'd1;
			end
	else
		begin 
			scan_timer <= scan_timer + 32'd1; 
			end
end
always@(posedge clk or negedge rst_n)
begin if(rst_n == 1'b0)
	begin 
		seg_sel <= 6'b111111;
		seg_data <= 8'hff;
		end
	else
	begin 
		case(scan_sel)
			4'd0:
			begin seg_sel <= 6'b11_1110;
				seg_data <= seg_data_0; 
				end
			… …		
		endcase
	end
end

在这里插入图片描述

模十计数器
always@(posedge clk or negedge rst_n) 
begin
    if(rst_n==0)
    begin
        data <= 4'd0;
        t <= 1'd0;
    end
    else if(clr)
    begin
        data <= 4'd0;
        t <= 1'd0;      
    end
        else if(en&led_d1==led_d2==led_d3==0)
    begin
        if(data==4'd9)
        begin
            t<= 1'b1;    //Counter to 9 to generate carry
            data <= 4'd0;
            end//Counter to 9 reset
        else
        begin
            t <= 1'b0;
            data <= data + 4'd1;
            end
    end
    else
        t <= 1'b0;
end

在这里插入图片描述

5秒一蜂鸣
always @(posedge clk)
	if(data==4'd5)
		 buzzer <= 1'b0;
	else if(data==4'd0)
		 buzzer <= 1'b0;
	else
		 buzzer <= 1'b1;

问题总结

  1. 同一信号不能在两个always里改变,但是可以在两个always里分别用作条件
  2. output从模块调用到主程序时,模块中要设置成reg形式;
  3. 逻辑层面要设计好,比如倒计时;比如信号到底在哪里修改;比如灵活性,简洁性
  4. 空的引脚可以不用分配,默认低电平;运行前一定要把电压设置好,还有芯片型号

给读者的问题

1.设置消抖的锁存器的时间,应该设置多少秒合适?
2.数码管交替的频率设置为多少较为合适?
3:请问单个数码管有几个控制段?是否全部可以同时点亮
4:两个always里,可以同时用一个信号作为判断条件么?

1.20ms左右,可以稍微加长;
2.1ms,实际20ms以内就行,太慢会闪烁,太快会导致亮度不够
3.8个,包含小数点;不可以同时点亮,6个通用一个电位
4.可以,只是不能在两处改变

整体代码(觉得麻烦可以直接在我资源里下载)

第一个文件

module seg_test(
	input   			clk,	//主时钟信号,50MHz
	input   			rst_n,	//复位信号,低有效
	input   		 	sw1_n,sw2_n,sw3_n, 	//三个独立按键,低表示按下
	output 	 	 	led_d0=0,led_d1,led_d2,led_d3,	//发光二极管,分别由按键控制
	output 			buzzer,
   output[5:0]		seg_sel,
   output[7:0]		seg_data
                );

reg EnFlat=1'b0;//锁存器,一人答题后阻止他人答题
reg key_rst;
 
always @(posedge clk  or negedge rst_n)
    if (!rst_n) key_rst <= 1'b1;
    else key_rst <= sw3_n&sw2_n&sw1_n;
 
reg key_rst_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中
 
always @ ( posedge clk  or negedge rst_n )
    if (!rst_n) key_rst_r <= 1'b1;
    else key_rst_r <= key_rst;
   
//当寄存器key_rst由1变为0时,led_an的值变为高,维持一个时钟周期 
wire key_an = key_rst_r & (~key_rst);

//---------------------------------------------------------------------------
reg[19:0]  cnt;	//计数寄存器
 
always @ (posedge clk  or negedge rst_n)
    if (!rst_n) cnt <= 20'd0;	//异步复位
	else if(key_an) cnt <=20'd0;
    else cnt <= cnt + 1'b1;
  
reg
智力竞赛抢答计时器的设计 一、 课题说明在许多比赛活动中,为了准确、公正、直观地判断出第一抢答者,通常设置一台抢答器,通过数显、灯光及音响等多种手段指示出第一抢答者。同时,还可以设置计时、计分、犯规奖惩计录等多种功能。 二、 设计要求 1、设计一个4组参加的智力竞赛抢答计时器。每组设置一个抢答按钮供抢答者使用。 2、电路具有第一抢答信号的鉴别和锁存功能。在主持人将系统复位并发出抢答指令后,当有某一组参赛者首先按下抢答开关时,数码管显示相应组别并伴有声响。此时,电路应具备自锁功能,使别组的抢答开关不起作用。 3、电路具有回答问题时间控制功能。要求回答问题时间小于等于100s(显示为0~99),时间显示采用倒计时方式。当达到限定时间时,发出声响以示报警。 三、设计思路根据设计要求可知,系统的输入信号有:各组的抢答按钮d1、d2、d3、d4,主持人按钮host,系统时钟信号clk,数码管的片选信号;系统的输出信号有:首先按下按钮的组别信号sel, 声音信号sound,倒计时显示信号q[6..0]。为实现设计要求,电路由抢答鉴别模块、锁存器模块、转换模块、倒计时模块、片选信号产生模块、3选1模块、显示译码模块和一些门电路组成。总体框图如图16-1所示。 四、设计文件 1、顶层原理图智力竞赛抢答计时器的顶层原理图如图16-1所示图16-1 智力抢答器的原理图 2、底层源程序 (1)抢答鉴别模块FENG的VHDL源程序抢答鉴别模块FENG如图16-2所示,该模块在第一个选手按下按键后,输出高电平给锁存器,锁存当时的按键状态。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值