Forum: Mikrocontroller und Digitale Elektronik Problem mit For Schleife


von Sebi (Gast)


Lesenswert?

Hey ich hab da mal ein Problem ich hab ein Programm geschreiben um mit 
den HC-SR 04 Entfernungen auszumessen. Das klappt auch super. Nun hab 
ich gemerkt das ab und zu mal in der Messung ein wert aus der reihe tanz 
und dachte mir ich mach 2 Messungen mehr und zieh den höchsten und 
geringsten wert vom Mittelwert ab. Da ganze funktioniert auch, aber ich 
bekomm beim Build immer 2 Warnings und 2 Messages.

kann mir vielleicht mal erklären warum ich die bekomme und was eine 
abhilfe wäre:
1
void HC_SR_04()
2
{
3
  unsigned char i,k;
4
  unsigned short Timedifferenz,Ergebnis = 0;
5
  unsigned short Messung[8];
6
  unsigned short min = 65535, max = 0;
7
  for (i=0;i<=9;i++)
8
  {
9
    wiederholung:;
10
    complete_hc = 0;
11
    PORTB |= (1<<HC_TRIGGER);
12
    _delay_us(40);
13
    PORTB &= ~(1<<HC_TRIGGER);
14
    while(complete_hc == 0)
15
    ;
16
    if (Timestart == Timestop)
17
    {
18
      Error();
19
      goto wiederholung;
20
    }
21
    else if (Timestart < Timestop)
22
    {
23
      Timedifferenz = (Timestop-Timestart);  
24
    }
25
    else if (Timestart > Timestop)
26
    {
27
      Timedifferenz = (Timestop-Timestart+65535);
28
    }
29
    else
30
    {
31
      Error();
32
      goto wiederholung;
33
    }
34
    
35
    
36
    Messung[i] = Timedifferenz;
37
    
38
    
39
  }
40
  
41
  for (k=0;k<=9;k++)
42
  {
43
    Ergebnis = Ergebnis+ Messung[k];
44
    if (Messung[k] <= min)
45
    {
46
      min = Messung [k];
47
    }
48
    if (Messung [k] >= max)
49
    {
50
      max = Messung[k];
51
    }
52
    
53
  }
54
  Ergebnis = Ergebnis - (min+max);
55
  Ergebnis_HC = Ergebnis / 8;
56
  if (error_counter != 0)
57
  {
58
    error_counter --;
59
  }
60
  
61
}

das ist der Code.

Und hier die Fehler :

    (247,31): warning: iteration 8u invokes undefined behavior 
[-Waggressive-loop-optimizations]
       Ergebnis = Ergebnis+ Messung[k];
                                   ^
(245,2): info: containing loop
      for (k=0;k<=9;k++)
      ^
(240,14): warning: iteration 8u invokes undefined behavior 
[-Waggressive-loop-optimizations]
       Messung[i] = Timedifferenz;
                  ^
(211,2): info: containing loop
      for (i=0;i<=9;i++)
      ^

von Tom (Gast)


Lesenswert?

> unsigned short Messung[8];
> Messung[i]
Welche Werte darf i haben?

> for (i=0;i<=9;i++)
Welche Werte nimmt i an?

von Dussel (Gast)


Lesenswert?

Sebi schrieb:
> (247,31): warning: iteration 8u invokes undefined behavior
> [-Waggressive-loop-optimizations]
>        Ergebnis = Ergebnis+ Messung[k];
k geht bis 9, Messung aber nur bis 7. Das andere dürfte was ähnliches 
sein.

von Sebi (Gast)


Lesenswert?

Oh das hab ich ganz vergessen mit zu ändern danke.

von Sebi (Gast)


Lesenswert?

Habs jetzt geändert und Warnung ist weg besten Dank.

von Rolf M. (rmagnus)


Lesenswert?

Aus dem Grund verteilt man solche Zahlen besser nicht auf das ganze 
Programm sondern definiert sich einmal eine symbolische Konstante und 
verwendet die an allen Stellen, so dass man immer nur genau eine Stelle 
anpassen muss, wenn man die Größe mal ändern möchte.

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Aus dem Grund verteilt man solche Zahlen besser nicht auf das ganze
> Programm sondern definiert sich einmal eine symbolische Konstante und
> verwendet die an allen Stellen, so dass man immer nur genau eine Stelle
> anpassen muss, wenn man die Größe mal ändern möchte.

oder verwendet countof(), um sicher zu gehen, dass beides 
zusammengehört.

von M. K. (sylaina)


Lesenswert?

Und vom goto würd ich mich auch verabschieden. So passieren nämlich auch 
solche Fehler wie oben: Das erste if und das else am Schluss 
unterscheiden sich nicht:

Sebi schrieb:
1
     if (Timestart == Timestop)
2
     {
3
       Error();
4
       goto wiederholung;
5
     }
