任务宗旨
结合数字电路书本知识,深刻理解数字逻辑的功能实现及设计流程
培养工程化设计理念、规范化的设计流程及解决未知问题的能力
探索使用行业新工具在项目研发中存在的问题和解决方法
项目要求
使用七段显示器作为输出设备,在小脚丫FPGA核心板上创建一个2位数秒表。 秒表应从 0.0 秒计数到 9.9秒,然后翻转,计数值每0.1秒精确更新一次。
秒表使用四个按钮输入:开始、停止、增量和清除(重置)。 开始输入使秒表开始以10Hz时钟速率递增(即每0.1秒计数一次); 停止输入使计数器停止递增,但使数码管显示当前计数器值; 每次按下按钮时,增量输入都会导致显示值增加一次,无论按住增量按钮多长时间; 复位/清除输入强制计数器值为零。
设计思路
我用来完成任务的方法是首先知道如何控制七段显示器。之后,我实现了计数逻辑,计数逻辑的输出连接到两个七段显示器。为了更新计数,我使用除法器获取芯片的12mhz时钟频率,并将其除以1200000得到10hz。按钮将触发if语句,这些语句将根据其名称进行操作,如开始、停止、重置和增量
硬件框图和软件流程图
1硬件框图
2软件流程图
这个流程图考虑到了 10Hz 的时钟:
1. **初始化**: 初始化变量并为七段显示的各个段赋值。
2. **分段赋值**: 为显示数字 0 到 9 的每个段分配值。
3. **10Hz 时钟分频 (除以 1200000)**: 从 12MHz 时钟输入产生 10Hz 时钟信号。
4. **按钮按下处理**: 检查按钮的状态(重置、启动、停止、递增)并相应地更新标志位。
5. **计数逻辑**: 控制计数器的逻辑,包括基于按钮状态的条件递增。
6. **LED赋值 (阳极)**: 根据按钮状态为 LED 赋值。
7. **7段LED显示输出**: 基于计数器的值驱动七段 LED 的输出。
简单的硬件介绍
FPGA板包括4个按钮、4个开关、8个LED、2个RGB LED和2个七段显示器。该处理器的时钟频率为12mhz。
实现的功能及图片展示
这是时钟第一次由usb供电时的状态。直到按下开始按钮,计数器才计数。
按下启动按钮后,计数器开始计数
按下停止按钮后,计数器停止计数,数字保持不变
主要代码片段及说明
顶层文件 watch.v
module counter
(
input clock, // Clock input
input rst, // Reset input
input start, // Start input
input inc, // Increment input
input hold, // Hold input
output reg [8:0] seg_led_1, // 7-segment LED 1 output
output reg [8:0] seg_led_2 // 7-segment LED 2 output
);
wire hold_pulse; // Hold pulse wire
wire start_pulse; // Start pulse wire
wire inc_pulse; // Increment pulse wire
wire rst_pulse; // Reset pulse wire
reg start_flag; // Start flag register
reg [6:0] segment [9:0]; // 7-segment segment values
reg [3:0] ones; // Ones digit register
reg [3:0] tens; // Tens digit register
reg [27:0] int_clock = 28'd0; // Internal clock register
initial
begin
segment[0] = 7'h3f; // 0
segment[1] = 7'h06; // 1
segment[2] = 7'h5b; // 2
segment[3] = 7'h4f; // 3
segment[4] = 7'h66; // 4
segment[5] = 7'h6d; // 5
segment[6] = 7'h7d; // 6
segment[7] = 7'h07; // 7
segment[8] = 7'h7f; // 8
segment[9] = 7'h6f; // 9
start_flag <= 1'd0;
ones <= 4'd0;
tens <= 4'd0;
end
debounce B1 (.clk(clock), .rst(1'b1), .key(rst), .key_pulse(rst_pulse)); // Debounce module for reset button
debounce B2 (.clk(clock), .rst(1'b1), .key(start), .key_pulse(start_pulse)); // Debounce module for start button
debounce B3 (.clk(clock), .rst(1'b1), .key(hold), .key_pulse(hold_pulse)); // Debounce module for hold button
debounce B4 (.clk(clock), .rst(1'b1), .key(inc), .key_pulse(inc_pulse)); // Debounce module for increment button
always @ (posedge clock)
begin
if (rst_pulse == 1'b1) begin
start_flag <= 0;
ones <= 4'd0;
tens <= 4'd0;
end
else if(start_pulse == 1'b1)begin // if the start button is pressed, set the start flag to 1
start_flag <= 1'b1;
end
else if (hold_pulse == 1'b1)begin // if the hold button is pressed, set the start flag to 0
start_flag <= 1'b0;
end
else if (inc_pulse == 1'b1)begin // if the increment button is pressed, increment the ones digit
if(ones == 4'd9)begin
ones <= 4'd0;
if(tens== 4'd9)begin
tens <= 4'd0;
end else begin
tens <= tens + 4'd1;
end
end else begin
ones <= ones+ 4'd1;
end
seg_led_1 <= {2'b00,segment[ones]}; // display the ones digit
seg_led_2 <= {1'b1,segment[tens]}; // display the tens digit
end
if ( int_clock == 28'd1200000)begin // we check if the internal clock is equal to 1200000. because 12Mhz/10hz = 1200000
int_clock <= 28'd0; // reset the internal clock
if(start_flag == 1'b1)begin // count if the start flag is 1
if(ones == 4'd9)begin // if the ones digit is 9, reset it to 0
ones <= 4'd0;
if(tens == 4'd9)begin // if the tens digit is 9, reset it to 0
tens <= 4'd0;
end else begin
tens <= tens + 4'd1; // else increment the tens digit
end
end else begin
ones <= ones + 4'd1; // else increment the ones digit
end
seg_led_1 <= {2'b00,segment[ones]}; // display the ones digit
seg_led_2 <= {1'b1,segment[ones]}; // display the tens digit
end
end else begin
int_clock <= int_clock + 28'd1;
end
seg_led_1 <= {2'b00,segment[ones]};
seg_led_2 <= {1'b1,segment[tens]};
end
endmodule
WATCH.V 解释
1.模块声明:
-该代码定义了一个名为“计数器”的模块,该模块具有各种输入和输出。
2.电线声明:
-声明导线用于指示不同按钮(重置、启动、增量、保持)的脉冲信号。
3.注册声明:
-寄存器被声明用于各种用途,如存储段数据、标志以及1和10的当前计数值。
4.初始区块:
-初始化段数据以显示从0到9的数字。
-初始化标志和计数值。
5.退出逻辑:
-该代码为每个输入按钮(重置、启动、递增、保持)实例化去跳动模块(‘B1’、‘B2’、‘B3’、‘B4’),以避免由于机械按钮跳动而引起的多次触发。
6.始终阻止:
-该块在时钟信号的正边缘触发。
-它处理基于按钮按下和时钟滴答的各种操作。
-如果按下重置按钮(“rst_pulse”),它将重置起始标志和计数值。
-如果按下启动按钮(“start_pulse”),将设置启动标志。
-如果按下保持按钮(“hold_pulse”),则清除启动标志。
-如果按下递增按钮(“inc_pulse”),它会根据某些条件递增计数值(一和十),并相应地更新段LED输出。
-此外,还有一个基于时间的条件(“int_clock”),每1200000个时钟周期触发一次 (12Mhz / 10hz = 1200000)。在这种情况下,如果设置了启动标志,则会增加计数值并更新分段LED输出。
-计数值更新之后是分段LED输出的更新。
debounce.v
module debounce (clk,rst,key,key_pulse);
parameter N = 1; // Number of keys to debounce
input clk;
input rst;
input [N-1:0] key; // Input keys
output [N-1:0] key_pulse; // Debounced key pulses
reg [N-1:0] key_rst_pre; // Register to store previous triggered key value
reg [N-1:0] key_rst; // Register to store current triggered key value
wire [N-1:0] key_edge; // Generates a high pulse when a key transition from high to low is detected
// Using non-blocking assignments, store key states at two different clock edges
always @(posedge clk or negedge rst)
begin
if (!rst) begin
key_rst <= {N{1'b1}}; // Initialize key_rst to all ones
key_rst_pre <= {N{1'b1}};
end
else begin
key_rst <= key; // Assign current key value to key_rst on first clock edge, and assign key_rst value to key_rst_pre
key_rst_pre <= key_rst; // Non-blocking assignment. After two clock edges, key_rst stores the current key value, key_rst_pre stores the key value from the previous clock
end
end
assign key_edge = key_rst_pre & (~key_rst); // Pulse edge detection. Generates a high signal for one clock cycle when a key transition from high to low is detected
reg [17:0] cnt; // Counter used for generating delay, assuming a system clock of 12MHz, requires at least an 18-bit counter for a delay of around 20ms
// Generate a 20ms delay, reset counter to zero when key_edge is detected
always @(posedge clk or negedge rst)
begin
if(!rst)
cnt <= 18'h0;
else if(key_edge)
cnt <= 18'h0;
else
cnt <= cnt + 1'h1;
end
reg [N-1:0] key_sec_pre; // Register to store delayed key levels
reg [N-1:0] key_sec;
// Check keys after delay, generate a high pulse for one clock cycle if the key state transitions low. If the key state is high, it indicates an invalid key press
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec <= {N{1'b1}}; // Initialize key_sec to all ones
else if (cnt==18'h3ffff)
key_sec <= key;
end
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec_pre <= {N{1'b1}}; // Initialize key_sec_pre to all ones
else
key_sec_pre <= key_sec; // Store delayed key levels
end
assign key_pulse = key_sec_pre & (~key_sec); // Output debounced key pulses
endmodule
debouce.v 解释
1.模块声明:模块名为“debounce”,有四个端口:“clk”、“rst”、“key”和“key_pulse”`clk’和rst’分别是时钟和复位输入`key'是表示要去抖动的键的状态的输入阵列,而key_pulse是表示去抖动的按键脉冲的输出阵列。
2.参数声明:参数“N”定义为1,表示要反跳的键数。这可以根据设计需要处理的键的数量进行调整。
3.信号声明:
-“key_rst_pre”和“key_rst”:这些寄存器分别保存密钥的先前状态和当前状态。
-“key_edge”:此连线用于检测键何时从高电平转换为低电平。
-“cnt”:此寄存器充当计数器,用于生成延迟。它用于去抖动,假设时钟频率为12MHz。
-“key_sec_pre”和“key_sec”:这些寄存器分别保存键的延迟前一状态和当前状态。
4.始终阻止:
-第一个“总是”块对时钟(“clk”)和复位(“rst”)信号敏感。它分别用键的当前状态和先前状态更新“key_rst”和“key_rst_pre”。如果断言重置(“!rst”),则会将两个寄存器初始化为全1。
-当键从高电平转换到低电平时,第二个“始终”块在“key_edge”上产生脉冲。它对时钟和复位信号也很敏感。
-第三个“始终”块在每个时钟周期递增计数器(“cnt”),当发生重置(“rst”)或检测到键沿时将其重置为零,如果计数器达到某个值(假设为12MHz时钟,对应于大约20ms),则用当前键状态更新“key_sec”。
-第四个“始终”块负责用延迟的密钥状态更新“key_sec_pre”。它还对时钟和复位信号敏感。
5.任务:
-“key_edge”被分配在“key_rst_pre”和“key_rst”的补码之间的逐位AND运算的结果,该补码检测从高到低的转换。
-“key_pulse”被分配在“key_sec_pre”和“key_sec”的补码之间的逐位AND运算的结果,该补码表示去抖动的键脉冲。
遇到的主要难题及解决方法
我遇到过一些问题,比如WEB IDE偶尔会崩溃,或者主板更新不正确。为了解决这个问题,我更改了浏览器,我的大部分问题都得到了解决。