Forum: Mikrocontroller und Digitale Elektronik Drehzahlmessung durch Auswerten der Inrerrupteingänge


von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich möchte mit einem induktiven sensor eine Drehzahl ausgeben. Der 
Sensor ist ein PNP-Sensor, der mit 12V betrieben wird, welche dann über 
einen Transistor in 5 V für einen Atmega 8 mit 16Mhz umgewandelt werden 
und an den INT0 Pin gelegt werden.

Ich hab im Moment keine Möglichkeit die Software zu testen, könnt ihr 
bitte mal drüberschauen, ob das so funktionieren wird ?

zur Funktionsweise: Es werden 5 Zeitmessungen zwischen den Impulsen 
gemacht und daraus dann ein Mittelwert gebildet. Daraus wird dann die 
Drehzahl berechnet und auf einem Lcd ausgegeben.

Hier die Auswertung des Interrupteinganges:
1
ISR(INT0_vect)
2
{
3
  static unsigned char ErsteFlankeErkannt = TRUE;
4
  static unsigned int i = 0;
5
  
6
  if (ErsteFlankeErkannt == TRUE)      // Es wurde eine erste Flanke erkannt
7
  {
8
    Timer_01ms = 0;          // Timer für die Messung auf 0 setzen
9
    ErsteFlankeErkannt == FALSE;
10
  }
11
  else
12
  {
13
    for( i=0; i<MAXVALUE; i++)
14
    {
15
      Messungen[i] = Timer_01ms;    // für Zehn Impulse die jeweilige Zeit in Liste abspeichern
16
      ErsteFlankeErkannt == TRUE;
17
    }
18
  }  
19
}

Rest siehe Anhang.

Schon mal im Voraus Danke

von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:

> Ich hab im Moment keine Möglichkeit die Software zu testen, könnt ihr
> bitte mal drüberschauen, ob das so funktionieren wird ?

Nein, sie wird nicht funktionieren.
Teste sie und beheb erst mal die offensichtlichen Fehler. Dann reden wir 
weiter.

zb
1
    for( i=0; i<MAXVALUE; i++)
2
    {
3
      Messungen[i] = Timer_01ms;    // für Zehn Impulse die jeweilige Zeit in Liste abspeichern
4
      ErsteFlankeErkannt == TRUE;
5
    }

welchen Sinn soll es haben, den immer gleichen Messwert zb 5 mal 
abzuspeichern? Du willst 5 verschiedene Messwerte abspeichern, die du 
erhältst indem die ISR 5 mal aufgerufen wurde jeweils einzeln 
abspeichern.

1
  for( i=0; i<MAXVALUE; i++)
2
  {
3
    Summe = Messung + Messungen[i];
4
  }
das ergibt aber keine Summe.

Wie gesagt: teste das alles mal. Zur Not geht das auch ohne µC, indem du 
Computer spielst und dein Programm abarbeitest :-) Ganz im Gegenteil 
sind solche Simulationen sehr erhellend.

von Sebastian W. (sebastian_w29)


Lesenswert?

Da sind so viele Fehler drin, da weiss man ja gar nicht wo man anfangen 
soll. Als erstes wäre es ja mal schön, wenn du ein ohne Fehler 
compilierendes Programm einstellen würdest ...

von Thomas (Gast)


Lesenswert?

Danke für die Antworten.
Sorry für die Fehler, aber ich habe im Moment nur Notepad 
hier...........

Also ich hab da mal die beiden Fehler berichtigt, jetzt müsste das so 
passen:
1
///////////////////////////////////////////////////////////
2
//          Flanken Interrupt
3
//  Wird bei steigender Flanke am INT0 Eingang aufgerufen
4
///////////////////////////////////////////////////////////
5
6
ISR(INT0_vect)
7
{
8
  static unsigned char ErsteFlankeErkannt = TRUE;
9
  static unsigned int i = 0;
10
  
11
  if (ErsteFlankeErkannt == TRUE)      // Es wurde eine erste Flanke erkannt
12
  {
13
    Timer_01ms = 0;          // Timer für die Messung auf 0 setzen
14
    ErsteFlankeErkannt == FALSE;
15
  }
16
  else
17
  {
18
    if( i != MAXVALUE)      // Kontrolle, ob nicht die max Anzahl der Werte überschritten wurde
19
    {
20
      Messungen[i] = Timer_01ms;    // aktuelle Zeit abspeichern
21
      ErsteFlankeErkannt == TRUE;
22
      i++;              // Zähler für nächste Messung erhöhen
23
    }
24
    else
25
    {
26
      i = 0;    // Zähler wieder auf 0 setzen
27
    }
28
  }  
29
}

und der andere Teil muss natürlich lauten :
1
  for( i=0; i<MAXVALUE; i++)
2
  {
3
    Summe = Summe + Messungen[i];
4
  }

von Hui (Gast)


Lesenswert?

ErsteFlankeErkannt == FALSE;
Da schreien meine Augen noch immer.

von Thomas (Gast)


Lesenswert?

Ach Mist,
das muss natürlich nur 1 Gleichheitszeichen sein.......;-)

von Sebastian W. (sebastian_w29)


Lesenswert?

Es wird besser, aber mit neuen Fehlern.