6
     ...
7
     else
8
     {
9
       Error();
10
       goto wiederholung;
11
     }

Das kannste mindestens einen Teil von löschen.

von Joe F. (easylife)


Lesenswert?

Dass dein Array Messung[] zu klein ist hast du jetzt gelernt.

Nun zu den anderen Problemen deines Programmes:

a)
1
    if (Timestart == Timestop)
2
    {
3
      Error();
4
      goto wiederholung;
5
    }
6
    else if (Timestart < Timestop)
7
    {
8
      Timedifferenz = (Timestop-Timestart);  
9
    }
10
    else if (Timestart > Timestop)
11
    {
12
      Timedifferenz = (Timestop-Timestart+65535);
13
14
      // macht das gleiche wie:
15
      // Timedifferenz = Timestop-Timestart-1;
16
      // also nicht wirklich hilfreich
17
    }
18
    else
19
    {
20
      // warum diese zusätzliche else condition?
21
      // das wäre der gleiche Fall wie der erste:
22
      // if (Timestart == Timestop)
23
24
      Error();
25
      goto wiederholung;
26
    }

Da also
Timedifferenz = (Timestop-Timestart+65535)
eh falsch ist, kannst du die ganze Berechnung auf das hier 
runterbrechen:
1
    if (Timestart == Timestop)
2
    {
3
      Error();
4
      goto wiederholung;
5
    }
6
    else 
7
        Timedifferenz = Timestop-Timestart;

Selbst bei einem Überlauf zwischen Timestop und Timestart kommt das 
richtige Ergebnis heraus.
Bei zwei Überläufen allerdings nicht mehr, und das ist eventuell auch 
der Grund für deine seltsamen Ausreißer.
In welchem Wertebereich befindet sich denn Timedifferenz üblicherweise?

b)
Sebi schrieb:
> und dachte mir ich mach 2 Messungen mehr und zieh den höchsten und
> geringsten wert vom Mittelwert ab.

Warum? Was soll das bringen?
Um Ausreisser herauszunehmen sortiert man üblicherweise die Messwerte 
der Größe nach, und nimmt sich einen (oder mittelt ein paar Werte) aus 
der Mitte des sortierten Feldes. Die Ausreisser bleiben dadurch komplett 
unberücksichtigt.

c)
Ergebnis = Ergebnis+ Messung[k];

Hier addierst du 10 16-bit Werte in einer Variablen, die selbst nur 
16-bit breit ist.
Das funktioniert nur, wenn die zu addierenden Werte so klein sind, dass 
"Ergebnis" nicht überläuft (also im Mittel <= 6553).
Ist das sichergestellt?

d)
Ergebnis_HC = Ergebnis / 8;

Hier stimmt dann der Teiler nicht. Müsste 10 sein.

Nachtrag:
Achso, jetzt verstehe ich. Du suchst dir den kleinsten und größten Wert 
und gehst davon aus, dass dies die Ausreisser sind. Daher ziehst du 
diese von der Summe ab, und teilst durch 8.
Kann man hilfsweise so machen.
Trotzdem mal prüfen, ob 10 Messwerte aufaddiert ohne Überlauf in 
"Ergebnis" passen.

: Bearbeitet durch User
von Sebi (Gast)


Lesenswert?

Also der Teiler ist richtg da ich min und max aus der Rechnung nehme:
1
Ergebnis = Ergebnis - (min+max);
2
  Ergebnis_HC = Ergebnis / 8;

das mit den 16bit werten passt auch der max wert einer Messung liegt bei 
300.
400*10 Messung sind 4000 liegt unterhalb der 65535.

Die andern tipps hab ich schon umgestzt besten dank.

mal so ne frage wie würde den der Code aussehen wenn ich 20 messungen 
pro durchlauf mache und nur die 10 in der mitte zur Mittelwert Bildung 
nutze?

von Joe F. (easylife)


Lesenswert?

Sebi schrieb:
> mal so ne frage wie würde den der Code aussehen wenn ich 20 messungen
> pro durchlauf mache und nur die 10 in der mitte zur Mittelwert Bildung
> nutze?

Du musst die 20 Array-Einträge nach der Messung der Größe nach 
sortieren, und nimmst aus dem sortierten Array dann die Werte 5..14.

Ich würde aber erstmal gucken, ob dein Problem mit den Ausreissern nicht 
schon dadurch verschwindet, dass du dein Array groß genug machst.

Es liegt momentan ziemlich nahe, dass die letzten 2 Werte einfach nur 
Müll sind, da sie momentan in anderweitig benutztem Speicher liegen.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Sebi schrieb:
> das mit den 16bit werten passt auch der max wert einer Messung liegt bei
> 300.

