2024年寒假练 - 基于Lattice MXO2的小脚丫FPGA核心板制作具有启动、停止、递增和清除功能的秒表
该项目使用了基于Lattice MXO2的小脚丫FPGA核心板,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。 秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。。
标签
FPGA
电小麦
更新2024-04-01
233

1. 项目需求

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

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

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

2.  需求分析

为了实现项目,将整个设计分为几个功能模块,包括时钟分频器、按键控制模块、计数器模块和数码管译码模块。以下是每个模块的简要描述:

1)时钟分频器模块:

·        生成一个10Hz的时钟信号,用于触发计数器递增。

·        可以使用一个简单的分频逻辑,将FPGA板上的12MHz时钟分频为10Hz。

2)按键控制模块:

·        监控四个按键输入,分别是开始、停止、增量和清除。

·        产生相应的控制信号,如启动计数、停止计数、增量计数和复位计数。

3)计数器模块:

·        通过10Hz时钟递增,实现秒表计数。

·        受到按键控制模块的控制,可以启动、停止、增量和复位计数。

4) 数码管译码模块:

·        将计数器的值转换为数码管显示的对应值。

·        管理两位数码管的显示,支持显示从0.0到9.9。

3. 实现的方式

1)使用思得普的WebIDE进行代码的逻辑综合、管脚分配和FPGA映射,通过数据线将程序烧录进小脚丫FPGA核心板中。

2)使用 GPT 来生成每一个模块的代码,并且格式规范,注释清晰。

edea012c2c193f2326f1063fe2dd7bb.png

c5c122e0596fd7d5be240329ad57812.png

4. 功能框图

1)硬件框图:

2) 软件框图:

5. 代码及说明

1)时钟分频器模块(ClockDivider)接收一个时钟信号(12MHz),并将其分频为更低频率的时钟信号(10Hz)。它通过计数器对输入时钟信号进行计数,当计数器达到一定值时,输出时钟信号翻转一次。这样,输出的时钟信号频率降低到10Hz,适合用于秒表等需要较低频率时钟的应用。

module ClockDivider(
input clk_12MHz, // 输入时钟为12MHz
output reg clk_10Hz // 输出时钟为10Hz
);

reg [25:0] counter; // 2^25 = 33,554,432,用于分频计数

always @(posedge clk_12MHz) begin
if (counter == 26'd0_600_000) begin // 计数到 2^25 - 1 时,约等于12MHz / 10Hz
counter <= 0;
clk_10Hz <= ~clk_10Hz;
end else begin
counter <= counter + 1;
end
end

endmodule

2)按键模块(ButtonControl)用于处理四个按键输入:开始、停止、增量和清除。它接收12MHz的时钟信号和四个按键输入,并输出相应的控制信号。模块内部使用 debounce 子模块处理每个按键输入,消除抖动并生成脉冲信号。在时钟信号的上升沿到来时,模块根据按键输入状态更新相应的控制信号。如果检测到按键脉冲信号,则相应的控制信号被置高;否则,保持低电平。总之,按键模块负责检测按键输入并生成控制信号,以便其他模块使用。

module ButtonControl(
input clk_12MHz, // 输入时钟为12MHz
input start, // 开始按钮输入
input stop, // 停止按钮输入
input inc, // 增量按钮输入
input clear, // 清除按钮输入
output reg control_start, // 开始控制信号输出
output reg control_stop, // 停止控制信号输出
output reg control_inc, // 增量控制信号输出
output reg control_clear, // 清除控制信号输出
input key_flag //按键判断完毕标志位
);

wire start_pulse, stop_pulse, inc_pulse, clear_pulse;

