2024寒假在家一起练-具有启动、停止、递增和清除功能的秒表
该项目使用了verilog语言,WEBIDE,modelsim软件,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。。
标签
FPGA
数字逻辑
参加活动
饲养乌鸦
更新2024-04-02
北京理工大学
188

1. 项目需求

通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从0.0秒计数到9.9秒,然后翻转,计数值每0.1秒精确更新一次。秒表使用四个按钮输入:开始、停止、增量和清除(重置)。

2. 需求分析

功能需求:

启动功能:当按下启动按钮时,秒表开始计时,且以10Hz的时钟速率递增。

   计时值从0.0秒开始递增,每0.1秒增加一次,到9.9后反转为0.0。

停止功能:当按下停止按钮时,秒表暂停计时,数码管维持在当前计数值。

   计时器停止递增,但不会清除当前的计数值。

增量功能:每次按下增量按钮,秒表的计数值增加1,无论按住秒表多长时间。

清除功能:当按下清除按钮时,秒表的计数值被强制清零,即复位为0.0秒。

3. 实现的方式

在stepfpga的WebIDE上编程、逻辑综合、分配管脚、FPGA映射,下载可执行的jed文件复制到fpga的文件夹中运行。

4. 功能框图


5. 代码及说明

  本项目共包含三个程序:stopwatch,debounce和divide。其中stopwatch为顶层模块,debounce负责按键去抖动操 作,divide负责把晶振的12MHz分出需要的10Hz。

由于debounce和divide主要来自stepfpga的例程,在此主要介绍stopwatch模块,即主程序。

模块定义与输入输出声明:

定义了一个名为counter的模块,包括时钟(clk)、开始(start)、保持(hold)、增量(increase)、复位(rst)输入信号,以及LED(led)和两个数码管(seg_led_1, seg_led_2)作为输出。

module counter
(
clk , //时钟
start , //开始
hold , //保持
increase , //增量
rst , //复位
led , //led
seg_led_1 , //数码管1
seg_led_2 //数码管2
);

input clk;
input start,hold,increase,rst;
output [8:0] seg_led_1, seg_led_2;
output reg [7:0] led;

wire clk1h; //10Hz时钟

wire start_pulse;
wire hold_pulse; //按键消抖后信号
wire increase_pulse;

reg increase_flag_set;
reg [1:0] increase_counter; // 新增加一个2位计数器来追踪高电平持续的周期数
reg start_flag;
reg hold_flag; //按键标志位
reg increase_flag;

reg back_to_zero_flag ; //计时完成信号

reg [6:0] seg [9:0];
reg [3:0] cnt_ge; //个位
reg [3:0] cnt_shi; //十位


初始化数码管显示编码:

在模块的初始块中定义了0到9数字在数码管上的显示编码,为后续的数码管显示提供基础。

	initial 
begin
seg[0] = 7'h3f; // 0
seg[1] = 7'h06; // 1
seg[2] = 7'h5b; // 2
seg[3] = 7'h4f; // 3
seg[4] = 7'h66; // 4
seg[5] = 7'h6d; // 5
seg[6] = 7'h7d; // 6
seg[7] = 7'h07; // 7
seg[8] = 7'h7f; // 8
seg[9] = 7'h6f; // 9
/*若需要显示A-F,解除此段注释即可
seg[10]= 7'hf7; // A
seg[11]= 7'h7c; // b
seg[12]= 7'h39; // C
seg[13]= 7'h5e; // d
seg[14]= 7'h79; // E
seg[15]= 7'h71; // F*/
end


按键消抖与时钟分频处理:

分别调用debounce和divide模块,完成按键消除抖动和时钟分频。

    // 启动/暂停按键进行消抖
debounce U2 (
.clk(clk),
.rst(rst),
.key(start),
.key_pulse(start_pulse)
);

// 按键 hold 的消抖
debounce U3 (
.clk(clk),
.rst(rst),
.key(hold),
.key_pulse(hold_pulse) //输出
);