naja, wenn der Maxwert da liegt und Du "Ausreißer" hast, dann teste das 
auch. Also zumindest < 400 oder so. Und m.E. auch ohne goto, also z.B.:
1
    for(i=0; i<10; /*nur bei Erfolg ++ am Ende */)
2
    {
3
        complete_hc = 0;
4
        PORTB |= (1<<HC_TRIGGER);
5
        _delay_us(40);
6
        PORTB &= ~(1<<HC_TRIGGER)
7
        while(!complete_hc) {}
8
        Timedifferenz = (Timestop-Timestart);  
9
        if((Timedifferenz < 1) || (Timedifferenz > 400)) 
10
        {
11
            Error();
12
        } 
13
        else
14
        {
15
            Messung[i++] = Timedifferenz;
16
        }
17
    }
statt 1 den minmalen plausiblen Wert eingeben. Wenn 0 falsch ist, wird 
es 3 oder 5 vermutlich auch sein, oder?

ich gebe zu, eine vergewaltigung der for-Schleife. Aber der Preis einer 
separaten inneren Schleife wäre mir in diesem Fall zu hoch. Und i-- im 
Errorfall auch.

von Sebi (Gast)


Lesenswert?

Habs jetzt geändert alles, jedoch hab ich immer noch nach ca. 15 
Ergebnissen leichte ausreißer(also nach 150 Messungen). Wenn ich jetzt 
ne aus 20 Messungen die 12 Aus der mitte nehme reicht da Bubblesort für 
aus oder ist es für 20 Messungen zu Zeit intensiv? was wäre dann den 
eine schnellere alternative ?

von A. S. (Gast)


Lesenswert?

Sebi schrieb:
> reicht da Bubblesort für aus

JA!

Wenn Du 1000 Messungen hast, kannst Du über optimierung nachdenken. Aber 
nur zu:

1) Quick-Bubble-Sort ;-)
2) nur die 3 kleinsten rausfiltern und die 3 größten. Also wie bisher, 
nur dann 3 mal.
3) die min- und max-Tests mit einbauen, dann sollten es noch weniger 
sein.

Aber es gibt auch eine Standard-C-Funktion zum sortieren: qsort(). Die 
kannst Du kaum durch eigenes basteln schlagen.

von A. S. (Gast)


Lesenswert?

Sebi schrieb:
> nach ca. 15 Ergebnissen leichte ausreißer(also nach 150 Messungen).
Du solltest Dir die Rohwerte ausgeben lassen, um zu wissen, was da 
passiert.

Wenn (als Beispiel) 10 Werte im Bereich von 190 bis 210 wild verteilt 
liegen, ist es etwas anderes als wenn Du manchmal auch 999 oder 1 misst. 
Bei ersterem reicht die Mittelung, bei solltest Du den Fehler 
korrigieren oder Kriterien zu dessen Erkennung herausarbeiten.

von Joe F. (easylife)


Lesenswert?

Achim S. schrieb:
> Und m.E. auch ohne goto, also z.B.:

Ja, das goto ist hässlich.
Und Ausreisser gleich bei der Erfassung zu erkennen, und gleichzeitig 
auch sicherzustellen, dass der Wertebereich für die spätere 
Aufsummierung nicht überschritten wird, ist eine wirklich sehr gute 
Idee.
Statt einem halben "for" fände ich dann "while" allerdings irgendwie 
schöner:
1
    i=0;
2
    while(i<10)
3
    {
4
        complete_hc = 0;
5
        PORTB |= (1<<HC_TRIGGER);
6
        _delay_us(40);
7
        PORTB &= ~(1<<HC_TRIGGER)
8
        while(!complete_hc)
9
            ;
10
11
        Timedifferenz = (Timestop-Timestart);  
12
        if((Timedifferenz < 1) || (Timedifferenz > 400)) 
13
            Error();
14
        else
15
            Messung[i++] = Timedifferenz;
16
    }

Achim S. schrieb:
> 2) nur die 3 kleinsten rausfiltern und die 3 größten. Also wie bisher,
> nur dann 3 mal.

Das könnte man auch so erledigen, indem man die 3 größten und die 3 
kleinsten Werte im Array einfach mit 0 überschreibt.
Beim Mittelwert-bilden über das gesamte Array fallen genau diese Werte 
dann also automatisch aus der Summe raus.

: Bearbeitet durch User
von Sebi (Gast)


Lesenswert?

Das mit der while gefällt mir auch schon besser vielen Danke. Wie groß 
der Ausreißer ist schau ich später mal nach gibt es für bubblesort auch 
eine fertige Funktion wie für qbubblesort und in welcher h sind die den?

von Achim (Gast)


Lesenswert?

qsort ist nicht quick-bubblesort, sondern einiges komplizierter und 
effektiver, bei größeren Datenmengen. Habe ich aber jahrelang auch 
gedacht und es "eben selbst implementiert", um den Overhead der compare 
Funktion zu sparen.

