【问题标题】:Incrementing a seven segment display in a state machine for de1 board在 de1 板的状态机中增加七段显示
【发布时间】:2014-11-29 14:26:42
【问题描述】:

我正在使用状态机创建一个倒计时计时器,它在启动时显示 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;                  

【问题讨论】:

标签: vhdl state-machine seven-segment-display


【解决方案1】:

在 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 低但永远不会高。上升沿表示button3 上从“0”到“1”的过渡。换句话说,你的驾驶 ff3 低而已。

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

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

signal minute2 : integer range 0 to 9;

minute2state 中达到10 时,b_input 模拟将因范围错误而退出。

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

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

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

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

执行此操作的方法是 a) 消除按钮的抖动,b) 检测 clk 域中的上升沿以检测一个 clk

这一切都很复杂,因为您同时使用 'clkandclock` 作为时钟。

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

或者,您可以考虑一堆时钟 ORing,您可以在其中使用按钮来增加事物并将 clock 门控为 c_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

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

50 Mhz 除以 4000000 得到 12.5 或 8 ms 的倒数的clock 波特率。对于用于去抖 button3 的时间过滤器来说,这是一个很好的启动率:

    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 毫秒足够长的时间间隔足以消除其中一个开关的抖动。 (如果它没有添加另一个阶段并且:

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;

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

关于 DE1 按钮去抖动声明的说明

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

您仍然需要同步到clk 并生成一个clk 期间启用。所以不多。

【讨论】:

  • 感谢 David Koontz 的帮助,因为我担心我有很多事情会发生冲突,并且以我有限的经验,我发现其中一些非常复杂,难以区分。感谢您澄清其中的一些内容。一个问题,我应该将按钮 3 的 Falling_Edge 用于 ff3,因为这样做我的代码也有问题
  • 评论不会出现在搜索中,也不会使其他可能在提出自己的问题之前搜索并找到您的问题的人受益。请明确地向您添加问题。
  • 你的知识太多了,我有一个有效的代码,谢谢十亿!
猜你喜欢
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
  • 2014-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
相关资源
最近更新 更多