按键防抖动电路编码器的VHDL实现(有限状态机设计方法总结)

        本文首先通过一个具体的问题入手,然后对有限状态机的设计进行分析和总结。

一、题目说明

        下图是键盘编码和防抖动控制电路,一般采用扫描和轮询的方法来检测对键盘的敲击,这样可以减少键盘与主电路之间的连线数量。该电路每次将outp0、outp1、outp2中的一个置‘0’,然后读入inp1、inp2、inp3、inp4,而其他行输入inp仍保持为1。

设计的电路需要完成:

①编码功能:每个数字必须被编为ASCII码。编码电路通过将new_data置为‘1’,表示电路检测到一次键盘敲击并输出有效编码。这种做法可以避免长期按下一个键而出现一长串相同的数字。

②防抖动电路:机械开关存在开关抖动问题,即开关在形成最终稳定接触之前的不稳定状态,其持续时间一般有几个ms。因此,时钟的频率非常重要,选择一个时钟频率使得5ms内至少进行3次读取操作,只有在5ms内的读数相同时,才会认为该键被触发。

二、题目分析

分析题目可知,我们需要设计的电路应满足以下特征:

1)功能:实现按键防抖动电路编码器,可以将状态分为轮询状态一(ROLL_POLING_1)、轮询状态二(ROLL_POLING_2)、轮询状态三(ROLL_POLING_3)、触发状态(TRIGGER)、输出状态(OUTPUT)。

2)端口:输入时钟CLK、复位信号RST、四位宽按键输入INP,三位宽轮询输出OUTP、按键有效输出信号NEW_DATA、七位宽按键编码信号DATA。

3)触发方式:时钟上升沿触发,RST异步且高电平有效。

4)时序解释说明:

        1>触发电路复位后,默认进入轮询状态一(ROLL_POLING_1)。

        2>时钟上升时若处于轮询状态一、轮询状态二或轮询状态三(ROLL_POLING_1、ROLL_POLING_2、ROLL_POLING_3),分别输出对应的OUTP。如果INP符合按键触发则进入触发状态(TRIGGER),如果INP不符合按键触发则进入对应的下一轮询状态。

        3>时钟上升时若处于触发状态(TRIGGER),判断INP是否为按键触发,若是,则计数累加,若否,则计数清零,且进入轮询状态一(ROLL_POLING_1)。当计数为3时,进入输出状态(OUTPUT)。

        4>时钟上升时若处于输出状态(OUTPUT),进入轮询状态一(ROLL_POLING_1)。

        5>无论何时,若为输出状态(OUTPUT)则NEW_DATA置‘1’,同时输出编码后的DATA。

状态转移图如下:
 

三、实现与总结

        对于这个问题可以使用有限状态机来描述,一般情况下,我们可以选择使用三进程来描述有限状态机,这样的结构清晰,可读性很高。但是对于本问题来说,这种方法并不适用。

        首先,先介绍一下描述有限状态机的三种方法,即单进程描述、双进程描述和三进程描述。

        根据功能来分,有限状态机由三部分结构组成,分别是由输入信号和现态决定的次态逻辑、由时钟、次态和复位信号控制的状态寄存器、由现态(或输入)决定的输出逻辑

        大家平时见到的有限状态机通常被分为两部分,即组合逻辑部分和时序逻辑部分。这两者都是对有限状态机的描述,一般来说,次态逻辑和输出逻辑就是组合逻辑电路,状态寄存就是时序逻辑电路。

        对于三进程描述,次态逻辑、状态寄存器、输出逻辑各自分别由一个进程块来描述,因此称之为三进程描述。对于双进程描述,将次态逻辑、状态寄存器合并为一个进程块(状态转移块)来描述,因此称之为双进程描述。对于单进程描述,将三部分合并为一个块来描述,这种方法很少见,只有当问题简单时才会使用,当问题复杂时,单进程描述的代码可读性大大下降。

        通过上面的描述,我们需要明确双进程和三进程的区别。

三进程描述:次态逻辑和输出逻辑都是组合逻辑,他们不需要时钟和复位信号,他们的进程敏感触发列表是现态和输入。举个例子,在某个现态下,输入突然改变,此时次态逻辑块会被触发,根据新的输入改变次态,然后等到下个时钟沿,现态才发生变化。这会存在状态转移的延迟。这当然是必要的,这可以使得状态的转移是时钟同步的。

