一、项目需求:
- 显示系统:
- 使用两个7段数码管来实时显示每次测试的响应时间(10毫秒为单位)。
- 使用两个7段数码管显示两个队友的平均响应时间和最终比较结果。
- LED x 8:指示当前的测试轮次。
- RGB三色灯 x 2:分别表示队友A和队友B的测试状态以及最终测试结果。
- 输入设备:
- 开关SW1: 选择正在测试的队友。
- 开关SW4:进行全局复位(reset)
- 轻触按键:
- K1:启动测试。
- K2:响应按键,停止计时。
- K3:计算并显示平均响应时间。
- K4:比较两个队友的平均响应时间并显示结果。
- 测试流程:
- 按下K1启动测试,数码管显示全0,随机延迟1-10秒后,相应轮次的LED亮起,同时启动毫秒计时器。
- 当LED亮起时,队友需尽快按下K2来停止计时器,停止的时间即为该次测试的响应时间,显示在数码管上。
- 重复8次测试,每次测试对应一个不同的LED,记录8次响应时间。
- 按下K3计算8次测试的平均响应时间,并在数码管上显示,同时点亮所有LED。
- 切换队友(通过SW1),重复上述测试。
- 完成两队测试后,按下K4比较两队的平均响应时间,数码管显示胜出队友的平均时间,RGB灯显示比赛结果(白色高亮表示胜利,白色暗淡表示失败)。
二、需求分析:
1.硬件平台需求
- FPGA开发板:选择一个具有足够输入/输出端口、内置时钟和足够逻辑资源的FPGA开发板,以便实现所需的功能。开发板需要有足够的GPIO引脚连接LEDs、按钮、数码管显示屏和开关。
- 输入设备:
- 开关SW1:用于选择测试的队友。
- 按钮 x 4(K1、K2、K3、K4):分别用于启动测试、响应停止计时、计算平均响应时间和比较结果。
- 显示设备:
- 7段数码管 x 2:实时显示每次测试的响应时间和平均响应时间。
- LED x 8:用于指示当前测试轮次。
- RGB LED x 2:用于显示两位队友的状态。
2.软件与逻辑设计需求
- 测试控制逻辑:需要设计FPGA内部的逻辑电路来实现启动、响应、平均计算和比较的功能。
- 随机延迟生成器:设计一个能够在1到10秒之间生成随机延迟的逻辑电路,用于每轮测试的开始。
- 计时器:实现一个毫秒级的计时器逻辑,用于计算从LED亮起到响应按钮被按下的时间。
- 平均值计算:设计逻辑以存储8次测试的值,并计算这些值的平均数。
- 状态指示逻辑:基于测试进度和结果,控制RGB LED的颜色显示状态。
- 用户界面逻辑:实现一个简单的用户界面逻辑,用于通过数码管和LEDs与用户交互。
3.功能性需求
- 在FPGA上实现一个精确的10毫秒级计时器,确保测试的时间测量准确无误。
- 能够存储和处理每位队友的8次响应时间,计算平均响应时间。
- 通过数码管显示实时响应时间和平均响应时间。
- 用色彩编码的方式通过RGB LED清晰指示当前状态和测试结果。
- 设计一个用户友好的交互方式,使得操作直观易懂。
4.性能需求
- 响应时间:系统应能够快速响应用户输入,包括按钮按下和开关切换,以确保测量准确性。
- 稳定性:系统需要在连续使用过程中保持稳定,确保长时间运行不出错。
三、实现方式
将整个项目需求任务细分为软件模块其划分如下:
1.数码管驱动模块
2.RGB驱动模块
3.二进制转BCD模块
4.时钟分频模块
5.按键消抖模块
6.PWM波生成模块
7.随机数生成模块
8.头文件主程序模块
1.显示模块
通过二进制转bcd模块将计时器的时间转为bcd码输入数码管驱动模块,显示对应反应时间以及平均成绩,利用RGB与LED驱动模块分别在对应状态输入对应驱动信号十七按照项目需求进行反应。
2.反应测试状态机
通过按键输入的启动以及停止信号,来进行单次测试中不同状态切换包括(初始等待状态,暗灯状态,亮灯状态,单次测试结束状态),并在对应状态获取随机等待时间(1-10s)传给计时器,以及控制对应测试次数的led灯的亮灭信号。
3.计时器模块
在随机时间计时结束后控制反应时间计数开始,并传入数码管显示模块,在响应按键按下后停止计数并存入对应序数的成绩寄存器。
四、功能框图
五、主要功能代码及说明
1.数码管驱动代码,输入7位二进制数,使用二进制转BCD模块将其转为8位bcd码,高四位为十进制个位,低四位位十进制十位,进行数码管驱动。
module Nixie_tube(num_data,seg_led_0,seg_led_1);
input wire[6:0] num_data;
output reg[8:0] seg_led_0;
output reg[8:0] seg_led_1;
wire [7:0] num;
wire [3:0] seg_num_0;
wire [3:0] seg_num_1;
assign seg_num_0=num[3:0];
assign seg_num_1=num[7:4];
always @(*)begin
case(seg_num_0)
4'd0:seg_led_0=9'h3f;
4'd1:seg_led_0=9'h06;
4'd2:seg_led_0=9'h5b;
4'd3:seg_led_0=9'h4f;
4'd4:seg_led_0=9'h66;
4'd5:seg_led_0=9'h6d;
4'd6:seg_led_0=9'h7d;
4'd7:seg_led_0=9'h07;
4'd8:seg_led_0=9'h7f;
4'd9:seg_led_0=9'h6f;
default:seg_led_0<=seg_led_0;
endcase
case(seg_num_1)
4'd0:seg_led_1=9'h3f;
4'd1:seg_led_1=9'h06;
4'd2:seg_led_1=9'h5b;
4'd3:seg_led_1=9'h4f;
4'd4:seg_led_1=9'h66;
4'd5:seg_led_1=9'h6d;
4'd6:seg_led_1=9'h7d;
4'd7:seg_led_1=9'h07;
4'd8:seg_led_1=9'h7f;
4'd9:seg_led_1=9'h6f;
default:seg_led_1=seg_led_1;
endcase
end
bcd_converter bcd_converter_inst1(
.bitcode(num_data),
.bcdcode(num)
);
endmodule
2.二进制转BCD模块,利用加三移位法实现,用于数码管驱动模块。
/*-------------------------------------*/
// Module name : bcd_converter
// Author : STEP
// Description : 8位二进制数转BCD码
// Web : www.stepfpga.com
/*-------------------------------------*/
module bcd_converter(bitcode, bcdcode);
input wire[6:0] bitcode;
output wire[7:0] bcdcode;
reg [14:0] data;
assign bcdcode = data[14:7];
always @(*) begin
data = {8'd0,bitcode};
repeat(7) begin //二进制码总共7位,所以循环位数是7
if(data[10:7]>=5)begin
data[10:7] = data[10:7] + 4'd3;
end
if(data[14:11]>=5)begin
data[14:11] = data[14:11] + 4'd3;
end
data = data << 1;
end
end
endmodule
3.按键消抖模块,利用延时检测的方式,在按键按下时检测到下降沿时开始计数,20ms后再次检测按键是否为低电平,并产生一个脉冲信号作为按键成功按下标志。
module key(key,clk,rst,key_flag);
parameter N=1;
input [N-1:0] key;
input clk;
input rst;
output [N-1:0] key_flag;
//按键数量参数
reg [17:0] cnt;//延时计时器
parameter delay_CNT=240000;//20ms延时
//parameter delay_CNT=240;
reg [N-1:0] key_rst;//当前按键电平
reg [N-1:0] key_rst_pre;//上一时刻按键电平
wire [N-1:0] key_edge;//按键下降沿捕捉
always @(posedge clk or negedge rst)begin
if(!rst)begin
key_rst<={N{1'b1}};
end
else begin
key_rst<=key;
end
end
always @(posedge clk or negedge rst)begin
if(!rst)begin
key_rst_pre<={N{1'b1}};
end
else begin
key_rst_pre<=key_rst;
end
end
assign key_edge=key_rst_pre & (~key_rst);
always @(posedge clk or negedge rst)begin
if(!rst)begin
cnt<=0;
end
else if(key_edge)begin
cnt<=0;
end
else begin
cnt<=cnt+1;
end
end
reg [N-1:0]key_sec;//20ms后按键当前电平
reg [N-1:0]key_sec_pre;//20ms后按键上一时刻电平
always @(posedge clk or negedge rst)begin
if(!rst)begin
key_sec<={N{1'b1}};
end
else if(cnt==delay_CNT-1)begin
key_sec<=key;
end
end
always @(posedge clk or negedge rst)begin
if(!rst)begin
key_sec_pre<={N{1'b1}};
end
else begin
key_sec_pre<=key_sec;
end
end
assign key_flag=key_sec_pre & (~key_sec);
endmodule
4.三路PWM波生成模块,此模块用于比较过程中展示暗淡效果白色灯光RGB,固定频率2kHz,占空比为16.7%。
module pwm(
input clk,
input rst,
output [2:0] pwm_out
);
parameter [31:0] period=6000; //pwm频率设置: = period = 本地时钟频率 / 期望pwm频率 (2kHZ)
parameter [31:0] pulse_width=1000; //pwm占空比设置 %(period-pulse_width/period)=83.3%
reg [31:0] cnt;
reg [2:0] wave;
always @(posedge clk or negedge rst) begin
if(!rst)
cnt <= 0;
else if(cnt==period-1)
cnt <= 0;
else
cnt <= cnt + 1;
end
always @(posedge clk or negedge rst) begin
if(!rst)
wave <= 3'b000;
else if(cnt<pulse_width)
wave <= 3'b000;
else
wave <= 3'b111;
end
assign pwm_out = wave;
endmodule
5.随机数生成模块,理论上为伪随机数,通过一个循环1-100的计数器,在启动按键按下时刻读出计数器计数值,并根据其所在区间,得到一个1-10计数上限值。
module random_num (
output reg[26:0] rand_num, // 输出的随机数
input clk, // 时钟信号
input rst // 复位信号
);
reg [6:0] cnt;
always @(posedge clk or negedge rst)begin
if(!rst)begin
cnt<=0;
end
else if(cnt==7'd99)begin
cnt<=0;
end
else begin
cnt<=cnt+1;
end
end
always @(posedge clk or negedge rst)begin
if(!rst)begin
rand_num<=0;
end
else if(cnt>=0 && cnt<7'd10) rand_num<=27'd12000000;//延时1s
else if(cnt>=7'd10 && cnt<7'd20) rand_num<=27'd24000000;//延时2s
else if(cnt>=7'd20 && cnt<7'd30) rand_num<=27'd36000000;//延时3s
else if(cnt>=7'd30 && cnt<7'd40) rand_num<=27'd48000000;//延时4s
else if(cnt>=7'd40 && cnt<7'd50) rand_num<=27'd60000000;//延时5s
else if(cnt>=7'd50 && cnt<7'd60) rand_num<=27'd72000000;//延时6s
else if(cnt>=7'd60 && cnt<7'd70) rand_num<=27'd84000000;//延时7s
else if(cnt>=7'd70 && cnt<7'd80) rand_num<=27'd96000000;//延时8s
else if(cnt>=7'd80 && cnt<7'd90) rand_num<=27'd108000000;//延时9s
else if(cnt>=7'd90 && cnt<7'd100) rand_num<=27'd120000000;//延时10s
end
endmodule
7.顶层文件中状态机以及计时器模块部分代码,通过启动按键、响应按键、测试人员转换开关sw1,来进行状态切换及控制。最开始为0状态(等待状态),等待启动按键按下,按下启动按键后从随机数生成模块读取一个随机延时时间并跳转到1状态(暗灯状态),并开始计时,当计数值达到延时时间时转为2状态(亮灯状态),此状态使对应led灯亮起并打开响应时间计时器,当响应按键按下后转为3状态(测试结束状态),停止响应时间计数器,并将其存入对应序号寄存器,等待下一次启动。
/*************************反应测试状态机***********************/
reg [1:0] state; // 状态机寄存器,0-等待,1-暗灯灯,2-亮灯,3-单次测试结束
reg [26:0] cnt; // 计时器寄存器
wire [7:0] led_sel; // LED 灯选择信号
reg [26:0] led_delay_time;//led延迟时间寄存器
reg [2:0] index; // LED 灯索引寄存器
reg [2:0] test_num; // 测试次数寄存器
reg [7:0] led_order [7:0];//led测试灯
initial begin
led_order[7]=8'h7f;
led_order[6]=8'hbf;
led_order[5]=8'hdf;
led_order[4]=8'hef;
led_order[3]=8'hf7;
led_order[2]=8'hfb;
led_order[1]=8'hfd;
led_order[0]=8'hfe;
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= 2'b00; // 初始状态为等待
cnt <= 0; // 初始计数为0
index <= 3'b000; // 初始索引为0
test_num <= 3'b000; // 初始测试次数为0
led_delay_time<=0;//led延迟时间
end
else begin
case (state)
2'b00: begin // 等待状态
if (key_flag[0]) begin // 如果收到启动信号的上升沿并且测试次数小于8
state <= 2'b01; // 跳转到暗灯状态
led_delay_time<=rand_num;
end
else begin
state <= 2'b00; // 保持等待状态
end
end
2'b01: begin // 暗灯状态
if (cnt == led_delay_time - 1) begin // 计数到达指定时间
cnt <= 0; // 计数清零
state <= 2'b10; // 跳转到亮灯状态
end
else begin
cnt <= cnt + 1; // 计数加一
end
end
2'b10:begin // 亮灯并测试状态
if (key_flag[1]) begin // 计数到达指定时间
state <= 2'b11; // 跳转到单次测试结束状态
if(test_num<3'b111)begin
test_num<=test_num+1;
end
if(index<3'b111)begin
index <= index + 1'b1; // 索引加一
end
end
else begin
state<=2'b10;
end
end
2'b11: begin // 测试结束状态
if (key_flag[0]) begin // 计数到达指定时间
led_delay_time<=rand_num;
state <= 2'b01; // 跳转到暗灯状态
end
if (Tester_switch_pulse) begin// 如果测试人员转换复位到状态0,测试测序索引复位为0
index <= 3'b000;
state<=2'b00;
test_num<=3'b000;
end
end
default:begin
state<=state;
end
endcase
end
end
assign led_sel = led_order[index]; // 根据索引从数组中取出 LED 灯选择信号
/************计时器模块,用于记录用户的反应时间***********/
parameter CNT_10MS=120000;//10ms对应时钟周期
reg [6:0] time_array[7:0]; // 反应时间数组
reg [6:0] reaction_time;//反应时间寄存器
reg [18:0] reaction_cnt;//反应时间计数器
reg reaction_cnt_en;//反应时间计数器
wire led_start_eage;//led亮起信号
reg [1:0] led_start_signal;//led状态寄存器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
led_start_signal<=2'b00;
end
else begin
led_start_signal[0]<=state[1];
led_start_signal[1]<=led_start_signal[0];
end
end
assign led_start_eage=led_start_signal[0] & ~led_start_signal[1];
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
reaction_time <= 8'h0; // 初始反应时间为0
reaction_cnt <= 0; // 初始计数为0
reaction_cnt_en <= 1'b0; // 初始使能为0
time_array[0] <=7'h0;
time_array[1] <=7'h0;
time_array[2] <=7'h0;
time_array[3] <=7'h0;
time_array[4] <=7'h0;
time_array[5] <=7'h0;
time_array[6] <=7'h0;
time_array[7] <=7'h0;// 初始反应时间数组为全0
end
else begin
if (led_start_eage) begin // 如果收到启动信号的上升沿
reaction_cnt_en <= 1'b1; // 使能计时器
end
if (reaction_cnt_en) begin // 如果计时器使能
if (state == 2'b10) begin // 如果是亮灯状态
if (reaction_cnt == CNT_10MS - 1) begin // 如果计数到达10ms秒
reaction_cnt <= 0; // 计数清零
if (reaction_time == 7'd99) begin // 如果反应时间到达990ms
reaction_time <= 7'd99; // 保持反应时间不变
end
else begin
reaction_time <= reaction_time + 1'b1; // 反应时间加一
end
end
else begin
reaction_cnt <= reaction_cnt + 1'b1; // 计数加一
end
end
end
if (key_flag != 4'h0) begin // 如果有按键按下
if (key_flag[1]) begin // 如果按键正确
reaction_time <= reaction_time; // 保持反应时间不变
reaction_cnt_en <= 1'b0; // 禁止计时器
time_array[test_num] <= reaction_time; // 将反应时间存入数组中
end
else if(key_flag[0] && state==2'b11)begin
reaction_cnt <= 0; // 计数清零
reaction_time <= 7'd0; // 反应时间清零
end
else begin // 如果按键错误
reaction_time <= reaction_time; // 反应时间设为990ms秒,表示错误
end
end
if(Tester_switch_pulse)begin
reaction_cnt <= 0; // 计数清零
reaction_time <= 7'd0; // 反应时间清零
end
end
end
8.显示模块,根据不同过程以及状态标志位,控制数码管,led,RGB的不同显示模式。
/*********显示*******/
reg [2:0]sw_RGB_0;//队员A指示灯//红110,绿101,蓝011,白000。
reg [2:0]sw_RGB_1;//队员B指示灯
wire [2:0] pwm_sw_RGB;//pwm调光
reg [6:0] display_seg;//数码管显示数据寄存器
always @(posedge clk or negedge rst_n)begin//RGB
if(!rst_n)begin
sw_RGB_0<=3'b111;
sw_RGB_1<=3'b111;
led<=8'b11111111;
display_seg<=7'd0;
end
else begin
if(test_flag_A)begin//A测试过程中
sw_RGB_0<=3'b101;//A指示灯亮绿灯
led <= (state == 2'b10) ? led_sel : 8'hff;//相应led根据测试次数索引点亮
display_seg<=reaction_time;//数码管显示响应时间
end
if(all_test_finish_flag_A)begin//A八次测试完成
sw_RGB_0<=3'b011;//A指示灯亮蓝灯
led<=8'b11111111;//led全灭
display_seg<=reaction_time;//数码管显示响应时间
end
if(avg_flag_A)begin//A平均进程
sw_RGB_0<=3'b110;//A指示灯亮红灯
led<=8'b00000000;//led全亮
display_seg<=Tester_A_score;//数码管显示平均时间
end
if(test_flag_B)begin//B进程同A
sw_RGB_1<=3'b101;
led <= (state == 2'b10) ? led_sel : 8'hff;
display_seg<=reaction_time;
end
if(all_test_finish_flag_B)begin
sw_RGB_1<=3'b011;
led<=8'b11111111;
display_seg<=reaction_time;
end
if(avg_flag_B)begin
sw_RGB_1<=3'b110;
led<=8'b00000000;
display_seg<=Tester_B_score;
end
if(cmp_flag)begin//比较进程中
led<=8'b11111111;//led全灭
if(Tester_A_score>Tester_B_score)begin//B获胜
sw_RGB_1<=3'b000;//B指示灯白色高亮
sw_RGB_0<=pwm_sw_RGB;//A指示灯白色暗淡
display_seg<=Tester_B_score;//数码管显示B平均时间
end
else if(Tester_A_score<Tester_B_score)begin//A获胜
sw_RGB_1<=pwm_sw_RGB;//B指示灯白色暗淡
sw_RGB_0<=3'b000;//A指示灯白色高亮
display_seg<=Tester_A_score;//数码管显示A平均时间
end
else begin//平局
sw_RGB_0<=pwm_sw_RGB;//A暗淡白
sw_RGB_1<=pwm_sw_RGB;//B暗淡白
display_seg<=Tester_A_score;//显示平均时间
end
end
end
end
以上为主要功能代码展示以及说明,完整代码见附件。
六、模块仿真:
1.数码管驱动模块仿真波形图
2.二进制码转BCD码模块仿真波形图
3.按键消抖仿真波形图
4.PWM生成模块仿真波形图
5.随机数生成模块仿真波形图
6.分频器模块仿真波形图
七、FPGA资源利用说明(源自stepfpga在线编译器的FPGA映射报告)
1.原报告:
2.整理表格:
Resource Type | Used | Total | Utilization (%) |
---|---|---|---|
Registers | 292 | 4635 | 6% |
PFU Registers | 292 | 4320 | 7% |
PIO Registers | 0 | 315 | 0% |
SLICEs | 307 | 2160 | 14% |
SLICEs as Logic/ROM | 307 | 2160 | 14% |
SLICEs as RAM | 0 | 1620 | 0% |
SLICEs as Carry | 119 | 2160 | 6% |
LUT4s | 606 | 4320 | 14% |
- Used as logic LUTs | 368 | ||
- Used as distributed RAM | 0 | ||
- Used as ripple logic | 238 | ||
- Used as shift registers | 0 | ||
PIO sites used | 43 | 105 | 41% |
Block RAMs | 0 | 10 | 0% |
GSRs | 1 | 1 | 100% |
Power Controller | 0 | 1 | 0% |
Dynamic Bank Controller (BCINRD) | 0 | 6 | 0% |
Dynamic Bank Controller (BCLVDSO) | 0 | 1 | 0% |
DCCA | 0 | 8 | 0% |
DCMA | 0 | 2 | 0% |
PLLs | 0 | 2 | 0% |
DQSDLLs | 0 | 2 | 0% |
CLKDIVC | 0 | 4 | 0% |
ECLKSYNCA | 0 | 4 | 0% |
ECLKBRIDGECS | 0 | 2 | 0% |