Quick-bubblesort ist ja nur, den inneren Zähler jedesmal 1 weniger 
zählen zu lassen.

von Nop (Gast)


Lesenswert?

Achim S. schrieb:
> Aber es gibt auch eine Standard-C-Funktion zum sortieren: qsort(). Die
> kannst Du kaum durch eigenes basteln schlagen.

Doch, kann man, und zwar recht problemlos. Denn in C artet jeder 
Vergleich und jeder Tausch in einen Funktionsaufruf aus, und dann auch 
noch über einen Zeiger. Zudem ist Quicksort bei kleinen Listen (bis etwa 
100 Elemente) langsam. Und rekursiv, was man embedded auch nicht will.

Abhilfe: man implementiert einen Shellsort mit der Sequenz nach Ciura. 
Shellsort ist iterativ, in-place, bei diesen Längen schneller als 
Quicksort, hat keine Pathologien, braucht keine Funktionsaufrufe.

Manche embedded-libs implementieren qsort zwar ohnehin von selber als 
Shellsort, aber der Overhead durch die Funktionaufrufe bleibt.

von Joe F. (easylife)


Lesenswert?

Für 20 Einträge ist auch insertionsort recht effektiv:
1
void insertionsort (unsigned short* a, int n)
2
{
3
    int i, j;
4
    unsigned short t;
5
6
    for (i=1; i<n; i++) 
7
    {
8
        t=a[i];
9
        j=i;
10
        while (j>=1 && a[j-1]>t) 
11
        {
12
            a[j]=a[j-1];
13
            j=j-1;
14
        }
15
        a[j]=t;
16
    }
17
}

von Sebi (Gast)


Lesenswert?

So hab jetzt einige ratschläge befolgt. bei großen Entfernungen also 
alles ab 10 cm Entfernung klappt es jetzt. jedoch schwanken die werte 
bei nahen Hindernissen kleiner 10cm gerne um 2-3 cm.

von Sebi (Gast)


Lesenswert?

Der Code schaut jetzt so aus hab den Timer auch mal empfindlicher 
eingestellt daher jetzt auch ein Max von 4000.
1
  unsigned char i=0,k;
2
  unsigned short Timedifferenz;
3
  uint32_t Ergebnis = 0;
4
  uint16_t Messung[20];
5
  while(i<20)
6
  {
7
      complete_hc = 0;
8
      PORTB |= (1<<HC_TRIGGER);
9
      _delay_us(60);
10
      PORTB &= ~(1<<HC_TRIGGER);
11
      while(!complete_hc);
12
      Timedifferenz = (Timestop-Timestart);
13
    if((Timedifferenz < 2) || (Timedifferenz > 4000))
14
    Error();
15
    else
16
    Messung[i] = Timedifferenz;
17
    i++;
18
    }
19
  
20
  int l, j;
21
  for (l = 0; l < 20 - l; ++l)
22
  {
23
24
    for (j = 0; j < 20 - l - 1; ++j)
25
    {
26
      if (Messung[j] > Messung[j + 1])
27
      {
28
        int tmp = Messung[j];
29
        Messung[j] = Messung[j + 1];
30
        Messung[j + 1] = tmp;
31
      }
32
    }
33
  }
34
  for (k=4;k<=15;k++)
35
  {
36
    Ergebnis = Ergebnis+ Messung[k];    
37
  }
38
  
39
  Ergebnis_HC = Ergebnis / 12;
40
  if (error_counter != 0)
41
  {
42
    error_counter --;
43
  }
44
  
45
}

von Sebi (Gast)


Lesenswert?

Also hab jetzt ein Messobjekt 1.50m entfernt.

Ergebniss liegt zwischen 1.49 und 1.51m das ist ok da der Timer auf eine 
Genauigkeit +- 1cm eingestellt ist.

Bei messungen unter 10cm hier jetzt mal 6cm habe ich Ergebnisse im 
Bereich vom 3-8cm. jemand eine Idee warum?

von Joe F. (easylife)


Lesenswert?

Dazu wäre es hilfreich den Code zu zeigen, der complete_hc, Timestop und 
Timestart setzt.

von Sebi (Gast)


Lesenswert?

1
ISR(INT0_vect)                                    /* Interrupt bei Pegel Wechsel an HC_ECHO */
2
{
3
  if (complete_hc == 0)    /* Solange die Ergebnisse nicht abgeholt wurden wird die Interupt Routine nicht ausgefuehrt */
4
  {
5
    if (MCUCR & (1 << ISC00))                           /* Wenn Pegel wechsel von LOW auf High */
6
    {
7
      Timestart = timer0;                                   /* Zeit Low >> High nehmen */
8
    }
9
    if (!(MCUCR & (1 << ISC00)))                         /* Wenn Pegel wechsel von High auf Low */
10
    {
11
      Timestop = timer0;                                     /* Zeit High >> Low nehmen */
12
      complete_hc = 1;               /* Routine sperren solange die Ergebnisse nicht gespeichert wurden */
13
    }
14
    
15
    MCUCR ^= (1<<ISC00);                    /* Wechsel der Interupt Routine (High >> Low) oder (Low >> High) */
16
  }
17
  
18
}