以下是三进程描述的代码模板:

---------------三进程描述-----------------

----------------STATE_REG----------------
PROCESS(CLK,RST)BEGIN                          --根据时钟和复位,决定现态的转移(时序逻辑)
    CASE RST IS
        WHEN '1'=>
            PR_STATE<=INITIAL;                 --复位时,将现态赋为初始态
        WHEN '0'=>
            IF CLK'EVENT AND CLK='1'THEN       --时钟上升沿时,将次态赋给现态,完成状态的转移
                PR_STATE<=NX_STATE;
            END IF;
        WHEN OTHERS=>NULL;
    END CASE;
END PROCESS;
--------------NX_STATE_LOGIC-------------
PROCESS(PR_STATE,INPUT)BEGIN                   --根据现态和输入,决定次态(组合逻辑)
    CASE PR_STATE IS
        WHEN INITIAL=>    IF INPUT='1'THEN
                              NX_STATE<=STATE_1;
                          ELSE 
                              NX_STATE<=INITIAL;
                          END IF;
        WHEN STATE_1=>    NX_STATE<=STATE_2;
        WHEN STATE_2=>    NX_STATE<=STATE_3;
        WHEN STATE_3=>    NX_STATE<=STATE_4;
        WHEN STATE_4=>    NX_STATE<=INITIAL;
    END CASE;
END PROCESS;
----------------OUTPUT_LOGIC-------------
PROCESS(PR_STATE)BEGIN                         --根据现态,决定输出(组合逻辑)
    CASE PR_STATE IS
        WHEN INITIAL=>    OUTPUT<=‘1’;
        WHEN STATE_1=>    OUTPUT<=‘0’;
        WHEN STATE_2=>    OUTPUT<=‘1’;
        WHEN STATE_3=>    OUTPUT<=‘0’;
        WHEN STATE_4=>    OUTPUT<=‘1’;
    END CASE;
END PROCESS;

双进程描述:此时没有次态和现态的区分,而统一用状态来描述。每次时钟沿触发时,根据输入和状态来决定下一状态和输出,这里的输出可以只和状态有关(Moore),也可以和输入、状态有关(Mealy)。这种描述相比三进程有十分重要的区别,首先避免了现态和次态的描述,其次可以对于时钟触发的输入信号进行描述,从而使得代码简洁并且符合问题要求。有些问题的输入信号是和时钟有关的(例如序列的检测),这种情况下,使用三进程描述会略微麻烦一些。这是因为我们需要把所有出现的状态划分清楚,以避免次态逻辑因为没有信号发生变化而不会触发执行,导致程序不符合预期。

以下是双进程描述的代码模板:

---------------双进程描述-----------------

----------------STATE_TRANFER----------------
PROCESS(CLK,RST)BEGIN                      --根据时钟、复位和输入,决定状态的转移(时序逻辑)
    CASE RST IS
        WHEN '1'=>
            STATE<=INITIAL;                --复位时,将现态赋为初始态
        WHEN '0'=>
            IF CLK'EVENT AND CLK='1'THEN   --时钟上升沿时,根据状态、输入,完成状态的转移
                CASE STATE IS
                    WHEN INITIAL=>    IF INPUT='1'THEN
                                          STATE<=STATE_1;
                                      ELSE 
                                          STATE<=INITIAL;
                                      END IF;
                    WHEN STATE_1=>    STATE<=STATE_2;
                    WHEN STATE_2=>    STATE<=STATE_3;
                    WHEN STATE_3=>    STATE<=STATE_4;
                    WHEN STATE_4=>    STATE<=INITIAL;
                END CASE;
            END IF;
        WHEN OTHERS=>NULL;
    END CASE;
END PROCESS;
----------------OUTPUT_LOGIC-------------
PROCESS(STATE)BEGIN                        --根据状态,决定输出(组合逻辑)
    CASE STATE IS
        WHEN INITIAL=>    OUTPUT<=‘1’;
        WHEN STATE_1=>    OUTPUT<=‘0’;
        WHEN STATE_2=>    OUTPUT<=‘1’;
        WHEN STATE_3=>    OUTPUT<=‘0’;
        WHEN STATE_4=>    OUTPUT<=‘1’;
    END CASE;
