2024寒假练—基于小脚丫核心板实现的具有启动、停止、递增和清除功能的秒表
通过小脚丫FPGA核心板上的2个数码管和轻触按键制作一个秒表,通过按键来控制秒表的功能,并在数码管上显示数值。
标签
FPGA
1820201030-艾比
更新2024-04-02
北京理工大学
93

任务宗旨


结合数字电路书本知识,深刻理解数字逻辑的功能实现及设计流程

培养工程化设计理念、规范化的设计流程及解决未知问题的能力

探索使用行业新工具在项目研发中存在的问题和解决方法


项目要求


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

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


设计思路


我用来完成任务的方法是首先知道如何控制七段显示器。之后,我实现了计数逻辑,计数逻辑的输出连接到两个七段显示器。为了更新计数,我使用除法器获取芯片的12mhz时钟频率,并将其除以1200000得到10hz。按钮将触发if语句,这些语句将根据其名称进行操作,如开始、停止、重置和增量


硬件框图和软件流程图


1硬件框图


image.png



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偶尔会崩溃,或者主板更新不正确。为了解决这个问题,我更改了浏览器,我的大部分问题都得到了解决。


附件下载
watch.v
debounce.v
implement.jed
团队介绍
北京理工大学
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号