1

我正在使用状态机创建一个倒数计时器,在启动时显示 00:00,当按下 Key1 时,您可以通过将分钟递增/递减 1 来输入时间,并且如果按住向上/向下按钮 5 个周期它将上升/下降 5。感谢一些很棒的帮助(@DavidKoontz),我已经完成了代码。无需对代码 b/c 中的按钮进行去抖动处理,我的 altera 板似乎可以很好地接收低信号。由于我只使用一个时钟,因此按钮反应缓慢 b/c,时钟设置为 1Hz。

Library ieee;
USE ieee.std_logic_1164.ALL;
ENTITY CountDownTimer IS
PORT(
CLK,RESET: IN STD_LOGIC;
a1, b1, c1, d1, e1, f1, g1 : OUT STD_LOGIC;
a2, b2, c2, d2, e2, f2, g2 : OUT STD_LOGIC;
a3, b3, c3, d3, e3, f3, g3 : OUT STD_LOGIC;
a4, b4, c4, d4, e4, f4, g4 : OUT STD_LOGIC;
--All 4 buttons for timer
BUTTON0, BUTTON1, BUTTON2, BUTTON3: IN STD_LOGIC;
--LEDR9
ON_OFF_LED: OUT BIT;
--LEDR9-R6
INPUT_LED1, INPUT_LED2, INPUT_LED3, INPUT_LED4: OUT BIT;
--LEDR0
DONE_LED: OUT BIT);
END CountdownTimer;
ARCHITECTURE Counter OF CountDownTimer IS
--Define state machine
TYPE STATE_TYPE IS (A_ON_OFF, B_INPUT, C_COUNTDOWN, D_DONE);
SIGNAL State : STATE_TYPE;
--Buttons produce 0 when pressed, signal for 1 when pressed
SIGNAL B3D0, B3D1, B3D2, B3D3: STD_LOGIC := '0';
SIGNAL B2D0, B2D1, B2D2, B2D3: STD_LOGIC := '0';
SIGNAL CLOCK: STD_LOGIC := '0';
SIGNAL Count: INTEGER:= 1;
--SIGNAL for range of integer values
SIGNAL Minute1 : INTEGER RANGE 0 TO 6;
SIGNAL Minute2 : INTEGER RANGE 0 TO 9;
SIGNAL Second1 : INTEGER RANGE 0 TO 5;
SIGNAL Second2 : INTEGER RANGE 0 TO 9;
--Output for the seven segment displays
SIGNAL OUTPUT_HEX0 :  STD_LOGIC_VECTOR(6 DOWNTO 0);
SIGNAL OUTPUT_HEX1 :  STD_LOGIC_VECTOR (6 DOWNTO 0);
SIGNAL OUTPUT_HEX2 :  STD_LOGIC_VECTOR (6 DOWNTO 0);
SIGNAL OUTPUT_HEX3 :  STD_LOGIC_VECTOR (6 DOWNTO 0);
SIGNAL B3_HOLD: STD_LOGIC := '0'; --Gets 1 if button3 was held
SIGNAL B2_HOLD: STD_LOGIC := '0'; --Gets 1 is button2 was held