END PROCESS;

        以本题为例,我们将状态分为了轮询状态一(ROLL_POLING_1)、轮询状态二(ROLL_POLING_2)、轮询状态三(ROLL_POLING_3)、触发状态(TRIGGER)、输出状态(OUTPUT)这5个状态,但其实这只是按功能完成的划分,事实上TRIGGER中还可以划分出若干个状态,这些状态的数量取决于需要计数的次数,使用双进程描述时,五个状态足矣,但是如果使用三进程描述,次态逻辑块就比较复杂了。有的同学可能会想,既然输入信号是和时钟有关的,那为什么我们不把次态逻辑块的敏感列表中加入时钟信号,使得每次时钟到来时,都能改变次态呢?这样的话我们避免了因为没有信号发生变化而不会触发执行,也可以使用五个状态来完成,那这样真的可行吗?

        答案是否定的。在这里我们忽略了一个重要的问题,那就是VHDL、Verilog的块与块之间都是并行执行的。为了更好的描述,我们分为几个步骤来描述,假设复位后的现态是IDLE。状态转移是IDLE--A--B--IDLE

        ①首先,因为复位,所以现态是IDLE。然后因为现态发生了变化,所以次态逻辑被触发,次态被赋为了状态A。

        ②等待直到下一个时钟上升沿,状态寄存块被触发,现态被赋为A,与此同时,次态逻辑因为时钟上升被触发,它会根据现态来决定次态,但是这里用来决定次态的现态是IDLE,而不是A;因为状态寄存和次态逻辑这两个块是同时进行的,因此次态依然被赋为A。

        ③在这之后因为现态被赋为了A,我们期望的是再次触发次态逻辑块,然后将次态赋为B。但这实际上并不会发生,因为一个块在一个时间点只能触发一次,也就是说,这里次态逻辑块的敏感列表是(PR_STATE,CLK)与(CLK)是一样的效果。因此次态依然是A。

        ④等到下一个时钟上升沿到来,现态还被赋为A,次态被赋为B,所以在下一个时钟上升到来之前,现态还是A。也就是说,这导致了现态持续了两个时钟周期的A状态,之后才转移。

        第一副图中的次态逻辑采用了(PR_STATE,CLK)的敏感列表,第二副图中的次态逻辑采用了(PR_STATE,INPUT)的敏感列表。可以看到第一幅图中的方法使得状态持续了两个时钟周期,这是不期望看到的结果。

        综上所述,对于序列的描述、计数累加来说,三进程描述会更加复杂。因为我们更倾向于将状态按功能划分,计数显然是一个高度一致的行为,我们可以使用变量做累加,用if做判断,这样更加简单。如果非要使用三进程描述,我们也还可以通过增加信号(SIGNAL)来描述子状态。

        为了简单起见,我们使用双进程描述来解决这个问题。下面是双进程描述代码:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Anti_shake_circuit_encoder is
    Port (INP:  IN  STD_LOGIC_VECTOR(3 DOWNTO 0);
      CLK,RST:  IN  STD_LOGIC;
         OUTP:  OUT STD_LOGIC_VECTOR(2 DOWNTO 0);
         DATA:  OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
     NEW_DATA:  OUT STD_LOGIC 
             );
end Anti_shake_circuit_encoder;

architecture Behavioral of Anti_shake_circuit_encoder is
    TYPE STATE_TPYE IS (ROLL_POLING_1,ROLL_POLING_2,ROLL_POLING_3,TRIGGER,OUTPUT);
    TYPE TRIGGER_POLING_STATE_TYPE IS (IDLE,TRIGGER_POLING_1,TRIGGER_POLING_2,TRIGGER_POLING_3);
    SIGNAL STATE:STATE_TPYE;
    SIGNAL KEY:INTEGER RANGE 1 TO 4;
    SIGNAL TRIGGER_POLING:TRIGGER_POLING_STATE_TYPE;
