Forum: Mikrocontroller und Digitale Elektronik Flanke + Zeitstempel am analogen Signal, Arduino UNO


von Tobias P. (genesis)



Lesenswert?

Guten Abend zusammen,
ich habe derzeit ein, mehr oder weniger, kleines Problem bei meinem 
Projekt bzw. stehe auf dem Schlauch und hoffe auf Hilfe ^^

In meinem Projekt benutze ich einen Ultraschallsensor (200lm450), einen 
US-Treiber (PW0268) und einen Arduino. Das Ziel ist es eine 
Distanzmessung unter Wasser durchzuführen. Das ganze funktioniert auch 
ganz gut für Distanzen 20cm bis ~560cm. Ab ca. 6m funktioniert der 
interne Komparator des US-Treibers nicht mehr, allerdings ist das 
gefilterte Echo-Signal vor dem Komparator noch sehr "schön" zu sehen.

Ich möchte nun dieses gefilterte Echosignal, als analoges Signal, auf 
den Arduino führen und auf die "Flanke" triggern. Daher habe ich mich 
für den internen Komparator des Arduino UNO entschieden. Als 
Referenzspanung führe ich ein PWM Signal über einen 10kOhm Widerstand 
auf Pin D6 [AIN0] und von Pin D6 mit einem Kondensator auf GND für eine 
DC Spannung. Als [AIN1] habe ich dann das analoge Signal (CH3).

Mit einem Interrupt möchte ich die Schwelle/Flanke erkennen und die Zeit 
aufnehmen. Dabei ist noch das Problem, dass ich beim Programmstart auch 
eine hohe Flanke habe (durch das "Ringing" des US-Sensors). Ich möchte 
also die Flanke erkennen, sobald der Sensor im "horchen"-Modus ist. Als 
einfache Lösung hatte ich mir gedacht, ich packe "einfach" ein 
delayMicroseconds(400) rein, nachdem der Rechteck-Puls gesendet wurde. 
Nur bekomme ich hier dann Zeitwerte von 408 µs zurück. Das dürfte ja 
eigentlich nicht passieren, da die Referenzspannung zu dem Zeitpunkt 
noch deutlich über dem Analogsignal hängt(?). Ebenfalls ändert sich an 
den 408µs nahezu nichts wenn ich die Distanz mal auf 30cm verkürze bzw. 
auf 80cm erhöhe. Ich denke mein Code ist dann fehlerhaft.

Vielleicht gibt es aber auch eine viel einfachere bzw. besssere Lösung? 
Ansonsten könnte ich mir auch vorstellen, dass der Interrupt nicht ganz 
so funktioniert wie ich mir das denke..


Angehängte Bilder
 - Messungen für 70cm, 9m, 50m von links nach rechts [gemessen im Becken 
2m x 60m]
 - Testmessung mit Komparator Signal [gemessen in Behälter 1m x 1m]

CH1: Rechteck-Signal -> Impuls zum anregen des Sensors
CH2: Echo-Signal nach Treiber-internem Komparator.
CH3: Gefiltertes Echo-Signal, nach internem Bandpass und vor dem 
Komparator
CH4: Referenzspannung für Arduino UNO Komparator (Gilt nur 
Komparator-Bild, in den anderen Bildern stellt CH4 den Strom am Sensor 
dar [Stromzange])


Datenblätter
http://www.prowave.com.tw/pdf/undertx.pdf      -> US-Sensor
http://www.farnell.com/datasheets/1714673.pdf  -> Sensor-Treiber

Arduino Code für Komparator und Interrupt
1
#include "analogComp.h"
2
3
4
#define OutputPIN 13// Gibt Rechtecksignal auf Schaltung
5
#define echoPin 12 //Nimmt Echosignal auf
6
7
#define referenceVoltagePin 3      //reference signal for comparator [AIN0]
8
9
#define thresholdvalue 180        // 0=0V, 255 = 5V
10
                                   //Referenzspannung in der Schaltung sind ~2V => Pegel von 102
11
12
volatile boolean obstacle = false;
13
14
15
16
void setup() {
17
  
18
  analogComparator.setOn(AIN0, AIN1); 
19
  analogWrite(referenceVoltagePin, thresholdvalue);
20
21
  Serial.begin(9600);
22
23
  pinMode(referenceVoltagePin, OUTPUT);
24
  pinMode(OutputPIN, OUTPUT);
25
  pinMode(echoPin, INPUT);
26
} //end-setup
27
28
29
void loop() {
30
  
31
  unsigned long start, ende, dauer = 0;
32
33
34
35
  analogComparator.disableInterrupt();    //Interrupt soll nicht IMMER aktiv sein
36
  Pulse();                                //Rechteck-Impuls, US-Sensor anregen
37
  delayMicroseconds(400);                 //"Ringing" beim Start des Sensors auslassen
38
39
 
40
  start = micros();
41
  analogComparator.enableInterrupt(ownTest);     //Interrupt starten, sobald Sensor im "horchen" Modus
42
 
43
  if(obstacle==true){
44
    ende = micros(); 
45
     obstacle = false;
46
   }
47
  
48
 
49
50
   dauer = ende-start;
51
   Serial.println((dauer));
52
53
  delay(100);
54
 
55
}//loop-ende
56
57
58
59
//myFunction
60
void ownTest(){
61
  obstacle = true; 
62
}
63
64
//Rechteck-Impuls
65
void Pulse(){   
66
  digitalWrite(OutputPIN, HIGH);
67
  delayMicroseconds(100);              //Pulsbreite
68
  digitalWrite(OutputPIN, LOW);
69
}

