内容介绍
内容介绍
设计目标及需求
本项目的目标是利用小脚丫FPGA核心板来制作一个能够显示2位数秒表计时(0.0-9.9秒)的装置。
秒表的计数范围应该是从0.0秒到9.9秒,然后重新开始计数。秒表的更新频率设置为每0.1秒更新一次,以保证计时的精确性。七段数码管作为显示器作为输出设备,将被用来显示秒表的数值。
需求分析包括以下几个方面:
- 显示功能:确保秒表能够以0.1秒的间隔准确地更新并显示数值。
- 按钮输入功能:包括开始按钮(启动秒表)、停止按钮(停止秒表但保持显示)、增量按钮(增加计数值)、清除按钮(将计数器重置为零)。
- 硬件实现:利用小脚丫FPGA核心板,通过核心板上的七段显示器和按钮来实现秒表的控制和显示。
设计思路
思路图如下:
所以实现方式上涉及三个底层部分的设计,这三个部分各自独立又互有串联,形成一个完整的计数器功能:
- 分频部分:用于控制输入时钟频率和输出时钟频率,以及控制复位和使能信号。
- 数码管部分:根据输入的数值控制两个七段数码管显示对应的数字。
- 按键消抖部分:通过状态机方式实现按键消抖,保证手动按下一次按键只产生一个按键脉冲。
代码及介绍
以下为代码介绍。
首先为计时器模块,在这里完成了分频部分和数码管显示部分,以及规定了按键功能。代码如下:
module timer
(
clk , //Clock
start , //Start button
reset , //Reset button
stop , //Stop button
increment , //Increment button
seg_led_1 , //7-segment display 1
seg_led_2 //7-segment display 2
);
input clk;
input start;
input reset;
input stop;
input increment;
output reg [8:0] seg_led_1,seg_led_2;
reg flag = 1'b0;
wire start_pulse; //Debounced signal for start button
wire reset_pulse; //Debounced signal for Reset button
wire stop_pulse; //Debounced signal for stop button
wire increment_pulse; //Debounced signal for Increment button
reg [3:0] cnt_ge = 4'd0; //Milliseconds
reg [3:0] cnt_shi = 4'd0; //seconds
reg [27:0] cnt = 28'd0;
reg [27:0] mcnt = 28'd0;
reg [8:0] seg [9:0];
wire resetn;
assign resetn = ~reset_pulse;
initial
begin
seg[0] = 9'h3f; // 0
seg[1] = 9'h06; // 1
seg[2] = 9'h5b; // 2
seg[3] = 9'h4f; // 3
seg[4] = 9'h66; // 4
seg[5] = 9'h6d; // 5
seg[6] = 9'h7d; // 6
seg[7] = 9'h07; // 7
seg[8] = 9'h7f; // 8
seg[9] = 9'h6f; // 9
flag = 1'b0;
cnt_ge <= 4'd0;
cnt_shi <= 4'd0;
seg_led_1 <= 9'd0;
seg_led_2 <= 9'd0;
seg_led_1 <= {seg[cnt_shi][8],1'b1,seg[cnt_shi][6:0]};
seg_led_2 <= seg[cnt_ge][8:0];
end
//debounce buttons and get signals
debounce b1 (
.clk(clk),
.reset(1'b1),
.key(start),
.key_pulse(start_pulse));
debounce b2 (
.clk(clk),
.reset(1'b1), .key(reset),
.key_pulse(reset_pulse));
debounce b3 (
.clk(clk),
.reset(1'b1),
.key(stop),
.key_pulse(stop_pulse));
debounce b4 (
.clk(clk),
.reset(1'b1),
.key(increment),
.key_pulse(increment_pulse));
always @(posedge clk)begin
if(reset_pulse == 1'b1)begin// Reset clock pulse detection
flag <= 1'b0;
cnt_ge <= 4'd0;
cnt_shi <= 4'd0;
end
else if(start_pulse == 1'b1)begin// Start button pulse detection
flag <= 1'b1;
end
else if(stop_pulse == 1'b1)begin // Stop button pulse detection
flag <= 1'b0;
end
else if(increment_pulse == 1'b1)begin // Increment button pulse detection
if(cnt_ge == 4'd9)begin
cnt_ge <= 4'd0;
if(cnt_shi == 4'd9)begin
cnt_shi <= 4'd0;
end
else begin
cnt_shi <= cnt_shi + 4'd1;
end
end
else begin
cnt_ge <= cnt_ge + 4'd1;
end
seg_led_1 <= {seg[cnt_shi][8],1'b1,seg[cnt_shi][6:0]};
seg_led_2 <= seg[cnt_ge][8:0];
end
// get 10 Hz clock and count increments every 0.1 second
if(mcnt == 28'd1200000)begin
mcnt <= 28'd0;
if(flag == 1'b1)begin
if(cnt_ge == 4'd9)begin
cnt_ge <= 4'd0;
if(cnt_shi == 4'd9)begin
cnt_shi <= 4'd0;
end
else begin
cnt_shi <= cnt_shi + 4'd1;
end
end
else begin
cnt_ge <= cnt_ge + 4'd1;
end
seg_led_1 <= {seg[cnt_shi][8],1'b1,seg[cnt_shi][6:0]};
seg_led_2 <= seg[cnt_ge][8:0];
end
end
else begin
mcnt <= mcnt + 28'd1;
end
seg_led_1 <= {seg[cnt_shi][8],1'b1,seg[cnt_shi][6:0]};
seg_led_2 <= seg[cnt_ge][8:0];
end
endmodule
这段 Verilog 代码实现了一个简单的计时器模块。
- 模块定义:
- 模块名为 timer。
- 输入包括时钟信号 clk,以及用于控制计时器行为的按钮信号 start、reset、stop 和 increment。
- 输出包括两个 7 段显示器的控制信号 seg_led_1 和 seg_led_2。
- 信号和寄存器:
- flag 用于表示计时器是否在运行状态。
- cnt_ge 和 cnt_shi 分别用于存储秒的个位和十位数。
- cnt 和 mcnt 分别用于计时和产生 10Hz 的时钟信号。
- seg 数组用于存储每个数字的 7 段显示器编码。
- 按钮消抖:
- 使用 debounce 模块对每个按钮进行消抖,并得到消抖后的信号 start_pulse、reset_pulse、stop_pulse 和 increment_pulse。
- 状态控制:
- 在时钟上升沿检测到 reset_pulse 时,将 flag 和计数器清零。
- 在检测到 start_pulse 时,设置 flag 为高,表示计时器开始运行。
- 在检测到 stop_pulse 时,设置 flag 为低,表示计时器停止运行。
- 在检测到 increment_pulse 时,根据当前计时器的状态对秒数进行递增,并更新显示器的输出。
- 计时器逻辑:
- 在每个时钟周期中,根据计时器是否在运行状态以及计数器的值,更新计时器的秒数。
- 通过 mcnt 计数器来生成 10Hz 的时钟信号,以便每隔 0.1 秒更新一次计时器的显示。
由此代码,可以通过按钮控制计时器的启动、停止和递增功能,并在 7 段显示器上显示计时器的秒数。
然后为按键消抖模块,在这里实现按键消抖,代码如下:
// Debounce module for buttons
module debounce (clk,reset,key,key_pulse);
parameter N = 1; //Number of buttons to debounce
input clk;
input reset;
input [N-1:0] key; //Input buttons
output [N-1:0] key_pulse; //Output pulses generated by button actions
reg [N-1:0] key_rst_pre; //Register to store previous triggered button values
reg [N-1:0] key_rst; //Register to store current triggered button values
wire [N-1:0] key_edge; //High pulse generated when button changes from high to low state
//Use non-blocking assignment to store button state in two register variables at two clock edges
always @(posedge clk or negedge reset)
begin
if (!reset) begin
key_rst <= {N{1'b1}}; //Initialize key_rst with all ones, {} represents N ones
key_rst_pre <= {N{1'b1}};
end
else begin
key_rst <= key; //Assign value of key to key_rst on the first positive clock edge, and assign value of key_rst to key_rst_pre
key_rst_pre <= key_rst; //Non-blocking assignment. After two clock edges, key_rst stores the current value of key, and key_rst_pre stores the value of key at the previous clock edge
end
end
assign key_edge = key_rst_pre & (~key_rst);//Pulse edge detection. When key detects a falling edge, key_edge produces a high level for one clock cycle
reg [17:0] cnt; //Counter used for generating a delay, system clock is 12MHz, need to delay for about 20ms, at least an 18-bit counter is required
//Generate 20ms delay, counter resets and starts counting when key_edge is valid
always @(posedge clk or negedge reset)
begin
if(!reset)
cnt <= 18'h0;
else if(key_edge)
cnt <= 18'h0;
else
cnt <= cnt + 1'h1;
end
reg [N-1:0] key_sec_pre; //Register variable for delayed level detection
reg [N-1:0] key_sec;
//Detect key after delay, generate a high pulse for one clock cycle if the button state changes to low. If the button state is high, it means the button is not valid
always @(posedge clk or negedge reset)
begin
if (!reset)
key_sec <= {N{1'b1}};
else if (cnt==18'h3ffff)
key_sec <= key;
end
always @(posedge clk or negedge reset)
begin
if (!reset)
key_sec_pre <= {N{1'b1}};
else
key_sec_pre <= key_sec;
end
assign key_pulse = key_sec_pre & (~key_sec);
endmodule
这段代码是一个用于消抖按钮的模块。
- 模块定义:
- 模块名为 debounce。
- 输入包括时钟信号 clk、复位信号 reset、以及一个 N 位宽的按钮输入 key。
- 输出包括一个 N 位宽的脉冲输出 key_pulse。
- 参数和信号:
- 参数 N 用于指定需要消抖的按钮数量。
- key_rst_pre 和 key_rst 是用于存储前一次和当前按钮触发状态的寄存器。
- key_edge 是用于检测按钮从高电平到低电平状态变化的脉冲信号。
- cnt 是一个用于产生延迟的计数器,以确保消除按钮按下时的抖动。
- key_sec_pre 和 key_sec 是用于延迟检测按钮状态变化的寄存器。
- 时钟边沿检测:
- 使用 always 块对时钟信号进行边沿检测,以便在时钟上升沿时进行操作。
- 在时钟上升沿或复位信号的负边沿时,根据 reset 信号来初始化或更新 key_rst 和 key_rst_pre 的值。
- 使用非阻塞赋值,保证在时钟的两个边沿更新两个寄存器的值。
- 按钮状态检测:
- 使用 always 块对时钟信号进行边沿检测,以便在时钟上升沿时进行操作。
- 根据复位信号初始化或更新 key_sec 的值,并通过计数器产生的延迟来确定按钮状态的变化。
- 使用 key_sec_pre 和 key_sec 寄存器来延迟检测按钮状态的变化。
- 最后,使用 assign 语句生成一个 key_pulse 信号,用于表示按钮按下的脉冲。
这段代码实现了一个简单但有效的按钮消抖功能,通过时钟信号和延迟计数器来确保稳定地检测到按钮按下的动作,并输出一个消除抖动的脉冲信号。
资源占用
运行结果
遇到的问题及解决
1.分时器分时设置问题
选择参考例程并询问chatGPT。
后期计划
1. 用小脚丫FPGA开发板去尝试实现各种数字电路通讯接口逻辑,如SPI、IIC、UART等。
2. 用FPGA实现编写数字信号处理代码,如滤波器和FFT等。
软硬件
附件下载
archive .zip
团队介绍
北京理工大学马帅
评论
0 / 100
查看更多
猜你喜欢
制作FPGA电子琴1. 存储一段音乐,并可以进行音乐播放,
2. 可以自己通过板上的按键进行弹奏,支持两个按键同时按下(和弦)并且声音不能失真,板上的按键只有13个,可以通过有上方的“上“、”下”两个按键对音程进行扩展
john
1636
2024寒假在家一起练-基于小脚丫FPGA核心板实现秒表功能该项目使用了基于Lattice MXO2的小脚丫FPGA核心板,实现了具有启动、停止、递增和清除功能的秒表的设计,它的主要功能为:1. 设计一个2位数秒表,显示范围从0.0到9.9秒,计时溢出后重新从0.0秒开始计时。 2. 使用七段数码管作为输出设备,用于在秒表上显示实时计时数值,精确度为0.1秒。 3. 提供四个按钮输入功能:开始、停止、增量和清除。按键控制秒表的启动、暂停、增量和清零功能。。
Walnut
159
2024年寒假练 - 基于小脚丫FPGA核心板 - 实现秒表基于Lattice MXO2的小脚丫FPGA核心板 - Type C接口实现制作具有启动、停止、递增和清除功能的秒表,过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。
levitate
336