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
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.
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?
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.
> 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 ...
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.
> 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 ;)
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.)
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 ...)
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
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
volatileintISRCount;
2
3
ISR(TIMER1_CAPT_vect){
4
ISRCount++;
5
}
6
7
voidprintAt(uint8_tx,uint8_ty,intValue)
8
{
9
charBuffer[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
intmain(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.
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?
> 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 =)
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 =(
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.
> 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
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.
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
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ß
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".
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) ?!
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".
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
Ist schon geändert, flashed aber noch ... hab doch nur so nen
seriellen-programmer mit ponyser .. dauert ewig -.-
>> Writing | ###################################### | 75% 332.46s
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.
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.
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)