Beitrag #5623392 wurde vom Autor gelöscht.
von Felix U. (ubfx)


Lesenswert?

Mit if (obstacle == true) prüfst du ja nur genau den Moment, in dem das 
if evaluiert wird.

Hast du mal
1
while (!obstacle);
2
3
ende = micros();
probiert?

von Jürgen W. (Firma: MED-EL GmbH) (wissenwasserj)


Lesenswert?

Ergänzend zum obigen Kommentar würde ich den Ende-Zeitstempel direkt in 
der ISR "ownTest" ausführen lassen - damit bist Du präziser.

Hinweis zur Terminologie: "Ringing" verwendet man  eher bei 
Resonanzeffekten; bei Dir liegt aber ein Nebensprechen vor - also 
"Crosstalk".

von Tobias P. (genesis)


Angehängte Dateien:

Lesenswert?

Vielen Dank für die Antworten.
Die Änderung von der if() - Anweisung zur while(!obstacle) hat super 
funktioniert :-)
Nach einigem rumprobieren habe ich noch gesehen, dass ich die Flag aus 
dem Interrupt am Anfang der loop() nochmal zurücksetzen muss, da diese 
sonst für immer auf "true" bleibt.

Nun bin ich auch schon fast zufrieden. Lediglich zeigt mir der 
Zeitstempel aus dem Interrupt ca. 100µs zuviel an. Warum das passiert 
kann ich nicht ganz nachvollziehen.

Als Referenzspannung speise ich nun direkt 3,3V vom Arduino ein. Ich 
hatte hier kurz die Befürchtung, dass die PWM-Anweisung etwas Zeit 
kostet und ich daher die 100µs zuviel hatte.. aber hier hat sich nichts 
geändert.

Am Montag kann ich dann wahrscheinlich nochmal im 50m Becken messen und 
schauen ob die ~100µs auch bei längeren Distanzen auftreten oder ob die 
Abweichung doch größer wird.
1
#include "analogComp.h"
2
#define OutputPIN 13// Gibt Rechtecksignal auf Schaltung
3
4
5
volatile boolean obstacle = false;
6
 
7
void setup() {
8
  
9
  analogComparator.setOn(AIN0, AIN1); 
10
  pinMode(OutputPIN, OUTPUT);
11
12
  Serial.begin(9600);
13
14
} //end-setup
15
16
17
void loop() {
18
  unsigned long start, dauer, ende = 0;
19
  
20
  analogComparator.disableInterrupt();           //Interrupt soll nicht IMMER aktiv sein
21
  obstacle = false;                              //obstacle bei erneutem loop() durchlauf zurücksetzen
22
  
23
  start = micros();
24
  Pulse();                                       //Rechteck-Impuls, US-Sensor anregen
25
  delayMicroseconds(300);                        //"Spitzen" des analogen Signals beim Start überspringen
26
  analogComparator.enableInterrupt(ownTest);     //Interrupt starten, sobald Sensor im "horchen" Modus 
27
                                                 //bzw. Interrupt starten sobald analoges Signal unter  
28
                                                 //Referenzspannung liegt                                           
29
 
30
  while(!obstacle){
31
    obstacle = false;                           //solange kein Hindernis erkannt wird, obstacle = false
32
  }
33
34
   ende = ownTest();
35
   dauer = ende-start;
36
   
37
  Serial.print("dauer: ");Serial.println(dauer);
38
  delay(100);
39
  
40
}//loop-ende
41
42
43
44
//ISR
45
unsigned long ownTest(){
46
  unsigned long ende = 0;
47
  obstacle = true;  
48
  ende = micros();
49
  return ende; 
50
}
51
52
//Rechteck-Impuls
53
void Pulse(){   
54
  digitalWrite(OutputPIN, HIGH);
55
  delayMicroseconds(100);              //Pulsbreite
56
  digitalWrite(OutputPIN, LOW);
57
}

von Felix U. (ubfx)


Lesenswert?

Tobias P. schrieb:
> Als Referenzspannung speise ich nun direkt 3,3V vom Arduino ein.

Läuft der Arduino selbstauch mit 3,3 V? Und ist das rote Signal direkt 
am Analogen Input gemessen?