Aber mal grundsätzlich: Dein ISR(INT0_vect) wird andauernd getriggert. 
Deine Hauptschleife erwartet aber eine sich nicht während der 
Mittelwertberechnung verändernde Datenstruktur. Das wird aber passieren!

Du möchtest doch eine Drehzahl messen und einen gemittelten Drehzahlwert 
ausgeben, nicht?

Am einfachsten wäre das, wenn du in deiner ISR einfach 5 Impulse 
abwartest und die Zeit dazwischen misst. Voila, ein Mittelwert.

Nur wenn du einen gleitenden Mittelwert mitführen möchtest (und 
brauchst du das wirklich?), nur dann brauchst du einen Array als 
Zwischenspeicher. Dann aber in Form eines Ringpuffers mit 
synchronisiertem Zugriff.

Überdenk noch mal deinen Ansatz.

LG, Sebastian

von Thomas (Gast)


Lesenswert?

Was ist denn, wenn ich während der Berechnung des Mittelwertes die 
Interrupts nicht zulasse, dass für die Zeit der Berechnung auch wirklich 
nur die Berechnung und Ausgabe auf dem Display durchgeführt wird.

Sonst kann man dann doch genausogut zwischen 2 impulsen messen und 
daraus dann die Drehzahl berechnen...

von Sebastian W. (sebastian_w29)


Lesenswert?

Das Problem in deinem code ist nicht nur, die Interrupts nicht 
zuzulassen. Es ist ja gar nicht garantiert, dass der Array an der Stelle 
schon vollständig gefüllt ist bzw. nicht gerade schon wieder begonnen 
wurde ihn zu füllen.

Dazu kommt, dass mir nicht so klar ist, was du eigentlich abspeichern 
willst. Die Zeit seit der ersten Flanke? Oder die Zeit seit dem letzten 
Impuls?

von Thomas (Gast)


Lesenswert?

Also ich möchte die Zeit seid dem letzten Impuls abspeichern. Also um 
die korrekte Befüllung zu gewährleisten kann man ja einen Merker setzen, 
dass die Messung voll ist und dieser Merker wird erst von der 
Auswertfunktion wieder zurückgesetzt.
Das mag doch so gehen oder ?

von Karl H. (kbuchegg)


Lesenswert?

Ja, das kann man so machen.
Und solange der Merker von der Auswertung nicht zurückgesetzt wird, hält 
sich die ISR zurück und schreibt keine Messwerte ins Array.


Über welche Drehzahlen reden wir eigentlich?

von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

Also Drehzahlen von 500 bis max 4000 Umdrehungen.
Ich hab den Code noch mal überarbeitet, wenn da jetzt noch mal jemand 
drübergucken kann. Hab den auch ohne Fehler kompilieren können.

von Karl H. (kbuchegg)


Lesenswert?

