Forum: Compiler & IDEs Frequenzzählung - Zu hohe Werte


von Philipp (Gast)


Angehängte Dateien:

Lesenswert?

Hallo allerseits,

ich bastel ja gerade an einem Tacho, also muss ich natürlich auch eine 
Frequenz messen. Dazu hab ich von einem alten Fahrrad-Tacho den 
Reed-Kontakt geklaut mit dem ich VCC nach ICP schalten, wann immer ein 
Magnet vorbei kommt. Ansonsten hängt an ICP ein Pull-Down mit 10k.

Komischerweise bekomm ich jetzt aber absolut zu hohe Werte für meine 
Frequenz (wenn ich den Magneten mit der Hand am Reed-Kontakt 
vorbeischiebe kommen schon ~20k Hz raus ...)

Ich hatte eigentlich geplant, aus 3 Messungen den Mittelwert zu bilden, 
und dann am LCD ausgeben.

Ich bin langsam am verzweifeln, was an dem Code nicht stimmt ...

Eine Vermutung von mir ist, das durch das "Prellen" des Reed-Kontakts 
natürlich die 3 Messungen ziemlich schnell durchlaufen ... Wie kann ich 
den Kontakt entprellen, und ist das sinnvoll beim Frequenzmessen?

Hoffe mir kann jemand Helfen und mal meinen Code durchschauen, ich find 
nichts was außergewöhnlich ist.

Gruß
Philipp

von Uwe (de0508)


Lesenswert?

Hallo,

der Code ist nicht vollstädig, keine Lib, Headerdateien und kein 
Makefile.

Dann könnte man schon auf die cpu schließen.

Vorschlag nimm doch mal einen ne555 als Generator, dann siehst Du wo es 
klemmt.

von Peter II (Gast)


Lesenswert?

warum lässt du es nich einfach mal im simulator laufen? du kannst sogar 
über das Inputfile den Redkontakt simulieren dann weisst du erstmal ob 
die software geht oder nicht.

Stimmt denn der Quarzt und die Fuse bits?

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:

> Eine Vermutung von mir ist, das durch das "Prellen" des Reed-Kontakts
> natürlich die 3 Messungen ziemlich schnell durchlaufen ... Wie kann ich
> den Kontakt entprellen, und ist das sinnvoll beim Frequenzmessen?

Kontakt entprellen ist immer sinnvoll.

Wie du das machen kannst.
Na zum Beispiel indem du das Wissen benutzt, das dein richtiger 
Kontaktimpuls in der Realität nicht beliebig schnell kommen kann, weil 
sich ja das Rad auch drehen muss.
Wenn also der End-Impuls kommt und nicht um mindestens xy höher ist 
(Overflows nicht vergessen) als der Startpuls, dann wirds wohl Prellen 
gewesen sein und die Flanke ist kein realer Endpuls.

> Hoffe mir kann jemand Helfen und mal meinen Code durchschauen, ich find
> nichts was außergewöhnlich ist.

Finde doch erst mal raus, ob die Hypothese 'Prellen' plausibel ist. Du 
hast ein LCD. Da darf man durchaus auch andere Werte ausgeben als nur 
das Endergebnis.

von Philipp (Gast)


Lesenswert?

> der Code ist nicht vollstädig, keine Lib, Headerdateien und kein
> Makefile.

Ist ein AVR-Studio Projekt, von daher gibts kein Makefile etc.
Die Headerdatein sind uninteressant, ich includiere nur die Header für 
das LCD und für das Logo das PROGMEM-Array ...

> warum lässt du es nich einfach mal im simulator laufen? du kannst sogar
> über das Inputfile den Redkontakt simulieren dann weisst du erstmal ob
> die software geht oder nicht.

Sorry, ich hab keine Ahnung wie das geht mit Inputfile, gibt's da i.wo 
ein Tutorial dazu?

> Finde doch erst mal raus, ob die Hypothese 'Prellen' plausibel ist.

Ich habs auch schon probiert, die Kontakte direkt zu schalten, via "ich 
nehme einen Draht und brücke schnell und öffne wieder" ... das selbe: 
Viel zu hohe Werte ...

von Peter II (Gast)


Lesenswert?

Was passiert wenn das Rad Plötzlich anhählt, also kein Impuls mehr 
kommt? Dann bleibt die alte geschwindigkeit stehen.

Ich habe bei meinem Tacho die geschwindigkeit in der main schleife 
berechnet. damit geht die geschwindigkeit gegen 0 wenn lange kein Impuls 
kommt.

