www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik impulszähler bauen


Autor: zähler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich versuche gerade einen Impulszähler mit einem ATmega16 zu bauen. Er 
soll die Impulse an einem Reedkontakt zählen und auf einem LCD die 
Gesamtanzahl der Impulse und die Impulse pro Sekunde ausgeben.

Mein Problem ist, dass ich nicht weiß, wie ich die einzelnen Schritte 
zeitlich einplanen soll.

Gedacht habe ich mir das so:



LCD initialisieren
Timer und Interrupts initialisieren

Auf Interrupts warten:


Bei Timerüberlauf:
{
Impulse zwischen zwei Interrupts durch Zeit zwischen zwei Überläufen 
dividieren und als Impulse/Sekunde auf LCD ausgeben
Gesamtanzahl der Impulse auf LCD ausgeben
}


Bei externem Interrupt (ausgelöst durch Reedkontakt):
{
Gesamtanzahl der Impulse erhöhen
Anzahl der Impulse seit dem letzten Timerüberlauf erhöhen
Warteschleife 10ms (wegen Schalterprellen)
}


Allerdings gefällt mir das ganze nicht wirklich, da ich die LCD-Ausgabe 
in die ISR eingebaut habe und ich auch nicht weiß was passiert, wenn 
diese durch die zweite ISR unterbrochen wird. Außerdem hält die 
Zeitschleife gegen das Schalterprellen den Timer auf.

Wie würde man das besser einplanen? Erfahrung mit Programmieren habe ich 
eigentlich nicht, das ganze ist recht neu für mich. Die Ansteuerung des 
LCDs funktioniert jedoch ganz gut (mit dem Code aus dem GCC-Tutorial).

Autor: Bernd O. (bitshifter)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zähler,

Du solltest unbedingt die Anzeige und die Erfassung trennen. 
Typischerweise macht man das für einfache Programme so, dass man in 
main() in einem festen Zeitraster, beispielsweise alle 200ms das Display 
aktualisiert.
Die Kopplung zwischen der "main()"-Ebene und der Interruptebene 
geschieht über statische globale Variablen mit dem Attribut volatile, 
deren Zugriff aus main() heraus über "ATOMIC_BLOCK" geschützt werden 
muß, damit der Inhalt für einen Durchlauf konsistent ist (jedenfalls bei 
Werten die größer als 1 Byte sind), da es passieren kann, dass die ISR 
den Wert zwischem dem Lesen des LSB und des MSB aktualisiert. 
Schlimmstenfalls passiert das beim Übergang 0x1FF -> 0x200. In Main 
kommt dann kurzzeitig gerne auch mal 0x2FF an.

Beispiel:
static volatile uint16_t isr_var; /* written within ISR, read from main */

main()
{
    while (1) {
        uint16_t local_copy_var;

        /* get values from ISR */
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
            local_copy_var = isr_var;
        }

        /* processing (and display) of local_copy_var */

        delay_ms(200);
    }
}

ISR(TIMER1_WHATEVER_vect) {
    isr_var++;
}


Desweiteren sind Delays in ISRs wie Du ja auch andeutest kritisch zu 
sehen. Wenn die Erfassung so hochgenau sein muß, dass sie in einen 
Interrupt muß, dann solltest Du über Hardware-Capture nachdenken, bei 
dem der Wert eines freilaufenden Zählers beim Eintreten eines 
Hardware-Ereignisses (beispielsweise Reedkontakt) in ein Register 
kopiert wird und der Interrupt aufgerufen wird. In der ISR kann dann 
unabhängig von Latenzen bis zur Ausführung der ISR die Zeit bearbeitet 
werden. Prellen kannst Du so beispielsweise dadurch erkennen, dass zu 
wenig Zeit zwischen zwei ISR-Aufrufen vergangen ist. Erst wenn die 
letzte ISR lange genug her war, wird der Interrupt wieder als gültiges 
Zählereignis gewertet.
ISR(TIMER1_WHATEVER_vect) {
    static uint32_t timestamp_last = 0;

    /* debounce */
    if ((CURRENT_TIME - timestamp_last) > MIN_DIST) {
        isr_var++;
    }
    timestamp_last = CURRENT_TIME;
}

Wenn das Prellen sehr stark und lange sein kann, dann kann das den 
Programmablauf stören, da immer wieder die ISR laufen muß und zu wenig 
Zeit für andere wichtige Aufgaben bleibt. In diesem Fall kann man auch 
darüber nachdenken, in der ISR den eigenen Interrupt zu sperren und über 
einen zweiten Timer nach Ablauf der erwarteten Prelldauer wieder 
aktivieren zu lassen.

Gruß,
Bernd

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Bei externem Interrupt (ausgelöst durch Reedkontakt):
Das ist Käse. Ein Interrupt auf einem mechanischen Kontakt wird dir 
niemals Freude bereiten. Das gibt garantiert ein Gemurkse...