begin
--------------------------STATE_TRANSFER-------------------------------
    STATE_TRANSFER:PROCESS(CLK,RST)
    VARIABLE COUNT:INTEGER RANGE 0 TO 3;
    BEGIN
        CASE RST IS
            WHEN '1'=>
                STATE<=ROLL_POLING_1;
                TRIGGER_POLING<=IDLE;
                COUNT:=0;
            WHEN '0'=>
                IF CLK'EVENT AND CLK='1'THEN
                    CASE STATE IS
                        WHEN ROLL_POLING_1=>
                            COUNT:=1;
                            CASE INP IS
                                WHEN "0111"=> STATE<=TRIGGER;    KEY <=1;    TRIGGER_POLING<=TRIGGER_POLING_1;
                                WHEN "1011"=> STATE<=TRIGGER;    KEY <=2;    TRIGGER_POLING<=TRIGGER_POLING_1;
                                WHEN "1101"=> STATE<=TRIGGER;    KEY <=3;    TRIGGER_POLING<=TRIGGER_POLING_1;
                                WHEN "1110"=> STATE<=TRIGGER;    KEY <=4;    TRIGGER_POLING<=TRIGGER_POLING_1;
                                WHEN OTHERS => STATE<=ROLL_POLING_2;COUNT:=0;
                            END CASE;
                        WHEN ROLL_POLING_2=>
                            COUNT:=1;
                            CASE INP IS
                                WHEN "0111"=> STATE<=TRIGGER;    KEY <=1;    TRIGGER_POLING<=TRIGGER_POLING_2;
                                WHEN "1011"=> STATE<=TRIGGER;    KEY <=2;    TRIGGER_POLING<=TRIGGER_POLING_2;
                                WHEN "1101"=> STATE<=TRIGGER;    KEY <=3;    TRIGGER_POLING<=TRIGGER_POLING_2;
                                WHEN "1110"=> STATE<=TRIGGER;    KEY <=4;    TRIGGER_POLING<=TRIGGER_POLING_2;
                                WHEN OTHERS => STATE<=ROLL_POLING_3;COUNT:=0;
                            END CASE;
                        WHEN ROLL_POLING_3=>
                            COUNT:=1;
                            CASE INP IS
                                WHEN "0111"=> STATE<=TRIGGER;    KEY <=1;    TRIGGER_POLING<=TRIGGER_POLING_3;
                                WHEN "1011"=> STATE<=TRIGGER;    KEY <=2;    TRIGGER_POLING<=TRIGGER_POLING_3;
                                WHEN "1101"=> STATE<=TRIGGER;    KEY <=3;    TRIGGER_POLING<=TRIGGER_POLING_3;
                                WHEN "1110"=> STATE<=TRIGGER;    KEY <=4;    TRIGGER_POLING<=TRIGGER_POLING_3;
                                WHEN OTHERS => STATE<=ROLL_POLING_1;COUNT:=0;
                            END CASE;
                        WHEN TRIGGER=>
                             CASE INP IS
                                WHEN "0111"=> 
                                    IF KEY=1 THEN COUNT:=COUNT+1;ELSE STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;END IF;
                                    IF COUNT=3 THEN STATE<=OUTPUT;END IF;
                                WHEN "1011"=>
                                    IF KEY=2 THEN COUNT:=COUNT+1;ELSE STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;END IF;
                                    IF COUNT=3 THEN STATE<=OUTPUT;END IF;
                                WHEN "1101"=>
                                    IF KEY=3 THEN COUNT:=COUNT+1;ELSE STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;END IF;
                                    IF COUNT=3 THEN STATE<=OUTPUT;END IF;
                                WHEN "1110"=>
                                    IF KEY=4 THEN COUNT:=COUNT+1;ELSE STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;END IF;
                                    IF COUNT=3 THEN STATE<=OUTPUT;END IF;
                                WHEN OTHERS =>STATE<=ROLL_POLING_1;COUNT:=0;
                             END CASE;
                        WHEN OUTPUT=>
                            STATE<=ROLL_POLING_1;COUNT:=0;TRIGGER_POLING<=IDLE;
                    END CASE;
                END IF;
            WHEN OTHERS=>NULL;
        END CASE;
    END PROCESS;

