西南交通大学 数字电子技术课程设计 反应计时器

前言

2023年秋数字电子技术课程设计,选择的是相对简单的一个题目。

一个反应计时器,详细资料欢迎前往github查看。

本资料仅用于学习交流,抄袭所产生一切后果请自负。

题目要求

(1)电路有三个输入按键:clear,start 和 stop,使用一个 LED 作为视觉刺激指示灯,
在七段数码管上显示相应的信息。
(2)当按下 clear 键时,电路回到初始状态,七段数码管给出一个初始显示,同时 LED
指示灯熄灭。
(3)当按下 start 键,七段数码管熄灭,产生一段 2s 到 6s(包括 2s 和 6s)之间的随机
时间之后,LED 指示灯点亮,计数器开始加计数。计数器每 1ms 加 1,它的值以 XXX 的
格式显示在数码管上。
(4)被测试者看到 LED 指示灯点亮后,立即按下 stop 键,此时计数器暂停计数,数
码管显示的就是被测试者的反应时间。大多数人的反应时间在 0.1-0.3s 之间。
(5)如果不按下 stop 键,计时器达到 999 之后停止计数。
(6)如果 LED 指示灯点亮前,按下 stop 键,被视为犯规,数码管上应给出犯规指示。
(7)连续进行多次测试后,可查阅所有测试结果中的最短时间、最长时间和平均时间。
(8)两个人比赛,显示两人的反应时间及获胜者。

整体框架

整体框架

模块划分:

顶层模块

  • FinalDesign.v

主控

  • MainLogic.v

计数器

  • Counter.v

动态扫描数码管

  • DynamicScanTubes.v

测试文件

  • FinalDesign_tb.v

源码

顶层文件

module FinalDesign (
    clk_50M,clear,start,stop,DIG,codeout,LED,LED_InRuning
);

//输入:
//clk_50M:50MHz时钟,clear:清零,start:开始,stop:停止
input clk_50M,clear,start,stop;

//输出:
//DIG:数码管选位
output wire[7:0] DIG;
//codeout:七段数码管输出
output wire[6:0] codeout;
//LED:LED指示灯
output wire LED;
//LED_InRuning:用于标识运行中
output wire LED_InRuning;

//内部信号:
//CounterOut_wire:连接计数器输出,要求计算到999
//CounterFlag_wire:用于控制计数器的开始、停止、清零
//00->清零,01->停止,10->开始
//ErrorFlag_wire:用于控制数码管显示犯规指示
wire [9:0]CounterOut_wire;
wire [1:0]CounterFlag_wire;
wire ErrorFlag_wire;

//主逻辑,生成随机数,在start信号到来时,开始计时,同时点亮LED,
//在stop信号到来时,停止计时,同时熄灭LED
//然后检查是否犯规,如果犯规,显示E,否则显示计时结果
MainLogic ML(
    .clk_50M(clk_50M),
    .clear(clear),
    .start(start),
    .stop(stop),
    .CounterFlag(CounterFlag_wire),//根据不同输入信号产生不同的CounterFlag
    .ErrorFlag(ErrorFlag_wire),//控制数码管显示犯规指示    
    .LED(LED),//控制LED指示灯
    .LED_InRuning(LED_InRuning)
);

//计数器,每1ms加1,计数到999时停止计数
//由MainLogic通过传递CounterFlag控制开始、停止、清零
Counter C(
    .clk_50M(clk_50M),
    .CounterFlag(CounterFlag_wire),
    .CounterOut(CounterOut_wire)
);

//动态扫描数码管,显示计数器的值,以及犯规指示(以E表示)
DynamicScanTubes DST(
    .clk_50M(clk_50M),
    .DataIn(CounterOut_wire),
    .ErrorFlag(ErrorFlag_wire),
    .DIG(DIG),
    .codeout(codeout)
);

endmodule

主控逻辑

//主逻辑,生成随机数,在start信号到来时,开始计时,同时点亮LED,
//在stop信号到来时,停止计时,同时熄灭LED
//然后检查是否犯规,如果犯规,显示E,否则显示计时结果
module MainLogic(
    clk_50M,clear,start,stop,CounterFlag,ErrorFlag,LED,LED_InRuning
);
//输入:
//clk_50M:50MHz时钟,clear:清零,start:开始,stop:停止
input clk_50M,clear,start,stop;
//输出:
//CounterFlag:用于控制计数器的开始、停止、清零
//00->清零,01->停止,10->开始
output reg [1:0]CounterFlag;
//ErrorFlag:用于控制数码管显示犯规指示
output reg ErrorFlag;
//LED:LED指示灯
output reg LED;
//LED_InRuning:用于标识运行中
output reg LED_InRuning;