Ich würde in der beschriebenen Anwendung genau 1 Interrupt 
initialisieren: den Timerinterrupt auf 1ms. Da rein eine brauchbare 
Entprellroutine (z.B. die kampferprobte von Peter Dannegger: 
http://www.mikrocontroller.net/search?query=Danneg...). 
Und an die Entprellung dann den Zähler angeschlossen und fertig.

Die komplette Displaygeschichte läuft in der Hauptschleife (aka 
mainloop). Die Entprellerei und Zählerei in der Interruptroutine. So 
läuft das bombensicher.

Autor: zähler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Ich würde in der beschriebenen Anwendung genau 1 Interrupt
>initialisieren: den Timerinterrupt auf 1ms. Da rein eine brauchbare
>Entprellroutine (z.B. die kampferprobte von Peter Dannegger:
>http://www.mikrocontroller.net/search?query=Danneg...).
>Und an die Entprellung dann den Zähler angeschlossen und fertig.

Ich verstehe nicht wirklich, was die Entprellroutine in der ISR des 
Timers tun soll. Der Taster kann ja zu jedem beliebigen Zeitpunkt 
gedrückt werden.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Der Taster kann ja zu jedem beliebigen Zeitpunkt gedrückt werden.
Ja, aber er wird verglichen mit dem Timerinterrupt sehr langsam 
gedrückt. Oder kannst du den Taster 500 mal pro Sekunde drücken und 
loslassen?

> Ich verstehe nicht wirklich, was die Entprellroutine in der ISR des
> Timers tun soll.
Sie fragt im Millisekundentakt den Zustand des Tasters ab. Das nennt man 
Pollen. Polling ist idR. einfacher und deterministischer umzusetzen, als 
so ein Pin-Change-Interrupt, der dir irgendwann irgendwo in den 
Programmablauf reinfunken kann.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
zähler schrieb:

> Ich verstehe nicht wirklich, was die Entprellroutine in der ISR des
> Timers tun soll. Der Taster kann ja zu jedem beliebigen Zeitpunkt
> gedrückt werden.

Solange nur die ISR um einiges öfter aufgerufen wird, als Zeit zwischen 
2 Drücken passiert, spielt das keine Rolle.

Wenn du dir eine Badewanne einlässt, bleibst du ja auch nicht daneben 
sitzen um nur ja nicht das Vollwerden zu verpassen. Wenn du alle 2 bis 3 
Minuten nachsiehst, ob die Wanne schon voll ist, reicht das locker.

Autor: Floh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lieber einen Optokoppler als ein Reedrelais verwenden.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Lieber einen Optokoppler als ein Reedrelais verwenden.
Und wenn man sich das nicht raussuchen kann?  :-/

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht sollte man erst mal am Erfassungspunkt anfangen.

Was wird da gezählt? Warum muss es ein Reed-Kontakt sein? Gibt es 
Möglichkeiten diesen Kontakt durch etwas anderes zu ersetzen? Mit 
wievielen Pulsen in welchem zeitlichen Abstand ist zu rechnen?

Ansonsten: Wenn das Prellen des Kontaktes eindeutig ausfilterbar ist und 
die Reed-Kontakte nicht verhandelbar sind, hat Lothar ja schon gesagt, 
wie man das softwaremässig aufzieht.

Autor: Ferkel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Um erst einmal Land zu sehen, würde ich vorschlagen, den Reedkontakt 
durch ein RC-Glied zu entprellen. Die ATmega haben eine kleine Hysterese 
an den Eingängen, sodass die Impulse richtig erkannt werden können.

Wenn dann die Programmiererfahrung zunimmt und die Ansprüche steigen, 
ist es kein Problem, die Entprellung per Software zu ergänzen.
Aber zunächst ist ein Erfolgserlebnis wichtig!

Autor: zähler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok das werde ich machen. Außerdem ist mir eingefallen, dass ich auch 
noch verhindern muss, dass ein Impuls mehrfach gezählt wird, weil der 
Reedkontakt geschlossen bleibt, also eine Flankenerkennung. Verwendet 
werden soll das ganze an einem Windrad (Umdrehungen zählen). Wirklich 
sinnvol muss es nicht sein, es dient eher zum Programmiererfahrung 
sammeln.

Zum RC-Glied: Der Reedkontakt soll Active-High geschaltet sein, R=22KOhm 
und C=1mikroF müssten zum Entprellen doch hinkommen?

Autor: Ferkel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Zum RC-Glied: Der Reedkontakt soll Active-High geschaltet sein, R=22KOhm
>und C=1mikroF müssten zum Entprellen doch hinkommen?

Kannste machen, weil es ja sowieso nicht sinnvoll sein soll.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.