Forum: FPGA, VHDL & Co. Ultraschallsensor HC-SR04 mit FPGA


von pico (Gast)


Lesenswert?

Guten Morgen liebe Mikrocontroller,

ich bin dabei einen Ultraschallsensor (HC-SR04) mit Hilfe eines FPGA's 
anzusteuern. Ich habe in VHDL ein periodisches Signal mit einer 
Periodendauer von 100 ms und einer Pulsbreite von 10 us erzeugt um den 
Sensor zu triggern. Mit einem Oszi konnte ich dann direkt am 
Echo-Ausgang das Signal abgreifen und mir die Pulsbreite des 
laufzeitverzögerten Signals anschauen und mit Hilfe der Formel 
(Pulsbreite in us / 58 )die Distanz in cm ausrechnen.

Jetzt würde ich gerne das Echosignal als Input für den FPGA nehmen um 
damit die Distanz von diesem berechnen lassen und anschließend das 
Ergebnis auf der 7 Segment Anzeige ausgeben.

Um dies zu bewerkstelligen, würde ich einen Zähler, beispielsweise als 
Integer, bauen, der, sobald der Echo-Eingang auf High switched, anfängt 
die aufsteigenden Taktflanken meines 50 MHz Taktsignals zu zählen und 
bei LOW-Switch wieder aufhört.

Meine Frage: Ginge es so, wie ich es beschrieben habe und falls ja, ob 
ich auch mit arithmetischen Operationen in VHDL rechnen kann?

Ich würde nämlich dann den Wert meine Zählers durch einen konstanten 
Integer mit dem Wert 50.000.000 teilen um somit die Pulsbreite in 
Sekunden zu bekommen. Das Ganze könnte ich dann nochmal durch 1000 
teilen, um die ms zu erhalten.

Oder müsste ich das Ganze statt der arithmetischen Operation mit einem 
weiteren Zähler realisieren?

Wäre um Rat sehr dankbar,

viele Grüße,

pico

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


Lesenswert?

pico schrieb:
> Meine Frage: Ginge es so, wie ich es beschrieben habe
Ja.
> und falls ja, ob
> ich auch mit arithmetischen Operationen in VHDL rechnen kann?
Ja (abgesehen von der Division, die doch recht aufwendig ist)

ABER:
> Ich würde nämlich dann den Wert meine Zählers durch einen konstanten
> Integer mit dem Wert 50.000.000 teilen um somit die Pulsbreite in
> Sekunden zu bekommen. Das Ganze könnte ich dann nochmal durch 1000
> teilen, um die ms zu erhalten.
Diese ganze Teilerei kannst du dir durch Nachdenken KOMPLETT sparen!
Und wie? Überleg dir, welche Zeit der Schall für einen cm braucht. Und 
jetzt lass einen Zähler mit der passenden Frequenz laufen. Wenn du 
diesen Zähler mit dem Startimpuls zurücksetzt und dann durchlaufen 
lässt, hast du, wenn dein Schallimpuls zurückkommt, den Zählerstand 
schon mal in cm im Zählerregister stehen. Das wär doch was, oder?

BTW:
> sobald der Echo-Eingang auf High switched
Ein guter deutscher Echo-Eingang schaltet (oder besser er /wird 
geschaltet/, denn ein Eingang ist passiv). Warum nimmst du unnötig 
Anglizismen? Wenn das deutsche Wort für "schwitched" so unglaublich 
unsäglichlangwäredassessichlohnt, dann dürftest du gern auch das 
passende englische Wort nehmen. Aber so hört sich das nur billig und 
populistisch an...

von pico (Gast)


Lesenswert?

Bei 20°C breitet sich der Schall durch das Medium Luft mit 343 m/s aus.
Dann würde der Schall für 1. Zentimeter Weg, 29,15 us benötigen. Da mich 
aber das Reflektierte Signal interessiert, welches ja den selben Weg 
wieder zurück legen muss, ergibt sich also aus einem ABSTAND zum Objekt 
pro Zentimeter 58,3 also 58 us.

