2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口制作具有启动、停止、递增和清除功能的秒表
该项目使用了Lattice MXO2的小脚丫FPGA核心板 - Type C接口,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:具有启动、停止、递增和清除功能的秒表。
标签
FPGA
数字逻辑
显示
开发板
2024寒假在家一起练
学分+1
更新2024-03-29
北京理工大学
117

一、项目需求

通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值:


使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表,秒表应从0.0 秒计数到9.9秒,然后翻转,计数值每0.1秒精确更新一次。


秒表使用四个按钮输入:开始、停止、增量和清除(重置):


开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次);停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。

二、需求分析

根据项目需求,将项目划分为以下模块:计时模块、递增模块、控制模块、显示模块。


计时模块需要实现从0.0秒到9.9秒的计时,每0.1秒更新一次。具体而言,低位的显示管显示的数字需要每0.1秒增加1,并且,当数字为9再次增加时应置0并且高位的显示管显示的数字增加1,当高位的显示管数字为9再次增加时也应置0。


递增模块需要实现递增按键每按下一次时低位的显示管数字增加1。具体而言,每次按下递增按键时,无论按住递增按键多长时间,该次动作均使低位显示管显示的数字增加1。当出现有显示管的数字大于9时,数字的变化规律应与计时模块中描述的相同。


控制模块需要实现对秒表启动、停止、清除功能的控制。具体而言,当按下启动按键时,秒表应按照计时模块中的规律显示数字,直到按下停止按键或清除按键;当按下停止按键时,秒表显示的数字应停止增加并一直保持显示,直到按下启动按键或清除按键;当按下清除按键时,秒表显示的数字应为0.0并停止增加,直到按下启动按键。


显示模块需要实现秒表的显示功能。具体而言,在输入经上述模块进行运算后,所得结果需要输出到核心板上显示为直观的、易于理解的数字。

三、实现方式

针对需求分析中不同模块需要实现的功能,我们采用了下述技术路线完成需求:


计时模块:


首先需要实现基本计时功能,即低位数字从0开始每次增加1,当低位数字为9再增加时则置0且高位数字加1,当高位数字为9再增加时则置0。我们设置了以时钟上升沿为触发每次加1的变量作为计时模块模块的低位数字输出,使用if语句判断计时模块的低位数字是否为9,若为9则在下次时钟上升沿时将计时模块的低位数字置0并使计时模块的高位数字加1,同样使用if语句判断计时模块的高位数字是否为9,若为9则在下次时钟上升沿时将计时模块的高位数字置0。


其次需要实现每0.1秒秒表跳动一次的需求。基本计时功能在系统的12MHz时钟的上升沿触发一次,与实际需求不符,我们需要将触发条件改为每0.1秒上升一次的信号,因此对系统时钟进行分频。设置100ms计数器,每次检测到系统时钟上升沿时加1直至到达指定数值时置0并重新累加,同时分频时钟电平翻转。由于系统时钟每100ms中有1.2M上升沿且分频时钟每周期需翻转2次,因此指定数值为600000。置0操作可使用if语句实现。使用完成的分频时钟作为基本计时功能中的时钟即可实现每0.1秒跳动一次的时钟。


递增模块:


由于实际编码中出现未查明的错误,暂时无法直接将递增模块产生的数字增量加到计时模块的数字上,我们最后采用了两模块分别计算数字后,在显示模块内再相加的方法。


为了实现基本的递增功能,我们持续检测递增键状态,当检测到递增按键被按下,即出现下降沿时递增模块的低位数字加1。递增模块的低位数字与高位数字的状态逻辑与计时模块相同,仅触发条件不同。


在实际的使用过程中,由于物理按键按下时存在不稳定期,实际波形会在按下与弹起时存在多次的翻转导致被检测到的次数多于实际按压次数。我们使用的消抖原理为设置一个递增标志并持续检测递增按键,当检测到按键被按下,即低电平时启用一个计数器开始计数,若在计数器达到设定次数时均为低电平则认为按键被按下,递增标志置0;若在计数器达到设定次数前出现高电平则认为是抖动期的翻转,递增标志置1,计数器置0。使用完成的递增标志的下降沿为触发即可实现每次按键数字增加一次。