------------------------------OUTPUT_LOGIC----------------------------- 
    OUTPUT_LOGIC_OUTP:PROCESS(STATE)
    BEGIN
        CASE STATE IS 
            WHEN ROLL_POLING_1=> OUTP<="110";
            WHEN ROLL_POLING_2=> OUTP<="101";
            WHEN ROLL_POLING_3=> OUTP<="011";
            WHEN TRIGGER=>
                CASE TRIGGER_POLING IS
                    WHEN TRIGGER_POLING_1=> OUTP<="110";
                    WHEN TRIGGER_POLING_2=> OUTP<="101";
                    WHEN TRIGGER_POLING_3=> OUTP<="011";
                    WHEN OTHERS => NULL;
                END CASE;
            WHEN OUTPUT=> NULL;
        END CASE;
    END PROCESS;
    
    NEW_DATA<='1' WHEN STATE=OUTPUT ELSE '0';
    DATA<=  B"011_0011" WHEN (TRIGGER_POLING=TRIGGER_POLING_1)AND KEY =1 AND STATE=OUTPUT ELSE--3
            B"011_0110" WHEN (TRIGGER_POLING=TRIGGER_POLING_1)AND KEY =2 AND STATE=OUTPUT ELSE--6
            B"011_1001" WHEN (TRIGGER_POLING=TRIGGER_POLING_1)AND KEY =3 AND STATE=OUTPUT ELSE--9
            B"010_0011" WHEN (TRIGGER_POLING=TRIGGER_POLING_1)AND KEY =4 AND STATE=OUTPUT ELSE--#
            B"011_0010" WHEN (TRIGGER_POLING=TRIGGER_POLING_2)AND KEY =1 AND STATE=OUTPUT ELSE--2
            B"011_0101" WHEN (TRIGGER_POLING=TRIGGER_POLING_2)AND KEY =2 AND STATE=OUTPUT ELSE--5
            B"011_1000" WHEN (TRIGGER_POLING=TRIGGER_POLING_2)AND KEY =3 AND STATE=OUTPUT ELSE--8
            B"011_0000" WHEN (TRIGGER_POLING=TRIGGER_POLING_2)AND KEY =4 AND STATE=OUTPUT ELSE--0
            B"011_0001" WHEN (TRIGGER_POLING=TRIGGER_POLING_3)AND KEY =1 AND STATE=OUTPUT ELSE--1
            B"011_0100" WHEN (TRIGGER_POLING=TRIGGER_POLING_3)AND KEY =2 AND STATE=OUTPUT ELSE--4
            B"011_0111" WHEN (TRIGGER_POLING=TRIGGER_POLING_3)AND KEY =3 AND STATE=OUTPUT ELSE--7
            B"010_1010" WHEN (TRIGGER_POLING=TRIGGER_POLING_3)AND KEY =4 AND STATE=OUTPUT ELSE--*
            B"000_0000";
end Behavioral;

仿真波形图如下

        接下来,我们采用三进程描述来完成这个问题。这里次态逻辑的敏感列表是NX_STATE_LOGIC:PROCESS(PR_STATE,INP,COUNT_STATE),其中count_state是用来描述计数状态的,如果没有它也是可以的,只不过需要另外加入状态,否则次态逻辑块可能不会被触发。此外,这里的count_state不是次态逻辑块内部变量count的简单赋值,而是需要将count先赋值给信号count_reg,然后在时钟上升触发时,再将count_reg赋给count_state。这样做是因为,次态逻辑块不会因为自己块中的信号更新而被触发,换句话说,敏感列表可以是外部输入、外部信号,而不能是内部信号。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Anti_shake_circuit_encoder is
    Port (INP:  IN  STD_LOGIC_VECTOR(3 DOWNTO 0);
      CLK,RST:  IN  STD_LOGIC;
         OUTP:  OUT STD_LOGIC_VECTOR(2 DOWNTO 0);
         DATA:  OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
     NEW_DATA:  OUT STD_LOGIC
         );
end Anti_shake_circuit_encoder;