// 按键 increase 的消抖
debounce U4 (
.clk(clk),
.rst(rst),
.key(increase),
.key_pulse(increase_pulse) //输出
);


// 用于分出一个10Hz的频率
divide #(.WIDTH(32),.N(1200000)) U1 (
.clk(clk),
.rst_n(rst),
.clkout(clk1h) //输出
);


计数控制逻辑:

通过几个always块处理startholdincrease信号,实现计数的开始、暂停、增量调整以及复位功能。start和hold的逻辑非常简单,reset的话置0,否则反转。increase比较复杂,我是借助gpt完成的。因为如果单纯取反,按一下递增信号会一直为高,导致小数位保持,整数位以10Hz计数。一开始改成只在第一个clk钟期拉高,然后置0,问题在于clk频率远大于10hz,可能这个动作完全没有被捕捉到,所以输出结果为按下increase没有反应。第二次改进为在第一个clk1h内拉高,但这样的问题是increase_pulse的上升沿和10Hz时钟可能不对齐,表现为输出不稳定:按下有可能加一有可能没反应。最终解决办法为:在前两个clk1h内拉高,之后置0,这样的输出就非常稳定了。

    //按键动作标志信号产生
always @ (posedge start_pulse)
if(!rst==1)
start_flag <= 0;
else
start_flag <= ~start_flag;

always @ (posedge hold_pulse)
if(!rst==1)
hold_flag <= 0;
else
hold_flag <= ~hold_flag;

// 在increase_pulse的上升沿设置increase_flag_set
always @(posedge clk or negedge rst) begin
if (!rst) begin
increase_flag_set <= 0;
end else if (increase_pulse) begin
increase_flag_set <= 1;
end else if (clk1h && increase_counter >= 2) begin // 如果计数器达到2,重置increase_flag_set
increase_flag_set <= 0;
end
end

// 在10Hz时钟的每个上升沿更新increase_counter
always @(posedge clk1h or negedge rst) begin
if (!rst) begin
increase_counter <= 0; // 复位时计数器清零
end else if (increase_flag_set) begin
if (increase_counter < 2) begin
increase_counter <= increase_counter + 1; // 增加计数器直到2
end
end else begin
increase_counter <= 0; // 如果increase_flag_set为低,则重置计数器
end
end

// 使用increase_flag_set和increase_counter来设置increase_flag
// 仅在increase_counter计数期间保持increase_flag为高
always @(posedge clk1h or negedge rst) begin
if (!rst) begin
increase_flag <= 0;
end else if (increase_flag_set && increase_counter > 0) begin
increase_flag <= 1;
end else begin
increase_flag <= 0;
end
end

//计时完成标志信号产生
always @ (*)
if(!rst == 1)
back_to_zero_flag <= 0;
else if(cnt_shi==9 && cnt_ge==9)
back_to_zero_flag <= 1;
else
back_to_zero_flag <= 0;

计数逻辑:

这里的注释由chatgpt生成,比较清楚。满9回0,否则递增。

// 0到99的计数器
always @ (posedge clk1h or negedge rst) begin
if (!rst) begin
cnt_ge <= 4'd0; // 如果复位信号激活,则个位计数器复位为0
cnt_shi <= 4'd0; // 十位计数器也复位为0
end
else if (increase_flag) begin
// 如果接收到增加计数的信号
if (cnt_ge == 9) begin
cnt_ge <= 4'd0; // 个位计数器满9回到0
if (cnt_shi == 9) begin
cnt_shi <= 4'd0; // 如果十位计数器也满9,则回到0
end
else begin
cnt_shi <= cnt_shi + 1; // 否则十位计数器加1
end
end
else begin
cnt_ge <= cnt_ge + 1; // 如果个位计数器未满9,个位计数器加1
end
// 当hold_flag为低时,不在暂停模式,计数器正常计数
if (!hold_flag) begin
// 如果不在暂停模式,则进行计数
if (cnt_ge == 9) begin
cnt_ge <= 4'd0; // 个位计数器满9回到0,十位增加已在上面处理
end
else begin
cnt_ge <= cnt_ge + 1; // 个位计数器加1
end
end
end
else if (start_flag && !hold_flag) begin
// 如果按下开始键且不在暂停模式,则开始计数
if (cnt_ge == 9) begin
cnt_ge <= 4'd0; // 个位计数器满9回到0
if (cnt_shi == 9) begin
cnt_shi <= 4'd0; // 如果十位计数器满9,则回到0
end
else begin
cnt_shi <= cnt_shi + 1; // 十位计数器加1
end
end
else begin
cnt_ge <= cnt_ge + 1; // 个位计数器加1
end
end
// 这里不处理increase_flag,通过increase_pulse信号直接触发增加逻辑
end