debounce u_start (
.clk(clk_12MHz),
.rst(1'b1),
.key(start),
.key_pulse(start_pulse)
);
//省略其他3个按键消抖​
//`````````````````​
always @(posedge clk_12MHz) begin
if(key_flag)
begin
control_start<=0;
control_stop<=0;
control_inc<=0;
control_clear<=0;
end
else;
if(stop_pulse) control_stop<=1;
else if(start_pulse) control_start<=1;
else if(inc_pulse) control_inc<=1;
else if(clear_pulse) control_clear<=1;
else;
end

endmodule

3)计数模块(CounterModule)用于实现秒表的计数功能。它接收10Hz的时钟信号和四个控制信号(开始、停止、增量和清除),并输出计数器的值以及按键判断完成标志位。模块内部使用两个寄存器(ones_place和tens_place)分别存储个位和十位的计数值,并根据控制信号更新计数器的值。当控制信号触发时,模块会根据当前模式(递增或递减)更新计数器的值,并输出计数器的值和按键判断完成标志位。总之,计数模块负责根据控制信号更新计数器的值,并输出计数器的值和按键判断完成标志位。

module CounterModule(
input CLK_10Hz, // 输入时钟为10Hz
input control_start, // 开始控制信号
input control_stop, // 停止控制信号
input control_inc, // 增量控制信号
input control_clear, // 清除控制信号
output reg key_flag, //按键判断完毕标志位
output reg [7:0] counter // 计数器输出
);
reg [3:0] ones_place;
reg [3:0] tens_place;
reg count_up=1;
always @(posedge CLK_10Hz) begin
key_flag<=0;
if(control_stop)begin
key_flag<=1;
end
else if(control_start) begin
if (count_up) begin
// 递增逻辑
ones_place<=ones_place+1;
if (ones_place == 4'd9) begin
ones_place <= 4'b0;
tens_place <= tens_place + 1;
end
end
else begin
// 递减逻辑
ones_place <= ones_place - 1;
if (ones_place == 4'd0) begin
ones_place <= 4'd9;
tens_place <= tens_place - 1;
end
end
end

else if(control_inc)begin
// 递增逻辑
ones_place<=ones_place+1;
if (ones_place == 4'd9) begin
ones_place <= 4'b0;
tens_place <= tens_place + 1;
end
key_flag<=1;
end
else if(control_clear)begin
ones_place <= 4'd0;
tens_place <= 4'd0;
key_flag<=1;
end
else ;
//判断递增/递减
count_up <= (ones_place == 4'd0 && tens_place == 4'd0) ? 1'b1 :
(ones_place == 4'd1 && tens_place == 4'd0) ? 1'b1 :
(ones_place == 4'd8 && tens_place == 4'd9) ? 1'b0 :
count_up;
counter[3:0]<=ones_place;
counter[7:4]<=tens_place;
end
endmodule

4)数码管译码模块(SevenSegmentDecoder)用于将计数器的值转换为数码管的七段显示输出。它接收10Hz的时钟信号和8位的计数器值,并输出数码管1和数码管2的七段数码管输出。该模块内部使用两个寄存器(bcd1和bcd2)存储转换后的BCD码,并根据计数器的值更新BCD码。然后,根据BCD码在存储器中查找对应的七段数码管输出,并将结果输出到数码管1和数码管2的七段数码管输出端口。总之,数码管译码模块负责将计数器的值转换为数码管的七段显示输出,并输出到数码管的输出端口。

module SevenSegmentDecoder(
input CLK_10Hz, // 输入时钟为10Hz
input [7:0] counter, // 9位计数器值
output wire [8:0] seg_led_1, // 数码管1的七段数码管输出
output wire [8:0] seg_led_2 // 数码管2的七段数码管输出
);

reg [3:0] bcd1, bcd2; // 两个四位BCD码
reg [8:0] seg [9:0];//定义了一个reg型的数组变量,相当于一个10*9的存储器,存储器一共有10个数,每个数有9位宽

initial //在过程块中只能给reg型变量赋值,Verilog中有两种过程块always和initial
//initial和always不同,其中语句只执行一次
begin
seg[0] = 9'h3f; //对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字 0
seg[1] = 9'h06; //7段显示数字 1
seg[2] = 9'h5b; //7段显示数字 2
seg[3] = 9'h4f; //7段显示数字 3
seg[4] = 9'h66; //7段显示数字 4
seg[5] = 9'h6d; //7段显示数字 5
seg[6] = 9'h7d; //7段显示数字 6
seg[7] = 9'h07; //7段显示数字 7
seg[8] = 9'h7f; //7段显示数字 8
seg[9] = 9'h6f; //7段显示数字 9
end

assign seg_led_1 = seg[bcd1]; //连续赋值,这样输入不同四位数,就能输出对于译码的9位输出
assign seg_led_2 = seg[bcd2]+9'h80; // 初始化BCD码对应的数字
initial
begin
bcd1 = 4'b0000;
bcd2 = 4'b0000;
end

// BCD码转换逻辑
always @(posedge CLK_10Hz) begin
// 将 8 位的计数器值转换为两个四位的 BCD 码
bcd1 = counter[3:0];
bcd2 = counter[7:4];
end
endmodule

5)顶层文件(TopModule将所有子模块集成在一起,以构建一个完整的秒表系统。它连接了时钟分频器模块、按键控制模块、计数器模块和数码管译码模块。

module TopModule(
input CLK_12MHz, // 输入时钟为12MHz
input btn_start, // 开始按钮
input btn_stop, // 停止按钮
input btn_inc, // 增量按钮
input btn_clear, // 清除按钮
output wire [8:0] seg_led1, // 数码管1的七段数码管输出
output wire [8:0] seg_led2 // 数码管2的七段数码管输出
);

wire [7:0] counter; // 8位计数器值
wire control_start, control_stop, control_inc, control_clear;
wire CLK_10Hz;
wire key_flag;
// 时钟分频器
ClockDivider u_ClockDivider(
.clk_12MHz(CLK_12MHz),
.clk_10Hz(CLK_10Hz)
);

// 按键控制模块
ButtonControl u_ButtonControl(
.clk_12MHz(CLK_12MHz),
.start(btn_start),
.stop(btn_stop),
.inc(btn_inc),
.clear(btn_clear),
.control_start(control_start),
.control_stop(control_stop),
.control_inc(control_inc),
.control_clear(control_clear),
.key_flag(key_flag)
);

// 计数器模块
CounterModule u_CounterModule(
.CLK_10Hz(CLK_10Hz),
.control_start(control_start),
.control_stop(control_stop),
.control_inc(control_inc),
.control_clear(control_clear),
.key_flag(key_flag),
.counter(counter)
);

// 数码管译码模块
SevenSegmentDecoder u_SevenSegmentDecoder(
.CLK_10Hz(CLK_10Hz),
.counter(counter),
.seg_led_1(seg_led1),
.seg_led_2(seg_led2)
);

endmodule

6. FPGA的资源利用说明

下图来自WebIDE中FPGA映射后产生的run.log文件,说明了FPGA资源利用情况。

7. 遇到的主要难题及解决方法

1)时序问题: FPGA 设计中经常会面临时序约束和时序分析的挑战,本项目中按键消抖检测时序为12MHz,但秒表时序为10Hz。解决方法是另设按键判断完毕标志位保证时序正确。

2)按键消抖: 按键的物理特性会导致按键按下时可能会产生抖动,即多次开关状态的快速切换。这可能会导致系统错误地检测到多次按键事件,影响系统的稳定性和可靠性。解决方法是在按键输入信号上使用软件消抖算法来滤除抖动信号。

3)数码管译码逻辑设计: 实现数码管的译码逻辑需要考虑到BCD码和数码管段的对应关系,并确保在不同的计数值下正确显示相应的数字。解决方法包括使用查找表来实现译码逻辑。

4)GPT使用: 本次项目每个功能模块均使用GPT进行开发,开始时向GPT输入的要求不明确导致功能无法实现。通过观看“电子森林”的直播课,学习了如何合理运用GPT,极大地提高的设计效率。

8.  未来的计划或建议

此次“2024寒假在家练活动”我选取了难度系数较小的FPGA项目,在“电子森林”的帮助下顺利地完成了我的第一个FPGA项目,加深了我对FPGA原理和设计方法的理解,包括数字逻辑、Verilog编程、时序控制等方面的知识。特别感谢本次活动,让我以较低的成本入门学习了FPGA开发,为我打开了一扇新的大门,期待能继续利用“小脚丫FPGA核心板”进行学习和开发项目。

附件下载
源代码.zip
团队介绍
2024年寒假练活动个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号