Forum: FPGA, VHDL & Co. Abtastung PWM Signal mit VHDL


von ronny (Gast)


Lesenswert?

Hallo...
Habe eine Frage zur Weiterverarbeitung eines PWM-Signals.
Habe einen Sensor der ein PWM Signal ausgibt. Dieses möchte ich mit 
einer bestimmten Frequenz abtasten, um somit einen Wert zuerhalten den 
ich weiterverarbeiten kann. Derzeit habe ich ein PWM-Signal mit einer 
Pulsbreite von 1ms und einer Periodendauer von 20ms.
Ich habe mir gedacht ich lasse einen Zähler mit einer Frequenz von 1MHz 
hochzählen solange der Puls anliegt. Und kann somit weiterarbeiten. 
Allerdings habe ich noch keine Möglichkeit gefunden wie ich den Wert 
speichern kann, da ja vom Sensor gleich wieder eine Veränderung 
übermittelt werden kann.

Vielleicht kann mir jemand einen guten ratschlag geben.

Hänge meine Idee einfach mal mit dazu.

Gruß Ronny

-- STANDARD LIBRARIES --
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;

-- BEGIN OF ENTITY --
ENTITY Sensor_REG IS
  -- GLOBAL VARIABLE MAX-COUNT (Periodendauer 20 ms) --
  PORT (
  -- GLOBAL CLOCK OF MAX II ALTERA --
    glb_clk:  IN BIT;
  -- CLOCK FOR DISTANCE MEASUREMENT 1 MHz --
    en:      IN STD_LOGIC;
  -- GLOBAL RESET USER PUSH BUTTON S1 --
    nReset:   IN BIT;
  -- RESULT works --
  --readyREG:   IN BIT;
  -- PULS WIDE --
    data_in:  IN BIT;
  -- DATA OUT --
    wide_out:  OUT INTEGER range 0 to 2**12-1
    );
END Sensor_REG;
-- END OF ENTITY --

-- BEGIN OF ARCHITECTURE --
ARCHITECTURE Result OF Sensor_REG IS
TYPE STATE_TYPE IS (POWER_ON_DELAY, INITIALIZE);
SIGNAL state: STATE_TYPE;
BEGIN

PROCESS (glb_clk, nReset, en, data_in)
  variable count: integer range 0 to 2**12-1:= 0;
  BEGIN
    IF (nReset = '0') THEN
    count    := 0;
    state    <= POWER_ON_DELAY;
  -- GENERATING NEW CLOCK --
    ELSIF (glb_clk'EVENT AND glb_clk='1') THEN
  -- START OF INITIALIZATION --
    IF en = '1' THEN
    CASE state IS
        -- BEGINNING OF INITIALIZATION --
  WHEN POWER_ON_DELAY=>
     IF data_in = '1' THEN
        count := count + 1;
     ELSE
        wide_out <= save;
        state<=INITIALIZE;
     END IF;
  WHEN INITIALIZE=>
        wide_out <= count;
        count := 0;
        state <= POWER_ON_DELAY;
                          END case;
      ELSE
       count := count;
    END IF;
  END IF;
  END PROCESS;
END Result;
-- END OF ARCHITECTURE --

von Tim R. (vref)


Lesenswert?

Naja, prinzipiell würde ich dein PWM Signal als Trigger nehmen und deine 
Speicheroperation darauf synchronisieren.
Bei positiver PWM Flanke resettest du deinen 1 MHz Zähler, bei negativer 
Flange nimmst du den aktuellen Zählerwert und machst deine 
Speicheroperation (wohin auch immer...)
So hast du immer die aktuelle Pulsbreite verarbeitet.

von Tim R. (vref)


Lesenswert?

Da ich vhdl nicht mag, weil ich es nicht lesen kann, hier mein Ansatz in 
verilog:

input         clk_1mhz;
input         pwm_in;
output [13:0] data_out;


reg [13:0] data_out_reg;
reg [13:0] counter;
reg        pwm_in_buf;

always @ (negedge clk_1mhz)
begin
  pwm_in_buf <= pwm_in;
end

always @ (posedge clk_1mhz)
begin
  // reset counter at pwm_in negedge else increment
  counter <= (pwm_in & ~pwm_in_buf) ? 0 : counter + 1;
end

always @ (negedge pwm_in)
begin
  data_out_reg <= counter;
end

assign data_out = data_out_reg;




Wenn deine Pulsbreite doch mal breiter sein sollte als 1ms, muss dein 
Zähler übrigens 14 Bit breit werden, da 1 MHz / 50 einen Maximalwert von 
20000 ergibt, und das passt in 12 Bit ja nicht rein.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Zu deinem Code::
Statt  PROCESS (glb_clk, nReset, en, data_in)
reicht PROCESS (glb_clk, nReset)


Ich würde es so machen:

PROCESS (glb_clk)
variable datainlast: bit;   -- fuer Flankenauswertung
variable count2: integer range 0 to 2**12-1:= 0;
BEGIN
    IF rising_edge(glb_clk) THEN
       IF (nReset = '0') THEN          -- synchroner Reset (wenn schon)
          count    := 0;
       ELSIF (en = '1') THEN
          datainlast <= data_in;
          IF data_in = '1' THEN        -- zaehlen solange High
             count1 := count1 + 1;
          ELSIF datainlast = '1' THEN  -- fallende Flanke
             wide_out <= count1;
          END IF;
       END IF;
    END IF;
END PROCESS;


@Tim R.
>always @ (negedge pwm_in)
ist eine zweite Taktdomäne.
Macht bei FPGAs fast ausschliesslich Probleme.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Das Zurücksetzen vom Counter hat noch gefehlt.
Und dann ersetzen wir sämtliche count1 und count2 durch count,
dann gehts ;-)

