通过用户接收刺激开始计时到按下按钮停止计时,测试用户的反应时间。
题目要求:
(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)连续进行多次测试后,可按下按键 Min,Max 和 Ave 查阅所有测试结果中的最短时间、最长时间和平均时间。按键 restart 可清除记录的信息。
一、分析与设计
由题意可得,用户有输入:
start(开始计时) stop(停止计时) clear(清零) restart(重置所有数据)
Min(显示最短时间) Max(显示最长时间) Ave(显示平均时间)
另有50MHz时钟clk。为迎合ms计数,分频出一1kHz(1ms)的时钟w1。
1、基础功能状态机实现
正常一次操作为:start(开始)->等待2~6s->计时1~999ms中按下暂停->结束。有四个状态。
此处使用状态机来实现操作间的转换。
状态控制变量:reg [1:0]control;
状态1:control = 2'b00 初始化:可start(开始计时) clear(清零) restart(重置所有数据)。
状态2:control = 2'b01 等待:可stop(停止计时)——对应题目要求(6)犯规。
状态3:control = 2'b10 正式计时:可stop(停止计时)。
状态4:control = 2'b11 处理:可clear(清零) restart(重置所有数据)。
状态转换图如下:
其中:count为计数器,用于2~6s计数和1~999ms计数。Random为随机数。
随机数Random生成:令一计数器值k在clk信号下循环计数1~4001;当按下start按钮时,取Random = k % 4001 + 2000 可获得随机时间2~6s。
2、显示实现(实时值,最大值等)
显示有:LED(LED显示) (Data3\Data2\Data1)(数码管显示三位)。
数码管显示状态控制reg [1:0]Judge;
熄灭 Judge = 2'b01 -> 等待2~6s时,数码管熄灭。
全亮 Judge = 2'b10 -> 在等待时间按下stop,给出违规显示数码管全亮,显示数字8。
显示三位数字 Judge = 2'b00 或 2'b11 -> 正常显示三位数字。
三位值确定:
1、实时值Now:在1~999ms中,通过计数器count求得实时显示值。
Now3 = count/100; Now2 = (count/10)%10; Now1 =count%10;
2、最大最小值Max\Min:按下暂停stop后,比较当前值Now与保存的最大最小值,进行更新。
3、平均值Ave:按下暂停stop后,更新总时长AllTime与总次数N,利用 AllTime / N 得到平均值。
显示三位值:
按照输入的 Min 、Max 、Ave 将实时值 或 最大值 或 最小值 或 平均值 赋值给显示的Data。
利用数码管动态扫描将三位Data值显示在数码管上。
二、程序代码
顶层文件
module Test(clk,clear,start,stop,restart,Min,Max,Ave,LED,codeout,SEG,w1,Data3,Data2,Data1,Data,Judge);
input clk; //50MHz时钟
input clear,start,stop,restart; //清零、开始、结束、重置
input Min,Max,Ave; //显示最小,最大、平均值
output LED; //LED显示
output [6:0]codeout; //数码管7段
output [7:0]SEG; //数码管8位
output w1; //分频后1kHz(1ms)
output [3:0]Data3; //第三位
output [3:0]Data2; //第二位
output [3:0]Data1; //第一位
output [3:0]Data; //显示位
output [1:0]Judge; //数码管显示模式控制
//主模块:按输入,将最大值(3位)等存入Data3~Data1中,控制LED显示和数码管显示模式
Test_1 t1(clk,w1,clear,start,stop,restart,Min,Max,Ave,LED,Data3,Data2,Data1,Judge);
//将三位数字Data3~Data1依次赋值给Data,同时控制数码管显示位数(SEG),使得显示出三位数字。Judge控制显示模式
Test_2 t2(w1,Judge,Data3,Data2,Data1,Data,SEG);
//将Data译码到七段数码管上
Test_3 t3(Data,codeout);
//将50MHz分频为1kHz(1ms),用于Test_1,Test_2
Test_4 t4(clk,w1);
endmodule
主要功能模块
module Test_1(clk,w1,clear,start,stop,restart,Min,Max,Ave,LED,Data3,Data2,Data1,Judge);
input clk; //50MHz
input w1; //1kHz
input clear,start,stop,restart;
input Min,Max,Ave;
output reg LED = 0;
output reg [3:0]Data3 = 4'b0000;
output reg [3:0]Data2 = 4'b0000;
output reg [3:0]Data1 = 4'b0000;
output reg [1:0]Judge = 2'b00;
//实时值
reg [3:0]Now3 = 4'b0000;
reg [3:0]Now2 = 4'b0000;
reg [3:0]Now1 = 4'b0000;
//最小值
reg [3:0]Min3 = 4'b0000;
reg [3:0]Min2 = 4'b0000;
reg [3:0]Min1 = 4'b0000;
//最大值
reg [3:0]Max3 = 4'b0000;
reg [3:0]Max2 = 4'b0000;
reg [3:0]Max1 = 4'b0000;
//平均值
reg [3:0]Ave3 = 4'b0000;
reg [3:0]Ave2 = 4'b0000;
reg [3:0]Ave1 = 4'b0000;
//总时长和使用次数
integer AllTime = 0;
reg [4:0]N = 1;
//随机数部分,通过计数器取余数获取随机值
integer Random = 0; //随机数
integer k = 1; //计数1~4001
always@(posedge clk)
begin
if(k <= 4000) k <= k + 1;
else k <= 1;
end
//主部分
integer count = 0; //计数器值1~999ms
reg [1:0]control = 2'b00; //状态转换机
always@(posedge w1)
begin
case(control)
2'b00: //初始化;可重置、清零、开始计时;可进入等待01
begin
if(restart) //重置restart
begin
count <= 1; //计数重置
LED <= 0; //LED显示关闭
Judge <= 2'b00;//数码管正常显示三位
//当前值清零
Now3 <= 0;
Now2 <= 0;
Now1 <= 0;
//最小值清零
Min3 <= 0;
Min2 <= 0;
Min1 <= 0;
//最大值清零
Max3 <= 0;
Max2 <= 0;
Max1 <= 0;
//平均值清零
Ave3 <= 0;
Ave2 <= 0;
Ave1 <= 0;
//总时长清零
AllTime <= 0;
//使用次数清零
N <= 1;
end
if(clear) //清零clear
begin
count <= 1; //计数重置
LED <= 0; //LED指示灯熄灭
Judge <= 2'b00; //数码管正常显示三位
//当前值清零
Now3 <= 0;
Now2 <= 0;
Now1 <= 0;
end
if(start) //开始计时start
begin
count <= 1; //计数器重置为1
Judge <= 2'b01; //数码管全熄灭
Random <= k % 4001 + 2000; //随机时间2~6s
control <= 2'b01; //进入等待部分
end
end
2'b01: //等待;可进入计时10,或者初始化00
begin
count <= count + 1;
if(count >= Random) //到达时间
begin
LED <= 1; //LED灯提示
count <= 1; //计数器重置为1
Judge <= 2'b00; //数码管正常显示三位
control <= 2'b10; //正式计时
end
if(stop) //犯规
begin
count <= 1; //计数器重置
Judge <= 2'b10; //数码管全亮给出提示
control <= 2'b00; //返回初始化
end
end
2'b10: //正式计时;可停止;可进入处理11(清零或重置)
begin
count <= count + 1;
if(count > 999) //大于999ms错误
begin
count <= 1; //计数器重置
control <= 2'b11; //进入处理11
end
if(stop) //停止
begin
AllTime <= AllTime + count; //总时间加上当前时间
N <= N + 1; //次数加一
//计算平均值(总时长除以次数)
Ave3 <= ((AllTime+count-N)/N)/100;
Ave2 <= (((AllTime+count-N)/N)/10)%10;
Ave1 <= ((AllTime+count-N)/N)%10;
//计算最大值(如果当前值更大,则替换最大值)
if((Max3<Now3)||(Max3==Now3&&Max2<Now2)||(Max3==Now3&&Max2==Now2&&Max1<Now1))
begin
Max3 <= Now3;
Max2 <= Now2;
Max1 <= Now1;
end
//计算最小值(如果当前值更小,则替换最小值,最小值为0也需替换)
if((Min3==0&&Min2==0&&Min1==0)||(Min3>Now3)||(Min3==Now3&&Min2>Now2)||(Min3==Now3&&Min2==Now2&&Min1>Now1))
begin
Min3 <= Now3;
Min2 <= Now2;
Min1 <= Now1;
end
control <= 2'b11; //进入处理11
end
//通过计数次数,给当前值赋值
Now3 <= count/100;
Now2 <= (count/10)%10;
Now1 <= count%10;
end
2'b11://处理;可进行重置和清零;可进入初始化
begin
if(restart) //重置restart
begin
count <= 1; //计数重置
LED <= 0;
Judge <= 2'b00;
//当前值清零
Now3 <= 0;
Now2 <= 0;
Now1 <= 0;
//最小值清零
Min3 <= 0;
Min2 <= 0;
Min1 <= 0;
//最大值清零
Max3 <= 0;
Max2 <= 0;
Max1 <= 0;
//平均值清零
Ave3 <= 0;
Ave2 <= 0;
Ave1 <= 0;
//总时长清零
AllTime <= 0;
//使用次数清零
N <= 1;
control <= 2'b00; //进入初始化
end
if(clear) //清零clear
begin
LED <= 0; //LED指示灯熄灭
count <= 1; //计数重置
Judge <= 2'b00; //正常显示
Now3 <= 0; //当前值清零
Now2 <= 0;
Now1 <= 0;
control <= 2'b00; //进入初始化
end
end
default:
begin
end
endcase
end
//输出
always@(posedge w1)
begin
if(control == 2'b10 || control == 2'b00) //在计时状态和初始化状态输出实时值
begin
Data3 <= Now3;
Data2 <= Now2;
Data1 <= Now1;
end
if(Min) //输出最小值
begin
Data3 <= Min3;
Data2 <= Min2;
Data1 <= Min1;
end
if(Max) //输出最大值
begin
Data3 <= Max3;
Data2 <= Max2;
Data1 <= Max1;
end
if(Ave) //输出平均值
begin
Data3 <= Ave3;
Data2 <= Ave2;
Data1 <= Ave1;
end
end
endmodule
显示功能模块
module Test_2(w1,Judge,Data3,Data2,Data1,Data,SEG);
input w1;
input [1:0]Judge;
input [3:0]Data1;
input [3:0]Data2;
input [3:0]Data3;
output reg[3:0]Data = 4'b0000;
output reg [7:0]SEG = 8'b00000000; //位选信号
reg [1:0]k = 2'b00;
//通过Test_1模块控制Judge的值
always@(posedge w1)
begin
if(Judge == 2'b01) //等待部分全熄灭
begin
SEG <= 8'b00000000;
end
else if(Judge == 2'b10) //错误部分全亮,显示8
begin
SEG <= 8'b11111111;
Data <= 4'b1000;
end
else //正常循环显示三位数字
begin
if(k == 2'b00) //第一位
begin
SEG <= 8'b00000001;
Data <= Data1;
k <= k + 2'b01;
end
else if(k == 2'b01) //第二位
begin
SEG <= 8'b00000010;
Data <= Data2;
k <= k + 2'b01;
end
else //第三位
begin
SEG <= 8'b00000100;
Data <= Data3;
k <= 2'b00;
end
end
end
endmodule
module Test_3(Data,codeout);
input[3:0] Data;//4位显示信号
output reg[6:0] codeout = 7'b0000000;//7位输出信号
always @(Data)//用always块语句描述逻辑
begin
case(Data)//case多分支条件语句,按输入显示对应输出
4'd0 : codeout = 7'b1111110;
4'd1 : codeout = 7'b0110000;
4'd2 : codeout = 7'b1101101;
4'd3 : codeout = 7'b1111001;
4'd4 : codeout = 7'b0110011;
4'd5 : codeout = 7'b1011011;
4'd6 : codeout = 7'b1011111;
4'd7 : codeout = 7'b1110000;
4'd8 : codeout = 7'b1111111;
4'd9 : codeout = 7'b1111011;
default : codeout = 7'bx;//其他输入情况
endcase
end
endmodule//结束
分频器模块
module Test_4(clk,w1);
input clk;
output reg w1 = 0;
integer k = 1;
always@(posedge clk)
begin
if(k <= 25000) //计数25000次
k <= k + 1;
else
begin
k <= 1;
w1 <= ~w1; //翻转一次
end
end
endmodule
三、ModelSim仿真
仿真包括三部分:
1、错误显示部分——在等待时间按下stop,数码管全亮显示数字8;
2、超时999ms部分——数码管显示三位数字999。
3、正常测试部分——此处测试三次,然后显示最大值、最小值、平均值;重置restart后,显示最大值、最小值、平均值。
注:需要计算好按下start和stop的时间,才能正常完成一次测试哟。
1、Test Bench文件
`timescale 10 ns/ 10 ns
module Test_vlg_tst();
reg Ave;
reg Max;
reg Min;
reg clear;
reg clk;
reg restart;
reg start;
reg stop;
// wires
wire [3:0] Data;
wire [3:0] Data1;
wire [3:0] Data2;
wire [3:0] Data3;
wire [1:0] Judge;
wire LED;
wire [7:0] SEG;
wire [6:0] codeout;
wire w1;
Test i1 (
.Ave(Ave),
.Data(Data),
.Data1(Data1),
.Data2(Data2),
.Data3(Data3),
.Judge(Judge),
.LED(LED),
.Max(Max),
.Min(Min),
.SEG(SEG),
.clear(clear),
.clk(clk),
.codeout(codeout),
.restart(restart),
.start(start),
.stop(stop),
.w1(w1)
);
initial
begin
clk = 0;
start = 0;
stop = 0;
clear = 0;
restart = 0;
Min = 0;
Max = 0;
Ave = 0;
/*
//错误显示,在等待时间按下stop
//按下开始
#100000
start = 1;
#100000
start = 0;
//按下结束
#200000
stop = 1;
#100000
stop = 0;
//按下清零
#200000
clear = 1;
#100000
clear = 0;
*/
/*
//超时999ms
//按下开始
start = 1;
#10000000
start = 0;
//按下清零
#500000000
clear = 1;
#10000000
clear = 0;
*/
/*
//三次正常测试,求最大,最小,平均
//第一次
//按下开始
start = 1;
#10000000
start = 0;
//按下停止
#350000000
stop = 1;
#10000000
stop = 0;
//按下清零
#50000000
clear = 1;
#10000000
clear = 0;
//第二次
//按下开始
#100000000
start = 1;
#10000000
start = 0;
//按下停止
#380000000
stop = 1;
#10000000
stop = 0;
//按下清零
#50000000
clear = 1;
#10000000
clear = 0;
//第三次
//按下开始
#120000000
start = 1;
#10000000
start = 0;
//按下停止
#270000000
stop = 1;
#10000000
stop = 0;
//按下清零
#50000000
clear = 1;
#10000000
clear = 0;
//最小值
#100000000
Min = 1;
#50000000
Min = 0;
//最大值
#100000000
Max = 1;
#50000000
Max = 0;
//平均值
#100000000
Ave = 1;
#50000000
Ave = 0;
//重置
#100000000
restart = 1;
#10000000
restart = 0;
//最小值
#50000000
Min = 1;
#30000000
Min = 0;
//最大值
#50000000
Max = 1;
#30000000
Max = 0;
//平均值
#50000000
Ave = 1;
#30000000
Ave = 0;
*/
$display("Running testbench");
end
always#1 clk = ~clk;
endmodule
2、波形图
1、错误显示
2、超时
3、正常测试
注:具体操作可查看本专栏中其他文章。