architecture Behavioral of Anti_shake_circuit_encoder is
    TYPE STATE_TPYE IS (ROLL_POLING_1,ROLL_POLING_2,ROLL_POLING_3,TRIGGER,OUTPUT);
    TYPE TRIGGER_POLING_STATE_TYPE IS (IDLE,TRIGGER_POLING_1,TRIGGER_POLING_2,TRIGGER_POLING_3);
    SIGNAL PR_STATE,NX_STATE:STATE_TPYE;
    SIGNAL KEY:INTEGER RANGE 1 TO 4;
    SIGNAL TRIGGER_POLING:TRIGGER_POLING_STATE_TYPE;
    SIGNAL COUNT_REG,COUNT_STATE:INTEGER RANGE 0 TO 3; 
    SIGNAL DATA_REG:STD_LOGIC_VECTOR(6 DOWNTO 0);
    SIGNAL DATA_OUT:STD_LOGIC_VECTOR(6 DOWNTO 0);
begin
---------------------------------STATE_REG-------------------------------
    STATE_REG:PROCESS(CLK,RST)
    BEGIN
        IF(RST='1')THEN 
            PR_STATE<=ROLL_POLING_1;
            COUNT_STATE<=0;
            DATA_OUT<="0000000";
        ELSIF CLK'EVENT AND CLK='1'THEN
            PR_STATE<=NX_STATE;
            COUNT_STATE<=COUNT_REG;
            DATA_OUT<=DATA_REG;
        END IF;
    END PROCESS;
------------------------------NX_STATE_LOGIC------------------------------
    NX_STATE_LOGIC:PROCESS(PR_STATE,INP,COUNT_STATE)
    VARIABLE    COUNT: INTEGER RANGE 0 TO 3;
        BEGIN
            CASE PR_STATE IS
                WHEN ROLL_POLING_1=>
                    COUNT:=1;
                    CASE INP IS
                        WHEN "0111"=> NX_STATE<=TRIGGER;    KEY <=1;    TRIGGER_POLING<=TRIGGER_POLING_1;
                        WHEN "1011"=> NX_STATE<=TRIGGER;    KEY <=2;    TRIGGER_POLING<=TRIGGER_POLING_1;
                        WHEN "1101"=> NX_STATE<=TRIGGER;    KEY <=3;    TRIGGER_POLING<=TRIGGER_POLING_1;
                        WHEN "1110"=> NX_STATE<=TRIGGER;    KEY <=4;    TRIGGER_POLING<=TRIGGER_POLING_1;
                        WHEN OTHERS => NX_STATE<=ROLL_POLING_2;COUNT:=0;
                    END CASE;
                WHEN ROLL_POLING_2=>
                    COUNT:=1;
                    CASE INP IS
                        WHEN "0111"=> NX_STATE<=TRIGGER;    KEY <=1;    TRIGGER_POLING<=TRIGGER_POLING_2;
                        WHEN "1011"=> NX_STATE<=TRIGGER;    KEY <=2;    TRIGGER_POLING<=TRIGGER_POLING_2;
                        WHEN "1101"=> NX_STATE<=TRIGGER;    KEY <=3;    TRIGGER_POLING<=TRIGGER_POLING_2;
                        WHEN "1110"=> NX_STATE<=TRIGGER;    KEY <=4;    TRIGGER_POLING<=TRIGGER_POLING_2;
                        WHEN OTHERS => NX_STATE<=ROLL_POLING_3;COUNT:=0;
                    END CASE;
                WHEN ROLL_POLING_3=>
                    COUNT:=1;
                    CASE INP IS
                        WHEN "0111"=> NX_STATE<=TRIGGER;    KEY <=1;    TRIGGER_POLING<=TRIGGER_POLING_3;
                        WHEN "1011"=> NX_STATE<=TRIGGER;    KEY <=2;    TRIGGER_POLING<=TRIGGER_POLING_3;
                        WHEN "1101"=> NX_STATE<=TRIGGER;    KEY <=3;    TRIGGER_POLING<=TRIGGER_POLING_3;
                        WHEN "1110"=> NX_STATE<=TRIGGER;    KEY <=4;    TRIGGER_POLING<=TRIGGER_POLING_3;
                        WHEN OTHERS => NX_STATE<=ROLL_POLING_1;COUNT:=0;
                    END CASE;
                WHEN TRIGGER=>
                    CASE INP IS
                        WHEN "0111"=> 
                            IF(KEY=1)THEN 
                                COUNT:=COUNT+1;
                                IF(COUNT=3)THEN NX_STATE<=OUTPUT;TRIGGER_POLING<=IDLE;ELSE NX_STATE<=TRIGGER; END IF;
                            ELSE
                                NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
                            END IF;
                        WHEN "1011"=> 
                            IF(KEY=2)THEN 
                                COUNT:=COUNT+1;
                                IF(COUNT=3)THEN NX_STATE<=OUTPUT;TRIGGER_POLING<=IDLE;ELSE NX_STATE<=TRIGGER; END IF;
                            ELSE
                                NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
                            END IF;
                        WHEN "1101"=>
                            IF(KEY=3)THEN 
                                COUNT:=COUNT+1;
                                IF(COUNT=3)THEN NX_STATE<=OUTPUT;TRIGGER_POLING<=IDLE;ELSE NX_STATE<=TRIGGER; END IF;
                            ELSE
                                NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
                            END IF;
                        WHEN "1110"=>
                            IF(KEY=4)THEN 
                                COUNT:=COUNT+1;
                                IF(COUNT=3)THEN NX_STATE<=OUTPUT;TRIGGER_POLING<=IDLE;ELSE NX_STATE<=TRIGGER; END IF;
                            ELSE
                                NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
                            END IF;
                        WHEN OTHERS => 
                            NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
                    END CASE;
                WHEN OUTPUT=> NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
            END CASE;
            COUNT_REG<= COUNT;
        END PROCESS;