von Philipp (Gast)


Lesenswert?

> Was passiert wenn das Rad Plötzlich anhählt, also kein Impuls mehr
> kommt? Dann bleibt die alte geschwindigkeit stehen.

Das ist richtig, das ist mir auch schon aufgefallen und hab auch schon 
kurz drüber nachgedacht, wollte aber erst einmal eine Lösung für die 
Frequenz überhaupt ;)

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:

> Ich habs auch schon probiert, die Kontakte direkt zu schalten, via "ich
> nehme einen Draht und brücke schnell und öffne wieder" ... das selbe:
> Viel zu hohe Werte ...

Netter Test, verrät dir aber nicht viel über das Prellen des echten 
Kontakts. Denn der "Draht" wird auf jeden Fall prellen.

Was bitte ist denn so schwer daran, sich auf dem LCD ein paar 
Zwischenergebnisse ausgeben zu lassen. Zb die letzten 30 (oder wieviele 
halt dein Platz zulässt) Messergebnisse?  Dann Taschenrechner nehmen und 
die Zeit dazwischen ausrechnen (oder das Programm ausrechnen lassen).

Solange du nicht anfängst, dein Ausgabegerät in der jetzigen Phase als 
dein hauptsächliches 'Debug-Werkzeug' zu sehen und es auch 
demenstprechend zu benutzen, solange es wichtiger ist, dass dort diverse 
Rähmchen und Knöpfchen und Copyrightmeldungen und Logos zu sehen sind, 
solange wirst du ewig zur Programmentwicklung brauchen.


(PS: Deine Einbeziehung des Overflows in die Rechnung ist fehlerhaft. 
Denk mal darüber nach, was deine Berechnung ergibt, wenn der Startwert 
65534 ist und der Endwert 2. Zwischen den beiden Messpunkten liegen 3 
Timerticks. Du rechnest aber noch 65535 zusätzliche dazu.)

von Philipp (Gast)


Lesenswert?

Gibts auch die Möglichkeit einer Simulation in AVR-Studio-5 ?

Das Flashen über Seriell dauert jetzt schon so ca. 10 minuten -.-

(Hab leider keinen ISP-Programmer ...)

von Peter II (Gast)


Lesenswert?

Philipp schrieb:
> Gibts auch die Möglichkeit einer Simulation in AVR-Studio-5 ?

ich denke schon

> Das Flashen über Seriell dauert jetzt schon so ca. 10 minuten -.-
das dauert bei mir maximal 20 sekunden

von Karl H. (kbuchegg)


Lesenswert?

Ein erster Test wäre zb:
In der ISR zähle ich mir einen globalen Zähler einfach nur um 1 hoch. 
Den Wert des Zählers lass ich mir in der Hauptschleife ständig ausgeben

So ungefähr
1
volatile int ISRCount;
2
3
ISR(TIMER1_CAPT_vect) {
4
  ISRCount++;
5
}
6
7
void printAt( uint8_t x, uint8_t y, int Value )
8
{
9
  char Buffer[8];
10
11
  itoa( Buffer, Value, 10 );
12
  LCDSetBrush(LCD_COLOR_BLUE);
13
  LCDFillRect(x, y, 7*5, 8);
14
  LCDSetBrush(LCD_COLOR_WHITE);
15
  LCDPrintText(Buffer, x, y);
16
}
17
18
int main(void)
19
{
20
  ...
21
22
  sei();
23
  
24
  while(1) {
25
    printAt( 64-10, 52, ISRCount );
26
  }
27
}

und dann streich ich mal mit dem Magneten ganz langsam über den Kontakt. 
Wenn der Magnet in die Nähe kommt und der Kontakt anzieht, muss der Wert 
sich von 0 auf 1 ändern. Aber NUR auf 1! Steht danach 2 oder noch etwas 
höheres auf dem LCD, dann prellt der Kontakt. Fahre ich mit dem Magneten 
weiter über den Kontakt, sodass der Kontakt wieder abfällt, dann darf 
dieses Abfallen den Zählerstand nicht verändern. Geht der trotzdem hoch, 
dann könnte zb die Flankeneinstellung auf die der Timer reagiert 
fehlerhaft sein (könnte aber auch wieder Kontaktprellen sein, wie könnte 
man die beiden Dinge auseinanderhalten?)

Und so kann man rausfinden, was da vor sich geht. Man überlegt sich eine 
Hypothese, überlegt sich wie man diese Hypothese testen kann und 
implementiert dafür Testcode.

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:

