Zustandsschalter in FSM

Ich habe ein einfaches Board mit 6 Knöpfen, bestehend aus 3 Spalten und 2 Reihen Ich möchte den gedrückten Knopf erkennen. Mein Code unten funktioniert mit diesem fsm:

  scan_fsm : process (reset, clk)
  begin  -- process key_scanner

     if reset = '1' then 
            Send <= '0';
            fsm_state <= start;
            scannel_val <= "0000";

    elsif clk'event and clk = '1' then

      if state_inc = '1' then

        scan_complete <= '0';

        case fsm_state is

          when start =>
               Send <= '0';
            scannel_val <= "0000";
            my_switch   <= "001";
            fsm_state <= state1;

          when state1 => 
            case bcd_val is
              when "01" =>
                    Send <= '1';
                    scannel_val <= "0001";
              when "10" =>
                    Send <= '1';
                    scannel_val <= "0010"; -- Value 2 
              when others =>
                    scannel_val <= "0000";
                    Send <= '0';
            end case;
            my_switch   <= "010";
            fsm_state <= state2;

          when state2 => 
            case bcd_val is
              when "01" => 
                  Send <= '1';
                  scannel_val <= "0011"; 
              when "10" => 
                  Send <= '1';
                  scannel_val <= x"0100";  -- Value 4
              when others => 
                    scannel_val <= "0000";
                    Send <= '0'; 
            end case;
            my_switch   <= "100";
            fsm_state <= state3;

          when state3 => 
            case bcd_val is
              when "01" => 
                    Send <= '1';
                    scannel_val <= "0101"; 
              when "10" => 
                    Send <= '1';
                    scannel_val <= "0110";
              when others => 
                    scannel_val <= "0000";
                    Send <= '0'; 
            end case;
                my_switch   <= "001";
            fsm_state <= state1;
            scan_complete <= '1';

           when others => scannel_val <= "0000";
        end case;

      end if;
    end if;
  end process scan_fsm;

Ich habe 4 Zustände, Start und einen Zustand für jede Spalte. Ich möchte mein Board verbessern und Störungen vorbeugen. Ich möchte Funktionen hinzufügen, z. B. wenn 2 Tasten gleichzeitig gedrückt werden, sollte ich den ersten Wert erhalten, der einige Millisekunden oder Nanosekunden vor dem anderen erreicht. Außerdem möchte ich verhindern, dass ein zweiter Wert gelesen wird oder ein dritter, wenn bereits eine Taste gedrückt wird.

Es sollte also immer nur ein Wert gelesen werden, egal wie viele Tasten gedrückt werden. Ich habe darüber nachgedacht, einen fünften Zustand hinzuzufügen, Leerlauf, wenn dies auftritt, aber wie komme ich dann aus dem Leerlauf heraus?

