Forum: FPGA, VHDL & Co. Wartezustand in Zustandsautomat


von Johannes R. (jruder)


Lesenswert?

Hallo zusammen,
mein Problem ist folgendes:
Ich habe einen Zustandsautomaten in VHDL programmiert. Wenn sich der 
Automat nun in einem best. Zustand z.B "zähl" befindet, soll er einen 
counter auf drücken eines Tasters um eins erhöhen. Da der Automat 
allerdings mit einer sehr hohen Taktrate läuft (soll auch so bleiben) 
wird der counter pro Tastendrücken um viele Stellen erhöht.
Hat von euch vielleicht jemand eine Lösung für dieses Problem? Ich habe 
schon überlegt, ob der Automat nach jedem Tastendruck in einen 
Wartezustand übergehen soll. Nach 50 ms dann wieder zurück. Aber ich 
wüsste jetzt auch nicht, wie man so etwas umsetzt.
Ich wäre euch für jeden Tipp wirklich dankbar,
Grüße, Hannes

von Joerg W. (joergwolfram)


Lesenswert?

Nach dem Zustand "zähl" einen Zustand "wart" einfügen, der auf das 
Loslassen der Taste wartet. Zusätzlich muss aber Dein Taster 
wahrscheinlich auch noch entprellt werden, am einfachsten extern.

Gruß Jörg

von Falk (Gast)


Lesenswert?

@Johannes Ruder

>Ich habe einen Zustandsautomaten in VHDL programmiert. Wenn sich der
>Automat nun in einem best. Zustand z.B "zähl" befindet, soll er einen
>counter auf drücken eines Tasters um eins erhöhen. Da der Automat
>allerdings mit einer sehr hohen Taktrate läuft (soll auch so bleiben)
>wird der counter pro Tastendrücken um viele Stellen erhöht.

Du musst deinen Taster

a) entprellen
b) einen Einzelpuls erzeugen

Geht alles in VHDL mittels Zähler. Mit dem Einzelimpuls kannst du dann 
deine State Machine füttern.

MFG
Falk


von Stefan H. (stefanhanke)


Lesenswert?

Nachdem das Tastensignal = 1 ist, gehst du in den Wartezustand. In dem 
bleibst du solange, wie Tastensignal = 1 gilt. Falls nicht, gehst du 
zurück in den Zählzustand.

Die 50 ms kannst du z.B. warten, indem du einen Zähler hochzählst, der 
quasi die Takte seit Eintritt in den Zustand zählt. Je nachdem, wie 
schnell getaktet wird, muss der größer oder kleiner sein.

 -- stefan

von Falk (Gast)


Lesenswert?

@Stefan Hanke

>Nachdem das Tastensignal = 1 ist, gehst du in den Wartezustand. In dem
>bleibst du solange, wie Tastensignal = 1 gilt. Falls nicht, gehst du

Und hoffentlich prellt der Taster nicht . . .

>zurück in den Zählzustand.

>Die 50 ms kannst du z.B. warten, indem du einen Zähler hochzählst, der
>quasi die Takte seit Eintritt in den Zustand zählt. Je nachdem, wie
>schnell getaktet wird, muss der größer oder kleiner sein.

Und damit bremst du deine State Machine massiv aus. -> Nicht gut.

Wie man es besser macht? Siehe mein anderes Posting.

MFG
Falk

von Stefan H. (stefanhanke)


Lesenswert?

Falk wrote:
> Und hoffentlich prellt der Taster nicht . . .
Wird schon schiefgehen ;-)

> Und damit bremst du deine State Machine massiv aus. -> Nicht gut.
Du meinst das Inkrementieren? OK, stimmt.

 -- stefan

von Johannes R. (jruder)


Lesenswert?

Vielen Dank für eure schnellen und zahlreichen Antworten!!!

Habe gerade die ersten Versuche mit euren Antworten ausprobiert. Beim 
Drücken einer Taste gehts in einen Wartezustand. Beim Loslassen 
derselben dann wieder zurück. Der Zähler zählt jetzt während dem Drücken 
nicht mehr hoch, der Wert erhöht sich aber trotzdem um mehr als eins. 
Denke das liegt am Prellen des Tasters, wie ihr bereits befürchtet habt.
Der Taster ist auf dem Xilinx Starter Board, so dass ich ihn nicht 
extern Entprellen kann. Wie würdet Ihr denn den Taster in VHDL 
entprellen und dann einen Einzelimpuls erzeugen? Habe überhaupt keine 
Vorstellung.
Vielen Dank nochmal,
Hannes

von Stefan H. (stefanhanke)


Lesenswert?

Da hilft dann meistens Google.
http://www.auto.tuwien.ac.at/~schi/LVAs/vhdl_beispiele1a.doc
sieht doch ganz gut aus.