Wenn startMeasure das 2-te mal aufgerufen wird, dann ...
1
int startMeasure()
2
{
3
  static unsigned int i = 0;
4
5
  for( i=0; i<MAXVALUE; i++)
6
  {
7
    Summe = Summe + Messungen[i];
8
  }
summierst du immer weiter auf

von Karl H. (kbuchegg)


Lesenswert?

Da
1
ISR(INT0_vect)
2
{
3
  static unsigned char ErsteFlankeErkannt = TRUE;
4
  static unsigned int i = 0;
5
6
  if(MessungVorhanden == 0)
7
  {
8
    if (ErsteFlankeErkannt == TRUE)      // Es wurde eine erste Flanke erkannt
9
    {
10
      Timer_01ms = 0;          // Timer für die Messung auf 0 setzen
11
      ErsteFlankeErkannt = FALSE;
12
    }
13
    else
14
    {
15
      for( i=0; i<MAXVALUE; i++)
16
      {
17
        Messungen[i] = Timer_01ms;    // für MAXVALUE Impulse die jeweilige Zeit in Liste abspeichern
18
        ErsteFlankeErkannt = TRUE;
19
      }
20
      if(i == MAXVALUE)
21
      {
22
        MessungVorhanden = TRUE;
23
24
      }
25
    }
26
  }
27
}

ist ja schon wieder eine for-Schleife drinnen!

von Udo S. (urschmitt)


Lesenswert?

Thomas schrieb:
> Also Drehzahlen von 500 bis max 4000 Umdrehungen.

Am Tag oder pro Sekunde?

von Karl H. (kbuchegg)


Lesenswert?

Hast du dir mal überaschlagsmässig ausgerechnet, in welcher 
Größenordnung die Messwerte liegen werden?
Die brauchst du, um zu überprüfen, ob du hier
1
    Drehzahl = Mittelwert * POLANZAHL * 10 * 1000 / Mittelwert * 60;

gefahr läufst, während der Berechnung in einen Overflow zu laufen.

Moment.
Die ganze Berechnung sieht komisch aus. Das kann nicht stimmen


               Mittelwert *  .......
  Drehzahl = ----------------------------
                 Mittelwert


da kürzt sich der Mittelwert weg und übrig bleibt nur das, was sich aus 
deinen Konstanten ergibt. Nochmal zurück an den Block und die Formel 
noch mal neu herleiten. Die stimmt sicher nicht.

von Karl H. (kbuchegg)


Lesenswert?

>   itoa(Drehzahl,lcdDrehzahlString, 10);

itoa ist die falsche FUnktion für einen unsigned int.

von Sebastian W. (sebastian_w29)


Lesenswert?

Umdrehungen pro Jahr?

for( i=0; i<MAXVALUE; i++) ist in der ISR schon wieder zurück -- das 
hatten wir doch schon :(

Und dann diese vielen Variablen (MessungVorhanden, ErsteFlankeErkannt, 
i) und der komplexe Code dadurch ... phew.


Wenn du schon die einzelnen Zeiten vorhalten willst, warum dann nicht 
einfach:

ISR(INT0_vect) {
  if (AnzahlMessungen<MAXVALUE) {
     Messungen[AnzahlMessungen] = Timer_01ms;
     AnzahlMessungen++;
  }
}

Und dann baust du alles andere darum herum.

von Karl H. (kbuchegg)


Lesenswert?

1
      if (MessungVorhanden == TRUE)  // Es liegt eine komplette Messung vor
2
      {
3
        cli();
4
        startMeasure();    // Starten der Drehzahlmessung
5
        sei();
6
        printMeasure();    // Ausgabe der Drehzahl auf dem Lcd
7
        MessungVorhanden = FALSE;  // Es kann wieder eine neue Messung beginnen
8
      }

Kann man so machen(*). Hauptsächlich weil dein Progrqamm nicht sehr 
zeitkritisch ist. Bei 4000 U/min langweilt sich dein µC sowieso.

Aber grundsätzlich: Die ISR könnte bereits wieder mit dem Sammelnb von 
Werten beginnen, wenn der Mittelwert berechnet ist. Während das Ergebnis 
für die Ausgabe aufbereitet und ausgegeben wird, gibt es keinen Grund 
das Messarray zu sperren.


(*) nein, kann man nicht.
DIe Interrupts sperren ist keine gute Idee. Denn Interrupts werden 
deswegen ja nicht ignoriert, sondern sie werden nur solange verzögert, 
bis die Interrupts wieder freigegeben werden. :-)
Und das bedeutet bei dir: du misst dann eine falsche Zeit.
OK. spielt insofern keine Rolle, weil du mit MessungVorhanden sowieso 
die Messaufnahme gestoppt hast. Aber: Wenn die sowieso gestoppt ist und 
daher sicher gestellt ist, dass die ISR das Array nicht beschreiben 
wird, gibt es auch keinen Grund die Interrupts zu sperren.

-> Das ist ein komplexes Zusammenspiel von 8 Merker Variablen, 12 
Countern, 78 Hilfsvariablen. Ein deutliches INdiz, dass man gerade drauf 
und drann ist, sich selbst auszutricksen mit unnötiger Komplexität

von Thomas (Gast)


Lesenswert?

Ok, Ok danke für die vielen Anregungen.

Also mit den Interrups sperren hab ich verstanden, die amch ich raus.
Was ist denn mit der itoa Funktion, die wandelt doch einen int in einen 
String um oder nicht ?

Die for Schleife habe ich geändert, warum die da noch drin war .....ka 
;-)

Mh mit der Berechnung werd ich noch mal gucken:

Ich habe 2 Impulse pro Umdrehung. der Timer zählt alle 0,1ms hoch.
Nach Bilden des Mittelwertes habe ich die Zeit zwischen 2 Impulsen, also 
den Wert * 2 ist die zeit pro komplette Umdrehung.

Also den Wert * 10 habe ich den Wert in ms. Dann muss ja die Umrechnung 
in s gemacht werden, also 1000ms durch den vorher berechneten Wert und 
dann noch mal 60 für U/min

von Thomas (Gast)


Lesenswert?

Ok also das mit dem itoa muss lauten utoa wegen dem unsigned int.

von Sebastian W. (sebastian_w29)


Lesenswert?

Thomas schrieb:
> Ich habe 2 Impulse pro Umdrehung. der Timer zählt alle 0,1ms hoch.
> Nach Bilden des Mittelwertes habe ich die Zeit zwischen 2 Impulsen, also
> den Wert * 2 ist die zeit pro komplette Umdrehung.

Ok.

> Also den Wert * 10 habe ich den Wert in ms. Dann muss ja die Umrechnung
> in s gemacht werden, also 1000ms durch den vorher berechneten Wert und
> dann noch mal 60 für U/min

Ähem. Wenn ich 100 0,1ms-Impulse zähle, dann sind das 100*10=1000 ms???

von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:

> Ich habe 2 Impulse pro Umdrehung. der Timer zählt alle 0,1ms hoch.
> Nach Bilden des Mittelwertes habe ich die Zeit zwischen 2 Impulsen, also
> den Wert * 2 ist die zeit pro komplette Umdrehung.
>
> Also den Wert * 10 habe ich den Wert in ms.

Fast.

> Dann muss ja die Umrechnung
> in s gemacht werden, also 1000ms durch den vorher berechneten Wert

Du dividierst aber nicht durch 'den vorher berechneten Wert'

von Thomas (Gast)


Lesenswert?

Mh da muss ich noch mal nachrechnen.......

Stimmen die Timer denn so, weil ich hab das Programm grade geflasht und 
mit nem taster an INT0 die Drehzahl simuliert und da kommt nur Mist.
Gebe da immer die Mittelwerte mal die Polpaarzahl aus....