Statt Leerlauf kann ich auch in den Zustand "Start" wechseln, aber das würde dann das sendSignal "0" machen, was falsch wäre, wenn bereits eine Taste gedrückt wird. Auch wie viele Prozesse sind in diesem FSM? Was ist ein Prozess in VHDL? Ist es 3; zurücksetzen, clk und state_inc? statce_inc wird mit Hilfe eines Zählers alle 200Hz "1". Ich habe einen Entprellungscode hinzugefügt, den ich aus dem Internet gefunden habe, aber er hilft nicht :( Hat jemand einen Entprellungsvorschlag?

startEin weiteres Problem, ich möchte den Zustand eigentlich loswerden , der einzige Grund, warum ich ihn habe, ist, weil ich ihn vorher my_switchzuweisen sollte . In meiner ersten Implementierung habe ich in und in und in zugewiesen . In dieser Umsetzung liegt mein Tastenfeld"001"state1my_switch"001"state1"010"state2"100"state3

______
|1|2|3|
|4|5|6|

Wenn ich 2 drückte, wurde 1 angezeigt, wenn ich 3 drückte, wurde 2 angezeigt. Ich habe an Cpp/Java gedacht, aber ich habe mich geirrt ... dann habe ich einen Dummy-Zustand hinzugefügt, startnur um ihn my_switchvor dem eigentlichen Scannen festzulegen ... Wenn ich das also lösen kann, kann ich auch einen Zustand speichern! Vorschläge sind willkommen! Ich habe das gelöst, indem ich den ersten Scan ohne gesetzten Wert eingegeben habe. Haben Sie bessere Ideen?

Was ist bcd_val? Welche Signale sind Ihre Spalten- und Zeileneingänge?
@StaceyAnne my_switch(Spalte) ist ein Ausgangssignal und bcd_val(Zeile) ist ein Eingang, bcd_valsollte also 2 Bit lang sein. Entschuldigung, dass ich jetzt den Code aktualisiere
Wofür ist die state_inc?
@StaceyAnne Vielen Dank erstmal für die Antwort! state_inc ist ein Freigabesignal, es ist alle 200 Hz '1'
Ich bin neugierig zu wissen, warum Sie die Zustandsmaschine regelmäßig aktivieren, sie sollte die ganze Zeit aktiviert funktionieren.
@StaceyAnne mein Code ohne enable hat nicht funktioniert, aber die eigentliche Idee dahinter ist, die Zustandsmaschine zu verlangsamen, um ein Entprellen zu verhindern. Ich habe die Frage auch etwas erweitert, würdest du bitte mal nachsehen?

Antworten (1)

Was Sie tun können, ist einen Ruhezustand zu schaffen, und wenn Sie einen Tastendruck erkennen, schalten Sie direkt in den Ruhezustand. Dann können Sie einen Zähler im Leerlauf haben, der eine Weile wartet, bevor Sie erneut mit dem Scannen von Schaltflächen beginnen.

Zum Beispiel:

-- include numeric std
signal idle_counter : unsigned(31 downto 0);

      ...

      when state3 => 
        case bcd_val is
          when "01" => 
                Send <= '1';
                scannel_val <= "0101"; 
                fsm_state <= idle;   -- switch to idle
          when "10" => 
                Send <= '1';
                scannel_val <= "0110";
                fsm_state <= idle;   -- switch to idle
          when others => 
                scannel_val <= "0000";
                Send <= '0'; 
                fsm_state <= state1;   -- carry on scanning
        end case;
        idle_counter <= (others => '0');
        ...
      when idle =>
        if (idle_counter < some_timeout) then  -- some_timeout is how long you want to wait (in clock cycles)
           idle_counter <= idle_counter + "1"; -- increment counter
           fsm_state <= idle;                  -- stay here
        else
           idle_counter <= (others => '0');    -- reset counter
           fsm_state <= state1;                -- go back into checking
        end
        --scannel_val is valid here, so you can do something with it if you'd like.
        ...
      when others => scannel_val <= "0000";

Es gibt einen Prozess in diesem Code, den Sie gepostet haben.

Es beginnt mit

scan_fsm : process (reset, clk) <= Sensitivity list
begin  -- process key_scanner

und endet mit

end process scan_fsm;

Die Uhr und das Zurücksetzen sind Teil der Empfindlichkeitsliste. Das bedeutet, dass dieser Code nur ausgelöst wird, wenn sich entweder die Uhr ändert oder sich ändert.

Das Entprellen kann in einem separaten Prozess erfolgen. Grundsätzlich möchten Sie sicherstellen, dass die Taste einige Mikrosekunden lang gedrückt wurde, bevor sie ausgelöst wird. Was wir tun möchten, ist den Wert des Eingangs für eine Anzahl von Taktzyklen zu speichern. Registrieren Sie dann nur dann eine 1, wenn die Historie für die letzten 32 Taktzyklen konstant 1 war.

Beispiel:

-- these are our row inputs (to become bdc_val)
signal row1 : in std_logic; 
signal row2 : in std_logic;

...
-- this is our history vector
signal row1_z : std_logic_vector(31 downto 0);
signal row2_z : std_logic_vector(31 downto 0);

-- debounced signal
signal bcd_val : std_logic_vector(1 downto 0);

-- a whole vector of ones for convenience
signal ones    : std_logic_vector(31 downto 0);
...

ones <= (others => '1');

-- generate our histories
gen_z : process (reset, clk)
begin  
  if (reset = '1') then
    row1_z <= (others => '0');
    row2_z <= (others => '0');
    bcd_val <= (others => '0');
  elsif (rising_edge(clk)) then
    row1_z(31 downto 1) <= row1_z(30 downto 0); -- shift everything up 1
    row1_z(0) <= row1;                          -- save the most recent input
    row2_z(31 downto 1) <= row2_z(30 downto 0); -- shift everything up 1
    row2_z(0) <= row2;                          -- save the most recent input

    -- we only want bcd_val to be 1 when the entire row history for the past 32 cc is 1
    if (row1_z = ones) then
      bcd_val(0) <= '1';
    else 
      bcd_val(0) <= '0';
    end

    if (row2_z = ones) then
      bcd_val(1) <= '1';
    else 
      bcd_val(1) <= '0';
    end
  end
end
Ich habe noch eine Frage, wenn ich 3 und 1 gleichzeitig drücke, scanned_valwird 0 angezeigt, wenn ich 3 und 6 drücke, wird auch 0 angezeigt, sodass dieselben Spalten- und Zeilentasten 0 erhalten, wenn sie beide gleichzeitig gedrückt werden, weil ich when others => scannel_val <= "0000";denke Das Ergebnis sollte auch Null sein, wenn ich gleichzeitig 3 und 4 drücke, weil die Spalte 001 erhält und 100 dann zu 101 wird, was als "andere" betrachtet werden sollte, nicht wahr? Oder ich möchte den Code ändern, anstatt die Werte auf Null zu setzen, wenn sie gleichzeitig aus derselben Spalte oder Zeile gedrückt werden, und den ersten gedrückten Wert beibehalten?