von Sebi (Gast)


Lesenswert?

Habs gefunden,

habe die Auflösung noch etwas erhöht auch 3.33mm Genauigkeit.

hab jetzt bei Entfernungen bis 1m eine Genauigkeit von +/-0.666cm

und bei Entfernungen von 1m bis 3m eine Genauigkeit von +/- 1.333cm

die Ungenauigkeit die ich jetzt noch hab ist dann Mathematischer Natur.

von Eric B. (beric)


Lesenswert?

Alternativ könnte man nur die 3 max und min werte in zwei Arrays 
speichern und bei jedem neuen Wert schauen ob es in das min- oder 
max-Array gespeichert werden soll, oder zu der laufende Summe 
hinzuaddiert.

Wenn der neue Wert zB in das max-Array gespeichert wird, wird statt der 
gemessene Wert das kleinste Element aus dem max-Array zu die Summe hinzu 
addiert.

z.B.
max[] = {200, 250, 300} und value = 275
Dann wird 200 zu der Summe hinzu addiert, und max[] enthält danach {250, 
275, 300}.

Die min und max Arrays werden während den Messungen sortiert gehalten 
(insertion sort).
1
#define MIN_LENGTH 3
2
#define MAX_LENGTH 3
3
#define NR_OF_MEASUREMENTS (MIN_LENGTH + MAX_LENGTH + 20)
4
5
uint16_t perform_measurements()
6
{
7
  uint16_t sum;
8
  uint16_t min[MIN_LENGTH];
9
  uint16_t max[MAX_LENGTH];
10
11
  // initialize
12
  memset(min, 0, sizeof(min));
13
  memset(max, 0, sizeof(max));
14
15
  // do measurements
16
  for(uint16_t i = 0; i < NR_OF_MEASUREMENTS; i++) {
17
    value = GET_MEASUREMENT_VALUE();
18
19
    // check with minima
20
    for(uint16_t j = 0; j < MIN_LENGTH; j++) {
21
      if(value < min[j]) {
22
         // swap value and min[j]
23
         uint16_t tmp = x; x = y; y = tmp;
24
      }
25
    }
26
27
    // check with maxima
28
    for(uint16_t j = 0; j < MAX_LENGTH; j++) {
29
      if(value > max[j]) {
30
         // swap value and max[j]
31
         uint16_t tmp = x; x = y; y = tmp;
32
      }
33
    }
34
  
35
    sum += value;
36
  }
37
38
  return sum / (NR_OF_MEASUREMENTS - MIN_LENGTH - MAX_LENGTH);
39
}

von ms (Gast)


Lesenswert?

Hallo,

der uC braucht eine gewisse Zeit um Anweisungen auszuführen. Diese Zeit 
sollte man bei der Fehler Berechnung miteinbeziehen (Start Stop 
abfrage). Bei großen Distanzen wird der Fehler klein sein. Je kleiner 
die Distanz wird umso größer wird der Fehler.

ms

von Joe F. (easylife)


Lesenswert?

Machs doch mal so:

In deiner main():
1
// ganz am Anfang:
2
MCUCR |= (1<<ISC00); // prepare interrupt for low to high transition
3
4
(...)
5
6
timer_state = 2;
7
PORTB |= (1<<HC_TRIGGER);
8
_delay_us(60);
9
PORTB &= ~(1<<HC_TRIGGER);
10
while(timer_state > 0)
11
  ;

und der Interrupt:
1
ISR(INT0_vect)
2
{
3
  unsigned short t = timer0;
4
5
  if (timer_state == 2)
6
  {
7
    Timestart = t;
8
    MCUCR &= ~(1<<ISC00); // prepare interrupt for high to low transition
9
    timer_state = 1;
10
  }
11
  else if (timer_state == 1)
12
  {
13
    Timestop = t;
14
    MCUCR |= (1<<ISC00); // prepare interrupt for low to high transition
15
    timer_state = 0;
16
  }
17
}

So nimmst du in deiner Interrupt routine immer direkt nach 
Flankenwechsel erstmal die Zeit, ohne dass noch andere Instruktionen 
verarbeitet werden müssen.
Danach entscheidest du dann, ob und was du mit dieser Zeitinformation 
machst.
Das sollte die Genauigkeit verbessern.

: Bearbeitet durch User
von Sebi (Gast)


Lesenswert?

Ach du meinst bevor ich die If abfrage Starte schonmal den Timerwert 
zwischwenspeichern und dann die if abfragen durchführen?

