设 计 报 告
参赛题目:具有启动、停止、递增和清除功能的秒表
题目编号: 任务一 _ ____
参赛队员:曹琪峰
赛题题目
摘 要:使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。
秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。
任务宗旨:
- 结合数字电路书本知识,深刻理解数字逻辑的功能实现及设计流程
- 培养工程化设计理念、规范化的设计流程及解决未知问题的能力
- 探索使用行业新工具在项目研发中存在的问题和解决方法
关键词:计数器 按键 数码管
一、系统方案论证:
- 概述:
- "Tube" 模块旨在与数码管或7段数码管进行接口。它包括一个状态机,用于控制段数据,并管理指示按键按下和按键释放的标志。
- 功能:
- 代码片段中描述的模块执行以下任务:
- 计时,同时精确到秒和100毫秒。
- 控制两个7段数码管上显示的段数据。
- 根据按键输入管理启动和停止标志。
- 设计分析:
- "Tube" 模块维护两个计数器,用于在秒和100毫秒内计时。它使用状态机来控制两个7段数码管的段数据。它还基于按键输入管理启动和停止标志。
- "Tube" 模块需要一个 "Segment" 实例来显示段数据,以及一个 "Key" 实例来处理按键输入。"Key" 实例管理按键输入并根据按键释放检测设置 "r_flag"。
- 考虑因素:
- "Tube" 模块的设计基于同步逻辑和正沿触发的触发器。按键输入用于控制启动和停止标志,从而影响段数据的显示。
- 重要的是要确保设计中的时序参数适合目标硬件平台,并满足7段数码管的电气规格。
- 总体而言,该设计展示了对段数据和按键输入管理的有效控制,以操作数码管。
二、理论分析与电路设计
图1 小脚丫
设计思路:利用计数器驱动数码管,从而达到一个秒表功能
1. 利用reset按键来实现数码管清空功能
2. 当按键key1被按下时,启动计数器,从而改变数码管显示
3. 当按键key2被按下时,停止计数器,让数码管保持当时的数值不变
4. 当按键key3(flag)被按下时,数码管递增0.1s,达到递增效果
整体设计思路如下:
- 模块功能:
- 模块包含了一个用于显示时间的数码管显示器和一个按键模块,能够实现倒计时功能。
- 数码管显示器分为两部分,分别用于显示秒和毫秒。
- 按键模块用于启动和停止倒计时功能。
- 输入输出:
- 输入包括时钟信号clk、复位信号rst_n、按键信号key和一个标志位add。
- 输出包括用于显示秒的数码管信号seg_led_1和用于显示毫秒的数码管信号seg_led_2。
- 寄存器定义:
- cnt_1s用于计时秒数,共24位。
- cnt_100ms用于计时毫秒数,共21位。
- flag用于控制倒计时的启动和停止。
- seg_data_1和seg_data_2分别用于存储数码管显示的数据。
- 状态机设计:
- 设计了三个时序逻辑块,分别用于计时秒数、计时毫秒数和控制数码管显示数据。
- 每个逻辑块在正边沿时钟信号或者负边沿复位信号下更新寄存器的值。
- 控制逻辑:
- 当按键为10时,启动倒计时;当按键为01时,停止倒计时。
- 每个时序逻辑块的更新受到flag标志位和按键信号的控制。
- 根据计时条件和数码管显示值,更新数码管显示数据。
- 子模块实例化:
- 实例化了名为segment_inst的segment模块用于控制数码管的显示。
- 实例化了名为key_inst的key模块用于按键检测与处理。
主要难题可能包括以下几点:
- 时序逻辑设计复杂性:
- 解决方法:时序逻辑设计中涉及多个计时器和状态机,需要确保逻辑正确性和时序稳定性。建议使用状态图等工具来清晰描述各状态转移和逻辑控制条件,同时对时序逻辑进行仿真验证。
- 数码管显示数据更新条件复杂:
- 解决方法:数码管数据更新受多个条件影响,包括秒数、毫秒数、标志位等。建议优化逻辑控制条件,确保更新条件清晰明确,减少逻辑混乱和可能出现的bug。
- 按键检测与处理:
- 解决方法:按键模块关键影响倒计时的启动和停止,正确检测和处理按键信号至关重要。建议添加消抖电路,确保按键信号稳定,并且对按键模块的逻辑进行充分测试。
未来的计划或建议包括:
- 功能拓展:可以考虑增加暂停、复位等功能,提高倒计时的灵活性。
- 性能优化:对代码进行优化,减少逻辑冗余,提高设计的效率和可维护性。
- 仿真验证:进行全面的功能仿真和时序仿真,确保设计的正确性和稳定性。
- 模块化设计:考虑将复杂的逻辑功能模块化,降低耦合度,方便后续的维护和调试。
通过不断优化设计和完善功能,可以提高代码质量和性能,确保系统稳定可靠。
三、结果显示
项目具体功能演示详见项目视频
功能框图:
资源占用:
附录
这段代码是一个 Verilog 模块的描述,它包含了一些信号定义、寄存器声明和状态机逻辑。这个模块包括以下部分:
- 输入信号:
clk
:时钟信号,频率为12M。rst_n
:复位信号。key
:2位输入信号。add
:输入信号。
- 输出信号:
seg_led_1
:9位输出信号,用于控制第一个数码管的显示。seg_led_2
:9位输出信号,用于控制第二个数码管的显示。
- 寄存器声明:
cnt_1s
:24位寄存器,用于计时1秒。cnt_100ms
:21位寄存器,用于计时100毫秒。flag
:1位寄存器,标志位。seg_data_1
:4位寄存器,用于存储第一个数码管的数据。seg_data_2
:4位寄存器,用于存储第二个数码管的数据。
- 逻辑部分:
- 使用
always@(posedge clk or negedge rst_n)
块描述了时钟上升沿或复位信号时的逻辑。 - 实现了计时逻辑,包括计时1秒和计时100毫秒的功能。
- 控制数码管数据的逻辑,根据计时和标志位来更新数码管的显示数据。
- 通过
always@(posedge clk or negedge rst_n)
块实现了状态转换的逻辑,根据按键输入来改变状态。
- 使用
整个模块还包括了一个 segment
实例和一个 key
实例,用于数码管的显示和按键输入的处理。
总体来说,这段代码实现了一个简单的数码管显示模块,可以计时并根据按键输入改变显示状态。
module tube(
input clk, // 12M
input rst_n,
input [1:0] key,
input add,
output [8:0] seg_led_1,
output [8:0] seg_led_2
);
reg [23:0] cnt_1s;
reg [20:0] cnt_100ms;
reg flag;
reg [3:0] seg_data_1;
reg [3:0] seg_data_2;
wire r_flag;
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt_1s <= 0;
else if((cnt_1s == 24'd12000_000 - 1)&&(flag == 1))
cnt_1s <= 0;
else if(flag == 1)
cnt_1s <= cnt_1s + 1;
else
cnt_1s <= cnt_1s; // Count 1 second
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt_100ms <= 0;
else if((cnt_100ms == 21'd1200_000 - 1)&&(flag == 1))
cnt_100ms <= 0;
else if(flag == 1)
cnt_100ms <= cnt_100ms + 1;
else
cnt_100ms <= cnt_100ms; // Count 100 milliseconds
always@(posedge clk or negedge rst_n)
if(!rst_n)
seg_data_2 <= 0;
else if(((seg_data_2 == 4'd9)&&(cnt_100ms == 21'd1200_000 - 1)&&(flag == 1))||((seg_data_2 == 4'd9)&&(r_flag)))
seg_data_2 <= 0;
else if(((cnt_100ms == 21'd1200_000 - 1)&&(flag == 1))||(r_flag))
seg_data_2 <= seg_data_2 + 1;
else
seg_data_2 <= seg_data_2; // Control segment data 2
always@(posedge clk or negedge rst_n)
if(!rst_n)
seg_data_1 <= 0;
else if((seg_data_2 == 4'd9)&&(cnt_100ms == 21'd1200_000 - 1)&&(flag == 1)&&(seg_data_1 == 4'd9)&&(cnt_1s == 24'd12000_000 - 1))
seg_data_1 <= 0;
else if((r_flag)&&(seg_data_2 == 4'd9)&&(seg_data_1 == 4'd9))
seg_data_1 <= 0;
else if(((cnt_100ms == 21'd1200_000 - 1)&&(flag == 1)&&(cnt_1s == 24'd12000_000 - 1))||((r_flag)&&(seg_data_2 == 4'd9)))
seg_data_1 <= seg_data_1 + 1;
else
seg_data_1 <= seg_data_1; // Control segment data 1
always@(posedge clk or negedge rst_n)
if(!rst_n)
flag <= 2'd0;
else
case(key)
2'b10:begin flag <= 1; end // Start
2'b01:begin flag <= 0; end // Stop
default:flag <= flag;
endcase
segment segment_inst(
.seg_data_1(seg_data_1), // Input for decoding 0~9 for the segment display
.seg_data_2(seg_data_2), // Input for the second segment display
.seg_led_1 (seg_led_1), // 9 signals required to control a segment display on the small footprint
.seg_led_2 (seg_led_2)
);
key key_inst(
.key (add),
.clk (clk),
.rst_n (rst_n),
.p_flag (), // Key pressed
.r_flag (r_flag), // Key released
.key_out()
);
endmodule