Der Atmega clampt intern eigentlich mit Schottky-Dioden gegen die 
Versorgungsspannung. Und eine Wechselspannung solltest du da sowieso 
nicht anschließen. Wenn das rote Signal nicht direkt am AIN Pin gemessen 
ist, könntest du sein, dass du durch die Dioden Richtung 3,3V 
Nichtlinearitäten bekommst und das den Komparator verfälscht.

Und das hier:
1
while(!obstacle){
2
    obstacle = false;                           //solange kein Hindernis erkannt wird, obstacle = false
3
  }

Ergibt auch keinen Sinn. Damit baust du dir eine klassische Race 
Condition.

Richtig wäre
1
while (!obstacle);
2
obstacle = false;

: Bearbeitet durch User
von Jürgen W. (Firma: MED-EL GmbH) (wissenwasserj)


Lesenswert?

Du hast da was mit den Interrupts wohl nicht ganz verstanden.

Ich habe gemeint, daß Du "Start" und "Ende" als globale Variablen 
definierst. Dann wird beim Aufruf des Interrupts sofort Ende ermittelt 
(das am besten gleich als erste Aktion).

Hier mal der Code, wie ich ihn verwenden würde. Dazu ein paar Hinweise:
1. Konstanten einsetzen, wo sinnvoll - macht die Nachbearbeitung 
einfacher
2. Konstanten immer in Großbuchstaben.
3. Funktionen tun etwas, man hat also idR. ein Verb im Funktionsnamen 
drin: Anstatt Pulse() also "GeneratePulse()", "ReceivePing()", 
"Wait4Ping()" o.ä.
4. Wenn Du mal jobmäßig etwas internationaler wirst, wäre es gut die 
Kommentare und auch sämtliche Funktionsnamen, Variablen, etc. auf 
englisch zu machen - es liest sich auch leichter, wenn davor if, for, 
o.ä. steht.
5. Am besten 4 Einrückungen (als Leerzeichen), nicht nur zwei, das ist 
etwas wenig.
6. Jetzt wird's kleinlich: Der Terminus "obstacle" ist zwar nicht 
falsch, aber ich würde da etwas Generischeres nehmen, eben wie u.a. 
"pingFound". Ob die Antwort wirklich durch einen Gegenstand enstanden 
ist oder die Hardware für das Signal verantwortlich ist, weißt Du nicht 
und Du kannst auf diesem Weg das Codefragment auch einfacher für andere 
Anwendungen portieren.
7. Die while()-Schleife hat keine Notfall-Abbruchmöglichkeit, ich habe 
das ergänzt.

1
#include "analogComp.h"
2
3
#define US_PULSEPIN   13                // Rectangular signal output
4
#define US_PULSEWIDTH 100               // Pulse width in us
5
#define US_DELAY      300               // Delay in us before receiver is armed
6
#define US_TIMEOUT    1000              // Timeout in us to leave loop if no ping is received.
7
8
volatile unsigned long tsStt, tsEnd;    // global timestamps for start and end
9
volatile unsigned long pingFound;       // set to one, when ping was detected
10
11
12
void setup()
13
{
14
    analogComparator.setOn(AIN0, AIN1); 
15
    pinMode(OutputPIN, OUTPUT);
16
    Serial.begin(9600);
17
}
18
19
20
21
void loop()
22
{
23
    unsigned long i;                        // internal variable
24
  
25
    analogComparator.disableInterrupt();    // Interrupt only active after pulse generation!
26
27
    pingFound = false;                      // Reset
28
    tsStt = micros();
29
    GeneratePulse();                        // Rect. pulse, excite US-sensor
30
    delayMicroseconds(US_DELAY);            // Delay to avoid erratic detection of sent signal.
31
    analogComparator.enableInterrupt(ownTest);  // Arm Interrupt after crosstalk from sent pulse has vanished.
32
33
    i = 0;                                  // 'Emergency' exit if no ping is received.
34
    while (!pingFound && (i < US_TIMEOUT))  // Remain here until ISR or timeout detected.
35
    {
36
        i = micros() - tsStt;
37
    }
38
39
    if (counter == 0)
40
    {
41
        Serial.print("Timeout - no ping detected.");
42
    }
43
    else
44
    {
45
        Serial.print("Duration: ");
46
        Serial.println(ts_end-ts_stt);
47
    }
48
    delay(100);
49
}
50
51
52
53
// ISR
54
void ownTest()
55
{
56
    pingFound = true;  
57
    tsEnd = micros();
58
}
59
60
61
// Rectangular Pulse Generation
62
void GeneratePulse()
63
{
64
    digitalWrite(US_PULSEPIN, HIGH);
65
    delayMicroseconds(US_PULSEWIDTH);
66
    digitalWrite(US_PULSEPIN, LOW);
67
}

: Bearbeitet durch User
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.