Forum: Mikrocontroller und Digitale Elektronik Seltsames Verhalten der Timer


von Luge (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
im Anhang ist mein C-Code für einen ATmega16.

Was das Programm machen soll ist recht simpel.
Ein Timer-Compare-Interrupt bildet die Zeitbasis von 1ms.

In der Mainschleife kann ich unabhängig voneinander 2 LEDs über 2 Taser 
einschalten. Beim Einschalten wird wird jede LED eine Variable auf 3000 
gestellt, die in der ISR dekrementiert wird.

Wird der Wert 0 erreicht (also nach 3s) soll die dazugehörige LED 
gelöscht werden.
Zur entprellung und um das ganze zu Strukturieren steuert eine 3. 
Variable die Hauptschleife, so dass diese immer 100ms pro durchlauf 
benötigt.

Soweit zur Theorie.
Meine Beobachtung ist leider ne ganz andere.
Aktiviere ich eine LED durch Drücken des Tasters, leuchtet diese 
zunächst "für immer". Beim Aktivieren der 2. LED leuchtet diese für 
immer, dafür geht die vorherige nach 3s aus. Da setzt mein Verständniss 
völlig aus, wie kann den ein Taster die LED des anderen Taster auf 
einmal irgendwie triggern?
Oder hab ich ein völlig falsches Verständniss der Timer und ISR?

Und dann noch eine eher allgemeine Frage.
Wie sieht den eigentlich eine "gute Programmstruktur" aus?
Da ich mir bisher alles selbst beigebracht habe mit diversen Tuts weis 
ich nicht wie guter Programmierstil eigentlich ausschaut. Bei mir ists 
eher auf Funktionalität ausgelegt :)

Wie liest man denn am besten Taster ein? Mainschleife? ISR? Funktion die 
ab und zu aufgerufen wird?
Wo löscht man am sinnvollsten LEDs?
usw.

Hoffe ihr könnt mir weiterhelfen,
Danke schon mal,
MfG Luge

von Karl H. (kbuchegg)


Lesenswert?

Wie schalten deine Taster?
Nach Masse oder nach Vcc?

(Was ist der Grundzustand der Bits an denen die Taster hängen. 0 oder 
1?)

Wie schalten deine LED ein? Portbit auf 0 oder 1 für einschalten?

von Karl H. (kbuchegg)


Lesenswert?

Luge schrieb:

> Und dann noch eine eher allgemeine Frage.
> Wie sieht den eigentlich eine "gute Programmstruktur" aus?

Deine Programmstruktur ist nicht schlecht.

> Da ich mir bisher alles selbst beigebracht habe mit diversen Tuts weis
> ich nicht wie guter Programmierstil eigentlich ausschaut.

Ist ok.
Für einen Autodidakten eigentlich sogar ziemlich gut.

> Wie liest man denn am besten Taster ein? Mainschleife? ISR? Funktion die
> ab und zu aufgerufen wird?

Über die PeDa Tastenentprellung :-)
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29


> Wo löscht man am sinnvollsten LEDs?

So wie du das hast, ist das schon ok.

von Luge (Gast)


Lesenswert?

Eingänge:
0 = GND
1 = VCC

Ausgänge:
0 = GND
1 = VCC

Aber ich spiel schon seit Tagen mit den LEDs und Tastern herum, geht 
alles, die Makros funken auch.

Denke irgendwo im Timer oder in der ISR liegt der Fehler.
Der uC arbeitet aber mit 16Mhz (ausprobiert mit _delay_ms()). Die ISR 
wird auch wirklich jede 1ms aufgerufen.
(Ausprobiert durch toggeln einer LED in der Hauptschleife im 5s 
Intervall und mit Stoppuhr :))

Aber Danke dir schon mal!

von Karl H. (kbuchegg)


Lesenswert?

Luge schrieb:
> Eingänge:
> 0 = GND
> 1 = VCC
>
> Ausgänge:
> 0 = GND
> 1 = VCC
>

:-)
Lol das hab ich mir schon gedacht, dass 0 einem GND Pegel entspricht.

Ich formuliere anders:
Wenn du nichts drückst, liefert dann der Tasterpin eine 0 oder eine 1?

Zusatzfrage:
Bist du dir da ganz sicher?

Bonuspunkte:
Kannst du mal die externe Beschaltung in Form eines (nicht von dir 
gezeichneten) Schaltbildes zeigen bzw. wenn es ein fertiges Board ist 
den Namen des Boards nennen?

