Forum: Mikrocontroller und Digitale Elektronik impulszähler bauen


von zähler (Gast)


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).

von Bernd O. (bitshifter)


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:
1
static volatile uint16_t isr_var; /* written within ISR, read from main */
2
3
main()
4
{
5
    while (1) {
6
        uint16_t local_copy_var;
7
8
        /* get values from ISR */
9
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
10
            local_copy_var = isr_var;
11
        }
12
13
        /* processing (and display) of local_copy_var */
14
15
        delay_ms(200);
16
    }
17
}
18
19
ISR(TIMER1_WHATEVER_vect) {
20
    isr_var++;
21
}


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.
1
ISR(TIMER1_WHATEVER_vect) {
2
    static uint32_t timestamp_last = 0;
3
4
    /* debounce */
5
    if ((CURRENT_TIME - timestamp_last) > MIN_DIST) {
6
        isr_var++;
7
    }
8
    timestamp_last = CURRENT_TIME;
9
}

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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=Dannegger&forums[]=1&forums[]=19&forums[]=9&forums[]=10&forums[]=2&forums[]=4&forums[]=3&forums[]=6&forums[]=17&forums[]=11&forums[]=8&forums[]=14&forums[]=12&forums[]=7&forums[]=5&forums[]=18&forums[]=15&forums[]=13&forums[]=16&max_age=-&sort_by_date=0). 
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.

von zähler (Gast)


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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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.

von Karl H. (kbuchegg)


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.

von Floh (Gast)


Lesenswert?

Lieber einen Optokoppler als ein Reedrelais verwenden.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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

von Karl H. (kbuchegg)


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.

von Ferkel (Gast)


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!

von zähler (Gast)


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?

von Ferkel (Gast)


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.

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.