点灯模块:

把得到的数值传递给数码管。需要注意的是segled2的dp位得手动置为1。

	//计时完成点亮led
always @ ( back_to_zero_flag)begin
if (back_to_zero_flag==1)
led = 8'b0;
else
led = 8'b11111111;
end

assign seg_led_1[8:0] = {2'b00,seg[cnt_ge]};

assign seg_led_2[8:0] = {2'b01,seg[cnt_shi]};



endmodule


6. 仿真波形图

由于我的电脑是mac,所以让同学帮忙用modelsim仿真。但这个是刚开始的初代版本,按键功能都还有问题。从digit0和digit1可知计数逻辑是对的。

AA022D895E17F121F2171B8C1652A3EC.png

A905FEAFC2DEF62D42593C9C55924DC2.png

7. FPGA的资源利用说明

由chatgpt读取run.log后生成:

### 设计总览


- **设备型号**:LCMXO2-4000HC

- **封装**:CSBGA132

- **速度等级**:5

- **家族**:MachXO2


### 资源利用情况


- **寄存器位**:使用了113个寄存器位,占总数4635位的2%。

- **逻辑单元(SLICEs)**:使用了99个SLICEs,占总数2160个的5%。这些SLICEs用作逻辑/ROM的是99个,作为RAM的是0个,作为Carry的是60个。

- **查找表(LUT4s)**:使用了198个,其中78个用作逻辑LUTs。

- **PIO(Programmable Input/Output)**:使用了31个加4个JTAG,总共105个中的33%。

- **块RAM(Block RAMs)**:没有使用(0个使用)。

- **全局设置复位(GSRs)**:使用了1个,占总数1个的100%。

- **时钟资源**:有4个时钟网络。

- 主要时钟资源(Primary clock resources)未使用。

- 次要时钟资源(Secondary clock resources)使用了5个,占可用的8个的62%。


### 时钟网络


- **主时钟(Global Clocks)**:

- 使用了一个主时钟(`clk_c`),负载58。

- **次时钟(Secondary Clocks)**:

- `clk1h`:负载7。

- 其他次级时钟资源主要用于控制信号,如`hold_pulse`和`start_pulse`。


### 输入/输出银行(I/O Bank)利用


- 提供了各个I/O银行的利用情况,每个银行的利用百分比范围从14%到50%不等,这取决于银行中使用的引脚数。


### 综述


这个设计有效地使用了FPGA的资源,只占用了小部分的可用资源,这表明还有大量的资源可以用于进一步的功能扩展。同时,设计中没有使用块RAM和PLL资源,这指示了当前设计的复杂度相对较低。使用的时钟资源和输入/输出资源都在合理范围内,未见资源过度集中或潜在的布局与布线问题。

8. 代码附件

见设计资源

附件下载
注释版增量修改.zip
修改后的所有代码,递增从原先十位改为个位
implement (25).jed
可执行文件
终极版.zip
被退回的原程序,因为忙于毕业设计所以视频来不及重拍了
团队介绍
北京理工大学电子信息工程通信方向本科在读
团队成员
饲养乌鸦
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号