> Das Flashen über Seriell dauert jetzt schon so ca. 10 minuten -.-
>
> (Hab leider keinen ISP-Programmer ...)

Ohne ordentliches Equipment zu arbeiten, ist ein 'Pain in the ass'. Klar 
kann man nur mit einer Nagelfeile bewaffnet einen Motorblock feilen. 
Aber Sinn macht es keinen.

Womit flasht du? Bootloader?
Kannst du die Baudrate erhöhen?

von Philipp (Gast)


Lesenswert?

> Womit flasht du? Bootloader?

Ich hab so ein Pollin Evaluations Board und Programmier mit AVR-Dude mit 
Programmer: ponyser

> Kannst du die Baudrate erhöhen?

Weiß ich nicht, geht das ohne Probleme? =)

> Und so kann man rausfinden, was da vor sich geht. Man überlegt sich eine
> Hypothese, überlegt sich wie man diese Hypothese testen kann und
> implementiert dafür Testcode.

Danke für das Beispiel, ich werde es mal Probieren =)

von Philipp (Gast)


Lesenswert?

Hab mir jetzt wie Karl Heinz Buchegger vorgeschlafen, mal alles 
relevante ausgeben lassen, was wichtig ist.

Im Code steht ja:
1
if(durchgang == 0) {
2
    timerStart = ICR1;
3
    frequenz = 0;
4
    timerOverflows = 0;
5
  } else {
6
    ...

So, jetzt ist zu beginn
>> durchgang = 0

trotzdem, nachdem ich einmal vorbei bin mit dem Magneten, setzt es nicht 
nur
>> timerStart
sondern auch
>> timerEnd
auf einen Wert, der aber anders ist als
>> timerStart
(größer)...

Nachdem ich den Magneten aber einmal rüber gezogen hab, steht
>> durchgang = 1
=> wie kann das sein, wenn
>> timerEnd
gesetzt wird, wird durchgang ja auch +1 genommen, dann müsste ja
>> durchgang = 2
sein ?!

Ich finde den Fehler nicht in der Logik =(

von Karl H. (kbuchegg)


Lesenswert?

Auch noch so ein Fehler

  if(durchgang == 0) {
    timerStart = ICR1;
    frequenz = 0;
    timerOverflows = 0;
  } else {
    timerEnd = ICR1;
    frequenz += 1000000 / ((timerOverflows*0xFFFF) + timerEnd - 
timerStart);
    timerOverflows = 0;
    timerStart = timerEnd;
  }
  if(durchgang == MEASURE_ITERATIONS)  {
    ...
    durchgang = 0;

da bei dir MEASURE_ITERATIONS gleich 3 ist, rechnest du den den Wert aus 
dem 2.ten bzw. 3.ten ISR Aufruf mit jeweils dem gleichen Startwert und 
unterschiedlichem Endwert.


Hast du denn jetzt schon geprüft ob dein Kontakt prellt? Deine 
Beobachtung würde ich jetzt mal so interpretieren.


PS: Du hast mehrere Probleme in deinem Code
der erste und wichtigste ist das Kontaktprellen. Das musst du unter 
Kontrolle kriegen, vorher macht es keinen Sinn irgendetwas anderes zu 
tun.

der zweite, der danach behoben werden muss (eigentlich sind es mehrere, 
aber sie haben alle die gleiche Ursache): Deine ISR macht zu viel! 
Konzentrier dich in der ISR darauf die Messwerte festzustellen. Nicht 
mehr und nicht weniger. Das Auswerten dieser Werte sowie die Verrechnung 
auf einen Mittelwert verlegst du in die Hauptschleife.

Aber eins nach dem anderen: Erst mal muss Gewissheit her, was das 
Prellen anbelangt und das es absolut zuverlässig in Schach gehalten 
wird.

von Philipp (Gast)


Lesenswert?

> da bei dir MEASURE_ITERATIONS gleich 3 ist, rechnest du den den Wert aus
> dem 2.ten bzw. 3.ten ISR Aufruf mit jeweils dem gleichen Startwert und
> unterschiedlichem Endwert.

aber ich setze doch timerStart = timerEnd sobald es nicht mehr der erste 
Aufruf ist ?!

Es ist tatsächlich das Prellen des Schalters gewesen, der die hohen 
Frequenzen verursacht hat, ab das ganze inzwischen so gelöst:

Da ich ja im Interrupt die Frequenz ausrechne (noch, werde ich jetzt 
dann auslagern in die main-loop) hab ich folgendes gemacht
1
  if(durchgang == 0) {
2
    ...
3
  } else {
4
    timerEnd = ICR1;
5
    tmpFrequenz = 1000000 / ((timerOverflows*0xFFFF) + timerEnd - timerStart);
6
    // Jetzt muss entprellt werden x)
7
    if(tmpFrequenz > MAX_FREQUENCY)
8
      return;
9
    ...

MAX_FREQUENCY hab ich mir so ausgerechnet, da das Endgerät, das den 
Tacho bekommt zu 100% nicht 100km/h fährt einfach so gewählt, das wenn 
es schneller als 100km/h wäre, kein Messwert ist.

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:
>> da bei dir MEASURE_ITERATIONS gleich 3 ist, rechnest du den den Wert aus
>> dem 2.ten bzw. 3.ten ISR Aufruf mit jeweils dem gleichen Startwert und
>> unterschiedlichem Endwert.
>
> aber ich setze doch timerStart = timerEnd sobald es nicht mehr der erste
> Aufruf ist ?!

mea culpa

von Philipp (Gast)


Angehängte Dateien:

Lesenswert?

Hab jetzt ein wenig anders gemacht, nicht so, dass ich den letzen 
TimerStop als neuen TimerStart nehme, sondern jeweils neu. Außerdem hab 
ich die Berechnungen jetzt in die Main-While verlegt. Mein AtMega8 ist 
mit 8MHz getaktet, mit einem Prescale von 256 funktioniert alles 
einwandfrei, aber sobald ich prescale z.B. auf 8 stelle (=> Timer läuft 
dann ja mit 1MHz) gibt es wieder falsche Werte. Ich nehme an es liegt an 
den Overflows, da ich bei einem Prescale von 256 nur ca. alle 2 SEKUNDEN 
einen Overflow bekomm => eig. fast immer 0 overflows => richtige 
berechnung.

Für den Tacho reicht es natürlich, mit einem Prescale von 256, da ich 
warsch. nie höhere Frequenzen als 20 Hz messen werden muss, aber mir 
gehts da grad um's Prinzip, weil es sollte ja anders auch funktionieren 
=)

Vllt kann sichs ja mal einer Anschauen ...

Gruß

von Philipp (Gast)


Lesenswert?

Hab jez bei jemand anderen gesehen, das der in dem Capture-Interrupt 
überprüft, ob ein Overflow gibt, der ZUR Messung gehört oder eben genau 
NICHT ?!
1
  if ( (TIFR & (1 << TOV1)) && (!(EndTime & 0x8000)) ){    // Überlaufstattgefunden, der zur Messung gehört?
2
  NrOverflows++;
3
  }  
4
  if ( EndTime < StartTime ){    // Overflow stattgefunden, der aber nicht zur Messung gehört?
5
  NrOverflows--;
6
  }

Versteh das nicht so genau, wieso genau EndTime & 0x8000 ?!

von Stefan E. (sternst)


Lesenswert?

Philipp schrieb:
> Versteh das nicht so genau, wieso genau EndTime & 0x8000 ?!

Um festzustellen, ob der noch nicht verarbeitete Overflow vor dem 
Capture-Ereignis stattgefunden hat (Capture-Wert ist klein), oder danach 
(Capture-Wert ist groß). Die Unterscheidung groß/klein ist hier einfach 
ein Test "größer oder kleiner als MAX/2", indem das MSB getestet wird.

Ich vermisse in dem von dir gezeigten Code-Fragment allerdings ein 
Löschen des Overflow-Flags im Falle von "muss mitgezählt werden".

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Reifenumfang 85 cm ? Bobycar ? Schubkarre ?

von Philipp (Gast)


Lesenswert?

Joachim Drechsel schrieb:
> Reifenumfang 85 cm ? Bobycar ? Schubkarre ?

GoKart ;)

Stefan Ernst schrieb:
> Ich vermisse in dem von dir gezeigten Code-Fragment allerdings ein
> Löschen des Overflow-Flags im Falle von "muss mitgezählt werden".

Hab den Code so nur übernommen aus einem anderen Thread, weil ich 
natürlich das Forum schon durchsucht habe zu meinen Problemen, jedoch 
hatte ich den Teil nicht verstanden mit dem MAX/2 ;)

Andere Frage:
Woher weiß man, das ein Overflow nicht zur Messung gehört, nur wenn 
EndTime < StartTime ist ?!

Wenn ich z.B. bei 0xFFF5 StartTime setze und 20-Timer-Takte später mein 
EndTime auf 0x0A Setze, dann ist EndTime ja auch < StartTime, trotzdem 
hatte ich einen Overflow drin, mit dem man wunderbar rechnen kann:
1*0xFFFF + 0x0A - 0xFFF5 = 0x14; (= 20) ?!

von Stefan E. (sternst)


Lesenswert?

Philipp schrieb:
> Wenn ich z.B. bei 0xFFF5 StartTime setze und 20-Timer-Takte später mein
> EndTime auf 0x0A Setze, dann ist EndTime ja auch < StartTime, trotzdem
> hatte ich einen Overflow drin, mit dem man wunderbar rechnen kann:
> 1*0xFFFF + 0x0A - 0xFFF5 = 0x14; (= 20) ?!

Vermutlich hat der Autor von obigen Code-Fragment folgende Formel 
benutzt:
1
y = (EndTime - StartTime) + NrOverflows * 0x10000

PS: Und ja, es muss "* 0x10000" heißen, und nicht "* 0xffff".

von Philipp (Gast)


Lesenswert?

Der Code ist von hier: 
Beitrag "Re: Input Capture Pin (ICP) auslesen ( Frequenz messen)"

Stefan Ernst schrieb:
> Vermutlich hat der Autor von obigen Code-Fragment folgende Formel
> benutzt:

(NrOverflows * 65536) + EndTime - StartTime;

=> EndTime - StartTime + (NrOverflows * 65536)

Hab ich ganz übersehen, das die hier auch 65536 statt 65535 verwenden, 
hab vergessen, das der Overflow an sich ja auch einen Takt braucht (von 
0xFFFF auf 0x0000)

Ich änder's mal eben ab, aber ich denke der Fehler wird trotzdem noch da 
sein x) Mir ist aufgefallen, das wenn TimerEnd > TimerStart immerl 
richtig gerechnet wird, andernfalls ist's i.wie ein flasches Ergebniss