BEGIN

             --Segment 1 display pins
             a1 <= OUTPUT_HEX0(6);
             b1 <= OUTPUT_HEX0(5);
             c1 <= OUTPUT_HEX0(4);
             d1 <= OUTPUT_HEX0(3);
             e1 <= OUTPUT_HEX0(2);
             f1 <= OUTPUT_HEX0(1);
             g1 <= OUTPUT_HEX0(0);
             --Segment 2 display pins
             a2 <= OUTPUT_HEX1(6);
             b2 <= OUTPUT_HEX1(5);
             c2 <= OUTPUT_HEX1(4);
             d2 <= OUTPUT_HEX1(3);
             e2 <= OUTPUT_HEX1(2);
             f2 <= OUTPUT_HEX1(1);
             g2 <= OUTPUT_HEX1(0);
             --Segment 3 display pins
             a3 <= OUTPUT_HEX2(6);
             b3 <= OUTPUT_HEX2(5);
             c3 <= OUTPUT_HEX2(4);
             d3 <= OUTPUT_HEX2(3);
             e3 <= OUTPUT_HEX2(2);
             f3 <= OUTPUT_HEX2(1);
             g3 <= OUTPUT_HEX2(0);
             --Segment 4 display pins
             a4 <= OUTPUT_HEX3(6);
             b4 <= OUTPUT_HEX3(5);
             c4 <= OUTPUT_HEX3(4);
             d4 <= OUTPUT_HEX3(3);
             e4 <= OUTPUT_HEX3(2);
             f4 <= OUTPUT_HEX3(1);
             g4 <= OUTPUT_HEX3(0);

            WITH Second2 SELECT
            --One's second place, 0 to 9
            OUTPUT_HEX0 <= "0000001" WHEN 0,
                                "1001111" WHEN 1,
                                "0010010" WHEN 2,
                                "0000110" WHEN 3,
                                "1001100" WHEN 4,
                               "0100100" WHEN 5,
                                "0100000" WHEN 6,
                                "0001101" WHEN 7,
                                "0000000" WHEN 8,
                                "0001100" WHEN 9,
                                "0000001" WHEN OTHERS;
            WITH Second1 SELECT
            --Tens second place, 0 to 5
            OUTPUT_HEX1 <= "0000001" WHEN 0,
                                "1001111" WHEN 1,
                                "0010010" WHEN 2,
                                "0000110" WHEN 3,
                                "1001100" WHEN 4,
                               "0100100" WHEN 5,
                                "0000001" WHEN OTHERS;
            WITH Minute2 SELECT
            --Ones minute place, 0 to 9
            OUTPUT_HEX2 <= "0000001" WHEN 0,
                                "1001111" WHEN 1,
                                "0010010" WHEN 2,
                                "0000110" WHEN 3,
                                "1001100" WHEN 4,
                               "0100100" WHEN 5,
                               "0100000" WHEN 6,
                                "0001101" WHEN 7,
                                "0000000" WHEN 8,
                                "0001100" WHEN 9,
                                "0000001" WHEN OTHERS;
            WITH Minute1 SELECT
            --Tens minute place, 0 to 6
            OUTPUT_HEX3 <= "0000001" WHEN 0,
                                "1001111" WHEN 1,
                                "0010010" WHEN 2,
                                "0000110" WHEN 3,
                                "1001100" WHEN 4,
                               "0100100" WHEN 5,
                               "0100000" WHEN 6,
                                "0000001" WHEN OTHERS;


PROCESS(CLK)
    BEGIN
        IF RISING_EDGE(CLK) THEN 
            Count <= Count + 1;
            IF (Count = 30000000) THEN
                CLOCK <= NOT(CLOCK);
                Count <= 1;
            END IF;
        END IF; 
END PROCESS;

PROCESS(CLOCK)
BEGIN
    IF RISING_EDGE(CLOCK) THEN
        B3D0 <= BUTTON3;
        B3D1 <= NOT B3D0;
        B3D2 <= B3D1;
        B3D3 <= B3D2;
        B2D0 <= BUTTON2;
        B2D1 <= NOT B2D0;
        B2D2 <= B2D1;
        B2D3 <= B2D2;
        B3_HOLD <= B3D1 AND B3D2 AND B3D3;
        B2_HOLD <= B2D1 AND B2D2 AND B2D3;
    END IF;
    END PROCESS;

PROCESS(CLOCK)
BEGIN       
IF RESET = '1' THEN --Async Reset
            State <= A_ON_OFF;
ELSIF RISING_EDGE(CLOCK) THEN

    CASE State IS
---------------------------------A_ON_OFF---------------------------------  
        WHEN A_ON_OFF =>
            --Red LED9
            ON_OFF_LED <= '1';
            Minute1 <= 0;
            Minute2 <= 0;
            Second1 <= 0;
            Second2 <= 0;
            IF (BUTTON0 = '0') THEN
                                    ON_OFF_LED <= '0';
                                    State <= B_INPUT;
            END IF; 