:
          ELSIF datainlast = '1' THEN  -- fallende Flanke
             wide_out <= count;
             count <= 0;              -- zurücksetzen
          END IF;
:

von Tim R. (vref)


Lesenswert?

>>always @ (negedge pwm_in)
>>ist eine zweite Taktdomäne.
>Macht bei FPGAs fast ausschliesslich Probleme.

stimmt. kein guter ansatz.

von Harald (Gast)


Lesenswert?

>>Macht bei FPGAs fast ausschliesslich Probleme.
>stimmt. kein guter ansatz.

Bei der Beachtung rudimentärer Designrichtlinien macht das gar keine 
Probleme.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

>Bei der Beachtung rudimentärer Designrichtlinien
>macht das gar keine Probleme.

pwm_in ist asynchron zu glb_clk.
So definiere ich ein Problem.

Wenn für dich "rudimentäre Designrichtlinien" so definiert sind, dass 
pwm_in vorher auf glb_clk synchronisiert wurde, dann gebe ich dir Recht.

von Harald (Gast)


Lesenswert?

-es ist doch egak, wie Du es machts: Am Ende wird Deine PWM mit der 
Taxtfrequenz des FPGA quantisiert.

von Tim R. (vref)


Lesenswert?

@Harald:
Ne, Lothar hat da schon ganz recht.
Es ist nicht sichergestellt, dass pwm_in nicht gleichzeitig mit einem 
counter increment eine negative flanke hat, und in dem Fall ist unklar, 
in welchem Zustand der counter ist.
= Problem.
Man kann das natürlich lösen, aber da ja bereits eine funktionierende 
VHDL Lösung da steht, verzichte ich darauf.

von Ronny (Gast)


Lesenswert?

Hey  Lothar Miller (lkmiller)...

Habe deinen Code probiert, und der funktioniert sehr gut. Danke nochmal. 
Hat mir sehr weiter geholfen. HAbe dabei auch meinen eigenen Fehler 
erkannt. So hatte ich es mir gedacht. In der Simulation zählt der 
wunderbar hoch und ich könnte mit dem Zählerstand weiter arbeiten. 
Allerdings wenn ich mein weiteres Programm, zum Beispiel die LCD anzeige 
mit einbinde, kann ich die Ausgabedaten nicht nutzen (wide_out). Kann 
das sein das diese Daten nicht lange genug anliegen, zur Verfügung 
stehen, und ich diese vielleicht nochmal speichern muss. Ciao

von Ronny (Gast)


Lesenswert?

Hey  Lothar Miller (lkmiller)...