von Sebastian W. (sebastian_w29)


Lesenswert?

Wie lautet denn ISR(INT0_vect) zur Zeit genau? Oben war ja noch die 
for-Schleife wieder reingeraten ...

von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

Also die lautet:
1
///////////////////////////////////////////////////////////
2
//          Flanken Interrupt
3
//  Wird bei steigender Flanke am INT0 Eingang aufgerufen
4
///////////////////////////////////////////////////////////
5
6
ISR(INT0_vect)
7
{
8
  static unsigned char ErsteFlankeErkannt = TRUE;
9
  static unsigned int AnzahlMessungen = 0;
10
11
  if(MessungVorhanden == FALSE)
12
  {
13
    if (ErsteFlankeErkannt == TRUE)      // Es wurde eine erste Flanke erkannt
14
    {
15
      Timer_01ms = 0;          // Timer für die Messung auf 0 setzen
16
      ErsteFlankeErkannt = FALSE;
17
    }
18
    else
19
      {
20
        if( AnzahlMessungen < MAXVALUE)      // Kontrolle, ob nicht die max Anzahl der Werte überschritten wurde
21
        {
22
          Messungen[AnzahlMessungen] = Timer_01ms;    // aktuelle Zeit abspeichern
23
          ErsteFlankeErkannt = TRUE;
24
          AnzahlMessungen++;              // Zähler für nächste Messung erhöhen
25
        }
26
        else
27
        {
28
        AnzahlMessungen = 0;    // Zähler wieder auf 0 setzen
29
      MessungVorhanden = TRUE;
30
      }
31
    }
32
  }
33
}

Rest siehe Anhang

von Sebastian W. (sebastian_w29)


Lesenswert?

Also gut, die ISR:

Erster Aufruf:
1
  static unsigned char ErsteFlankeErkannt = TRUE;
2
  static unsigned int AnzahlMessungen = 0;
3
4
  if(MessungVorhanden == FALSE)
5
  {
6
    if (ErsteFlankeErkannt == TRUE)      // Es wurde eine erste Flanke erkannt
7
    {
8
      Timer_01ms = 0;          // Timer für die Messung auf 0 setzen
9
      ErsteFlankeErkannt = FALSE;
10
    }
11
    else ...
12
  }

Zweiter Aufruf:
1
  if(MessungVorhanden == FALSE)
2
  {
3
    if (ErsteFlankeErkannt == TRUE)      // Es wurde eine erste Flanke erkannt
4
    ...
5
    else
6
      {
7
        if( AnzahlMessungen < MAXVALUE)      // Kontrolle, ob nicht die max Anzahl der Werte überschritten wurde
8
        {
9
          Messungen[AnzahlMessungen] = Timer_01ms;    // aktuelle Zeit abspeichern
10
          ErsteFlankeErkannt = TRUE;
11
          AnzahlMessungen++;              // Zähler für nächste Messung erhöhen
12
        }
13
        else
14
        ...
15
    }
16
  }

Dritter Aufruf:
1
  if(MessungVorhanden == FALSE)
2
  {
3
    if (ErsteFlankeErkannt == TRUE)      // Es wurde eine erste Flanke erkannt
4
    {
5
      Timer_01ms = 0;          // Timer für die Messung auf 0 setzen
6
      ErsteFlankeErkannt = FALSE;
7
    }
8
    else ...
9
  }

Vierter Aufruf wieder wie zweiter Aufruf.

Du misst also die Zeit von Drehzahlimpuls 1 bis Drehzahlimpuls 2, dann 
die Zeit von Drehzahlimpuls 3 bis Drehzahlimpuls 4, dann 5-6, 7-8, 9-10.

Aber warum misst du nicht 1-2, 2-3, 3-4, 4-5, 5-6?

Das erwartet man so nicht. Ist das wirklich so gewollt?

von Walter S. (avatar)


Lesenswert?

Thomas schrieb:
> Stimmen die Timer denn so, weil ich hab das Programm grade geflasht und
> mit nem taster an INT0 die Drehzahl simuliert und da kommt nur Mist.

a) ist der Taster entprellt
b) kannst du schnell genug tasten damit keine Überläufe entstehen?

von Karl H. (kbuchegg)


Lesenswert?

Hier
1
 unsigned int startMeasure()
2
{
3
  static unsigned int AnzahlMessungen = 0;
4
5
  for( AnzahlMessungen=0; AnzahlMessungen<MAXVALUE; AnzahlMessungen++)
6
  {
7
    Summe = Summe + Messungen[AnzahlMessungen];
8
  }
9
  if( Summe == 0)    // Wenn keine Werte abgespeichert wurden
10
  {
11
    Drehzahl = 0;  // Es wurde keine Drehzahl gemessen
12
    return Drehzahl;
13
  }
14
  else
15
  {
16
    Mittelwert = Summe / MAXVALUE;    // Bildung des Mittelwertes der Messungen
17
    Drehzahl = Mittelwert * POLANZAHL/1000;  // Zeit zwischen den Impulsen mal Pole mal 10 (Umrechnung 0,1ms in 1ms) mal 1000/Mittelwert (1ms in 1s) mal 60 (1s in 1m)
18
    return Drehzahl;
19
20
    Summe = 0;    // Summe für nächste Messung wieder zurücksetzen
21
    AnzahlMessungen = 0;
22
  }
23
24
}