---------------------------------B_INPUT/PAUSE---------------------------------                 

        WHEN B_INPUT =>

            --Light up LEDs
            INPUT_LED1 <= '1';
            INPUT_LED2 <= '1';
            INPUT_LED3 <= '1';
            INPUT_LED4 <= '1';
            IF (Minute1 = 6) THEN
                Minute2 <= 0;
                Second1 <= 0;
                Second2<= 0;
                State <= B_INPUT;
            END IF;
            --Count up button   
            IF (BUTTON3 = '0' AND B3_HOLD = '0') THEN   
                IF (Minute1 = 6 AND Minute2 >= 0) THEN
                    Minute1 <= 0;
                    Minute2 <= 1;
                    Second1 <= 0;
                    Second2 <= 0;
                        State <= B_INPUT;
                ELSIF (Minute2 < 9) THEN
                    Minute2 <= (Minute2 + 1);
                    State <= B_INPUT;
                ELSIF (Minute2 = 9) THEN
                    Minute1 <= (Minute1 + 1);
                    Minute2 <= 0;
                    State <= B_INPUT;
                END IF;
            END IF;
            IF (BUTTON3 = '0' AND B3_HOLD = '1') THEN   
                IF (Minute1 = 6 AND Minute2 >= 0) THEN
                        Minute1 <= 0;
                        Minute2 <= 5;
                        Second1 <= 0;
                        Second2 <= 0;
                        State <= B_INPUT;
                ELSIF (Minute2 < 5) THEN
                    IF (Minute2 = 0) THEN
                        Minute2 <= (Minute2 + 5);
                        State <= B_INPUT;
                    ELSE
                        Minute2 <= (Minute2 + 1);
                        State <= B_INPUT;
                    END IF;
                ELSIF (Minute2 = 5) THEN
                    Minute2 <= 0;
                    Minute1 <= (Minute1 + 1);
                    State <= B_INPUT;
                ELSIF (Minute2 > 5) THEN
                    IF (Minute2 = 9) THEN
                        Minute2 <= 0;
                        Minute1 <= (Minute1 + 1);
                        State <= B_INPUT;
                    ELSE
                        Minute2 <= (Minute2 + 1);
                        State <= B_INPUT;
                    END IF;
                END IF;
            END IF;
            --Count down button
            IF (BUTTON2 = '0' AND B2_HOLD = '0') THEN
                IF ((Minute1 = 0) AND (Minute2 = 0)) THEN
                    Minute1 <= 6;
                    Minute2 <= 0;
                    Second1 <= 0;
                    Second2 <= 0;
                ELSIF (Minute2 = 0) THEN
                    Minute2 <= 9;
                    Minute1 <= (Minute1 - 1);
                    ELSE 
                        Minute2 <= (Minute2 - 1);
                END IF;
                State <= B_INPUT;
            END IF;
            IF (BUTTON2 = '0' AND B2_HOLD = '1') THEN
                IF (Minute1 = 0 AND Minute2 = 0) THEN
                        Minute1 <= 6;
                        Minute2 <= 0;
                        Second1 <= 0;
                        Second2 <= 0;
                        State <= B_INPUT;
                ELSIF (Minute2 = 0 AND Minute1 > 0) THEN
                    Minute1 <= (Minute1 - 1);
                    Minute2 <= 5;
                    State <= B_INPUT;
                ELSIF (Minute2 < 5) THEN
                    IF (Minute2 = 0) THEN
                        Minute1 <= (Minute1 - 1);
                        Minute2 <= 5;
                        State <= B_INPUT;
                    ELSE
                        Minute2 <= (Minute2 - 1);
                        State <= B_INPUT;
                    END IF;
                ELSIF (Minute2 = 5) THEN
                    Minute2 <= (Minute2 - 5);
                    State <= B_INPUT;
                ELSIF (Minute2 > 5) THEN
                    Minute2 <= (Minute2 - 1);
                    State <= B_INPUT;
                END IF;
            END IF;
            --Clear button
            IF (BUTTON1 = '0') THEN
                Minute1 <= 0;
                Minute2 <= 0;
                Second1 <= 0;
                Second2 <= 0;
                State <= B_INPUT;
            END IF;
            --Start Button
            IF (BUTTON0 = '0') THEN
                                    --Turn off LEDs
                                    INPUT_LED1 <= '0';
                                    INPUT_LED2 <= '0';
                                    INPUT_LED3 <= '0';
                                    INPUT_LED4 <= '0';
                                    State <= C_COUNTDOWN;
            END IF;
