1. 项目需求分析与设计思路
篮球计分器是FPGA入门学习的经典项目,它涵盖了数字逻辑设计的大部分核心概念。我在第一次做这个项目时,也是从最基础的需求分析开始,一步步拆解功能模块。篮球计分器需要实现24秒进攻倒计时、12分钟比赛计时、两队比分统计、暂停功能以及数码管显示等核心功能。
刚开始接触这个项目时,最容易犯的错误就是直接开始写代码。我建议先花时间做好需求分析,把每个功能模块的输入输出关系理清楚。比如24秒倒计时需要精确到0.01秒,而12分钟计时只需要精确到1秒,这两种不同的时间精度会直接影响我们的时钟分频设计。
在实际设计过程中,我发现模块化设计特别重要。把整个系统分解为按键消抖、计时模块、计分模块、显示控制等独立单元,不仅让代码更清晰,也便于后期调试。记得我第一次做的时候,就是因为没有做好模块划分,导致调试时花了大量时间排查问题。
从硬件描述语言的角度来看,Verilog的模块化特性非常适合这种系统级设计。每个功能模块都可以独立编写和测试,最后通过顶层模块进行例化连接。这种设计方法不仅提高了代码的可维护性,也让我们能够更好地理解数字系统的层次化设计思想。
2. 按键消抖模块的实现细节
按键消抖是数字系统设计中的基础但重要的环节。在实际使用中,机械按键在按下和释放时会产生10-20ms的抖动,如果不进行处理,会导致单次按键被误识别为多次触发。我在项目中踩过的坑就是忽略了消抖处理,导致计分系统经常出现误操作。
按键消抖模块的核心原理很简单:通过采样和延时来消除抖动信号。具体实现时,我通常使用状态机的方式,设置采样周期为20ms左右。当检测到按键状态变化时,启动计数器,只有在连续多个采样周期都检测到相同状态时,才认为按键状态真正改变。
module key_filter(
input i_clk,
input i_rst_n,
input i_key,
output reg o_key_flag
);
parameter DEBOUNCE_TIME = 20'd1000000; // 20ms at 50MHz
reg [19:0] counter;
reg [1:0] state;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
state <= 2'b00;
counter <= 0;
o_key_flag <= 0;
end else begin
case (state)
2'b00: begin // 等待按键按下
if (!i_key) state <= 2'b01;
end
2'b01: begin // 消抖计数
if (counter == DEBOUNCE_TIME) begin
state <= 2'b10;
counter <= 0;
end else begin
counter <= counter + 1;
end
end
2'b10: begin // 确认按下
o_key_flag <= 1;
state <= 2'b11;
end
2'b11: begin // 等待释放
o_key_flag <= 0;
if (i_key) state <= 2'b00;
end
endcase
end
end
endmodule
在实际测试中,我发现消抖时间需要根据具体按键特性进行调整。有些按键的抖动时间较长,可能需要30-40ms的消抖时间。建议在实现时参数化消抖时间,便于后期调整。
3. 24秒精确倒计时模块设计
24秒倒计时是篮球计分器的核心功能之一,要求精度达到0.01秒。这个模块的设计难点在于如何实现高精度计时同时保证资源的有效利用。我在实现这个模块时,尝试过多种方案,最终选择了分频计数的方式。
首先需要计算分频参数。假设系统时钟为100MHz,要获得0.01秒的时间精度,我们需要每0.005秒产生一个时钟边沿(因为上升沿和下降沿各计数一次)。计算公式为:计数次数 = 时钟频率 × 时间 = 100,000,000 × 0.005 = 500,000次。
module cnt_24s(
input i_clk,
input i_rst_n,
input i_en_24s, // 倒计时使能
input i_en_stop_n, // 暂停控制
input i_en_time, // 清零信号
output [15:0] o_time_24s
);
parameter P_TIME_1 = 19'd500000; // 0.005s分频参数
reg [15:0] ro_time_24s;
reg [18:0] r_cnt;
reg r_div_1s;
// 分频计数器
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_cnt <= 0;
end else if (!i_en_stop_n) begin
r_cnt <= r_cnt; // 暂停时保持计数值
end else if (r_cnt == P_TIME_1 - 1) begin
r_cnt <= 0;
end else begin
r_cnt <= r_cnt + 1;
end
end
// 生成0.01秒时钟
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_div_1s <= 0;
end else if (r_cnt == P_TIME_1 - 1) begin
r_div_1s <= ~r_div_1s;
end
end
// 倒计时逻辑
always @(posedg


123

被折叠的 条评论
为什么被折叠?