von Sebi (Gast)


Lesenswert?

1
ISR(INT0_vect)                                    /* Interrupt bei Pegel Wechsel an HC_ECHO */
2
{
3
  unsigned short t_tmp = timer0;
4
  if (complete_hc == 0)    /* Solange die Ergebnisse nicht abgeholt wurden wird die Interupt Routine nicht ausgefuehrt */
5
  {
6
    if (MCUCR & (1 << ISC00))                           /* Wenn Pegel wechsel von LOW auf High */
7
    {
8
      Timestart = t_tmp;                                   /* Zeit Low >> High nehmen */
9
    }
10
    if (!(MCUCR & (1 << ISC00)))                         /* Wenn Pegel wechsel von High auf Low */
11
    {
12
      Timestop = t_tmp;                                     /* Zeit High >> Low nehmen */
13
      complete_hc = 1;               /* Routine sperren solange die Ergebnisse nicht gespeichert wurden */
14
    }
15
    
16
    MCUCR ^= (1<<ISC00);                    /* Wechsel der Interupt Routine (High >> Low) oder (Low >> High) */
17
  }
18
  
19
}

von Joe F. (easylife)


Lesenswert?

Ja, so gehts auch. Ich würde halt nur das MCUCR Register immer in einem 
definierten Zustand halten.

MCUCR ^= (1<<ISC00);
ist relativ zufällig beim allerersten mal.

Ich muss ja mal sagen, dass mir dieser Thread hier sehr gefällt.
Der Ton ist durchweg freundlich, viele nützliche Tipps von allen Seiten, 
TO lernt schnell und viel, und das Ergebnis kann sich sehen lassen.

: Bearbeitet durch User
von Sebi (Gast)


Lesenswert?

Da muss ich dir recht geben. Daher auch von mir noch mal sehr vielen 
dank.

Die erste Benutzung vom Register hab ich durch ne Init abgesichert:
1
void int0_init()
2
{
3
  GICR |= (1<<INT0);                                    /* Sonderfunktion INT0 aktivieren */
4
  MCUCR |= (1<<ISC00);                                    /* Interrupt von LOW auf High */
5
}

von Joe F. (easylife)


Lesenswert?

Na, ist doch prima. Sag bitte noch bescheid, wie sich das Zeitnehmen 
direkt am Anfang des Interrupts auf die Genauigkeit ausgewirkt hat.

von Sebi (Gast)


Lesenswert?

Minimal,es  macht 8 Takte im Debug Modus(bei 16 Mhz) weniger fehler, bei 
einem timer der sich alle 20us erhöht kann es bei dem ein oder anderen 
das kippen zwischen den Ergbnissen verhindern.

An der Hardware ist der wert jetzt bei gleicher Entfernung konstant.
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
5
5
5
5
5
5
5
5
5
5
5
5
4
4
4
4
4
4

hier mal die Ergebniss aus der Uart in cm also keine Ausreisser mehr.

hier jetzt auch mal bei 1.495m Entfernung Schreibtisch zu Deckenhöhe:
148
148
148
148
148
149
148
148

schaut auch sehr gut aus angenommen ist der Schall bei 20 Grad Celsius 
im Raum sind 20.8 Grad von daher Top.

von Sebi (Gast)


Lesenswert?

Eine kleine frage hab ich noch:

Kann man ein Reset auch anders Ausführen, als über den WD, also über ein 
direkten befehl?

im Moment hab ich das jetzt so gemacht:
1
void RESET()
2
{
3
  WDTCR |= (1<<WDE);                                         /* WatchDog Aktivieren */
4
   _delay_us(16300);     /* Wartezeit bis WD-Signal benötigt, bevor WD Auslöst, um WD auszulösen und Reset durchzufuehren */
5
}

nur wirklich schön find ich es nicht.

von Joe F. (easylife)


Lesenswert?

Sebi schrieb:
> Minimal,es  macht 8 Takte im Debug Modus(bei 16 Mhz) weniger fehler, bei
> einem timer der sich alle 20us erhöht kann es bei dem ein oder anderen
> das kippen zwischen den Ergbnissen verhindern.

Ah ok.
Also ein "Fehler" von 0.5us.
Auch wenn es in deinem Fall jetzt keine wirkliche Rolle spielt, würde 
ich es prinzipiell so machen um die Fehlerquellen von vorneherein so 
gering wie möglich zu halten.

von ms (Gast)


Lesenswert?

Sebi schrieb:
> Kann man ein Reset auch anders Ausführen

Port Pin Inverter usw.

ms

von M. K. (sylaina)


Lesenswert?

Sebi schrieb:
> Kann man ein Reset auch anders Ausführen, als über den WD, also über ein
> direkten befehl?

Klar, du kannst direkt zur Resetadresse springen wenn du das unbedingt 
willst.

von Sebi (Gast)