控制模块:


与递增模块类似,通过启动按键、停止按键、清除按键实现计时的启动、停止、清除。启动按键、停止按键使用了产品例程中的消抖方法,原理与递增模块中的类似,重复部分不再赘述,与例程中不同的部分在于例程由消抖后按键是否按下状态来改变作为模块输出的标志,每按下一次标志翻转一次,我们的程序中设置了启动按键、停止按键共同控制的启动/停止标志,若检测到消抖后启动按键按下置1,停止按键按下置0,均无则保持。根据启动/停止标志是否为1,计时模块中分频部分的计数器决定是否在时钟上升沿加1,以此控制包括数字增加等后续运算。


清除按键无需消抖,检测到按键按下则将所有模块中的变量置0,则秒表显示0.0,且由于启动/停止标志置0,显示的数字不再增加。由此实现了通过启动按键、停止按键、清除按键对秒表的控制。


显示模块:


上述模块中计算得到的计时模块低位数字、计时模块高位数字、递增模块低位数字、递增模块高位数字作为显示模块的输入,最终转换为显示管上的数字。


先将计时模块和递增模块的低位数字相加,若结果大于10则将结果减10以保留个位数作为低位数字,并将进位标志置1,反之直接保留结果作为低位数字,并将进位标志置0;再将计时模块和递增模块的高位数字和进位标志相加,结果大于10则将结果减10以保留个位数作为高位数字,反之直接保留结果作为高位数字。


为了将上述计算所得的高位数字与低位数字显示在显示管上,我们建立了以实际数字数值为索引,以显示对应数字的七段数码管电平为元素的数组,将高位或低位数字作为索引即可得到对应数字的电平作为输出变量,正确设置引脚即可显示对应数字。小数点引脚对应变量保持高电平。使用系统时钟上升沿作为触发持续对输出变量赋值,显示管可以保持较高亮度。

四、功能框图

五、代码及说明

因为没有GPT4的账号而3.5写的代码不太行我就自己写了,评分标准里应该只占5分。


因为我没怎么用过verilog,所以代码写得比较丑,所有功能都放在一个module里了,在module内用注释区分各功能。说明在实现方式和注释里,这里不再赘述。


module文件代码:

1.输入输出及中间变量的申明与初始化

module LED (clk,start,stop,up,rst,led_1,led_2,pt,flr1,flr2);

input clk;/*时钟*/
input start;/*启动按键*/
input stop;/*停止按键*/
input up;/*增量按键*/
input rst;/*清除按键*/
output reg [6:0] led_1;/*高位数码管输出*/
output reg [6:0] led_2;/*低位数码管输出*/
output reg pt;/*小数点输出*/
output reg flr1;/*不知道是什么但是数码管例程里有这一位*/
output reg flr2;
reg stop_start_flg;/*启动/停止标志*/
reg up_flg;/*递增标志*/
reg [19:0]cnt100ms;/*计时的100ms分频计数*/
reg clkdiv100ms;/*分频时钟*/
reg [17:0]cnt20ms;/*启动按键、停止按键的防抖20ms计数*/
reg [18:0]cnt30ms;/*递增按键的防抖30ms计数*/
reg [3:0]data_1;/*高位数码管计时部分数字*/
reg [3:0]data_2;/*低位数码管计时部分数字*/
reg [3:0]data_11;/*高位数码管递增部分数字*/
reg [3:0]data_22;/*低位数码管递增部分数字*/
reg [3:0]data1;/*高位数码管实际数字*/
reg [3:0]data2; /*低位数码管实际数字*/
reg [3:0]data3;/*低位数码管进位标志*/
reg [6:0] seg [9:0];/*存放数字0-9数码管输出的数组*/

initial
begin
cnt100ms=20'd0;
clkdiv100ms=0;
cnt20ms=18'd0;
data_1=4'b0;
data_2=4'b0;
data_11=4'b0;
data_22=4'b0;
data1=4'b0;
data2=4'b0;
data3=1'b0;
seg[0]=7'b0111111;
seg[1]=7'b0000110;
seg[2]=7'b1011011;
seg[3]=7'b1001111;
seg[4]=7'b1100110;
seg[5]=7'b1101101;
seg[6]=7'b1111101;
seg[7]=7'b0000111;
seg[8]=7'b1111111;
seg[9]=7'b1101111;
stop_start_flg=1'b0;
up_flg=1'b0;
end