ist auch noch ein Fehler drinn.
Das 0 setzen der Summe wird nicht erreicht, weil vorher der return 
stattfindet.

Warum setzt du nicht die Summe ganz einfach 0, BEVOR du in der Schleife 
dann die Einzelteile darin aufsummierst?

Und welchen Sinn soll es haben die Variable ANzahlMessungen static zu 
machen? Das ist eine ganz einfache lokale Variable, deren einziger Zweck 
es ist, dir als Laufvariable in der for Schleife zu dienen. Die muss 
nicht static sein!


Lerne mit deinem Debugger um zugehen! Das kann doch nicht ewig so 
weitergehen, dass dir hier die Leute deine Fehler zeigen. Es ist schon 
ok, wenn man am laufenden µC nur sieht, dass die Sache nicht 
funktioniert. Aber dazu hast du deinen Debugger, damit du deinem 
Programm (in der Simulation) während der Arbeit zusehen kannst, dir 
Variablen ansehen kannst, die Logik kontrollieren kannst.

von Thomas (Gast)


Lesenswert?

Moin,

dann werde ich mich heute mal um den Debugger kümmern. Sollte man da 
lieber AVR Studio zum Simulieren nehmen oder mit Eclipse arbeiten ?

Und noch was zum Entprellen: Also normale Eingangspins zu entzprellen 
wurde ja schon oft beschrieben, aber wie muss man denn einen 
Interrupteingang entprellen, wenn man es softwaremäßig und nichr 
hardwaremäßig entprellen möchte ?

von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:

> Und noch was zum Entprellen: Also normale Eingangspins zu entzprellen
> wurde ja schon oft beschrieben, aber wie muss man denn einen
> Interrupteingang entprellen, wenn man es softwaremäßig und nichr
> hardwaremäßig entprellen möchte ?

Wenn man einen Interrupt Eingang entprellen muss, dann macht man etwas 
grundsätzlich falsch. Denn die beiden Dinge beißen sich.

Einen Interrupt Eingang benutzt man, wenn man unbedingt so schnell wie 
möglich auf ein externes Ereignis reagieren muss. Punkt.

Entprellen hingegen bedeutet, dass man irgendeine Form der Zeitsteuerung 
hat, um alle Pinbewegungen unterhalb einer gewissen zeitlichen Schwelle 
als 'falsche Alarme wegen Prellens' zu unterdrücken.

D.h. man hat hier kontradiktorische FOrderungen. Auf der einen Seite 'so 
schnell wie möglich', auf der anderen Seite 'lass uns ein wenig warten 
um zu sehen, ob es falscher Alarm ist'. Das geht nicht zusammen.

Daraus folgt: Taster einzulesen, indem man sie an einen Interrupt 
klemmt, ist Nonsense. Regelmässiges Pollen durch zb einen Timer tut es 
auch und erledigt das Entprellen im selben Aufwasch gleich mit.


Du hast jetzt natürlich einen Sonderfall, indem du den Taster als 
Simulation deines richtigen Eingangs benutzt. Gut, bei denen Zahlen 
hätte es Pollen genauso zuverlässig getan - dafür müsste man keinen 
Interrupt benutzen, aber seis drum. Da der Taster nur zum Testen ist, 
würde ich mir ansehen, ob Prellen momentan überhaupt ein Problem ist. 
Wenn deine Taster einigermassen neu und von guter Qualität sind, stehen 
die Chancen nicht schlecht, dass sie NOCH NICHT prellen. Für die paar 
Tage, die sie noch für die Entwicklung gebraucht werden, ist das schon 
ok.
Allerdings: Ich würde mir da überhaupt keinen Taster anschliessen. Ich 
sag dir auch wieso. Eines der Um und Aufs der Entwicklung bzw. des 
Programmtestens bzw. des Debuggens ist es, dass man REPRODUZIERBARE 
Testbedingungen hat. Das ist das ein. Das andere ist, dass man im 
Vorfeld schon weiß, was (welche Zahlenwerte) das Programm eigentlich 
errechnen müsste. D.h. in deinem Fall: Anstatt da mit einem Taster 
rumzumachen schnapp ich mir eine bekannte Frequenz, zb mit einem 555 
erzeugt oder zb aus den 50Hz der 230V abgeleitet (zb mit der 
Schreibtischlampe die mit ihren 50Hz einen Phototransistor schaltet). 
Oder aber, warum nicht?, ich lass mir vom µC selber an einem Pin mittels 
Timer eine Frequenz erzeugen, die das restliche Programm messen soll :-) 
Aber egal wie, ich hab dann reproduzierbare Bedingungen von einem Test 
Durchgang zum nächsten.

von Sebastian W. (sebastian_w29)


Lesenswert?

Hallo Thomas,

du hast meine Frage zu deiner ISR noch nicht beantwortet :)

Noch ein paar generelle Hinweise zu deinem code:

* Variablen, die nur in einer Prozedur benutzt werden, sollten auch nur 
in dieser einen Prozedur lokal deklariert werden. Du machst das in der 
ISR gut mit ErsteFlankeErkannt und AnzahlMessungen. Summe zum Beispiel 
sollte aber auch lokal nur in startMeasure bekannt sein.

* Lokale Variablen sollten nur static deklariert werden wenn der Wert 
zwischen Prozeduraufrufen erhalten werden muss. AnzahlMessungen in 
startMeasure sollte also nicht static sein.

* Konstrukte wie if (ErsteFlankeErkannt == TRUE) bzw. if 
(MessungVorhanden == FALSE) sind unschön. Wahrheitswerte sind 
Wahrheitswerte, sie sollten nicht mit TRUE bzw. FALSE verglichen werden. 
Ich finde if (ErsteFlankeErkannt) bzw. if (not MessungVorhanden) auch 
viel lesbarer.

* Prozedurnamen sollten immer den Zweck der Prozedur erklären. 
startMeasure startet aber gar nicht die Messung.

* startMeasure() liefert einen Wert zurück. Zusätzlich verändert 
startMeasure() aber die global Variable Drehzahl. Ent oder weder.

* Deine Drehzahlberechnung ist immer noch falsch. Sieh mal, wenn der 
Mittelwert, also die gemittelte Zeit zwischen den Impulsen, steigt, 
dann steigt auch deine Drehzahl. Dass kann nicht sein.

Nimm dir mal etwas Zeit, überarbeite das Programm entsprechend, und dann 
sehen wir weiter ob noch kleinere Macken drin sind.

LG, Sebastian

von Martin (Gast)


Lesenswert?

Hallo,

in deinen Berechnungen ist auch noch ein Fehler, wenn du die korrekten 
Werte haben möchtest, musst du mit float arbeiten, da dir dann auch die 
genauen Kommawerte berechnet werden und keine gerundeten Werte.

Die Berechnung der drehzahl müsste außerdem folgendermaßen aussehen:

                          1000ms
                     ---------------------------    * 60 U/min
         Drehzahl =    Mittelwert     * POLZAHL
                      ------------
                         10ms

Gruß

von m.n. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Daraus folgt: Taster einzulesen, indem man sie an einen Interrupt
> klemmt, ist Nonsense. Regelmässiges Pollen durch zb einen Timer tut es
> auch und erledigt das Entprellen im selben Aufwasch gleich mit.

Da widerspreche ich Dir, denn nicht jeder mechanische Schaltkontakt wird 
per Finger ausgelöst (Reedrelais), prellt dennoch und will schnell 
erkannt werden. Ich will es hier aber nicht weiter ausführen.

Ich hatte vor einiger Zeit das Schaltverhalten von zwei 
unterschiedlichen Tastern aufgezeichnet und auch gezeigt, wie man per 
Interrupt entprellen kann. 
Beitrag "Re: Entprellen von Schalter mit Interrupt und PinChange"

von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

Danke für die vielen Antworten / Verbesserungsvorschläge.

Ich hab den Code so angepasst, wie ich meine, wie ihr es meint:

Fazit: Beim tastendrücken werden vernünftige Werte angezeigt. Heute 
Abend werde ich es mit einem Sensor mal ausprobieren.

In der ISR werden jetzt auch immer der 1-2 2-3 3-4 4-5 5-6 Zeitabstand 
gemessen, wie ich es auch eigentlich wollte ;-)

Ist das mit meinen float Werten bei der Berechnung denn so richtig, da 
bei der Berechnung eine fehlende Kommastelle doch schon eine große 
abweichung bewirken würde.

Gruß

von Sebastian W. (sebastian_w29)


Lesenswert?

Du hast noch nicht alles berücksichtigt, es sieht aber schon besser aus. 
Was noch zu tun wäre:

volatile unsigned int Summe = 0;
-> kann oben wegfallen

unsigned char MessungAuslesen = 0;
-> verschieben nach Measure()

unsigned char lcdDrehzahlString[8];
-> verschieben nach printMeasure()

float AktuelleDrehzahl = 0;
-> verschieben nach main()

ErsteFlankeErkannt
-> umbenennen in erwarteErsteFlanke

unsigned int Measure()
-> unsigned float Measure ()
-> return Drehzahl; ans Ende der Funktion. Wo immer möglich sollte eine 
Funktion am Ende einen return-Ausgang haben. Hier ist es sehr einfach 
möglich und spart auch noch eine Zeile ein.

printMeasure(float Drehzahl)
-> Du ruft utoa mit einem float auf. Solche versteckten Typwandlungen 
könnnen oft zu fiesen Fehlern führen.

LG, Sebastian