Kurz: Der asynchrone Eingang wird auf das erste einer Kette von n (hier 
3) FFs gelegt. Erst, wenn alle drei den gleichen Wert haben, wird ein 
Ausgang erzeugt.

 -- stefan

von Falk (Gast)


Angehängte Dateien:

Lesenswert?

@Stefan Hanke

>http://www.auto.tuwien.ac.at/~schi/LVAs/vhdl_beispiele1a.doc
>sieht doch ganz gut aus.

Naja . . .

>Kurz: Der asynchrone Eingang wird auf das erste einer Kette von n (hier
>3) FFs gelegt. Erst, wenn alle drei den gleichen Wert haben, wird ein
>Ausgang erzeugt.

Dumm nur wenn meine FSM mit 100MHz läuft und der Taster 5 ms prellt.

Besser gehts so. Siehe Anhang.

MFG
Falk


von Johannes R. (jruder)


Lesenswert?

Hallo nochmal,
vielen Dank auch wieder für eure Antworten. Der Entpreller von Falk 
scheint mir sehr vernünftig. Trotzdem funktioniert mein Zustandsautomat 
leider immer noch nicht. Vielleicht habe ich aber auch einen Denkfehler 
im Automaten. Er sieht in etwa so aus:

entity stateMachine is
  Port ( clk: in std_logic;
         button: in std_logic_vector(3 downto 0);
         switch: in std_logic_vector(7 downto 0);
         k1: out SIGNED(7 downto 0));
end stateMachine;

architecture Behavioral of stateMachine is

COMPONENT key_debounce
    Generic (debounce : integer := 1000);  -- number of clks for 
debounce
    Port ( key      : in std_logic;
           clk      : in std_logic;
           key_up   : out std_logic;
           key_down : out std_logic);
END COMPONENT;

type state_type is (st_standby, st_k1);

signal state, next_state: state_type;
signal k1_i: SIGNED(7 downto 0) := to_signed (-21,16);
signal button_reg , button_up, button_down: std_logic_vector(3 downto 
0);
signal switch_reg : std_logic_vector (7 downto 0);

begin

KEY_DEBOUNCE0: key_debounce Generic Map (debounce => 1000)
          Port Map ( key => button_reg(0),
               clk => clk,
               key_up => button_up(0),
               key_down => button_down(0));

SYNC_PROC: process (clk)
begin
  if (clk'event and clk = '1') then
      state <= next_state;
      k1 <= k1_i;
      -- Alle Eingangssignale, die Zustandsübergänge beeinflussen,
      -- müssen über zusätzliches Flip-Flop
      switch_reg <= switch;
      button_reg <= button;
  end if;
end process;

--MEALY State Machine - Outputs based on state and inputs
OUTPUT_DECODE: process (state, button_down, switch_reg)
begin
  if (state = st_k1) then
     case button_down(0) is
  when '1' => k1_i <= k1_i+1;
  when others => k1_i <= k1_i;
     end case;
  end if;
end process;

------------------------------------------------------------------------ 
-------------------
   NEXT_STATE_DECODE: process (state, button_down, switch_reg)
   begin
      next_state <= state;  --default is to stay in current state

      case (state) is
         when st_standby =>
      if switch_reg(0) = '1' then
    next_state <= st_k1;
            end if;
  when st_k1 =>
      if switch_reg(0) = '0' then
    next_state <= st_standby;
      end if;
  when others =>
            next_state <= st_standby;
      end case;
   end process;

end Behavioral;

Die eingangsseitige Clock läuft mit 3.125 MHz. k1 wird in einem anderen 
Modul dann über eine 7-Segmentanzeige ausgegeben. Die Initialisierung 
mit -21 funktioniert und wird auch richtig angezeigt. Sobald ich dann 
allerdings die Taste drücke, springt der Wert auf -1. Nochmaliges 
drücken der Taste ändert jetzt nichts mehr am Wert, der Zustandsautomat 
ist aber nicht abgestürzt, alles andere funktioniert noch. Ich weiß 
jetzt echt nicht mehr weiter. Habe das ganze Wochenende versucht, es 
hinzubekommen. Kann ja eigentlich nicht so schwer sein, oder?
Es wäre wirklich sehr nett, wenn ihr mir nochmal weiterhelfen könntet.
Grüße, Hannes

von Falk (Gast)


Lesenswert?

@Johannes Ruder

>drücken der Taste ändert jetzt nichts mehr am Wert, der Zustandsautomat
>ist aber nicht abgestürzt, alles andere funktioniert noch. Ich weiß

Naja, diese getrennte Modellierung von Dekoderlogik und 
Zustandsregistern halte ich für wenig sinnvoll.
Aber man kann es so machen. ALlerdings braucht es dazu immer doppelt 
soviele Variablen, was der Übersicht nicht unbedingt förderlich ist.
Dort hast du dich auch verhauen.

Hier steckt das Problem.

>--MEALY State Machine - Outputs based on state and inputs
>OUTPUT_DECODE: process (state, button_down, switch_reg)
>begin
>  if (state = st_k1) then
>     case button_down(0) is
>  when '1' => k1_i <= k1_i+1;
>  when others => k1_i <= k1_i;
>     end case;
>  end if;
>end process;

Hier stecken zwei Fehler drin.

1.) Welchen Wert bekommt k1_i wenn state= st_k1 nicht erfüllt ist? Dort 
steht nix, also wird ein Latch generiert. -> Schlecht

