Hallo, ich plane eine Frequenz mittels einem Reedkontakt und einem Magneten zu messen. Das ganze geschieht mit einem Atmega8 am externen Interrupt INT0 (der ICP Pin wird anderweitig für eine zweite Frequenzmessung benötigt). Der Reedokontakt hängt also an PD2 (INT0) und ist gegen Masse geschaltet. Interner Pullup ist an diesem Pin aktiv. Zusätzlich hängt an PB1 eine LED (aktiv low), die im Takt des Reedkontaktes leuchtet. Sie wird zunächst bei der fallenden Flanke (Reed zieht an) eingeschaltet und bei steigender Flanke (Reed fällt ab) ausgeschaltet. Pustekuchen, geht leider nicht (immer). Irgendwie bleibt die LED manchmal an. 16-bit Timer liest die Frequenz ein bzw. die Zeit von fallender zu fallender Flanke. Der 8-bit Timer dient zum Entprellen und wurde so vorgeladen, dass er nach 1ms überläuft. Der Reedkontakt hat eine 0,5ms Schaltzeit laut Datenblatt inkl. Prellzeit. Daher habe ich das Programm so geschrieben und der 8-bit Timer mit 1ms konfiguriert: Ext. Interrupt tritt auf --> ext. Interrupt sperren, sodass keine Flanken durch prellen erkannt werden. Zeit vom 16-bit Timer wird eingelesen, 8-bit Timer wird vorgeladen, sodass er nach 1ms überläuft. Die ISR des 8-bit Timer (1ms vergangen) prüft nun, ob der INT0-Pin immernoch auf Masse gezogen ist, wenn ja, Signal erkannt&entprellt, wenn nein, Neustart der Messung. Zusätzlich schaltet die ISR noch die Flanken um, sodass nun die nächste Flanke am INT0 als steigend erkannt wird und aktiviert wieder die externen Interrupts. Nun kommt die steigende Flanke in das Interrupt und gleiches Spiel wieder. INT0 sperren, 8-bit Timer vorladen, prüfen ob INT0 auf Masse gezogen ist--> wenn nein--> high und damit Steigende Flanke. Aber genau dies wird oft nicht erkannt und er springt mir bei der if-Abfrage in den else-Zweig zum die Messung neu starten (da Flanke nicht erkannt). Da dieser Zweig eigentlich die LED wieder ausschalten soll, aber diese manchmal an bleibt, muss hier irgendetwas scheitern. Wenn ich in den else-Zweig schreibe "LED ausschalten", dann wird diese auch ausgeschaltet, aber dadurch wird nichts gemessen, der er immer wieder die Messung neu startet aufgrund der nicht erkannten Flanke. Das Programm ist ausgiebig kommentiert und sollte daher gut lesbar sein. Ich wäre dankbar, wenn jemand eine Idee hätte. --> zum Programm: http://ebay321.pytalhost.com/frequenz.c
Hallo, ich weiß, dass die Antworten hier im Forum auf freiwilliger Basis geschehen. Aber hat denn keiner eine Idee? Ich wäre auch dankbar, wenn jemand mir das Konzept von der Idee bestätigen kann oder Dinge aufzeigt, wo der Fehler mit großer Wahrscheinlichkeit nicht liegt, sodass man ein Teil des Codes ausschließen kann. Danke.
ehm, ohne jetzt genau draufgeguckt zu haben, warum sind da so viele sachen in den int routinen ? Rufst du jetzt wirklich ein timer overflow int in dem int0 ? Ob das gut geht ... Ach, und für bessere sichtbarkeit könntest du dein source als dateianhang statt link posten.
Hallo, der Gedanke dabei ist, das das externe Interrupt beim ersten Schließen des Reedkontakts anspringt. Wird sofort dann in der INT Routine das externe Interrupt deaktiviert (dadurch fange ich mir keine erneuten Reedkontaktprellungen ein). In dieser Interrupt Routine wird dann nur der Timer0 vorgeladen, dass ab sofort in 1ms überlauft und evtl. anstehende Interrupt Flags des Timers gelöscht, falls dieser zufällig in der Zwischenzeit beim Abarbeiten der externen Interrupt Routine übergelaufen wäre. Nach 1ms wird dann die Timer0 Interrupt Routine aufgerufen und prüft, ob der Reedkontakt noch betätigt ist. Wenn ja, müsste er nicht mehr prellen, da Schaltzeit inkl. Prellzeit bei 0,5ms liegen (laut Datenblatt). Viele Sachen sind das eigentlich nicht, es werden nur Interrupts freigeschalten und gesperrt, sowie eben wegen der Prüfung auf fallende oder steigende bzw. 1. und 2. Flanke ein paar If-Anweisungen. Aber in den If-Anweisungen steht jedesmal mehr oder weniger das selbe, eben Interrupts sperren, freigeben, rücksetzen von Zählern... jedoch eben nur auf die jeweilige Flanke konfiguriert.
Um welche Frequenzen handelt es sich denn? (scheinbar kleiner als 500Hz) Zur Not wäre ein Polling des Pins per 8bit-Timer sinvoller als auf ein Interrupt zu reagieren (mit dem damit verbundenen Aufwand mit Entprellen etc.)
Hallo Stefan, nur um sicher zu gehen: so erwarten wir das prellende Signal, oder? ______________ Schalter offen _______| |________ _____ pegel an int0 __________/\/\/\/\/ \/\/\/\____ |-- <0.5ms--| Erstmal ist es wirklich wichtig zu wissen, mit welcher Frequenz fs dein Signal maximal schaltet. Bei einer Schaltzeit von 0.5ms muss fs/2 < 1/0.5ms = 2kHz, fs also kleiner 1kHz sein. Wenn deine Prellunterdrückung 1ms aktiv ist muß die Frequenz wie STK500-Besitzer schon andeuted < 0.5kHz sein. Wenn dieser Teil des Codes erreicht wird, dann hast du sowieso ein Problem: //Messung wird nicht gewertet, Variablen rcksetzen, so das neue Messung beginnen kann Entweder a) dauert das Prellen länger als 1ms oder b) deine Frequenz ist größer als 0.5kHz. Kannst du Fall a) ausschließen, müsstest das Ergebnis deiner Messung f>0.5kHz sein. Was mir auffällt: Du schalltest den Timer0 im timer 0 interrupt nicht ab. Angenommen du hast eine fallende Flanke (Reed auf Masse) detektiert, und auch nach 1ms ist dieser noch auf Masse. Dann schaltest du die LED ein. Angenommen nach 2ms ist Reed immer noch auf Masse und der 2. Overflow Interrupt des Timer0 tritt auf. In diesem Fall gilt (MCUCR & (1 << ISC00)) ist wahr da du es beim 1. Overflow interrupt gesetzt hast: MCUCR = (1 << ISC00) | (1 << ISC01) gleichzeitig hattest du hier den interrupt aber nicht ab sonder nochmal angeschaltet: GICR |= (1 << INT0) ; (PIND & (1 << Reedkontakt) ist beim 2. Overflow false, da du *immer noch* mit dem Reed auf 0 bist. Falls es das nicht ist: Da dein Problem nur in eine Richtung auftritt könnte man nach Unterschieden der beiden Schaltrichtungen suchen. 1. Dein Code reagiert aufgrund eines Fehler auf positive Flanken anders als auf negative Flanken . Um das zu analysieren wäre es sicherlich hilfreich Teile des Codes der in allen if-else zweigen gleich ist in eine Funktion zu verschieben und diese jeweils aufzurufen. 2. Die Schaltung reagiert auf positive Flanken anders als auf negative Flanken. Da fällt mir auf den ersten Blick nur der Pull-Up ein. Wenn dein Kontakt sich schließt, dann wird der Pegel des Pins sehr schnell auf 0 gezogen. Wenn dein Kontakt sich öffnet, dann zieht der Pull up den Pegel auf 1. Dabei muss eine eventuelle Kapazität am Pin umgeladen werden. Je größer diese Kapazität und je größer der Pull-up, desto länger dauert dies. Beispiel ________________ Schalter offen _| |_____ |-umladen-|--1ms---:-- -| :________:__ : Pegel an int0 ___________| : |_:nach 1ms Pegel schon wieder 0 : : :->: pull down schnell Was gegen diesen Effekt spricht ist, dass deine Frequenz ja scheinbar unter 100Hz liegt (du kannst die LED ja noch beobachten). Ich würde die Frequenzberechnung an deiner Stelle sowieso in den Interrupt Handler ziehen. Damit wuerdest du etwas Synchronisationsaufwand (MessungAusgewertet,...) sparen. Meine Idee wäre folgende: 1. In timer0 oder INT0 Interrupt die Anzahl der Flanken zählen (N++). 2. In timer1 Interrupt die Anzahl der Überläufe zählen 3. Alle N timer0 Interrupts dann die Durchschnittsfrequenz über die letzten N/2 vollen Takte berechnen (T=((überläufe*timer1_max)+timer1)/N und die Anzahl der timer1 Überläufe zurücksetzen. So kannst du N so lange erhöhen bist du mit der Frequenzberechnung schnell genug bist, sie innerhalb von N/2 vollen Signaltakten abzuarbeiten. Ich hätte auch noch eine Idee, wie du timer0 sparen kannst, indem du: 1. den timer 0 interrupt durch den compare match interrupt des timer1 ersetzt 2. den compare Wert des timer1 in der INT0 service Routine auf ( aktuellen timer Wert + 1ms ) mod timer1_max setzt.
>Zur Not wäre ein Polling des Pins...
Manchmal frag ich mich, ob die Leute es als unanständig ansehen, einen
Taster (oder Reedkontakt) schlicht in festen, geeigneten, von einem
einzigen Hardwaretimer erzeugten Zeitintervallen abzufragen und zu
entprellen. Als würde es Taster irgendwie adeln, wenn man sie an
externen Interrupts betreibt (dem µC-Feature mit der magischen
Anziehungskraft) und mindestens zwei bis drei Hardwaretimer mit der
Entprellung beschäftigt. Das immer gleiche Ergebnis kann man dann in
diesem oder 84 anderen Threads nachlesen.
Hi, habe selbst schon massig Erfahrung mit Reedkontakt und Interrupt und kann aus der Praxis bestätigen dass die ganze Softwareentprellerei zumindest in den Fällen wo ich das mache, unnötig ist. Schaltung: 4,7K Widerstand von 5V an den Reed, Reed mit 100nF Kondensator parallel gegen Masse. Heißes Ende des Reed an den Interrupt, dieser feuert bei fallender Flanke. Hab damit schon diverse Motorradtachos gebaut die tausende Kilometer ohne Problem gezählt haben. Geschwindigkeitsmessung klappt auch wunderbar. Beim Oszilografieren entsteht eine messerscharfe fallende Flanke gefolgt von einer recht steilen Aufladekurve. Prellen ade!! Ist aber möglicherweise nicht bei schnelleren Drehzahlen realisierbar (evtl. tüfteln). Funktioniert bei einem 21 Zoll Vorderrad bis über 150 Km/h. Gruß Thomas
Hallo, also die Frequenz ist in der Tat ziemlich gering so max. an die 40Hz, eher geringer. Die 40Hz treten nur selten als Maximalwert auf. Das mit dem Prellen/Signalverlauf ist, sowie du es schreibst/skizziert hast @Januar2010, Du hast recht mit dem Timer0 Interrupt, hier hatte ich einen Denkfehler. Also habe ich nun in die Timer0 ISR direkt anfang den Timer0 deaktiviert
1 | TIMSK &= ~(1 << TOIE0); /*Timer0 (8-bit) Interrupt wird gesperrt, sodass kein Timer0-Interrupt auftreten kann, wird immer |
2 | erst wieder aktiviert, durch externes Interrupt an INT0*/
|
Und aktiviere diesen jeweils wieder in den einzelnen Verzweigungen des externen Interrupts. Nur jetzt macht das Programm folgendes: Die LED blinkt nur einmal, d.h. wird einmal angeschaltet und einmal abgeschaltet beim Drüberziehen des Magneten über den Reedkontakt, danach reagiert die LED nicht mehr und blinkt nicht mehr. Daraus folgerte ich, dass die Timer0 ISR irgendwie nicht mehr aufgerufen wird (da nur diese ja die LED's schaltet). Dies kann nur geschehen, wenn:
1 | if (MessungAusgewertet){ |
FALSE ist und er in den else-Zweig springt, wo nur die Anweisung "return;" steht. Also geschaut, wann MessungAusgewertet=FALSE wird, und das nur, wenn im Timer0 ISR der Zähler "Messung_vollstaendig" 2x erhöht wurde. Testweise habe ich den Zähler dann auf 10 gesetzt und siehe da, die LED blinkt 5x. Aber die MessungAusgewertet wird doch im Hauptprogramm wieder direkt auf TRUE gesetzt, wenn es ausgewertet wird?!? Außerdem was seltsam ist, dass wenn der Zähler Messung_vollstaendig auf 2 steht, leuchtet die LED nur 1x auf. Irgendwie scheint es mir, als prellt da doch noch was, aber wieso? Anbei nochmal das Programm mit dem Timer0 Änderung. http://ebay321.pytalhost.com/frequenz1.c
@Thomas Schattat Meinst du das Entprellen so, wie ich es im Anhang als Schaltplan eingefügt habe? Reed1 nud Reed2 stellen die Kontakte des Reeds dar. Hast du die internen Pullups dabei aktiviert? Wenn es dir nichts ausmacht und du das willst, wäre ich dankbar, wenn du mir ein Programm mal zum Vergleich bereitstellen würdest, gerne auch per e-mail: merryj@gmx.de Danke.
Nimm anstatt Magnet mit REED Kontakt, einen Manget mit Hall Element mit nachgeschaltetem Schmitttrigger mit Hysterese. Damit entfallen alle Prelleffekte die ein mechanischer Kontakt immer hat. Sowas erleichtert die Arbeit ungemein.
Hi Also, die Schaltung in der Skizze wird so nicht funktionieren. Vcc ------- ! --- ! ! ! ! R --- ! !---- Port x S \ ! ! --- ! --- C ! ! Gnd ------- Funktion: S offen wird über R geladen, Port x erhält irgendwann einmal ~Vcc S geschlossen, C wird entladen und Prellsignale werden kurzgeschlossen. So sollte es in der Regel funktionieren. Bei max. 40 Hz würd ich aber die Eingänge pollen, einen Grund für eine IO -ISR sehe ich nicht und macht nur unnötig das Leben schwer. Gruß oldmax
Hi, bin gerade beruflich im Ausland. Kannste aber hier finden: Beitrag "Standard LCD mit Controller steuern" Hatte da mein Programm mal für solche wie dich hinterlegt, aber da war das Augenmerk auf die LCD Steuerung gerichtet. Hoffe es hilft. Ach so: Die Schaltung im Bild oben ist genau so wie ich es meine. Der interne Pullup ist ein, (wie groß ist der eigentlich so??). Kannst nach mir gugeln, dann findeste auch Fotos und so... Viel Erfolg, Gruß Thomas
Gastofatz schrieb: > Manchmal frag ich mich, ob die Leute es als unanständig ansehen, einen > Taster (oder Reedkontakt) schlicht in festen, geeigneten, von einem > einzigen Hardwaretimer erzeugten Zeitintervallen abzufragen und zu > entprellen. Die Frage ist ob das "zu entprellen" hier unbedingt einfacher ist. Man kann aber auch mit einem externen Interrupt basierten Ansatz und einem Timer auskommen. Man kann das so programmieren, dass man dann pro Schaltflanke nur 2 ISR abarbeiten muss. Ich weiß nicht genau mit welcher Frequenz man Timer-Interrupt gesteuert pollen muß. Ich würde auch nicht den externen Beschaltungsaufwand erhöhen um die Software etwas einfacher zu machen. Bei 40Hz ist eine Softwarelösung sicher billiger und flexibler als teurere "Hardware". Stefan, ich kann leider den Link zu deinem Sourcecode nicht mehr öffnen. Versuche doch mal im Hauptprogramm zu Testzwecken nur while(1) { MessungAusgewertet = TRUE; } Dann hast du MessungAusgewertet als einzige Variable auch nicht als volatile deklariert. Versuche das auch nochmal. Viele Gruesse,
Hallo nochmal, erstmal sehe ich den Unterschied nicht zwischen der Schaltung A und B, die sind doch gleich nach meinem Verständnis. Wieso die erste nicht funktionieren soll ist mir unklar, ich hab das so schon wiederholt erfolgreich gemacht. Der Grund für den IRQ und kein Polling ist, dass der Prozessor wegen Stromsparen dauernd pennt und immer nur für den Timer oder den Reedkontakt aufwacht. Jaja, das geht auch mit Polling, ich weiß. Da ich auch die Geschwindigkeit messen will ist es auch erwünscht, den Kontakt SOFORT zu erkennen. Sodann: Wenn ich 40 Hz habe so dauert eine Umdrehung also 25ms. Der Reed schließt nur weniger als 10% der Umdrehung, den Rest braucht der Magnet um herumzufahren. Ich muss also dann mindestens jede ms pollen, in meiner Schaltung pennt der Prozessor aber ganze 16ms ausser wenn der IRQ feuert. Ein Kontakt MUSS sicherlich nicht per IRQ eingelesen werden, man sollte es aber auch nicht als total daneben abtun. Ist sicherlich eine Frage der Philosophie. Warum hab ich keinen Hallsensor genommen: Weniger ist manchmal mehr. Der braucht wieder Stromversorgung (meine Batterien halten so etwa drei Monate) und er braucht drei statt zwei Drähte. Reed ist eben einfacher und billiger. KISS (Keep it simpel stupid...) Gruss und schönen Sonntag Thomas
Thomas Schattat schrieb: > Wieso die erste nicht > funktionieren soll ist mir unklar Weil der Portpin direkt an Masse liegt ... Gruß Jobst
Hallo, ich wollte mich auch mal zurückmelden. Das Programm läuft jetzt. Es lag tatsächlich an dem blöden Fehler mit dem volatile (arrgh). Im Moment entprelle ich den Reed nach der obigen Schaltung von oldmax, habe die Softwareentprellung mit dem Timer noch drin. Morgen probiere ich mal nur mit Software und mal nur mit Hardware zu entprellen. Je nachdem, ob ich am Ende noch einen Timer übrig habe oder einem noch die Entprellungsaufgabe zuschreiben kann, wird dann ggf. per Software entprellt, ansonsten per Hardware. Vielen Dank an alle für die Unterstützung.
Hallo Thomas, eine Frage bleibt mir jedoch noch: In deinem Beispielprogramm von oben schreibst du, dass du mit 1µF (ich denke Elko) entprellst (+4k7). Hier in dem Beitrag hast du geschrieben 100nF. Was ist sinnvoller oder geht beides problemlos? Den internen Pullup aktivierst du ja trotzdem an dem INT-Pin, so wie ich dem Beispielprogramm entnehmen kann, richtig? Danke
Hallo Sefan, das hat sich historisch entwickelt, die ersten Versuche waren eher "konservativ", also lieber mal mehr entprellen. Es stellte sich heraus dass 100nF zum Schluss ausreichend waren. Gruss Thomas
Und noch: Ich betreibe es mit dem internen Pullup und 4,7K parallel. Zum Stromsparen schalte ich den 4,7K ab wenn das Ding abgeschaltet wird. Der interne reicht aus um den wieder aufzuwecken, dann schalte ich den externen 4,7K wieder zu. Das Ding ist batteriebetrieben, das muss man schon auf den Stromverbrauch achten. Alles klar?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.