von Daniel (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
ich habe den ausgabewert fürs Display wieder auf int gekastet, dann 
werden die Kommastellen eben aufgerundet.
Habe grade eben mit dem Drehzahlsensor ausprobiert und vorher mit einem 
anderen die Drehzahl vorgemessen zwecks Vergleich.

Der PNP induktive sensor ist über einen Spannungsteiler(da12V) an den 
INT0 angeschlossen.
Es werden Drehzahlen angezeigt, jedoch um bis zu 1000Umdrehungen 
schwankend. obwohl die drehzahl konstant anliegt. Fuses ist ein externer 
High Frequenty Quarz eingestellt, am Atmega 8 ist dann der 16Mhz Quarz 
angeschlossen.

Wodran kann das jetzt noch liegen ?

Code noch mal im Anhang mit den Verbesserungen noch mal danke dafür

von Sebastian W. (sebastian_w29)


Lesenswert?

Oszilloskop oder Logikanalysator an INT0, da könnten ja Störungen drauf 
liegen, oder die Flanke ist nicht sauber, was dann natürlich massiv die 
Messung beeinflusst.

Wenn öfters zwei Impulse fehlerhaft ganz kurz hintereinander auflaufen, 
dann könntest du das im Programm recht einfach abfangen dadurch dass du 
deine ISR verlässt falls Timer_01ms einfach zu klein ist um Sinn zu 
machen.

Schau dir die falschen Werte mal genauer an, und/oder protokolliere in 
Measure() die gemessenen rohen Zeiten auf Messungen[] auf die serielle 
Schnittstelle. Kann es sein dass die Falschmessungen dadurch entstehen 
dass die fünf Impulse in der Zeit einlaufen die bei korrekter Messung 
vier Impulse brauchen würden?

Ein kleiner Kinken ist auch noch im Programm: return Drehzahl; in float 
Measure() muss hinter den else-Block.

LG, Sebastian

von Thomas (Gast)


Lesenswert?

Mh,

also Logikanalysator oder Oszzi hab ich leider net :-(. Ausgaben auf der 
seriellen schnittstelle könnte ich wohl einrichten.
Aber eig müsste das mit den 5 Messungen egal sein, ob nun 5 oder 4, weil 
wenn die Drehzahl, welche anliegt konstant ist, sich auch die werte alle 
konstant verhalten sollten. Außerdem sind die Pole genau 180° 
verschoben, also können da keine fehler auftreten.

Vll sollte man echt lieber erst einen Filter davorsetzen. welchen sollte 
ich den nehmen, so einen Hoch oder Tiefpass, wie aus dem Tut hier ?

von Thomas (Gast)


Lesenswert?

Hallo,
ich habe jetzt einfach mal mit einem taster(neu, nicht entprellt, weil 
ich dafür keine Bauteile hab)

Folgende Werte konnte ich aufzeichen beim ziemlich gleichmäßigem drücken 
des Tasters. Diese Werte zeigen deutlich die Nicht Entprellung des 
tasters oder ist da noch in der Software was falsch ?

Ach ja die Werte sind immer die 5 Messwerte zwischenden Impzlsen, welche 
danach gemittelt werden.

Wert: 1179

Wert: 14

Wert: 6

Wert: 8

Wert: 4

Wert: 3131

Wert: 3075

Wert: 2989

Wert: 12553

Wert: 170

Wert: 2587

Wert: 3756

Wert: 4

Wert: 0

Wert: 8

Wert: 337

Wert: 191

Wert: 2366

Wert: 3206

Wert: 688

Wert: 42

Wert: 42

Wert: 2989

Wert: 6

Wert: 191

Wert: 3

Wert: 69

Wert: 2961

Wert: 1

Wert: 13

Welchen Filter kann ich am einfachsten und mit vauteilen realisieren, 
welche ich auch hoffentlich zu Hause habe ?

Gruß

von Sebastian W. (sebastian_w29)


Lesenswert?

Thomas schrieb:
> Ach ja die Werte sind immer die 5 Messwerte zwischenden Impzlsen, welche
> danach gemittelt werden.

Also sind das jetzt die Werte wie sie in Messungen[] drinstehen? Also 
die Anzahl der 100μs-Impulse seit dem letzen Impuls? Und die Ausgabe auf 
die serielle Schnittstelle ist wirklich in Measure() und nicht in der 
ISR?

von Thomas (Gast)


Lesenswert?

Also die Ausgabe über uart ist in der measure Funktion. Und da werden 
genau die 5 zwischenwerte ausgegeben.

Können solche Schwankungen wirklich vom prellen kommen ?

von m.n. (Gast)


Lesenswert?

Thomas schrieb:
> Welchen Filter kann ich am einfachsten und mit vauteilen realisieren,
> welche ich auch hoffentlich zu Hause habe ?

Geschickterweise bewertet man die fallenden Flanken bei der Messung.

Für erste, einfache Versuche reicht Folgendes:
Aktiviere den Pullup zum INT-Eingang und schalte von diesem Eingang 
100nF gegen GND. Im Ruhezustand ist der Kondensator auf VCC aufgeladen.
Über einen 10k Widerstand in Reihe zum Taster schaltest Du den Eingang 
gegen GND.

Wenn alles nicht funktioniert, gibt es in der Codesammlung auch Beiträge 
zur Drehzahlmessung.

von Drehzahlmesser (Gast)


Lesenswert?

schau dir doch mal die tausend Varianten von Frequenzzählern an.
Die machen genau das gleiche wie Du willst. Da hast Du reichlich Stoff 
zum anschauen.

von Thomas (Gast)


Lesenswert?

Also ich habe jetzt mal die negative Flanke gemessen und siehda, die 
Messwerte sind schon wesentlich besser.
Wert: 103

Wert: 785

Wert: 5282

Wert: 5908

Wert: 5041

Wert: 5641

Wert: 5907

Wert: 5507

Wert: 5743

Wert: 5699

Wert: 696

Da sind noch einige totale Abweichungen, aber die schieben wir mal auf 
den nicht entprellten Taster. Ich habe jetzt 10k gegen VCC und dann den 
Taster gegen GND. Die variante mit dem Kondensator und Widerstand 
parallel hat gar nicht gefunzt. Ich glaub das is auch ein bissl falsch 
erklärt oder ?.

Ich werde mir jetzt einen 74HC 14 kaufen und damit eine Entprellung so 
wie imForum erklärt bauen. Ist das denn genau genug, wenn ich die 
durchgeschaltete Spannung vom Sensor über einen Spannungsteiler dann auf 
den Schmidt Trigger lege ?

von Sven (Gast)


Lesenswert?

Es kann auch an deiner Eingangsschaltung für den Sensor liegen. Ich weiß 
nicht, ob das einfach mit einem PNP-Transistor hinhaut, ob der sauber 
sperrt und wie die Flanken aussehen.
Kannst du mal den genauen Schaltplan posten und wenn möglich auch ein 
paar Infos über den Sensor rausrücken?

von m.n. (Gast)


Lesenswert?

Thomas schrieb:
> Die variante mit dem Kondensator und Widerstand
> parallel hat gar nicht gefunzt. Ich glaub das is auch ein bissl falsch
> erklärt oder ?.

Von Kondensator und Widerstand parallel ist auch nirgends die Rede.

Liebe Grüße noch an Herrn Schmidt!

von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

Also ich hatte den Sensor einfach mit nem einfachen Spannungsteiler an 
den Eingang des Atmega 8 gehängt. Ohne Transistor, etc.

Der Sensor ist der im Anhang.

Ich habe mit einem Frequenzgenerator meine Software ausprobiert und habe 
dort eine Genauigkeit von + - 1 Umdrehung. Also die Software wertet das 
Eingangssignal schon mal sehr genau aus.

Jetzt muss halt nur noch geguckt werden, wie ich die Eingangsflanken vom 
sensor stabil und ohne Prellen hinbekomme.

von Thomas (Gast)


Lesenswert?

Hallo,

jetzt hab ich doch noch mal eine frage bezüglich des Spannungsteilers.
Wie groß ist der innenwiderstand des Atmega 8 Einganges, damit ich den 
belasteten Spannungsteiler berechnen kann.

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:
> Hallo,
>
> jetzt hab ich doch noch mal eine frage bezüglich des Spannungsteilers.
> Wie groß ist der innenwiderstand des Atmega 8 Einganges, damit ich den
> belasteten Spannungsteiler berechnen kann.

Für alle praktischen Zwecke kannst du ihn ignorieren.

Einen eingeschalteten Pullup Widerstand kannst du mit rund 35kOhm in 
Richtung Vcc in Rechnung stellen. Also auch hier wieder: Wenn du deinen 
Spannungsteiler auf 10k Gesamtwiderstand auslegst ist das eine zu 
vernachlässigende Größe.

von Thomas (Gast)


Lesenswert?

Also ich habe jetzt mit einer Eingangsspannung von 13V gerechnet (hängt 
an einer Batterie). Ausgangsspannung sind die 5V, welche direkt an den 
Eingangspin vom Atmega 8 gehen.

Dann habe ich für R2 4,7kOhm angenommen und somit ergibt sich für R1 
7,2kOhm:

              -------                   -------
+12V---------|   R1  |-------- --------|   R2  |------------ GND
              -------         |         -------
                              |
                             5V

Jetzt werte ich per software ja die positiven Flanken aus. Wie ist ds 
mit dem PullDown Widerstand, reichen da die 4,7k jetzt oder muss ich da 
zusätzlich noch einen Widerstand gegen GND setzen ?

Gruß

von Thomas (Gast)


Lesenswert?

Keiner eine Idee ?

von Udo S. (urschmitt)


Lesenswert?

Thomas schrieb:
> Jetzt werte ich per software ja die positiven Flanken aus. Wie ist ds
> mit dem PullDown Widerstand, reichen da die 4,7k jetzt oder muss ich da
> zusätzlich noch einen Widerstand gegen GND setzen ?

Wofür brauchst du noch einen Pull Down? Jeder zusätzlicher Widerstand 
würde dir den Spannungsteiler verstellen.
Das ist gut so.
Wenn der Eingang an einer Autobatterie hängt solltest du dir allerdings 
noch die Infos zu Schutzmaßnahmen durchlesen, da in einem KFZ ziemlich 
gemeine Störungen auf dem Bordnetz sein können:
http://dse-faq.elektronik-kompendium.de/dse-faq.htm#F.23

von Thomas (Gast)


Lesenswert?

Hallo,
Danke für den Tipp, aber das ist einen einzelne kleine Gelbatterie, 
welche in einem Gehäuse sitzt, damit der Drehzahlmesser transportabel 
ist. Also können da vom KFZ keine Störgrößen draufkommen.

Gruß

von Thomas (Gast)


Lesenswert?

Hallo,

wollte mich noch einmal für die große Unterstützung bedanken, Drehzahl 
wird jetzt genau angezeigt, habe einen anderen Sensor genommen und mit 
dem funktioniert es wunderbar.

Gruß

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.