von Stefan E. (sternst)


Lesenswert?

1
(timerOverflows*0xFFFF + timerEnd - timerStart)
Das hier wird bei dir ja auch nur in 16-Bit gerechnet.

von Philipp (Gast)


Lesenswert?

Ist schon geändert, flashed aber noch ... hab doch nur so nen 
seriellen-programmer mit ponyser .. dauert ewig -.-

>> Writing | ######################################             | 75% 332.46s

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:

> Wenn ich z.B. bei 0xFFF5 StartTime setze und 20-Timer-Takte später mein
> EndTime auf 0x0A Setze, dann ist EndTime ja auch < StartTime, trotzdem
> hatte ich einen Overflow drin, mit dem man wunderbar rechnen kann:
> 1*0xFFFF + 0x0A - 0xFFF5 = 0x14; (= 20) ?

Wenn die Endtime kleiner als die Starttime ist, rechnest du einfach mit 
1 Overflow weniger.

Solange die Differenz Endtime - Starttime kleiner als 65536 ist, muss 
nämlich 1 Overflow überhaupt nicht berücksichtigt werden. Durch die 
unsigned Rechnung kommt trotzdem das richtige raus. Erst wenn die 
Endtime absolut größer als die Starttime ist, werden tatsächlich alle 
registrierten Overflows gezählt.