2.) Du hast dich mit den Variablen verhauen. Das erzeugt wilde 
kombinatorische Schleifen, das müsste der Compiler eigentlich anmeckern. 
Siehe unten.

OUTPUT_DECODE: process (state, button_down, k1)
begin
  if (state = st_k1) then
    case button_down(0) is
      when '1'    => k1_i <= k1+1;    -- hier war der Fehler, der neue 
Wert muss immer aus dem alten berechnet werden!
      when others => k1_i <= k1;      -- alten Wert behalten
    end case;
  else
    k1_i <= k1;      -- alten Wert behalten
  end if;
end process;

Wobei man das sinnvoll anders schreibt

OUTPUT_DECODE: process (state, button_down, k1)
begin
  if (state = st_k1) and (button_down(0)='1') then
    k1_i <= k1+1;
  else
    k1_i <= k1;      -- alten Wert behalten
  end if;
end process;

MfG
Falk


von Johannes R. (jruder)


Angehängte Dateien:

Lesenswert?

Hallo und vielen dank für euren großartigen Support!
Habe den Zustandsautomaten jetzt endlich mit eurer Hilfe hinbekommen und 
viel dabei gelernt! Funktioniert einwandfrei! Vielen Dank!

Leider steht bereits das nächste Problem vor der Tür:
Mein Design besteht aus mehreren Komponenten, wie z.B ein 
SPI-Controller, zwei Differenzierer, ein Zustandsregler, eine 
PWM-Komponente und dem Zustandsautomat! Nun ist mir allerdings schon 
mehrmals aufgefallen, dass die SPI-Komponente falsche Daten liefert, 
sobald ich in anderen Komponenten etwas ändere. Wenn ich z.B. den 
Zustandsautomaten mit einer geringeren Taktrate laufen lasse, gibt die 
SPI-Komponente falsche Werte aus. Oder wenn ich in statt dem Fehler, den 
um 12 Bits nach rechts geshifteten Fehler über die PWM-Komponente 
ausgeben will, liefert die SPI-Komponente falsche Werte. Diese 
Änderungen haben eigentlich überhaupt nichts mit der SPI-Komponente zu 
tun.
Ich habe allerdings die Vermutung, dass ich mit dem DCM was falsch 
gemacht habe, da am Ende der Erstellung folgendes ausgegeben wird:
WARNING:PhysDesignRules:372 - Gated clock. Clock net sclk_OBUF is 
sourced by a
   combinatorial pin. This is not good design practice. Use the CE pin 
to
   control the loading of data into the flip-flop.
WARNING:PhysDesignRules:372 - Gated clock. Clock net
   SPICONTROLER1/SIMPLESPI1/sck_1 is sourced by a combinatorial pin. 
This is not
   good design practice. Use the CE pin to control the loading of data 
into the
   flip-flop.
WARNING:PhysDesignRules:372 - Gated clock. Clock net
   SPICONTROLER1/SIMPLESPI1/rcv_load is sourced by a combinatorial pin. 
This is
   not good design practice. Use the CE pin to control the loading of 
data into
   the flip-flop.
INFO:PhysDesignRules:772 - To achieve optimal frequency synthesis 
performance
   with the CLKFX and CLKFX180 outputs of the DCM comp
   DCM1_1/DCM_INST/DCM1_1/DCM_INST, consult the device Interactive Data 
Sheet.

Im Anhang habe ich die gesamte Ausgabe des Compilers angehängt. 
Vielleicht sieht ja von euch jemand besser, was ich falsch gemacht habe. 
Das wäre wirklich super nett! Danke nochmal,
Hannes

von Falk (Gast)


Lesenswert?

@ Johannes Ruder

>Zustandsautomaten mit einer geringeren Taktrate laufen lasse, gibt die

Geringere Taktrate? Lass mich raten, du nimmst einen wilden Teiler + 
Mux.

>Im Anhang habe ich die gesamte Ausgabe des Compilers angehängt.
>Vielleicht sieht ja von euch jemand besser, was ich falsch gemacht habe.

Das sagt dir der Compiler doch schon. Des Englischen nicht so mächtig? 
Du verwendest einen Gated-Clock, sprich du schaltest irgendwie deinen 
Takt mittels kombinatorischer Logik um. Das macht man so nicht. Siehe 
hier.

Beitrag "Clock Switching"

MfG
Falk

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.