Nun müsste ich also einen Zähler bauen, der jede aktive Taktflanke 
meines 50 MHz Signals bis 2900 zählt.
Denn wenn 50.000.000 Takte = 1 Sekunde beträgt, betragen 58 us 2900 
Takte.

Sehe ich das so richtig?

Vielen Dank

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


Lesenswert?

pico schrieb:
> Nun müsste ich also einen Zähler bauen, der jede aktive Taktflanke
> meines 50 MHz Signals bis 2900 zählt.
> Denn wenn 50.000.000 Takte = 1 Sekunde beträgt, betragen 58 us 2900
> Takte.
>
> Sehe ich das so richtig?
Ja, ist aber ein wenig umständlich berechnet... ;-)

Ich würde einfach aus den 58,3us eine Frequenz errechnen und bekäme
17152 Hz. Damit muss also ein Vorteiler mit 50MHz/17152Hz = 2915 her. 
Der erzeugt dann quasi einen "Zentimer-Impuls".


> pro Zentimeter 58,3 also 58 us.
Warum in so früh schon ungenau werden?

von pico (Gast)


Lesenswert?

Ein kleines Problem habe ich noch. Und zwar benötigt der HC-SR04 eine 
Betriebsspannung von 5 V. Meine Eingänge am FPGA Board vertragen laut 
Betriebsanleitung aber nur 3.3 V. Womit oder wie kann ich am besten die 
Spannung auf max. 3.3 V verkleinern? Mit einer Z-Diode, oder gäbe es 
noch einen einfacheren Weg oder ein fertiges Bauteil als Pmod-Connector, 
welches ich zwischen dem Pmod-Eingang und der 5 V Spannung 
zwischenintegrieren kann?

von Duke Scarring (Gast)


Lesenswert?


von pico (Gast)


Lesenswert?

Den richtigen Takt konnte ich erzeugen. Diesen habe ich mir auch mit 
einem Logik-Analysator und dem Oszi angeschaut. Wenn ich nun das 
Echosignal in den Input vom FPGA Board mit der Pulsbreite x einspeise, 
steht auf der 7 Segmentanzeige nur der Wert, welcher unter "when others" 
steht.

Nun habe ich mir eine Testbench erstellt und habe für die clk_periode 20 
ns (50 MHz) und für das echo_in <= '0', '1' after 100 ms, '0' after 102 
ms, '1' after 200 ms, '0' after 208 ms; vorgegeben.

Im ISIM fiel mir auf, das ab 100 ms, also ab dem Zeitpunkt, als echo_in 
das erste mal auf 1 schaltet, meine Werte für die DIG_0 Anzeige von 1 
bis 9 einmal komplett hintereinander ausgegeben wurden. Danach trifft 
der "when others" Fall ein und blieb bei jeder neuen Periode ebenfalls 
auf "when others".

Wie bekomme ich das hin, dass ich nur den Wert bekomme, der auch der 
distance zugeordnet ist? Ich habe versucht am Ende des Prozesses die 
distance wieder zurück zusetzen, jedoch gab es da immer einen 
Synthetisierungsfehler.

Den Code habe ich mal angehangen:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.numeric_std.all;
4
5
entity Ultraschall is
6
    Port ( clk : in  STD_LOGIC;
7
          -- reset : in  STD_LOGIC;
8
        echo_in: in STD_LOGIC;
9
        DIG_0 : out STD_LOGIC_VECTOR(6 downto 0);
10
           ultra_takt : out  STD_LOGIC);
11
end Ultraschall;
12
13
architecture Behavioral of Ultraschall is
14
signal counter: natural range 4999999 downto 0;        -- Zähler für die Periodendauer von 100 ms
15
signal counter2: natural range 2914 downto 0;        -- Zähler für die Pulsbreite von 58 us, welche 1 Zentimeter Abstand beschreibt. 
16
signal distance: integer:= 0;
17
18
begin
19
  clock_gen: process(clk)
20
  begin