von Stefan E. (sternst)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn die Endtime kleiner als die Starttime ist, rechnest du einfach mit
> 1 Overflow weniger.
>
> Solange die Differenz Endtime - Starttime kleiner als 65536 ist, muss
> nämlich 1 Overflow überhaupt nicht berücksichtigt werden. Durch die
> unsigned Rechnung kommt trotzdem das richtige raus. Erst wenn die
> Endtime absolut größer als die Starttime ist, werden tatsächlich alle
> registrierten Overflows gezählt.

Es kommt halt darauf an, wie die Formel konkret aussieht. So wie Philipp 
es geschrieben hat (und man es nicht versehentlich auf 16-Bit 
beschneidet, wie bei ihm passiert), muss man sich tatsächlich nicht 
darum kümmern, und kann die Overflows einfach immer komplett mit 
einbeziehen.

von Philipp (Gast)


Lesenswert?

Wie ist das gemeint, mit
> Erst wenn die Endtime absolut größer als die Starttime ist

Ansonsten funktioniert's jetzt schon ein wenig besser, ab und zu bekomm 
ich aber falsche Werte ?! Hab an nen Akkuschrauber nen Magneten und lass 
Ihn über nen Reed-Kontakt drehen ... bei gefühlten 3min^(-1) zeigt es 
diese auch an, aber kurz dann auch mal z.B. 7min^(-1)

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.