1 项目需求
秒表应从0.0秒计数到9.9秒,然后翻转,计数值每0.1秒精确更新一次。
秒表使用四个按钮输入:开始、停止、增量和清除(重置)。开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次);停止输入使计数器停止递增,但使数码管显示当前计数器值;每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间;复位/清除输入强制计数器值为零。
2 需求分析
2.1 输入输出
输入:四个按钮信号(开始、停止、增量、清除)。
输出:数码管显示当前的计数器值。
2.2 时序要求
计数器值每0.1s更新一次。
使用FPGA内置的时钟资源来驱动计数器。
2.3资源消耗
FPGA的资源占用尽可能的小。
3 实现方式
•将整个功能分为三部分实现:计数器、数码管显示、按键逻辑
•使用三个计数器,第一个用于计数0.1s,第二个第三个分别用于计数小数部分和个位
•将左边的数码管与第三个计数器相连,右边的数码管与第二个计数器相连
4 实现效果
4.1初始状态
4.2按下开始按钮
4.3按下暂停按钮
4.4按下增量按钮
4.5按下复位按钮
5 功能框图
注:每个框图右下角名称为执行该功能的主要文件
6 代码实现
6.1 计数器部分(count.v)
第一个计数器的计数上限是0.1s,当计数器计数到0.1s时,翻转,并传出翻转信号,作为第二个计数器的开始条件使用。计数器模板如下,因为该时钟初始状态为0.0,需要按下开始按钮才进行计数,则可以将第一个计数器的开始条件连接到使能信号en上,当en=1时才能开始计数。第二个和第三个计数器只需要修改其开始条件、翻转条件就能实现小数位和个位的计数。
//The first counter, with a counting limit of 0.1s
always @(posedge clk or negedge rst)
begin
if(!rst)//Reset after pressing rst
begin
cnt0<=0;
end
else if(add_cnt0)
begin
if(end_cnt0)
cnt0<=0;
else
cnt0<=cnt0+1;
end
end
assign add_cnt0=en;//Condition for incrementing cnt0
assign end_cnt0=add_cnt0&&cnt0==1_200_000-1;//Condition for flipping cnt0
计数器count模块
因为在数码管显示模块中需要使用个位、小数的计数,所以会输出cnt1和cnt2。同时,因为增量按钮按下后使当前个位计数+1,可以在cnt2的开始条件加上add信号,当增量按下时add=1,此时cnt2也可以+1
module count(
input clk,
input rst,
output wire [3:0] cnt1_o,
output wire [3:0] cnt2_o,
input en,
input add
);
6.2 数码管显示部分(seg.v)
当计数器计数到某个数字时,显示相应的数字。
always @(posedge clk or negedge rst)//Displaying on the digital display
begin
if(!rst)
begin
seg_ment_a<=7'h7e;
end
else if(cnt1==0)
seg_ment_a<=7'h7e;
else if(cnt1==1)
seg_ment_a<=7'h30;
else if(cnt1==2)
seg_ment_a<=7'h6d;
else if(cnt1==3)
seg_ment_a<=7'h79;
else if(cnt1==4)
seg_ment_a<=7'h33;
else if(cnt1==5)
seg_ment_a<=7'h5b;
else if(cnt1==6)
seg_ment_a<=7'h5f;
else if(cnt1==7)
seg_ment_a<=7'h70;
else if(cnt1==8)
seg_ment_a<=7'h7f;
else if(cnt1==9)
seg_ment_a<=7'h7b;
end
数码管显示seg模块
将显示数码管所用到的信号都输出,同时因为en,add信号是从clock模块中产生的,而seg模块中例化了count模块,所以还需要输入en,add信号以使count模块工作
module seg(
clk,
rst,
seg_ment_a,
seg_ment_b,
seg_sel,
seg_dp,
en,
add
);
6.3 按键逻辑部分(clock.v)
key1为开始,key2为暂停,当它们被按下时start值保持不变,start与en连接,则可以操控计数器的开始和暂停,并且只有在rst(复位)按下时才能够停止。
key3为递增,通过检测key3按下之前的状态与按下时的状态进行对比,只有当上升沿来临时,也就是从0变1时递增,同时检测start是否为0(暂停状态),此时给add赋值来控制计数器递增。
always @(posedge clk,negedge key1,negedge rst,negedge key2)//Logic for key1 and key2
begin
if(!rst)
start<=0;
else if(!key1)
start<=1;
else if(!key2)
start<=0;
end
always @(posedge clk) //Logic for key3
begin
if(start==0)
begin
if (key3 == 1'b1 && key_last == 1'b0) //Detect rising edges
add <= 1'b1;
else
add <= 1'b0;
key_last <= key3; //Update the value of Key_last
end
else
add<=1'b0;
end
顶层设计clock模块
1个时钟输入,4个按键输入,4个显示输出
module clock(
clk,
rst,
key1,
key2,
key3,
seg_ment_a,
seg_ment_b,
seg_sel,
seg_dp
);
6.4 管脚分配
7 FPGA资源利用
8 设计过程中所遇到的困难
因为我是第一次接触FPGA,在通过GPT生成代码无法实现功能时,我也不知道如何解决。所以做该项目的大部分时间都是在学习verilog,同时是第一次使用软件,所以大部分便利的功能都不知道如何使用。因为我还没学习到仿真,所以每次调试都是直接综合然后FPGA映射到板子上观察功能。在处理按键逻辑时,大部分时候代码没有报错但是没有实现功能,浪费了很多时间。后来发现能直接生成RTL视图查看按键逻辑是否连上了模块,能排除部分代码逻辑错误,节省了不少时间。
9 未来的建议和规划
该项目已经成功实现了多功能秒表的功能,并达到了预期指标。然而还有部分可以提升和完善的地方:
因为时间原因,设计中并没有设计按键消抖模块来防止误读信号。
未来考虑继续学习FPGA,特别是仿真,如果能够掌握仿真的话能少走大部分不必要的弯路。