2.计时模块

/*计时模块*/
always @(posedge clk or negedge rst) begin/*分频*/
if (!rst) begin
cnt100ms=20'd0;
data3=1'b0;
end
else if (cnt100ms==20'd600000)/*600000_30,上板的数_仿真的数*/
begin
cnt100ms=20'd0;
clkdiv100ms=~clkdiv100ms;
end
else if (stop_start_flg) cnt100ms=cnt100ms+1'b1;
end
always @(posedge clkdiv100ms or negedge rst) begin/*计时部分数字变化*/
if (!rst)
begin
data_1=4'b0;
data_2=4'b0;
end
else if (data_2<4'b1001) data_2=data_2+1'b1;
else if (data_1<4'b1001)
begin
data_2=4'b0;
data_1=data_1+1'b1;
end
else
begin
data_1=4'b0;
data_2=4'b0;
end
end

3.递增模块

/*递增模块*/
always @(posedge clk) begin/*递增按键防抖,从网上学的*/
if (!up)
begin
if (cnt30ms==19'd360000) up_flg<=1'b1;/*360000_18*/
else begin
cnt30ms<=cnt30ms+1'b1;
up_flg=1'b0;
end
end
else begin
cnt30ms<=19'd0;
up_flg<=1'b0;
end
end
always @(posedge up_flg or negedge rst) begin/*递增部分数字变化*/
if (!rst)
begin
data_11=4'b0;
data_22=4'b0;
end
else if (data_22<4'b1001) data_22=data_22+1'b1;
else if (data_11<4'b1001)
begin
data_22=4'b0;
data_11=data_11+1'b1;
end
else
begin
data_11=4'b0;
data_22=4'b0;
end
end

4.控制模块

/*控制模块*/
reg start1;
reg stop1;
always @(posedge clk or negedge rst)/*启动按键、停止按键防抖,参考例程*/
if (!rst)
begin
start1<=1'b1;
stop1<=1'b1;
end
else
begin
start1<=start;
stop1<=stop;
end

wire start2=(start1==start)? 0:1;
wire stop2=(stop1==stop)?0:1;
always @ (posedge clk or negedge rst)
if (!rst) cnt20ms <= 18'd0;
else if(start2||stop2) cnt20ms<=18'd0;
else cnt20ms<=cnt20ms+1'b1;

reg start3;
reg stop3;
always @(posedge clk or negedge rst)
if (!rst)
begin
start3<=1'b1;
stop3<=1'b1;
end
else if (cnt20ms==18'd240000)/*240000_12*/
begin
start3<=start;
stop3<=stop;
end

reg start4;
reg stop4;
always @ (posedge clk or negedge rst )
if (!rst)
begin
start4<=1'b1;
stop4<=1'b1;
end
else
begin
start4<=start3;
stop4<=stop3;
end

wire start5;
wire stop5;
assign start5=start4&(~start3);
assign stop5=stop4&(~stop3);
always @(posedge clk or negedge rst)
if (!rst) stop_start_flg<=1'b0;
else if (start5) stop_start_flg<=1'b1;
else if (stop5) stop_start_flg<=1'b0;
else stop_start_flg<=stop_start_flg;

5.显示模块

/*显示模块*/
always @(posedge clk or negedge rst) begin/*计时部分和递增部分相加*/
if (!rst) data3=1'b0;
if (data_22+data_2<4'd10) begin
data2=data_22+data_2;
data3=1'b0;
end
else begin
data2=data_22+data_2-4'd10;
data3=1'b1;
end

if (data_11+data_1+data3<4'd10) data1=data_11+data_1+data3;
else data1=data_11+data_1+data3-4'd10;
/*显示输出*/
led_1=seg[data1];
led_2=seg[data2];
pt=1'b1;
flr1=1'b0;
flr2=1'b0;
end

endmodule

顶层文件代码:

module top(
input clk,
input start,
input stop,
input up,
input rst,
output [6:0] led_1,
output [6:0] led_2,
output pt,
output flr1,
output flr2
);

LED LED1(
.clk(clk),
.start(start),
.stop(stop),
.up(up),
.rst(rst),
.led_1(led_1),
.led_2(led_2),
.pt(pt),
.flr1(flr1),
.flr2(flr2)
);

endmodule

六、仿真波形图

使用WEBIDE进行仿真,但网页存在bug,很多时候只能显示到1200ns的波形,缩放移动均无法观察到后续波形,所以对于仿真文件以及作为仿真对象的代码中的分频计数器做了缩小,这在计时模块的代码注释中有提到。


仿真文件代码:

`timescale 1ns / 1ps

module tb();

reg clk;
reg start;
reg stop;
reg up;
reg rst;
wire [6:0] led_1;
wire [6:0] led_2;
wire pt;
wire flr1;
wire flr2;

initial begin
clk=1'b1;
rst=1'b1;
start=1'b1;
stop=1'b1;
up=1'b1;
#100
start=1'b0;
#100
start=1'b1;
#200
stop=1'b0;
#100
stop=1'b1;
#20
up=1'b0;
#100
up=1'b1;
#20
up=1'b0;
#50
up=1'b1;
#20
up=1'b0;
#50
up=1'b1;
#50
start=1'b0;
#100
start=1'b1;
#200
rst=1'b0;
end

always #1 clk=~clk;

LED LED1(
.clk(clk),
.start(start),
.stop(stop),
.up(up),
.rst(rst),
.led_1(led_1),
.led_2(led_2),
.pt(pt),
.flr1(flr1),
.flr2(flr2)
);

endmodule

仿真波形图:

图1 两组显示管波形

图2 重要变量波形

因为只有一个模块LED1,所以看图2就能观测所有重要变量了。图1主要观察显示管电平是否对应应显示的数字。


可以看到根据代码,启动按键start在100ns处被按下并持续100ns后松开,经消抖判断后启动/停止标志stop_start_flg置1,100ms分频计数器cnt100ms计数且100ms分频时钟clkdiv100ms开始计时,计时模块的低位数字data_2开始增加,显示模块的低位数字data2和显示管电平led2、led1均符合预期。


在400ns处停止按键stop被按下并持续100ns后松开,经消抖判断后stop_start_flg置0,cnt100ms停止计数且clkdiv100ms不再计时,data_2保持当前数值2不再增加,data2、led2、led1均符合预期。


从520ns处按下3次递增按键up,其中第一次持续较长时间。可以看到无论按下的时间长短递增模块低位数字data_22均只增加一次,显示模块将data_2、data_22求和得到的data2以及led2、led1均符合预期。


在810ns处再次按下start,秒表再次启动,计时模块中的data2再次随时间增加,且led2、led1均符合预期。


在1110ns处按下清除按键rst,所有与计时直接相关数字清0,包括计时模块data_2、递增模块data_22、显示模块data2等,同时stop_start_flg置0,代表计时停止在0.0不再变化直到start按键被按下。


限于WEBIDE的bug无法充分对进位置0的功能进行仿真,这部分会在演示视频中展示。

七、FPGA资源利用说明

资源利用率如下图所示,来自于WEBIDE的FPGA映射部分:

八、演示视频

见基本信息中的视频代码部分。

九、代码附件

见设计资源中的附件部分。



附件下载
archive.zip
含.v源码文件及可下载至核心板的.jed文件
团队介绍
姓名:高捷 单位:北京理工大学
团队成员
学分+1
评论
0 / 100
查看更多
猜你喜欢
2024年寒假练-基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口实现具有启动、停止、递增和清除功能的秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板 - Type C接口,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。 秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。。
Aurora.
119
2024寒假练——基于Lattice MXO2的小脚丫FPGA核心板制作具有启动、停止、递增和清除功能的秒表该项目使用了Lattice MXO2的小脚丫FPGA核心板,基于stepfpga网页版编译器,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:创建一个2位数码管秒表 ,秒表从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次,通过按键可以控制开始、停止,递增和清零。。
MSCu
225
2024寒假在家一起练-基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口设计具有启动、停止、递增和清除功能的秒表该项目使用了FPGA编程,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:具有启动、停止、递增和清除功能的秒表。
碧24可以
93
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号