Lesenswert?

Also mit setzen des Flags im MCUCSR Register ist es nicht getan ?

von A. S. (Gast)


Lesenswert?

Folgefehler mangels Klammern:

aus meinem
> if((Timedifferenz < 1) || (Timedifferenz > 400))
>         {
>             Error();
>         }
>         else
>         {
>             Messung[i++] = Timedifferenz;
>         }
wurde (zwar noch korrekt, aber "gefährlich")

Joe F. schrieb:
> if((Timedifferenz < 1) || (Timedifferenz > 400))
>             Error();
>         else
>             Messung[i++] = Timedifferenz;

was Du entzerrt hast zu
Sebi schrieb:
> if((Timedifferenz < 2) || (Timedifferenz > 4000))
>     Error();
> else
>     Messung[i] = Timedifferenz;
>     i++;

was hier falsch ist.

von Sebi (Gast)


Lesenswert?

ok stimmt die klammern

von Sebi (Gast)


Lesenswert?

[c]
if((Timedifferenz < 1) || (Timedifferenz > 400))
{
    Error();
}
else
{
     Messung[i] = Timedifferenz;
     i++;
}
[/c)

hab dann mal geändert

von Sebi (Gast)


Lesenswert?

Gibt es eig eine Möglichkeit das delay für den Trigger durch was anders 
zu ersetzten mit selben Effekt ?

von Joe F. (easylife)


Lesenswert?

Sebi schrieb:
> Gibt es eig eine Möglichkeit das delay für den Trigger durch was anders
> zu ersetzten mit selben Effekt ?

Es gibt da viele Möglichkeiten, allerdings ist das ja keine allzu 
schlechte Lösung, wenn dein uC während der Messung nichts anderes tun 
muss.

Allerdings fällt mir bei dieser Gelegenheit ein weiteres Problem auf:
Der Sensor kann max. 50 Messungen pro Sekunde machen.
Die Berechnung der Zeitdifferenz wird vernachlässigbar wenig Zeit 
brauchen, so dass deine Abtastung u.U. viel zu schnell abläuft.
Du solltest nach der Berechnung der Zeitdifferenz also noch ein weiteres 
Delay einbauen, damit die nächste Abtastung frühestens 1/50s nach der 
vorherigen startet.
Als ersten Ansatz könnte man ja die Zeitdifferenz in ms umrechnen, und 
dann ein weiteres Delay einfügen mit t_wait = 20ms - Zeitdifferenz(in 
ms)

: Bearbeitet durch User
von Sebi (Gast)


Lesenswert?

stimmt ein Messzyklus liegt im Moment bei 72us + der Laufzeit.

von Sebi (Gast)


Lesenswert?

Wundert mich nur das er im Moment nicht einen Fehler mehr macht wobei er 
im schnitt mit fast 1kHz läuft.

von Joe F. (easylife)


Lesenswert?

Vielleicht geht es technisch ja und die 50Hz sollten nicht überschritten 
werden, damit das Ding nicht zu heiss wird?
Der Transducer haut unter Umständen ziemlich laute Pulse raus und möchte 
dann ein wenig abkühlen.

von Sebi (Gast)


Lesenswert?

Also hab gerade noch mal alles durchgerechnet: der Programmablauf 
benötigt 72us. Die Messung für eine Entfernung von 10cm nimmt ca 600us 
in Anspruch dann wäre ich bei einer Messfrequenz oberhalb von 1,5kHz

von Sebi (Gast)


Lesenswert?

Also Temperatur ist es schonmal nicht. Das teil läuft seit 2 Stunden 
ohne pause und hat Raumtemperatur.

von Joe F. (easylife)


Lesenswert?

Achso, ne die 50Hz ergeben sich durch die max. zu erwartende Laufzeit.
Bei der Reichweite des Moduls von 3m sind das 17.5-18ms.
D.h. also, je näher dein Objekt ist, desto öfter kannst du abfragen.
Ergibt sich ja dann auch automatisch dadurch, dass complete_hc früher 
auf 1 geht.
Ich würde es trotzdem nicht übertreiben, und auch bei nahen Objekten 
eine vernünftige Zeit warten. Du hast ja auch Echos im Raum, die erstmal 
abklingen sollten.

: Bearbeitet durch User
von Sebi (Gast)


Lesenswert?

Ich hatte das mit den 50 Hz Messfrequenz auch schonmal gelesen aber hab 
mir gerade das Datasheed mal angesehen. Da steht kein Limit der 
Messungen drinne

von Sebi (Gast)


Lesenswert?

Also wenn man kein Signal empfängt geht er sogar 200ms auf High damit 
wären nur 5 Messungen pro sekunde möglich passt auch wenn man den 
Trigger mal beim messen zuhält.

von Joe F. (easylife)


Lesenswert?