> Aber ich spiel schon seit Tagen mit den LEDs und Tastern herum, geht
> alles, die Makros funken auch.

Das muss nicht unbedingt etwas heißen, Wenn du alle Logik rumdrehst 
funktionieren die meisten Programme bei denen es nur um Taste 
gedrückt/nicht_gedrückt und Led_an/Led_aus geht, völlig gleich.

> Denke irgendwo im Timer oder in der ISR liegt der Fehler.

Sieht ok aus.

von Luge (Gast)


Lesenswert?

Sry dann hab ich deine Frage falsch verstanden :)

Also, grad nachgemessen. Bei gedrücktem Taster liegen am Eingangspin 5V 
an.
Ausserdem schaltet dann das Register PINB von 0x00 auf zb 0x10 bei 
Taster 5. (getestet über UART und Terminalprogramm, sowas erleichtert 
die Fehlersuche echt ungemein wenn man kein JTAG hat :))

von Karl H. (kbuchegg)


Lesenswert?

Luge schrieb:
> Sry dann hab ich deine Frage falsch verstanden :)
>
> Also, grad nachgemessen. Bei gedrücktem Taster liegen am Eingangspin 5V
> an.
> Ausserdem schaltet dann das Register PINB von 0x00 auf zb 0x10 bei
> Taster 5. (getestet über UART und Terminalprogramm, sowas erleichtert
> die Fehlersuche echt ungemein wenn man kein JTAG hat :))

OK

Ich such weiter

von Luge (Gast)


Lesenswert?

Danke dir!

ich hatte schon Angst dass meine ISR zu lang ist. Aber bei 16Mhz dauert 
ein Takt ja grad mal ~60 nS, das reicht eigentlich dicke.

Aber ich kanns mir nur so erklären dass die ISR teilweise nicht 
aufgerufen wird/unvollständig ausgeführt wird.

Ich hab mir auch die 2 Timerstände mal aufs Terminal ausgegeben. Da 
stand dann zB timer_a = 0, trotzdem hat die LED geleuchtet. Und das 
obwohl die Bedingung jede mS ja geprüft wird!

von holger (Gast)


Lesenswert?

ISR (TIMER0_COMP_vect){

Was hat das mit Overflow zu tun?

von Karl H. (kbuchegg)


Lesenswert?

Da ist der erste Fehler
1
#define   LED_AUS(LED)    ((OUTPORT) &= (1 << (LED)))

muss heissen
1
#define   LED_AUS(LED)    ((OUTPORT) &= ~(1 << (LED)))

von Karl H. (kbuchegg)


Lesenswert?

Luge schrieb:
> Danke dir!
>
> ich hatte schon Angst dass meine ISR zu lang ist. Aber bei 16Mhz dauert
> ein Takt ja grad mal ~60 nS, das reicht eigentlich dicke.

Du sagst es

> Aber ich kanns mir nur so erklären dass die ISR teilweise nicht
> aufgerufen wird/unvollständig ausgeführt wird.

Nein.
Du machst den Kardinalfehler: Bei Fehlern kannst du zu 99% davon 
ausgehen, dass DU einen Programmfehler gemacht hast, und nicht dass CPU 
oder Compiler irgendetwas schräges treiben.

von holger (Gast)


Lesenswert?

Jetzt hab ichs;)

>  TIMSK |= (1<<OCF0);

OCF0 gibts nicht in TIMSK. Das Ding heisst OCIE0.

von Karl H. (kbuchegg)


Lesenswert?

holger schrieb:
> Jetzt hab ichs;)
>
>>  TIMSK |= (1<<OCF0);
>
> OCF0 gibts nicht in TIMSK. Das Ding heisst OCIE0.

Prinzipiell richtig.
Nur leider nicht das Problem :-)
(korrigier erst mal die fehlende ~)

OCIE   ist ein #define auf 1
OCF0   ist ein #define auf 1

d.h. in beiden Fällen steht dort

  TIMSK |= ( 1 << 1 );

und das schaltet den Compare Match Interrupt ein :-)

D.h. Obwohl du die falsche Bitbezeichnung genommen hast passiert 
trotzdem das richtige.

von Karl H. (kbuchegg)


Lesenswert?

Nächster Fehler
1
    if TASTER(T5){
2
      LED_AN(LED1);
3
      timer_a = 3000;
4
    
5
    }

hier ist die Reihenfolge falsch, bzw. überhaupt der Zugriff.