--------------------------------ENCODER--------------------------------- 
    PROCESS(TRIGGER_POLING,KEY) BEGIN
        CASE TRIGGER_POLING IS
            WHEN TRIGGER_POLING_1=>
                CASE KEY IS
                    WHEN 1=> DATA_REG<=B"011_0011";   --3
                    WHEN 2=> DATA_REG<=B"011_0110";   --6
                    WHEN 3=> DATA_REG<=B"011_1001";   --9
                    WHEN 4=> DATA_REG<=B"010_0011";   --#
                END CASE;
            WHEN TRIGGER_POLING_2=>
                CASE KEY IS
                    WHEN 1=> DATA_REG<=B"011_0010";   --2
                    WHEN 2=> DATA_REG<=B"011_0101";   --5
                    WHEN 3=> DATA_REG<=B"011_1000";   --8
                    WHEN 4=> DATA_REG<=B"011_0000";   --0
                END CASE;
            WHEN TRIGGER_POLING_3=>
                CASE KEY IS
                    WHEN 1=> DATA_REG<=B"011_0001";   --1
                    WHEN 2=> DATA_REG<=B"011_0100";   --4
                    WHEN 3=> DATA_REG<=B"011_0111";   --7
                    WHEN 4=> DATA_REG<=B"010_1010";   --*
                END CASE;
            WHEN OTHERS => DATA_REG<=B"000_0000";
        END CASE;
    END PROCESS;
        
------------------------------OUTPUT_LOGIC----------------------------- 
    OUTPUT_LOGIC:PROCESS(PR_STATE)
    BEGIN
        CASE PR_STATE IS 
            WHEN ROLL_POLING_1=> OUTP<="110";
            WHEN ROLL_POLING_2=> OUTP<="101";
            WHEN ROLL_POLING_3=> OUTP<="011";
            WHEN TRIGGER=>
                CASE TRIGGER_POLING IS
                    WHEN TRIGGER_POLING_1=> OUTP<="110";
                    WHEN TRIGGER_POLING_2=> OUTP<="101";
                    WHEN TRIGGER_POLING_3=> OUTP<="011";
                    WHEN OTHERS => NULL;
                END CASE;
            WHEN OUTPUT=> NULL;
        END CASE;
    END PROCESS;
    NEW_DATA<='1' WHEN COUNT_REG=3 ELSE '0';
    DATA<=DATA_OUT WHEN COUNT_REG=3 ELSE B"000_0000";
end Behavioral;

仿真波形图为:

        这里存在一个小问题,复位信号触发时,改变了现态,从而触发了次态逻辑,导致计数加1,这是一个小问题,如果要更改,可以在次态逻辑的敏感列表中添加RST,并加入复位时的处理。

可以看到,这种题目使用三进程比较复杂和繁琐,不如双进程描述来的简单。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值