Ja, wie gesagt, das ist glaube ich eher ein theoretisches Limit, dass du 
auch bis 3m mit 50Hz abtasten kannst.
Aber es könnte ein Grund für deine Ausreisser sein.

Wenn dein Hauptobjekt sagen wir mal 20cm entfernt ist, und in 30cm 
Entfernung ein weiteres Objekt steht, dann misst du das 1. Echo bei 
20cm.
Dann startest du direkt die nächste Messung, und der Sensor empfäng das 
2. Echo der vorherigen Messung.
Das sieht dann so aus, als ob das Objekt plötzlich nur 10cm entfernt 
ist.

von Sebi (Gast)


Lesenswert?

ok das ist richtig dann müsste ich aber von der Frequenz noch weiter 
runter gehen, da er zwar nur eindeutig bis 4m die echos aber keine 
grenze haben. Bzw die grenze durch ihre Wellenlänge. Aber ich behalte es 
mal im hinter kopf.

von Joe F. (easylife)


Lesenswert?

Vermutlich sind deswegen die 50Hz empfohlen.

Ich glaube ich muss mir mal so ein Modul bestellen. Hab grad selber Bock 
damit rumzuspielen.
So richtig geil wäre ja, man würde das Modul so modifizieren, dass man 
den Empfänger für eine definierte Zeit "taub" stellen kann.
Dann könnte man das 1. Echo messen, wüsste die Entfernung zum ersten 
Objekt, und würde bei der 2. Messung den Empfänger so lange disablen, 
bis das erste Echo vorbei ist. Dann müsste man das nächste Echo "sehen " 
können, usw... Verstehste?

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Eine Frage hätte ich noch: Wofür brauchst Du eigentlich die 
Interrupt-Routine?

Du schaltest doch vermutlich einen Sender mit

>       PORTB |= (1<<HC_TRIGGER);
oder
>       PORTB &= ~(1<<HC_TRIGGER)
ein und wartest dann auf die Antwort. Warum nicht einfach plain code?

Sebi schrieb:
1
  while(i<20)
2
  {
3
    /* einschalten (?) */
4
    PORTB |= (1<<HC_TRIGGER);
5
    /* Zeit Messen */
6
    Timestart = timer0;
7
    delay(40);
8
    /* abschalten */
9
    PORTB &= ~(1<<HC_TRIGGER);
10
    /* warten bis empfang */
11
    while(...); 
12
    /* Ende der Zeitmessung */
13
    Timestop = timer0;
14
    /* wie gewohnt ... */
15
    if((Timedifferenz < 2) || (Timedifferenz > 4000))
16
    {
17
       ...
18
    }
19
    ...
20
  }
Wenn der Empfang schon im Delay sein darf ... na, auch das wäre einfach.

Eventuell macht es sogar Sinn, den Timer neu zu starten für jede 
Messung, dann entfällt die Subtraktion. Oder Du startest die Messung 
beim 0-Durchgang des Timers, dann hast Du die Verzögerung.

von Sebi (Gast)


Lesenswert?

Ich war gerade am überlegen wie viele Messwerte ich Prozentual für die 
Mittelwertbildung nutzen sollte. Dachte da an Gauß mit knapp 2/3 der 
Messergebnisse, da sollten die groben aussreisser ja raus sein oder ?

von Sebi (Gast)


Lesenswert?

Jap versteh was du meinst Joe.

Zum Timer mit den zurücksetzten hatte ich auch schonmal überlegt wurde 
mir aber von abgeraten, da ich im schlimmsten fall eine Zeiteinheit 
verlieren kann, wenn während des zurücksetztens gerde der Timer um 1 
hochzählt.

Zu dem Interrupt ich habe es per Interrupt gemacht, weil es schneller 
ging und ich noch 3 andere Interrupts am laufen habe welche gesperrt 
werden während der Interrupt des HC ausgeführt wird, und somit 
gewährleistet ist das kein Interrupt die Messungstört.

von Achim (Gast)


Lesenswert?

Sebi schrieb:
> weil es schneller ging und ich noch 3 andere Interrupts am laufen habe
> welche gesperrt werden während der Interrupt des HC ausgeführt wird,

Verstehe ich nicht. Was sind das für Interrupts und ist sicher, dass der 
Interrupt der Messung die anderen unterbricht?

von Sebi (Gast)


Lesenswert?

Ja ist weiter oben eingestellt, das keine Interrupts während eines 
Interrupts statt findet.

von A. S. (Gast)


Lesenswert?

Sebi schrieb:
> Ja ist weiter oben eingestellt, das keine Interrupts während eines
> Interrupts statt findet.

hast Du oben was dazu geschrieben? Naja, wenigstens weisst Du nun, warum 
Du manchmal ausreißer hast. Wenn die anderen Interrupts während der 
Messung wichtig sind, brauchst du um ein input capture timer.

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.