21
    if (clk'event and clk='1') then    
22
        if (counter = 4999999) then
23
          counter <= 0;
24
        else
25
          counter <= counter + 1;
26
        end if;
27
28
if(counter < 500) then          -- 500 entspricht 10 us
29
          ultra_takt <= '1';          -- Asynchroner Takt mit Tp = 100 ms und Pulsweite von 10 us
30
          else 
31
          ultra_takt <= '0';  
32
        end if;  
33
    end if;    
34
  end process;
35
    
36
  echo: process(clk)
37
    begin
38
      if (clk'event and clk='1') then      
39
        if (echo_in = '1') then
40
          if (counter2 = 2914) then
41
            counter2 <= 0;
42
            distance <= distance + 1;            
43
          else
44
            counter2 <= counter2 + 1;
45
          end if;
46
        end if;        
47
      end if;    
48
    end process;
49
    
50
  segment: process(clk, distance)
51
      begin
52
        case distance is
53
          when 0 => DIG_0 <= "1111110";        --  0
54
          when 1 => DIG_0 <= "1111001";        --  1
55
          when 2 => DIG_0 <= "0010010";        --  2
56
          when 3 => DIG_0 <= "0000110";        --  3
57
          when 4 => DIG_0 <= "1001100";        --  4
58
          when 5 => DIG_0 <= "0100100";        --  5
59
          when 6 => DIG_0 <= "0100000";        --  6
60
          when 7 => DIG_0 <= "0001111";        --  7
61
          when 8 => DIG_0 <= "0000000";        --  8
62
          when 9 => DIG_0 <= "0001100";        --  9
63
          --when others => DIG_0 <= "1111110";    --  -  
64
          when others => DIG_0 <= "0000000";    --  -  
65
        end case;      
66
      end process;    
67
end Behavioral;


Vielleicht kann mir ja einer einen Tipp geben, woran es liegen, bzw. wie 
ich den Fehler beheben könnte.

Vielen Dank,

von Andi (Gast)


Lesenswert?

Das liegt vielleicht daran dass Du distance immer nur raufzählst.
Sobald der wert > 9 ist greift nur noch der others-Zweig

von pico (Gast)


Lesenswert?

Ja, das stimmt.

Wenn ich nun distance vor dem Prozess auf 0 setze,
1
distance <= 0;
2
  echo: process(clk)
3
    begin      
4
      if (clk'event and clk='1') then      
5
        if (echo_in = '1') then
6
          if (counter2 = 2914) then      
7
            counter2 <= 0;
8
            distance <= distance + 1;            
9
          else
10
            counter2 <= counter2 + 1;
11
          end if;
12
        end if;        
13
      end if;    
14
    end process;

kommt folgende Fehlermeldung: "Line 80. Signal distance has a multi 
source.
"

Wenn ich die distance innerhalb des Pozesses zurücksetze, würde ich doch 
bei jedem Taktflankenwechsel distance zurück setzen, oder? Zumindest 
kommt dann folgende Fehlermeldung:

1
echo: process(clk)
2
    begin
3
      distance <= 0;
4
      if (clk'event and clk='1') then      
5
        if (echo_in = '1') then
6
          if (counter2 = 2914) then      
7
            counter2 <= 0;
8
            distance <= distance + 1;            
9
          else
10
            counter2 <= counter2 + 1;
11
          end if;
12
        end if;        
13
      end if;    
14
    end process;


"line 74: Signal distance cannot be synthesized, bad synchronous 
description. The description style you are using to describe a 
synchronous element (register, memory, etc.) is not supported in the 
current software release."

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


Lesenswert?

pico schrieb:
> signal counter: natural range 4999999 downto 0;
Wofür soll das downto gut sein?
Mach da mal besser einen aufsteigenden Wertebereich draus, ich weiß 
nicht, ob das was hilft, aber "üblicher" ist es auf jeden Fall...

von bko (Gast)


Lesenswert?

das Signal "distance" wird wegen dieser Zeilen zu
Flipflops synthetisiert:
1
  if (clk'event and clk='1') then      >>> Takt
2
        if (echo_in = '1') then
3
          if (counter2 = 2914) then      
4
            counter2 <= 0;
5
            distance <= distance + 1;  >>> machwas bei jedem Takt=FFlops
6
            ...
nun kann ein FF nur mit Takt laufen, also kann "distance"
nur im getakteten Teil eines Prozesses zugewiesen werden.

Was Fehlt ist eine Bedingung für das zurücksetzen von
"distance" also in etwa sowas:
1
  if (clk'event and clk='1') then      
2
        if (echo_in = '1') then
3
          if (counter2 = 2914) then      
4
            counter2 <= 0;
5
            distance <= distance + 1;
6
          elsif (hiereinSignalalsBedingung = '1')
7
            distance <= 0;
8
         ....

von vektor (Gast)


Lesenswert?

> Was Fehlt ist eine Bedingung für das zurücksetzen von
> "distance" also in etwa sowas:
1
  if (clk'event and clk='1') then      
2
        if (echo_in = '1') then
3
          if (counter2 = 2914) then      
4
            counter2 <= 0;
5
            distance <= distance + 1;
6
          elsif (hiereinSignalalsBedingung = '1')
7
            distance <= 0;
8
        ....

oder so:
1
  if (clk'event and clk='1') then      
2
    if (echo_in = '1') then
3
      if (counter2 = 2914) then      
4
        counter2 <= 0;
5
        distance <= distance + 1;
6
      end if;
7
    else
8
      distance <= 0;
9
10
    ...


d. h. immer wenn echo_in nicht '1' ist, wird distance auf '0' gesetzt.
Uebrigens brauchst du im letzten Process in der sensitivliste clk nicht
angeben, da dieses signal im process gar nicht auftauch.

der

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


Lesenswert?

pico schrieb:
1
echo: process(clk)
2
    begin
3
      distance <= 0;
4
      if (clk'event and clk='1') then      
5
        if (echo_in = '1') then
6
          if (counter2 = 2914) then      
7
            counter2 <= 0;
8
            distance <= distance + 1;            
9
          else
10
            counter2 <= counter2 + 1;
11
          end if;
12
        end if;        
13
      end if;    
14
    end process;
Böse, böse...
Ich behaupte einfach mal frech, die Simulation passt da sicher nicht zur 
Realität. In der Simulation zählt der Zähler distance fröhlich, in der 
Realität ist der immer 0...

Das Problem: die Realität schert sich nicht um die Sensitivliste. Der 
Prozess wird also "dauernd" ausgeführt. Und nur genau bei einer 
Taktflanke (Dauer=0) ändert sich die distance möglicherweise auf 1, um 
sofort wieder auf 0 zurückgesetzt zu werden. Der Zähler ist also 
abgesehen von unendlich kurzen Zeitabschnitten immer 0.

Die Lösung: das "distance <= 0;" muss woanders hin.

bko schrieb:
> das Signal "distance" wird wegen dieser Zeilen zu Flipflops
> synthetisiert:
Oder eher: wegen dieser Zeile gleich komplett wegoptimiert...
  distance <= 0;

von pico (Gast)


Lesenswert?

So, jetzt funktioniert es und die Werte in cm werden auf der 7 
Segmentanzeige angezeigt ;-) Habe einen distance_buffer eingefügt, der 
meine Distanz zwischenspeichert. Und das Zurücksetzen der distance ist 
nun Abhängig von dem low Pegel meines echo_in's .
1
echo: process(clk)
2
    begin      
3
      if (clk'event and clk='1') then      
4
        if (echo_in = '1') then
5
          if (counter2 = 2914) then      
6
            counter2 <= 0;
7
            distance <= distance + 1;  
8
            distance_buffer <= distance;              
9
          else
10
            counter2 <= counter2 + 1;
11
          end if;          
12
        end if;  
13
        if (echo_in = '0') then        
14
          distance <= 0;
15
        end if;
16
      end if;    
17
    end process;

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


Lesenswert?

pico schrieb:
> Und das Zurücksetzen der distance ist nun Abhängig von
> dem low Pegel meines echo_in's .
Ich hätte da eine Flankenerkennung gebastelt, und mit der Flanke den 
Zählerstand übernommen. Den Zähler zurücksetzen würde ich mit dem 
Aktivieren des Triggereingangs.

von Bugi (Gast)


Lesenswert?

Is it possibble to share the latest version of the code?

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.