内容介绍
内容介绍
一、项目需求
1.核心功能
- 测试反应时间:系统能够测量两位队友(A和B)从LED亮起至按键按下的反应时间,并将其显示在数码管上。
- 多次测试与平均:每位队友需进行8次测试,系统应能存储并计算这8次测试的平均反应时间。
- 比较与结果展示:系统应比较两位队友的平均反应时间,判断胜负,并通过RGB灯和数码管展示结果。
2.硬件要求
- 数码管:用于实时显示反应时间。
- LED灯组:8个LED灯,用于指示测试轮次。
- RGB三色灯:2个,分别代表队友A和B,显示测试状态和结果。
- 开关:用于切换测试的队友。
- 轻触按键:包括启动、响应、平均和比较功能。
3.操作流程
- 按下启动键,数码管归零,随机时间后LED灯亮起,计时开始。
- 队友看到LED灯亮起后尽快按下响应键,计时停止,时间显示在数码管上。
- 重复上述步骤8次,每次不同的LED灯亮起。
- 按下平均键,计算并显示8次测试的平均时间。
- 切换开关至另一位队友,重复上述步骤。
- 两位队友均完成后,按下比较键,显示胜者及其平均反应时间,RGB灯指示结果。
二、需求分析
1.测试反应时间
- 系统需要实现一个毫秒级精度的计时器,能够准确记录从LED灯亮起至按键按下的时间差。
- 计时器需要在LED灯亮起时开始,并在按键按下时停止,同时记录下时间并在数码管上显示。
2.多次测试和数据存储
- 系统需要具备存储功能,以便能够保存每位队友的8次测试数据。
- 在每次测试完成后,系统需要将当前测试数据加入到存储中,并准备进行下一次测试。
3.平均反应时间计算
- 在8次测试全部完成后,系统需要计算并显示这8次测试的平均反应时间。
- 平均反应时间的计算应准确无误,并能够以易于阅读的方式在数码管上显示。
4.比较结果与展示
- 系统需要比较两位队友的平均反应时间,判断哪位队友的反应更快。
- 比较结果需要通过RGB三色灯和数码管进行直观的展示,包括胜者的标识和平均反应时间。
三、实现方式
1.硬件实现
- 数码管使用核心板板载的两位数码管,时间单位为10ms。
- LED灯组使用核心板板载的8位LED,通过对应端口的高低电平控制LED亮灭。
- RGB灯使用核心板板载2个RGB灯表示玩家状态。
- 板载SW1开关状态对应参加测试的两位队友,SW2作为重启开关(rst)。
- 按下板载按键1~按键4对应启动、响应、平均、比较功能。
2.软件实现
- 初始化设置:在系统上电后,进行必要的初始化设置,包括数码管清零、LED灯组关闭、RGB LED设置为初始状态。
- 测试流程控制:编写测试流程控制逻辑,实现按下启动键后的随机延时、LED灯亮起、计时器启动等功能。在按键按下后,停止计时器,并将时间显示在数码管上。
- 数据存储与处理:定义变量或数组来存储每位队友的8次测试数据;在每次测试完成后,将数据保存到相应的存储位置;在按下平均键后,计算并显示平均反应时间。
- 比较与结果展示:编写比较逻辑,比较两位队友的平均反应时间,确定胜者。控制RGB LED显示胜者的颜色(白色高亮),并通过数码管显示胜者的平均反应时间。
四、功能框图
五、代码说明
1.数码管显示(部分)
always @ (posedge clk) begin
// 查表分离个位和十位 00 ~ 99
case (cnt_s)
8'd0:begin dataH <= 0; dataL <= 0; end
8'd1:begin dataH <= 0; dataL <= 1; end
8'd2:begin dataH <= 0; dataL <= 2; end
8'd3:begin dataH <= 0; dataL <= 3; end
8'd4:begin dataH <= 0; dataL <= 4; end
8'd5:begin dataH <= 0; dataL <= 5; end
8'd6:begin dataH <= 0; dataL <= 6; end
8'd7:begin dataH <= 0; dataL <= 7; end
8'd8:begin dataH <= 0; dataL <= 8; end
8'd9:begin dataH <= 0; dataL <= 9; end
8'd10:begin dataH <= 1; dataL <= 0; end
8'd11:begin dataH <= 1; dataL <= 1; end
......
8'd97:begin dataH <= 9; dataL <= 7; end
8'd98:begin dataH <= 9; dataL <= 8; end
8'd99:begin dataH <= 9; dataL <= 9; end
default: begin dataH <= 10; dataL <= 10; end // 对于100及以上的数字不显示
endcase
seg_led_1 <= seg[dataH];
seg_led_2 <= seg[dataL];
end
- cnt_s:需要显示的数字,通常为两位数。
- 两位数显示,使用查表法对每个数码管显示的数字进行确定,方便快捷。
- dataH和dataL分别代表显示数字的高位和低位。
- seg_led1和seg_led2为八段数码管输出端口。
2.按键脉冲发生器(部分)
output [3:0] key_pulse; // 按键有效脉冲输出
reg [3:0] key_rst_pre; //定义一个寄存器型变量存储上一个触发时的按键值
reg [3:0] key_rst; //定义一个寄存器变量储存储当前时刻触发的按键值
reg [3:0] key_sec_pre; //延时后检测电平寄存器变量
reg [3:0] key_sec;
wire [3:0] key_edge; //检测到按键由高到低变化是产生一个高脉冲
initial begin
key_rst <= {4{1'b1}}; //初始化时给key_rst赋值全为1
key_rst_pre <= {4{1'b1}};
cnt <= 18'h0;
key_sec <= {4{1'b1}};
key_sec_pre <= {4{1'b1}};
end
assign key_pulse = key_sec_pre & (~key_sec);
//利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
always @(posedge clk)
begin
key_rst <= key; //第一个时钟上升沿触发之后key的值赋给key_rst,同时key_rst的值赋给key_rst_pre
key_rst_pre <= key_rst; //非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值
end
assign key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平
reg [17:0] cnt; //产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器
//产生20ms延时,当检测到key_edge有效是计数器清零开始计数
always @(posedge clk)
begin
if(key_edge)
cnt <= 18'h0;
else
cnt <= cnt + 1'h1;
end
//延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
always @(posedge clk)
begin
if (cnt==18'h3ffff)
key_sec <= key;
end
always @(posedge clk)
begin
key_sec_pre <= key_sec;
end
- 按键脉冲发生器借鉴WebIDE中示例项目,对按键信号进行消抖处理,并产生对于的脉冲信号。
3.随机数生成器(伪随机数)
output reg [7:0] random_number; // 8位随机数输出
reg [7:0] number_temp; // 临时存储的8位数值,用于计算随机数
// 10-99 循环递增
always @ (posedge clk or negedge rst) begin
if(!rst) begin
number_temp <= 8'd0;
random_number <= 8'd10;
end
else begin
if(number_temp >= 89)
number_temp <= 0;
else
number_temp <= number_temp + 8'd1;
random_number <= number_temp + 8'd10;
end
end
- 利用clk时钟信号,生成10~99的两位数(对应1~10秒),周期性循环递增,配合启动按键按下获取当前数字作为伪随机数。
4.RGB显示
reg [2:0] rgb_color_lose; // 输的一方的白色pwm
reg [31:0] rgb_cnt; // 基础计数
reg [31:0] rgb_lose_cnt; // CNT,PWM参考值
localparam ToggleFreq = 1000000; // 反转计数频率
localparam ToggleCntMaxPriod = 1000; // ARR pwm频率为 ToggleFreq / ToggleCntMaxPriod
localparam ToggleDuty = 100; // ToggleDuty/ToggleCntMaxPriod 为白色占空比
localparam ToggleMaxPriod = 12_000_000 / ToggleFreq; // 最大计数值
always @ (posedge clk or negedge rst) begin
if(!rst) begin
rgb_cnt <= 0;
end
else begin
rgb_cnt <= rgb_cnt + 1;
if(rgb_cnt == (ToggleMaxPriod - 1)) begin
rgb_lose_cnt <= rgb_lose_cnt + 1;
if(rgb_lose_cnt == (ToggleCntMaxPriod - 1)) begin
rgb_lose_cnt <= 0;
end
rgb_cnt <= 0;
end
// pwm
if(rgb_lose_cnt < (ToggleDuty - 1)) begin
rgb_color_lose <= 3'b000;
end
else begin
rgb_color_lose <= 3'b111;
end
end
- 以上为RGB中,PWM信号的生成,题目要求输的一方白色暗淡,对RGB灯三路输出同时设置pwm信号(以一定频率和目标计数值反转三路电平),设置占空比较低,则为白色暗淡。
initial begin
rgb_color[0] = 3'b000; // 白
rgb_color[1] = 3'b001; // 青
rgb_color[2] = 3'b010; // 紫
rgb_color[3] = 3'b011; // 蓝
rgb_color[4] = 3'b100; // 黄
rgb_color[5] = 3'b101; // 绿
rgb_color[6] = 3'b110; // 红
rgb_color[7] = 3'b111; // 无
rgb_color_lose = 3'b000;
end
// 比赛未结束, 角色判断
if(player1_state != 2'b11) begin
case(player1_state)
2'b00: begin rgb_1 <= rgb_color[5]; end
2'b01: begin rgb_1 <= rgb_color[3]; end
2'b10: begin rgb_1 <= rgb_color[6]; end
endcase
else begin
if(winner != 2'b10) begin
rgb_1 <= rgb_color[0];
end
else begin
rgb_1 <= rgb_color_lose;
end
end
- 以上以一个玩家的状态灯为例(另一个代码相同)
- 将非pwm(即高低电平控制)的8种颜色的RGB状态封装在数组当中。
- play1_state为2位的数据,四种状态代表不同的队友状态,其中2'b11代表已经完成比较。
- 未完成比较则按情况赋予RGB灯状态,若完成比较则按输赢给予RGB灯普通白光或者PWM暗淡白光。
- 加入了平局状况下,两个RGB灯皆为白色高亮。
5.反应测试逻辑部分
/* player1 相关变量 */
reg p1_start_flag; // 玩家1开始标志
reg [2:0] led_idx_p1;
reg [7:0] led_start_p1; // led启动
reg [7:0] led_time_delay_start_p1; // led随机延时启动
reg [7:0] led_time_delay_finished_p1; // led随机延时结束
reg [7:0] led_time_encode_start_p1; // led记录响应时间
reg [7:0] led_finished_p1; // led响应结束
reg [15:0] cnt_s_total_p1; // 记录总时间
reg [7:0] cnt_s_avg_p1; // 记录平均时间
reg cnt_avg_flag_p1; // 平均标志位
reg all_led_finished_flag_p1; // 所有led相应测试结束
/* player2 相关变量 */
reg p2_start_flag; // 玩家2开始标志
reg [2:0] led_idx_p2;
reg [7:0] led_start_p2; // led启动
reg [7:0] led_time_delay_start_p2; // led随机延时启动
reg [7:0] led_time_delay_finished_p2; // led随机延时结束
reg [7:0] led_time_encode_start_p2; // led记录响应时间
reg [7:0] led_finished_p2; // led响应结束
reg [15:0] cnt_s_total_p2; // 记录总时间
reg [7:0] cnt_s_avg_p2; // 记录平均时间
reg cnt_avg_flag_p2; // 平均标志位
reg all_led_finished_flag_p2; // 所有led相应测试结束
/* 共用变量 */
reg [7:0] led_delay_time; // 延时时间
reg [31:0] cnt_temp_led; // 延时事件记录
reg [7:0] cnt_s_led; // 延时时间记录
reg compare_flag; // 比较标志位
reg [1:0] winner; // 获胜者 00-比赛未结束 01-玩家1获胜 10-玩家2获胜 11-平局
reg [7:0] led;
- 以上反应测试过程中所用到的各种标志位和寄存器,player1和player2分开两套相同的数据储存,避免相互影响。
// 启动
always @ (posedge key_pulse[0] or negedge rst) begin
if(!rst) begin
led_start_p1 <= 8'd0;
led_start_p2 <= 8'd0;
p1_start_flag <= 0;
p2_start_flag <= 0;
end
else begin
// 玩家1
if(!player) begin
if(!p1_start_flag) begin
p1_start_flag <= 1;
end
if(led_start_p1[led_idx_p1] == 0) begin
led_start_p1[led_idx_p1] <= 1;
end
end
// 玩家2
else begin
if(!p2_start_flag) begin
p2_start_flag <= 1;
end
if(led_start_p2[led_idx_p2] == 0) begin
led_start_p2[led_idx_p2] <= 1;
end
end
end
end
// 响应
always @ (posedge key_pulse[1] or negedge rst) begin
if(!rst) begin
led_finished_p1 <= 8'd0;
led_finished_p2 <= 8'd0;
end
else begin
// 玩家1
if(!player) begin
if(led_start_p1[led_idx_p1] && led_time_delay_start_p1[led_idx_p1] && led_time_delay_finished_p1[led_idx_p1] && led_time_encode_start_p1[led_idx_p1] && !led_finished_p1[led_idx_p1]) begin
led_finished_p1[led_idx_p1] <= 1;
end
end
// 玩家2
else begin
if(led_start_p2[led_idx_p2] && led_time_delay_start_p2[led_idx_p2] && led_time_delay_finished_p2[led_idx_p2] && led_time_encode_start_p2[led_idx_p2] && !led_finished_p2[led_idx_p2]) begin
led_finished_p2[led_idx_p2] <= 1;
end
end
end
end
// 平均
always @ (posedge key_pulse[2] or negedge rst) begin
if(!rst) begin
cnt_avg_flag_p1 <= 0;
cnt_avg_flag_p2 <= 0;
cnt_s_avg_p1 <= 0;
cnt_s_avg_p2 <= 0;
end
else begin
// 玩家1
if(!player) begin
if(all_led_finished_flag_p1 && !cnt_avg_flag_p1) begin
cnt_avg_flag_p1 <= 1;
cnt_s_avg_p1 <= (cnt_s_total_p1 >> 3);
end
end
// 玩家2
else begin
if(all_led_finished_flag_p2 && !cnt_avg_flag_p2) begin
cnt_avg_flag_p2 <= 1;
cnt_s_avg_p2 <= (cnt_s_total_p2 >> 3);
end
end
end
end
// 比较
always @ (posedge key_pulse[3] or negedge rst) begin
if(!rst) begin
compare_flag <= 0;
end
else begin
if(cnt_avg_flag_p1 && cnt_avg_flag_p2) begin
compare_flag <= 1;
end
end
end
- 以上key_pulse[0]~[4]为四路按键脉冲信号,分别对应启动、响应、平均、比较。
- 当对应按键按下时,改变对应标志位。
- 比较按键按下,对应总时间右移3位(除以8)得到平均时间。
if(!player) begin
// 未启动
if(!led_start_p1[led_idx_p1] && led_idx_p1 == 3'b000) begin
led <= 8'hff;
end
// LED打开准备,准备延时
else if(led_start_p1[led_idx_p1] && !led_time_delay_start_p1[led_idx_p1]) begin
led_time_delay_start_p1[led_idx_p1] <= 1;
led_delay_time <= random_number; // 获取随机数
cnt_temp_led <= 0;
cnt_s_led <= 0;
cnt_s <= 0;
cnt_temp <= 0;
end
// 延时中
else if(led_start_p1[led_idx_p1] && led_time_delay_start_p1[led_idx_p1] && !led_time_delay_finished_p1[led_idx_p1]) begin
if(cnt_temp_led == (1_200_000 - 1)) begin
if(cnt_s_led == led_delay_time) begin
cnt_s_led <= 0;
led_time_delay_finished_p1[led_idx_p1] <= 1;
end
else begin
cnt_s_led <= cnt_s_led + 8'b1;
end
cnt_temp_led <= 0;
end
else begin
cnt_temp_led <= cnt_temp_led + 32'b1;
end
end
// 打开LED,开始计时
else if(led_start_p1[led_idx_p1] && led_time_delay_start_p1[led_idx_p1] && led_time_delay_finished_p1[led_idx_p1] && !led_time_encode_start_p1[led_idx_p1]) begin
led[led_idx_p1] <= 0;
led_time_encode_start_p1[led_idx_p1] <= 1;
end
// 记录时间直到按键响应
else if(led_start_p1[led_idx_p1] && led_time_delay_start_p1[led_idx_p1] && led_time_delay_finished_p1[led_idx_p1] && led_time_encode_start_p1[led_idx_p1] && !led_finished_p1[led_idx_p1]) begin
if(cnt_temp == (CNT_PERIOD - 1)) begin
if(cnt_s == CNT_MAX) begin
cnt_s <= CNT_MAX; // 最大值限制
end
else begin
cnt_s <= cnt_s + 8'b1;
end
cnt_temp <= 0;
end
else begin
cnt_temp <= cnt_temp + 32'b1;
end
end
// 检测响应,关闭LED
else if(led_start_p1[led_idx_p1] && led_time_delay_start_p1[led_idx_p1] && led_time_delay_finished_p1[led_idx_p1] && led_time_encode_start_p1[led_idx_p1] && led_finished_p1[led_idx_p1]) begin
led[led_idx_p1] <= 1;
led_idx_p1 <= led_idx_p1 + 3'b1;
//cnt_s_total_p1 <= cnt_s_total_p1 + cnt_s;
if(led_finished_p1 == 8'hff) begin
all_led_finished_flag_p1 <= 1;
end
if(!all_led_finished_flag_p1) begin
cnt_s_total_p1 <= cnt_s_total_p1 + cnt_s;
end
end
end
- 以上以player1的反应测试过程逻辑代码为例
- player是通过sw1切换的队友状态0或1
- 反应测试是一个循序渐进的过程,首先当启动键按下,逻辑开始运行:开始->获取随机时间->延时->打开led并开始计时->等待响应->响应完成并且计时停止->总用时增加->结束。
- 以上过程经过8次,测试完成,测试完成标志位置1。
//玩家1
//测试未完成
if(!all_led_finished_flag_p1) begin
player1_state <= 2'b00;
end
//测试完成,平均未完成
else if (all_led_finished_flag_p1 && !cnt_avg_flag_p1) begin
player1_state <= 2'b01;
end
//测试完成,平均完成,比较未完成
else if (all_led_finished_flag_p1 && cnt_avg_flag_p1 && !compare_flag) begin
player1_state <= 2'b10;
if(!player) begin
cnt_s <= cnt_s_avg_p1;
led <= 8'h00;
end
end
//测试完成,平均完成,比较完成
else if (all_led_finished_flag_p1 && cnt_avg_flag_p1 && compare_flag) begin
player1_state <= 2'b11;
end
- 以上根据测试过程中以及按键的响应的标志位来改变队友状态。
// 比较
if(compare_flag) begin
if(cnt_s_avg_p1 < cnt_s_avg_p2) begin
winner = 2'b01;
cnt_s <= cnt_s_avg_p1;
end
else if(cnt_s_avg_p1 > cnt_s_avg_p2) begin
winner = 2'b10;
cnt_s <= cnt_s_avg_p2;
end
else if(cnt_s_avg_p1 == cnt_s_avg_p2) begin
winner = 2'b11;
cnt_s <= cnt_s_avg_p1;
end
led <= 8'h00;
end
end
- 以上对两个平均时间进行对比,得到比赛结果。
六、仿真波形图
1.按键脉冲发生器
- Key[0]和Key[1]两个脉冲代表按键按下和释放的过程,对应的key_pulse延时一段时间后(即消抖)产生一次脉冲。
2.数码管显示
- 显示数字cnt_s(0x31),十进制数字为51,得到数字高位和低位5和1。
3.随机数生成器(伪随机,循环递增)
- 一个周期,random最大值为0x63,即99(对应10s)。
4.PWM生成器(低电平有效)
七、FPGA资源利用说明
附件下载
Code.zip
工程文件
implement.jed
烧录文件
团队介绍
人工智能 大三在读
团队成员
kalworth
评论
0 / 100
查看更多
猜你喜欢
2024年寒假练 - 使用基于Lattice MXO2的小脚丫FPGA核心板设计反应时间测试系统该项目使用了基于Lattice MXO2的小脚丫FPGA核心板,实现了反应时间测试系统的设计,它的主要功能为:通过测量被试者在板上LED亮起到按下按键之间的时间作为其单次反应时间。两名受试者轮流测试8次,平均反应时间短者为胜,胜负结果在板上RGB LED显示。。
Violeta
514
2024年寒假练 - 用Lattice MXO2的小脚丫FPGA核心板设计反应时间测试系统该项目使用了Lattice MXO2的小脚丫FPGA核心板,实现了反应时间测试系统的设计,它的主要功能为:分别测试并记录两个人8次的反应时间,将获胜方的反应时间平均值显示在数码管上。
SCP基金会YHW
575
2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板 实现秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板,实现了秒表的设计,它的主要功能为:启动、停止、复位、递增功能。
Astesia
62