//内部信号:
//RandomGenerator:随机数生成器,用于产生随机数
//实质是一个32位的计数器,每个时钟周期加1。因为时钟频率非常高
//所以可以认为每个时钟周期加1的时间间隔非常短,可以认为是随机的
reg [31:0]RandomGenerator;

//RandomNum:用于表示随机时间,2s到6s之间
//在50MHz时钟下,2s到6s之间的时间为100000000到300000000
reg [31:0]RandomNum;

//clear_temp:保存clear信号的上一次状态,用于检测clear信号的上升沿
//start_temp:保存start信号的上一次状态,用于检测start信号的上升沿
//stop_temp:保存stop信号的上一次状态,用于检测stop信号的上升沿
reg clear_temp,start_temp,stop_temp;

//counter,内置计数器,用于计算随机时间,在随机时间结束后,开始计时,同时点亮LED
reg [31:0]counter;

initial begin
    //初始化
    clear_temp <= 1'b0;
    start_temp <= 1'b0;
    stop_temp <= 1'b0;
    CounterFlag <= 2'b00;
    ErrorFlag <= 1'b0;
    LED <= 1'b0;
    counter <= 32'b0;
    RandomNum <= 32'b0;
    RandomGenerator <= 32'b0;
end

always @(posedge clk_50M) begin
    //更新随机数生成器,无所谓溢出问题
    RandomGenerator <= RandomGenerator + 1;
    
    //检测clear信号的上升沿
    if(clear_temp == 1'b0 && clear == 1'b1) begin
        CounterFlag <= 2'b00;   
        ErrorFlag <= 1'b0;
        LED <= 1'b0;
        LED_InRuning <= 1'b0;
        counter <= 32'b0;
        RandomNum <= 32'b0;
    end

    //检测start信号的上升沿
    if(start_temp == 1'b0 && start == 1'b1) begin
        LED_InRuning <= 1'b1;
        //随机数范围为0到2^32-1,将其限制在100000000到300000000之间
        RandomNum <= RandomGenerator % 200000000 + 100000000;
    end

    //检测stop信号的上升沿
    if(stop_temp == 1'b0 && stop == 1'b1) begin
        //停止计时,同时熄灭LED
        CounterFlag <= 2'b01;
        LED_InRuning <= 1'b0;
        LED <= 1'b0;
    end

    //检查是否到达随机时间
    if (RandomNum != 0 && counter == RandomNum && CounterFlag == 2'b00) begin
        //开始计时,同时点亮LED
        CounterFlag <= 2'b10;
        LED <= 1'b1;
    end
    //如果没有到达随机时间,更新内置计数器
    else if (RandomNum != 0 && counter != RandomNum && CounterFlag == 2'b00) begin
        counter <= counter + 1;//更新内置计数器
    end
    //如果此时处于暂停状态,检查是否犯规
    else if (RandomNum != 0 && CounterFlag == 2'b01) begin
        //检查是否犯规,如果犯规,将ErrorFlag置1,否则置0
        if (counter < RandomNum) begin  
            ErrorFlag <= 1'b1;
        end
        else begin
            ErrorFlag <= 1'b0;
        end
    end
    
    //更新clear_temp,start_temp,stop_temp
    clear_temp <= clear;
    start_temp <= start;
    stop_temp <= stop;
end

endmodule

计数器

//计数器,每1ms加1,计数到999时停止计数
//由MainLogic通过传递CounterFlag控制开始、停止、清零
module Counter(
    clk_50M,CounterFlag,CounterOut
);
//输入:
//clk_50M:50MHz时钟
//CounterFlag:用于控制计数器的开始、停止、清零
//00->清零,01->停止,10->开始
input clk_50M;
input [1:0]CounterFlag;
//输出:
//CounterOut:计数器输出,要求计算到999
output reg [9:0]CounterOut;

//内部信号:
//counter:内置计数器,在50MHz时钟下,每1ms计数50000次
reg [31:0]counter;

initial begin
    //初始化
    counter <= 32'b0;
    CounterOut <= 10'b0;
end

always @(posedge clk_50M) begin
    //根据CounterFlag控制计数器的开始、停止、清零
    case(CounterFlag)
        2'b00:begin
            //清零
            counter <= 32'b0;
            CounterOut <= 10'b0;
        end
        2'b01:begin
            //停止
            counter <= counter;
            CounterOut <= CounterOut;
        end
        2'b10:begin
            //计数
            if (counter != 32'd50000) begin
                counter <= counter + 1;
            end
            else begin
                counter <= 32'b0;
                if (CounterOut != 10'd999) begin
                    CounterOut <= CounterOut + 1;
                end
                else begin
                    CounterOut <= 10'd999;
                end
            end
        end
        default:begin
            //异常情况,停止计数
            counter <= counter;
            CounterOut <= CounterOut;
        end
    endcase
end
endmodule

动态扫描数码管

module DynamicScanTubes(clk_50M, DataIn, ErrorFlag, DIG, codeout);
    //输入信号
    input clk_50M;//50MHz时钟
    input ErrorFlag;//犯规指示
    input [9:0] DataIn;//计数器的值
    
    //输出信号
    //DIG:数码管选位
    output reg[7:0] DIG;
    //codeout:七段数码管输出
    output reg[6:0] codeout;

    //内部信号
    //SEL:数位选择信号,控制显示DataIn的某一位
	reg [1:0] SEL;
    //Y:数码管显示的值,由DataIn和SEL决定
	reg [9:0] Y;
    //new_clk:时钟分频后的时钟
	reg new_clk;
    //counter:时钟分频计数器
	reg [15:0] counter;

    initial begin
        counter <= 0;//计数器清零
        new_clk <= 0;//时钟初始化
		  SEL <= 2'b00;
		  Y <= 10'b0;
		  DIG <= 8'b0000_0000;
    end

    //时钟分频
    always @(posedge clk_50M) begin
        if (counter == 1500) begin
            counter <= 0;//计数器清零
            new_clk <= ~new_clk;//时钟跳变
        end else begin
            counter <= counter + 1;//计数器加一
        end
    end

    //模三计数器,用于控制数位选择信号SEL
    always @(posedge new_clk)
    begin
        if (SEL == 2'd2)
            SEL <= 2'd0;
        else
            SEL <= SEL + 1;
    end

    //数码管显示
    always @(SEL)
    begin
        case (SEL)
            2'd0: Y <= DataIn % 10;//输入数据的倒数第一位
            2'd1: Y <= (DataIn / 10) % 10;//输入数据的倒数第二位 
            2'd2: Y <= DataIn / 100;//输入数据的倒数第三位
            default: Y <= 4'b0000; 
        endcase
    end

    //数码管译码
    always @(*)
    begin
        if (ErrorFlag == 1)
            codeout = 7'b1111001;//E
        else
            case(Y)
            //gfedcba
            4'd0 : codeout = 7'b0111111;
            4'd1 : codeout = 7'b0000110;
            4'd2 : codeout = 7'b1011011;
            4'd3 : codeout = 7'b1001111;
            4'd4 : codeout = 7'b1100110;
            4'd5 : codeout = 7'b1101101;
            4'd6 : codeout = 7'b1111101;
            4'd7 : codeout = 7'b0000111;
            4'd8 : codeout = 7'b1111111;
            4'd9 : codeout = 7'b1101111;
            default: codeout = 7'b0000000;
            endcase
    end
    
    //数码管显示
    always @(SEL)
    begin 
        case (SEL)
            2'd0: DIG <= 8'b0000_0001;//第一个数码管亮起
            2'd1: DIG <= 8'b0000_0010;//第二个数码管亮起
            2'd2: DIG <= 8'b0000_0100;//第三个数码管亮起
            default: DIG <= 8'b0000_0000;
        endcase
    end
	 
endmodule

测试文件

`timescale 1ns/1ns
module FinalDesign_tb;

reg clk_50M;
reg clear;
reg start;
reg stop;
wire [7:0] DIG;
wire [6:0] codeout;
wire LED;

// Instantiate the design under test
FinalDesign DUT (
    .clk_50M(clk_50M),
    .clear(clear),
    .start(start),
    .stop(stop),
    .DIG(DIG),
    .codeout(codeout),
    .LED(LED)
);

// Clock generation, 20ns period, 50MHz frequency
always #10 clk_50M = ~clk_50M;
//1 second = 1000000000;

// Testbench stimulus
initial begin
    // Initialize inputs
    clk_50M = 0;
    stop = 0;
    start = 0;
    
    clear = 0;
    #1000;
    clear = 1;

    // Wait for a few clock cycles
    #10000;

    // Release clear signal
    clear = 0;

    // Wait for a few clock cycles
    #1000000000;
    //1s

    // Set start signal
    start = 1;
    #10000;
    start = 0;

    // Wait for 2~6 seconds
    #1000000000
    #1000000000
    #1000000000
    //3s

    // Wait for 0~1 second(Catch the signal)
    #500000000
    //0.5s

    //Set stop signal
    stop = 1;
    #10000;
    stop = 0;

    #100000000;//Waiting for 0 ~ 1 second(0~1000000000ns)
end

endmodule
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值