---------------------------------C_COUNTDOWN---------------------------------                       

            WHEN C_COUNTDOWN =>

            IF (Second2 > 0) THEN
                Second2 <= (Second2 - 1);
            ELSIF (Second2 = 0) THEN
                IF (Second1 > 0) THEN
                    Second2 <= 9;
                    Second1 <= (Second1 - 1);
                ELSIF (Second1 = 0) THEN
                    IF (Minute2 > 0) THEN
                        Second1 <= 5;
                        Minute2 <= (Minute2 - 1);
                        IF (Second2 = 0) THEN
                            Second2 <= 9;
                        END IF;
                    ELSIF (Minute2 = 0) THEN
                            IF (Minute1 > 0) THEN
                                IF (Second1 = 0) THEN
                                    Second1 <= 5;
                                    Minute2 <= (Minute2 - 1);
                                    IF (Second2 = 0) THEN
                                        Second2 <= 9;
                                    END IF;
                                END IF;
                                Minute2 <= 9;
                                Minute1 <= (Minute1 - 1);
                            ELSIF (Minute1 <= 0) THEN
                                                        State <= D_DONE;
                            END IF;
                    END IF;
                END IF;
            END IF;
            --Reset Button
            IF (BUTTON1 = '0') THEN
                                        STATE <= A_ON_OFF;
            --Input button
            ELSIF (BUTTON0 = '0') THEN
                                            STATE <= B_INPUT;
            END IF;
---------------------------------D_DONE---------------------------------                        

            WHEN D_DONE =>
            --LEDR0
            DONE_LED <= '1';
                --Reset button
                IF (BUTTON1 = '0') THEN
                                            DONE_LED <= '0';
                                            STATE <= A_ON_OFF;

                --Input button
                ELSIF (BUTTON0 = '0') THEN
                                                DONE_LED <= '0';
                                                STATE <= B_INPUT;
                END IF;                  
        END CASE;
END IF;
END PROCESS;

END Counter;                  
4

1 回答 1

0

在 case 语句中的选择when b_input中,您将为minute2每一个clk button3低值递增,这是一个启用。它需要转换为事件,除非你指望一个真正的慢clk(注意你也在产生一个慢clock)。此启用与 不同步clk,它可能导致建立或保持时间违规的亚稳态。

进行中

PROCESS(BUTTON3)
BEGIN   
IF RISING_EDGE(BUTTON3) THEN
FF3 <= NOT(BUTTON3);
END IF;
END PROCESS;

这将分配ff3低但从不高。上升沿表示从“0”到“1”的过渡button3。换句话说,您ff3只能低速行驶。

这就是为什么您的计数器不会改变的原因。除了潜在的亚稳态问题之外,还应该为单个clk.

对于仿真整数范围限制计数器(例如minute2)将溢出。VHDL 不做范围限制的模运算。

signal minute2 : integer range 0 to 9;

模拟中的minute2到达时将由于范围错误而退出。10state b_input

选择的增量b_input应该类似于:

                    if minute2 = 9 then
                        minute2 <= 0;
                    else
                        minute2 <= minute2 + 1;
                    end if;

(并且有一个微妙的提示你应该模拟这个东西。)

您需要使用clk按钮中的单个启用来递增计数器。

clk做到这一点的方法是 a) 去抖按钮,b) 检测域中的上升沿one clk

这一切都很复杂,因为您同时使用“clk andclock”作为时钟。

关于如何实现去抖动、边沿检测和单次clk启用的建议取决于您的实际时钟速率。

或者,您可以考虑一堆时钟 ORing,您可以在其中使用按钮来增加事物并clockc_countdown.

您没有在实体声明之前包含上下文子句:

library ieee;   
use ieee.std_logic_1164.all;

一个问题,我是否应该将按钮 3 的 Falling_Edge 用于 ff3,因为这样做我的代码也有问题

如果您遇到下降沿问题,您似乎也可能遇到上升沿问题。所有问题的根源很可能是接触反弹。