Was ist das Problem:
Dein Code schaltet zb die LED ein. Und genau wie es der Teufel haben 
will, kommt der Compare Match Interrupt noch ehe timer_a auf 3000 
gesetzt wird. Die Folge: da die ISR abläuft, ehe timer_a auf 3000 
gesetzt wird, schaltet die ISR die LED wieder aus :-(

Da allerdings timer_a sowieso eine 16 Bit Variable ist, muss man den 
Zugriff atomic machen, also ein cli() / sei() Pärchen drumm herum legen.
1
    if TASTER(T5){
2
      cli()
3
      timer_a = 3000;
4
      LED_AN(LED1);
5
      sei();
6
    }

Für die andere Taste und den anderen Zähler genau gleich

von Luge (Gast)


Lesenswert?

@ Holger:
1. Ich will den Interrupt ja nicht bei Overflow (bei 8 Bit nach 255) 
sondern bei Compare Match. Deshalb beleg ich das Compare-Register ja 
auch mit 249, das entspricht bei 64er-Prescaler und 16Mhz genau eine mS.

2. Ups, mit dem OCF0 hast du recht! Peinlich peinlich!
Daran liegts aber trotzdem nicht, weil zufällig OCF0 und OCIE0 genau 
jeweils Bit# 1 sind und deswegen in der include-Datei auch jeweils den 
Wert 1 enthalten :)
Aber danke für den Hinweiß!

@ Karl Heinz:
Danke dir, werd ich sofort ausprobieren. Nur wie gesagt, bei Spielereien 
ohne Timer und ISR gabs nie Probleme.

Evtl werd ich auch das Timer-Zeugs aufschieben und erstmal eine geprüfte 
Routine zur Entprellung einbauen, damit ich mir Fehler von den Tastern 
sicher ausschliesen kann. Hab das Gefühl die prellen ordentlich :)
Pollin oO

Danke euch beiden!

von Karl H. (kbuchegg)


Lesenswert?

Luge schrieb:

> @ Karl Heinz:
> Danke dir, werd ich sofort ausprobieren. Nur wie gesagt, bei Spielereien
> ohne Timer und ISR gabs nie Probleme.

Das lässt nur einen Schluss zu:
Du hast ganz einfach die falschen Dinge getestet.

(Durch Testen kann man immer nur das Vorhandensein von Fehlern 
nachweisen. Nie das Fehlen. Wenn also ein Programm so tut als ob, heißt 
das noch lange nicht das alles richtig ist)

> Evtl werd ich auch das Timer-Zeugs

Dein Problem ist nicht der Timer!
der arbeitet einwandfrei.
Das Problem ist dein Drumherum :-)

> aufschieben und erstmal eine geprüfte
> Routine zur Entprellung einbauen,

in dem Fall hättest du überhaupt keine Entprellung benötigt :-)

von Luge (Gast)


Lesenswert?

Nachtrag: (Sry fürn Doppelpost)
@ Karl Heinz:
Danke dir, klingt einleuchtend! Achja, und 16Bit Variablen, da war ja 
auch was...

Wird auch geändert. Nur, mal im Ernst, wie groß ist die Chance dass der 
Interrupt immer genau nach dem Einschalten, bzw zwischen den Takten die 
für die Variablenzuweisung nötig sind, auftritt?
Naja geändert wirds trotzdem.

Falls das nicht klappt, siehe vorherigen Post, dann wird das Thema 
erstmal aufgeschoben.

Danke euch trotzdem!

von Karl H. (kbuchegg)


Lesenswert?

Luge schrieb:

> Wird auch geändert. Nur, mal im Ernst, wie groß ist die Chance dass der
> Interrupt immer genau nach dem Einschalten, bzw zwischen den Takten die
> für die Variablenzuweisung nötig sind, auftritt?

Ist uninteressant.
Passieren kanns (und ist mir auch in der Simulation passiert, ich habe 
allerdings andere Timereinstellungen benutzt, damit ich nicht ewig davor 
sitze) und damit muss es behandelt werden.


Mit den beiden Änderungen verhält sich dein Pgm in der Simulation so wie 
gewünscht. Hauptschuldiger war die fehlende ~

von Luge (Gast)


Lesenswert?

Danke dir nochmal, alles Bestens jetzt!

Wieder was gelernt.

Letzte Woche hatte ich solche Spielereien getestet:

if (TASTER(T1))
   LED_TOGGLE(LED1);

...

if (Taster(T8))
   LED_TOGGLE(LED8);

Seltsam dass sich die fehlende ~ da nicht bemerkbar gemacht hatte.
Sonst wär ich nicht so überzeugt gewesen dass meine Makros stimmen.

