Autor |
Tastenabfrage in C Atmega32 |
|
|
|
|
BID = 918518
peterschrott Gesprächig
Beiträge: 167 Wohnort: Düsseldorf
|
|
Hallo Leute,
ich brauch mal wieder eure Hilfe bei der Programmierung eines Atmega32.
Vorab:Ich fange gerade erst an, mit der Programmierung in C.
Ich benutze AStudio 6.1 und versuche mit Jtag ICE3 zu debuggen.
Es geht um Tastenabfrage. Ich benutze zum Üben das Pollin Board.
Die Software die ich benutze ist aus dem Netz und ist unter
"Tastenabfrage in C" auf www.rn-wissen.de zu finden.
Der Timer0 soll jede msec einen Interrupt erzeugen und alle 10msec die Tasten
abfragen.
Hier tritt jedoch folgendes Problem auf:
Die Zählvariable "ovl0" im Interrupt wird nicht hochgezählt.
Sie bleibt letztendlich auf 255 bei Eintritt in den Interrupt, wird incrementiert und landet dann wieder auf 0.
Das bedeutet, daß die Tasten nie abgefragt werden.
Wenn ich die Sache richtig verstanden habe, sollte die Variable, da sie "volatile und auch static"ist, ihren Wert bei Eintritt in den Interrupt beibehalten haben.
Die Compiler Optimierung habe ich vorerst auf keine Optimierung eingestellt.
Ich versuche mal, den betreffenden Abschnitt zu posten.
Vielleicht hat jemand noch eine Idee dazu.
[code]
/* ISR für Timer 0
(16000000/64/256 Hz = 976,5Hz = 1,02ms)
Bei 16MHz Grundtakt läuft Timer0 alle 1,02ms über.
* Um auf rund 10ms zu kommen, rufen wir get_taster nur
* jedes 10. mal auf. */
ISR (TIMER0_OVF_vect)
{
volatile static unsigned char count_ovl0;
volatile unsigned char ovl0 = count_ovl0 +1;
if (ovl0 >= 10)
{
get_taster (0,PIND & (1<<PD2));
get_taster (1,PIND & (1<<PD3));
get_taster (2,PIND & (1<<PD4));
ovl0 = 0;
}
}
[code]
|
|
BID = 918522
der mit den kurzen Armen Urgestein
Beiträge: 17433
|
|
Wenn du jede ms einen Interrupt (Aufforderung das Programm zu unterbrechen und in die Interuptroutine zu springen) auslöst kommt die gar nicht dazu auf 10 ms hoch zu zählen. Also nur alle 10 ms einen Interrupt auslösen und dann die Tasten abfragen. Nach der Abfrage springt das Programm zurück zum Hauptprogramm und macht da weiter wo es unterbrochen wurde.
_________________
Tippfehler sind vom Umtausch ausgeschlossen.
Arbeiten an Verteilern gehören in fachkundige Hände!
Sei Dir immer bewusst, dass von Deiner Arbeit das Leben und die Gesundheit anderer abhängen! |
|
BID = 918556
peterschrott Gesprächig
Beiträge: 167 Wohnort: Düsseldorf
|
Also ich hab den Fehler jetzt gefunden.
Es fehlt lediglich eine ganze Zeile in Code !!
Den Timer Interrupt nur alle 10msec ausführen zu lassen ist keine so gute Idee. Es wird unter anderem geprüft wie lange eine Taste gedrückt wurde. Dazu dient als Zeitgeber dieser Interrupt (Ticks). Bei einer lang gedrückten Taste gibt es z.B. eine variable 60Ticks. Wären bei 10msec dann 600msec. Das ist zu lange.
Aber so funktionierts:
Code : |
// Interruptserviceroutine für Timer 0
// hier 1ms
ISR( TIMER0_OVF_vect ) // every 1ms
{
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 1e-3 + 0.5); // preload for 1ms
volatile static unsigned char count_ovl0;
volatile unsigned char ovl0 = count_ovl0 +1;
if (ovl0 >= 10)
{
get_taster (0,PIND & (1<<PD2));
get_taster (1,PIND & (1<<PD3));
get_taster (2,PIND & (1<<PD4));
ovl0 = 0;
}
count_ovl0 = ovl0;
}
|
|
|
BID = 918625
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Zitat :
| Wenn du jede ms einen Interrupt (Aufforderung das Programm zu unterbrechen und in die Interuptroutine zu springen) auslöst kommt die gar nicht dazu auf 10 ms hoch zu zählen. |
Wos?
Dein zweiter Quellcode ist besser, wobei ovl0 nicht volatile sein muss sondern nur der Zähler.
ovl0 als temporäre Variable zu benutzen ist aber eine gute Idee, dann muss der Zählerwert nicht jedes mal aus dem RAM zurückgelesen werden (bzw. auch beim Inkrementieren geschrieben werden). Das kostet geringfügig mehr Zeit und Platz.
_________________
|
BID = 918704
perl Ehrenmitglied
Beiträge: 11110,1 Wohnort: Rheinbach
|
Zitat :
| Dein zweiter Quellcode ist besser, |
...Aber dies ist bei Timern extrem schlecht:
Zitat :
|
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 1e-3 + 0.5); // preload for 1ms |
Da man nicht genau weiss, wie lange vor der Ausführung dieses Befehls der Überlauf passiert ist, lädt man den Wert für den Timer nicht einfach in das Register, sondern man addiert ihn besser zum aktuellen Registerinhalt.
|
BID = 920322
peterschrott Gesprächig
Beiträge: 167 Wohnort: Düsseldorf
|
Hab diese Zeile nicht verstanden.
Erklär mir doch bitte mal was da passiert ?
den zweiten Teil kann ich ja noch ausrechnen, doch was bedeuten hier die
Deklariationen uint8_t und uint16_t ?
Ist das die Subtraktion des aktuellen Zählerstandes von TCNT0 vom max Zählerstand ?
|
BID = 920326
perl Ehrenmitglied
Beiträge: 11110,1 Wohnort: Rheinbach
|
Zitat :
| doch was bedeuten hier die Deklariationen uint8_t und uint16_t ? |
Das siehst du am besten in der Dokumentation deines C-Komplizierers nach (und lernst es), oder du lässt es übersetzen und schaust dir den erzeugten Maschinencode an (sicherer, wenn man nah an der Hardware programmiert).
|
BID = 920427
peterschrott Gesprächig
Beiträge: 167 Wohnort: Düsseldorf
|
@Perl
Hab letztendlich doch noch eine Erklärung, sogar vom Autor persönlich verfasst, im Netz gefunden.
Für mich als Anfänger wäre es fast unmöglich diese Notation zu verstehen.
Es handelt sich um einen CAST, bzw zwei CASTS. Der Ausdruck rechts wird erst in eine 16bit unsigned int und dann in eine 8bit unsigned int gewandelt. Timer 0 hat nur 8bit.
Nach der Umwandlung wird der Wert ins TCNT0 Register geladen (hier 6).
Ich werde die Routine so abändern, daß ich vorher noch den Wert des Registers TCNT0 auslese und dann den Rest zuaddiere.
Das hilft mir jetzt weiter.
LG
Peterschrott
|
BID = 920434
perl Ehrenmitglied
Beiträge: 11110,1 Wohnort: Rheinbach
|
Zitat :
| Nach der Umwandlung wird der Wert ins TCNT0 Register geladen (hier 6) |
Ja, der ganze lange Ausdruck lädt nur eine Konstante ins Register, d.h. zur Laufzeit wird da gar nichts mehr gerechnet.
Zitat :
| Ich werde die Routine so abändern, daß ich vorher noch den Wert des Registers TCNT0 auslese |
Mach das lieber nicht explizit, sondern addiere einfach die Konstante zum Register.
Mit etwas Glück, d.h. wenn der aktuelle Wert von TCNT0 nicht noch woanders gebraucht wird, optimiert der Compiler das vorige Auslesen sowieso weg.
|