不,我不提倡在这种情况下使用按钮 3 的下降沿。

这里的问题是从按钮 3 产生单个时钟启用(或者将其用作时钟)。它还需要去抖动。开关或按钮弹跳的持续时间取决于几件事——触点的质量、操作的难度以及开关臂的弹性。

有几种方法可以摆脱反弹。例如,您可以使用一对常开和常闭触点来操作 RS 锁存器,这需要两个开关或按钮信号处于相反的二进制状态。您可以临时过滤(时钟间隔在 10 毫秒范围内)需要 N 个稳定样本。这也过滤掉了从异步域到同步(时钟)域的亚稳态。

要将 FF3 的等效项用作 的启用clk,您需要对按钮进行去抖动,并检测“clk”域中的上升沿。

因为您的按钮几乎可以保证是单极开关,所以您需要某种时间过滤。如果时钟与“clk”相关,您可以简单地使用计数器产生的长间隔启用来连续采样按钮。这允许您在域中使用额外的触发器clk来检测它曾经是低电平而现在是高电平,现在使用的那个门的输出FF3

如您所说,如果clk是 50 MHz,则去抖动可能需要从Count计数器生成启用。

让我们称之为debounce_en

请注意,这是设置为在按钮 3 的下降沿之后的某个时间进行采样。一个旧的高点和一个或两个连续的低点。这是 button3 下降后的毫秒数,所以它应该是安全的。

50 Mhz 除以 4000000 得出clock12.5 或 8 ms 的倒数的波特率。对于用于去抖按钮 3 的时间滤波器来说,这是一个很好的启动率:

    signal debounce_en: std_logic;
    signal button3_d0, button3_d1, button3_d2:

    begin

...

UNLABELLED1:
    process(clk)
    begin
        if rising_edge(clk) then 
            debounce_en <= '0';  -- one ping only.
            count <= count + 1;
            if count = 4000000 then
                clock <= not clock;
                debounce_en <= '1';  -- for one clk every 4000000
                count <= 1;
            end if;
        end if; 

    end process;

我们可以使用debounce_en采样button3而不是生产FF3

process (clk)
begin
    if rising_edge(clk)  and debounce_en = '1' then
        button3_d0 <= button3;
        button3_d1 <= button3_d0;
        button3_d2 <= button3_d1;
     end if;


button3_enable <= not button3_d1 and button3_d2 and debounce_en;

这会起作用的原因是,很可能 8 ms 足够长的时间间隔来消除其中一个开关的抖动。(如果不添加另一个阶段并且:

button3_enable <= not_button3_d1 and not button3_d2 and button3_d3 and debounce_en;

这要求三个连续的触发器(30 ms)不会捕获“振铃”。

'button3_enable 将用于代替FF3,所有三个(或更多)新信号都是 std_logic 类型,触发器用作移位寄存器。

使用需要长时间已知值的时间过滤器可能会过滤掉按钮上的短脉冲,您很难按那么快地脉冲按钮。

至于在case语句中操作`minute2:

            when b_input =>
                if button3_enable = '1' then  -- count up button 
                    if minute2 = 9 then
                        minute2 <= 0;
                    else
                        minute2 <= minute2 + 1;
                    end if;
                    input_led <= '1';  --green led7
                end if;

如果您更改' 的时钟频率(我很想自己让它实时运行),您将如何从中获得debounce_en可能会发生变化。您没有完成 C_COUNTDOWN,但看起来理想情况下它会以一秒的速率启用并具有级联的秒和分钟计数器。Countclock

关于 DE1 声称去抖动按钮的注释

我找到了一份 DE1-SoC 板手册,在图 3-14 和第 3.6.1 节中,它声称使用 74HC245 施密特触发缓冲器(名义上是双向的,无疑是单向使用的)足以将按钮用作时钟。这将取决于电路板布局、其中一个按钮的电容和电感、上拉电阻器的大小,如果他们这么说......(它很可能有效,他们已经在几代电路板设计中提供了它),这对上述描述有何影响?

好吧,您仍然需要同步clk并生成一个clk周期启用。所以不多。

于 2014-11-29T20:58:02.620 回答