Wie auch immer, langsam wirds! :)

von Karl H. (kbuchegg)


Lesenswert?

Luge schrieb:
> Danke dir nochmal, alles Bestens jetzt!
>
> Wieder was gelernt.
>
> Letzte Woche hatte ich solche Spielereien getestet:
>
> if (TASTER(T1))
>    LED_TOGGLE(LED1);

Ja und.

LED_TOGGLE ist ja ein ganz anderes Makro als LED_aus

von Luge (Gast)


Lesenswert?

Achja, mir is da noch ne blöde Frage eingefallen:

Dadurch dass die Anweisungen bei Tasterdruck jetzt durch cli() und sei() 
atomar sind kann es ja sein dass sich meine ISR um einige hundert 
Nanosekunden verschiebt. Im schlimmsten Fall bei jedem Durchlauf der 
Hauptschleife. An sich hier nicht tragisch.

Aber was wäre zB, wenn ich eine Uhr oder sowas in der Richtung bauen 
will?
Irgendwann würden sich ja die Zeiten doch soweit addieren, dass die Uhr 
immer mehr falsch gehen würde? Sehe ich das richtig?
Also nur aus Interesse, ich bau mir so schnell keine Uhr :)

von Luge (Gast)


Lesenswert?

Achja, natürlich ist Toggle was andres als Aus.

Kopf -> Tisch.

Ich glaub für heute reichts, ich hock scho wieder zulange davor :)

von Karl H. (kbuchegg)


Lesenswert?

Luge schrieb:

> Dadurch dass die Anweisungen bei Tasterdruck jetzt durch cli() und sei()
> atomar sind kann es ja sein dass sich meine ISR um einige hundert
> Nanosekunden verschiebt.

Das kann sein. Ja

> Im schlimmsten Fall bei jedem Durchlauf der
> Hauptschleife. An sich hier nicht tragisch.
>
> Aber was wäre zB, wenn ich eine Uhr oder sowas in der Richtung bauen
> will?
> Irgendwann würden sich ja die Zeiten doch soweit addieren,

Nur solange du nicht einen ISR Aufruf überhaupt verpasst.

Der Timer läuft ja im Hintergrund unbeeinflusst weiter.
Lediglich der eine ISR Aufruf kommt dann ein klein wenig später. Aber 
der nächste passt dann wieder, weil der Aufruf ja nur vom Timer selbst 
abhängt und nicht davon, wann die ISR zuletzt aufgerufen wurde.

Diese kleinen Fehler summieren sich also nicht auf.
Lediglich die Zeitabstände der ISR Aufrufe sind ein wenig unregelmässig. 
Aber wenn ein Aufruf ein klein wenig zu spät erfolgt, kommt dafür der 
nächste ein klein wenig früher und alles gleicht sich wieder aus.

von MarioT (Gast)


Lesenswert?

Luge schrieb:
> Hab das Gefühl die prellen ordentlich :)
> Pollin oO

Hast Du zufällig das Pollin-Board? Sind die Taster richtig angeklemmt?

von Luge (Gast)


Lesenswert?

Ja, hab ich, + Erweiterungsboard.
Bereue es auch nicht, lediglich die Kondensatoren an den Tastern musste 
ich auslöten. Seitdem läufts! (zumindest Hardwaremäsig, aber der größte 
Fehler sitzt halt immer noch zwischen Lehne und Bildschirm :) )

von MarioT (Gast)


Lesenswert?

Luge schrieb:
> lediglich die Kondensatoren an den Tastern musste
> ich auslöten.

genau.
Ich kann auch nicht meckern. Für das Geld kann man mit dem Board nichts 
falsch machen.

von Karl H. (kbuchegg)


Lesenswert?


von MarioT (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Den Artikel kennst du?

Den hab ich noch nicht gekannt. Es giebt aber wirklich viele hier mit 
dem Thema "Pollin Schalter". Die Kondensatoren sind bei mir auch 
ausgelötet.
Mein Gedanke war ja, das bei Luge der Fehler mit daran liegen könnte.

von Luge (Gast)


Lesenswert?

Ich kannte den Artikel. Den Umbau im vorletzten Beitrag auf aktiv-low 
fand ich bisl overkill, wenns doch durch auslöten auch klappt :)
Wie heißts, never touch a running system. Und zumindest hardwaremäsig 
runnts bei mir :)

Der letzte Tipp aber mit den kleineren Kapazitäten klingt interessant. 
Evtl mal probiern, nur hab ich zZ keine rumliegen. Naja, und so tuts ja 
auch.

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.