Ich habe da nochmal eine Frage zu deinem Syntax.
Du nutzt ja "datainlast", wo du nochmal eine Flanke abfragst, mit 
welchem Hintergrund benötige ich die genau. Ciao

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Ronny wrote:
> Habe deinen Code probiert, und der funktioniert sehr gut.
Das wundert mich fast etwas...
Der ist ja ko***bel...
Wieso hatte ich bei meinem ersten Post eigentlich variable drin?
Das müssen signale sein, ich hasse variable...
Da kam sicher vom copy&paste :-(
Und den reset habe ich jetzt auch pensioniert, das geht auch ohne.
Jetzt gefällt mir der code besser....
1
:
2
signal dsr: std_logic_vector(3 downto 9);   -- fuer Flankenauswertung
3
signal count: integer range 0 to 2**12-1:= 0;
4
:
5
PROCESS (glb_clk)
6
BEGIN
7
    IF rising_edge(glb_clk) THEN
8
       IF (en = '1') THEN              -- 1MHz
9
          dsr <= dsr(2 downto 0) & data_in;  -- Schieberegister
10
          IF (data_in='1') THEN        -- zaehlen solange High
11
             count <= count + 1;
12
          END IF;
13
          IF (dsr="1100") THEN         -- fallende Flanke
14
             wide_out <= count;        -- Ausgabewert aktualisieren
15
             count <= 0;               -- und Zähler löschen
16
          END IF;
17
       END IF;
18
    END IF;
19
END PROCESS;
20
:

> Ich habe da nochmal eine Frage zu deinem Syntax.
> Du nutzt ja "datainlast", wo du nochmal eine Flanke abfragst, mit
> welchem Hintergrund benötige ich die genau. Ciao
Die Idee ist, nur bei der fallenden Flanke den Ausgabewert 
abzuspeichern.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

>um Beispiel die LCD anzeige mit einbinde,
>kann ich die Ausgabedaten nicht nutzen (wide_out).
>Kann das sein das diese Daten nicht lange genug anliegen,
>und ich diese vielleicht nochmal speichern muss.
Klar: das Display weiß nichts von der PWM und andersrum genauso. Die 
beiden laufen einfach nur irgendwie vor sich hin.
Deshalb musst du die Daten für die Ausgabe noch einmal 
zwischenspeichern, oder das Abspeichern nach wide_out anderweitig mit 
der Ausgabe synchronisieren.

von Ronny (Gast)


Lesenswert?

Hi.....

Bei dieser Zeile: "dsr <= dsr(2 downto 0) & data_in;" kommt folgende 
Meldung:
Error (10327): VHDL error at Sensor_Reg.vhd(82): can't determine 
definition of operator ""&"" -- found 0 possible definitions

Kann das daran liegen das da versucht wird einen Std_Logic_Vector mit 
einer Bit Variable zu verknüpfen?

Wenn ich das & durch ein AND ersetze. Dabei bin total überfordert :)
Error (10476): VHDL error at Sensor_Reg.vhd(82): type of identifier 
"data_in" does not agree with its usage as std_logic_vector type


Wenn ich die & Operation herausnehme, kann ich compilieren allerdings 
bekomme ich noch ein Warning:
Bei dieser Zeile " IF (dsr="1100") THEN " kommt diese Meldung:
Warning (10620): VHDL warning at Sensor_Reg.vhd(86): comparison between 
unequal length operands always returns FALSE

Total unverständlich. Vielleicht sagt es dir etwas.

Ciao

von Joko (Gast)


Lesenswert?

Hi..

  "&" ist eine "Anhängung", keine "Verundung".
  Lösung: erst data_in nach std_logic konvertieren:

   "lesenswert":
       http://dz.ee.ethz.ch/support/ic/hdl/vhdlsources.en.html


Gruß
Joko

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

>Kann das daran liegen das da versucht wird einen Std_Logic_Vector mit
>einer Bit Variable zu verknüpfen?
Ja, du mußt data_in noch konvertieren/casten.
>Lösung: erst data_in nach std_logic konvertieren:
Oder noch besser:
verwende nur std_logic(_vector) und integer, und das Leben wird einfach.
Gut, das wird nicht immer klappen, aber den Versuch ist es wert...

Der &-Operator ist (wie von Joko schon gesagt) eine Concatenation, keine 
logische Operation.

von Chris (Gast)


Lesenswert?

>Wenn ich die & Operation herausnehme, kann ich compilieren allerdings
>bekomme ich noch ein Warning:
>Bei dieser Zeile " IF (dsr="1100") THEN " kommt diese Meldung:
>Warning (10620): VHDL warning at Sensor_Reg.vhd(86): comparison between
>unequal length operands always returns FALSE

Im Code von Lothar hat sich ein kleiner Tippfehler eingeschlichen

>signal dsr: std_logic_vector(3 downto 9);

müsste heissen: signal dsr: std_logic_vector(3 downto 0);

dann müsste die Warnung verschwinden

Gruß Chris

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.