Hallo allerseits,
vielleicht möchte ja jemand mir meine Frage beantworten, auch wenn das
Thema schon zig mal diskutiert wurde, nur leider werde ich daraus noch
nicht ganz schlau.
Ich habe versucht das Programm aus dem Tutorial zum Tasten entprellen zu
schreiben, habs dann in meinen Atmega16 geladen und versucht zum laufen
zu bringen, klappt nur leider nicht.
Könnte sich vielleicht mal jemand zum Quellcode äußern?
#include <avr/io.h>
#include <inttypes.h>
#ifndef F_CPU
#define F_CPU 16000000 /*Quarz 16 Mhz*/
#endif
#include <util/delay.h>
/*Funktion zum entprellen*/
inline uint8_t warte(volatile uint8_t *port, uint8_t pin)
{
/*Pin auf Masse gezogen, warten*/
if (! (*port & (1 << pin)))
{
_delay_ms(15);
_delay_ms(15);
_delay_ms(15);
_delay_ms(15);
_delay_ms(15);
if (*port & (1 << pin)) /*Zeit zum loslassen*/
{
_delay_ms(15);
_delay_ms(15);
_delay_ms(15);
_delay_ms(15);
return 1;
}
}
return 0;
}
int main(void)
{
DDRB &= ~(1 << PB0); /*Pin PB0 auf Eingang(Taster)*/
PORTB &= ~(1 <<PB0); /*PullUp Widerstand aus, da extern*/
if (warte(&PINB, PB0)) /*Wenn Taster an PB0 gedrückt..*/
{
PORTD = PIND ^ (1 << PD6); /*LED an Port PD6 an bzw. aus*/
}
return 0;
}
Vielen Dank!
Lars
Stefan "stefb" B. wrote:
> Es fehlt die Einrichtung des PORTD als Ausgabeport. Es muss eine Zeile> mit DDRD in den Code...
So, dass hab ich mal geändert, dürfte ja eigentlich so sein:
int main(void)
{
DDRB &= ~(1 << PB0); /*Pin PB0 auf Eingang(Taster)*/
PORTB &= ~(1 <<PB0); /*PullUp Widerstand aus, da extern*/
if (warte(&PINB, PB0)) /*Wenn Taster an PB0 gedrückt..*/
{
DDRD = 0xff; /*PortD Als Ausgang*/
PORTD = PIND ^ (1 << PD6); /*LED an Port PD6 an bzw. aus*/
}
return 0;
}
Funktionieren tus damit leider imme rnoch nicht, ich bin mir nicht ganz
sicher, ob in der Warte-Funktion nicht einmal ein return fehlt?
Gruß lars
> PORTD = PIND ^ (1 << PD6); /*LED an Port PD6 an bzw. aus*/
Das kann auch schiefgehen, wenn andere Pins des Port verwendet werden.
Wenn ein Ausgang umgeschaltet werden soll, immer das PORT -Register
lesen, weil nur darin der aktuelle Zustand des Ausgangstreibers abgelegt
ist.
Wenn andere Pins an dem Port als Eingänge geschaltet sind, kann das oben
zum munteren Pull-Up-ein-und-ausschalten führen. Also entweder (z.B.
beim Mega16):
1
PORTD=PORTD^(1<<PD6);//LED an Port PD6 toggeln
oder (bei neueren AVRs, die über das PIN-Register toggeln können, indem
man eine 1 hineinschreibt)
1
PIND=1<<PD6;//LED an Port PD6 toggeln
Aber warum machst Du die Initialisierung von DDRD in der if-Abfrage? Die
gehört an den Anfang von main.
Und der dickste Bock überhaupt ist, dass Du die Abfrage nur genau einmal
durchführst und der µC danach gar nichts mehr macht! Es fehlt eine
Endlosschleife am Ende von main.
Aha,
danke für die hinweise.
Hier mal die neue Version des Hauptprogramms, wie gesagt, ich fange grad
erst an mich mit AVR's zu beschäftigen, also verzeiht mir meine
Unwissenheit.
int main(void)
{
DDRB &= ~(1 << PB0); /*Pin PB0 auf Eingang(Taster)*/
PORTB &= ~(1 <<PB0); /*PullUp Widerstand aus,da extern*/
DDRD |= (1 << PD6); /*PD6 als Ausgang*/
while (1)
{
if (warte(&PINB, PB0)) /*Wenn Taster an PB0 gedrückt..*/
{
PORTD = PORTD ^ (1 << PD6); /*LED an Port PD6 toggeln*/
}
return 0;
}
}
Funktion ist nach wie vor nicht gegeben, aber ich werds weiterhin
versuchen.
Gruß Lars
Lars Elsner wrote:
> Funktion ist nach wie vor nicht gegeben, aber ich werds weiterhin> versuchen.
Fang jetzt gleich damit an, dir eine vernünftige Code
Formatierung anzugewöhnen. Dazu gehört auch ein konsistentes
Einrückschema
Dein Code mal umformatiert
if(warte(&PINB,PB0))/*Wenn Taster an PB0 gedrückt..*/
11
{
12
PORTD=PORTD^(1<<PD6);/*LED an Port PD6 toggeln*/
13
}// end if
14
15
return0;
16
}// end while
17
}// end main
Und jetzt meine Frage:
Was glaubst du wird mit deiner Endlosschleife passieren, wenn
mitten in der Endlosschleife der return (also das Beendigen
der Funktion main()) steht?
LED und Taster sind richtig angeschlossen? Die LED kannst und solltest
du testen. Im Moment bin ich auf dem Blinken-Trip, daher empfehle ich
diese Variante ;-)
if(warte(&PINB,PB0))/*Wenn Taster an PB0 gedrückt..*/
25
{
26
PORTD=PORTD^(1<<PD6);/*LED an Port PD6 toggeln*/
27
}
28
}
29
30
return0;// <=== nicht im while-Block!
31
}
Fragen:
1/ Kommt dein Aufbau auf 10x Blinken in 10 Sekunden oder blinkt es
deutlich langsamer? Damit kann man Rückschlusse auf die eingestellte
Taktquelle und deine Compiler-Eistellungen beim Übersetzen (Optimierung
an/aus) ziehen. Bei falscher Einstellung müsstesr du den Taster statt
75ms im Extremfall 1,2s und länger halten!
2/ Nach dem Blinken ohne Tastendruck: Ist die LED AN oder AUS? Damit
kann man Rückschlusse auf den Anschluss der LED ziehen.
3/ Wie ist dein Taster angeschlossen (Schaltbild)?
Karl heinz Buchegger wrote:
> Und jetzt meine Frage:> Was glaubst du wird mit deiner Endlosschleife passieren, wenn> mitten in der Endlosschleife der return (also das Beendigen> der Funktion main()) steht?
Ah, jetzt seh ichs auch. Funktionieren tut es jetzt schonmal, allerdings
wird nicht jeder Tastendruck ausgeführt, manchmal passiert einfach
nichts.
Vielen Dank schonmal dafür.
Gruß Lars
Hallo,
das Problem liegt in der warte-Funktion.
Die kehrt nur mit 1 zurück, wenn der Pegelwechsel genau innerhalb der
ersten 5 delay-Aufrufe erfolgt.
Tschüss
Torsten
Lars Elsner wrote:
> Karl heinz Buchegger wrote:>> Und jetzt meine Frage:>> Was glaubst du wird mit deiner Endlosschleife passieren, wenn>> mitten in der Endlosschleife der return (also das Beendigen>> der Funktion main()) steht?>>> Ah, jetzt seh ichs auch.
Da siehst du, was eine durchgezogene Einrückung ausmachen kann :-)
> Funktionieren tut es jetzt schonmal, allerdings> wird nicht jeder Tastendruck ausgeführt, manchmal passiert einfach> nichts.
Das liegt daran, dass diese Art der Entprellung ziemlicher
Mist ist. Nimm es als Anregung um das Problem der Entprellung
zu erkennen, mehr aber nicht. Für ein praktisches Programm ist
diese Entprellung sowieso nicht zu gebrauchen.
Karl heinz Buchegger wrote:
> Das liegt daran, dass diese Art der Entprellung ziemlicher> Mist ist. Nimm es als Anregung um das Problem der Entprellung> zu erkennen, mehr aber nicht. Für ein praktisches Programm ist> diese Entprellung sowieso nicht zu gebrauchen.
Das konnte ich bis jetzt noch nicht beurteilen, daher bin ich davon
ausgegangen, dass es ein gebräuchlicher weg wäre. Ich war zuvor schon
auf diesen Beitrag Entprellung gestoßen, bin da aber noch nicht ganz
mit durch, sprich ich grübel noch über den Code.
Was wäre denn eine gebräuchliche und sinnvolle Methode Taster
abzufragen?
Gruß Lars
Lars Elsner wrote:
> Karl heinz Buchegger wrote:>> Das liegt daran, dass diese Art der Entprellung ziemlicher>> Mist ist. Nimm es als Anregung um das Problem der Entprellung>> zu erkennen, mehr aber nicht. Für ein praktisches Programm ist>> diese Entprellung sowieso nicht zu gebrauchen.>>> Das konnte ich bis jetzt noch nicht beurteilen, daher bin ich davon> ausgegangen, dass es ein gebräuchlicher weg wäre. Ich war zuvor schon> auf diesen Beitrag Entprellung gestoßen, bin da aber noch nicht ganz> mit durch, sprich ich grübel noch über den Code.>> Was wäre denn eine gebräuchliche und sinnvolle Methode Taster> abzufragen?
Die PeDa Komfort Methode, wie sie in deinem Beitrag beschrieben
ist, ist schon eine sehr gute Methode. Nimm sie am Anfang als
'Black-Box', die du so wie sie ist einfach benutzt. Diese
Entprellfunktion ist ziemlich trickreich aufgebaut und es wird
sicherlich noch eine Zeit dauern, bis du da durchblickst. Das geht
nicht gegen dich, auch gestandene Programmierer durchschauen das
nicht auf Anhieb.
Bei dem Einsatz in der Schleife werden die Parameter aber genau
andersherum übergeben.
1
if(debounce(&PINB,PB0))
Muss innherhalb der debounce-Funktion nicht alles genau andersherum
definiert sein, würde heißen: pin ist gleich port und port ist gleich
pin. Die Bedingungen verändern sich zwar nicht, aber mir würde es
logischer erscheinen.
Berichtigt mich!;)
Für vom Menschen betätigte Hardwareschalter ist das Beste im
Timer-Interrupt alle x ms zu pollen, dann hat man automatisch eine
Entprellung und das Problem ist aus Softwaresicht sauber gelöst.
Wie ich sehe wird diese Variante sogar im verlinkten Artikel
vorgeschlagen.
Gruß Tom
P.S. Die Sleep/Delay-Variante ist für einen kleinen Test, wie in deinem
Fall, schon die richtige Variante (vergleiche mal die nötigen Codezeilen
der Lösungen), aber wenn dein Programm irgendwann mal noch mehr machen
soll, dann willst du nicht überall Verzögerungen drin haben in denen
dein Programm nichts sinnvolles machen kann.
Tom schrieb:> Für vom Menschen betätigte Hardwareschalter ist das Beste im> Timer-Interrupt alle x ms zu pollen, dann hat man automatisch eine> Entprellung und das Problem ist aus Softwaresicht sauber gelöst.>> Wie ich sehe wird diese Variante sogar im verlinkten Artikel> vorgeschlagen.>>>> Gruß Tom>> P.S. Die Sleep/Delay-Variante ist für einen kleinen Test, wie in deinem> Fall, schon die richtige Variante (vergleiche mal die nötigen Codezeilen> der Lösungen), aber wenn dein Programm irgendwann mal noch mehr machen> soll, dann willst du nicht überall Verzögerungen drin haben in denen> dein Programm nichts sinnvolles machen kann.
Ja, alles klar! Aber die Nachteile dieser Lösung kann ich ja nur
verstehen, wenn ich die Nachteile dann auch verstehe ;)
Ich will innerhalb meines ersten kleinen Projektes nur einen
Programmablauf ein- und ausschalten kÖnnen. Das sollte da reichen!
Mir gings hier nur um die Richtigkeit und Verständlichkeit des Tutorials
für noobs, wie mich. Was innerhalb der debouncefunktion erwartet wird
und letzlich übergeben wird, würde für mich bei umgekehrter
Parameterbenennung (*port->*pin,pin->port) einfach mehr sinn machen. So
funktionierts ebenfalls und sieht auch noch gut aus... wär das falsch?
Hallo Peter
bin auch gerade dabei deinen Code zu verstehen und zu nutzen. Habe dabei
ein Problem. Ich habe in meinem Prg ein Timer mit 10ms drin, denn ich
auch für andere Sachen nutze. In deinem Prg ist auch einer drin. Wie
kann ich meinen jetzt verwenden und wo ist der Punkt zum Einsprung?
achim
Alex alex schrieb:> Muss innherhalb der debounce-Funktion nicht alles genau andersherum> definiert sein, würde heißen: pin ist gleich port und port ist gleich> pin. Die Bedingungen verändern sich zwar nicht, aber mir würde es> logischer erscheinen.>> Berichtigt mich!;)
Das passt schon.
Aus Sicht der Funktion wird die Bezeichnung 'port' für den Anschluss
benutzt, während die Bezeichnung 'pin' die Bitnummer an diesem Anschluss
darstellt.
Am Port B gibt es aber 3 Register, die diesem Anschluss zugeordnet sind,
je nachdem welche Funktionalität man von einem Bit an diesem Anschluss
haben will.
Port B PORTB das Ausgangsregister
PINB das Eingangsregister
DDRB das Konfigurationsregister.
Hier ist PINB auch nichts anderes als ein Port. Nämlich der Teil des
Port B an dem externe Signale reinkommen.
Lass dich nicht von den Bezeichnungen täuschen. Das PINB ist
konzeptionell etwas völlig anderes, als der 'pin' in der Funktion.
PINB <==> ein kompletter Anschluss (ein Port) mit 8 Bit
pin <==> die Nummer eines Bits
Ausserdem ist die Funktion bescheuert. Die C-Tuorial Macher sollten sich
überlegen, diesen Quatsch aus dem Tut rauszunehmen.
Achim Seeger schrieb:> Hallo Peter> bin auch gerade dabei deinen Code zu verstehen und zu nutzen. Habe dabei> ein Problem. Ich habe in meinem Prg ein Timer mit 10ms drin, denn ich> auch für andere Sachen nutze. In deinem Prg ist auch einer drin. Wie> kann ich meinen jetzt verwenden und wo ist der Punkt zum Einsprung?> achim
Übernimm den ISR Teil in deine 10ms ISR. Den Teil mit der Neuberechnung
des Timer Counters lässt du einfach weg.
Alex alex schrieb:> Ich will innerhalb meines ersten kleinen Projektes nur einen> Programmablauf ein- und ausschalten kÖnnen. Das sollte da reichen!
Nur wenn Dein Programm fast nichts macht.
Die Routine lebt davon, daß die CPU zu 99% darin verweilt.
Dauert Deine Funktion aber selber schon 100ms, bist Du nur zu 50% darin,
d.h. im Mittel kriegst Du nur jeden 2. Tastendruck erkannt.
Und solange Du drückst, läuft Dein Programm auch nur halb so schnell.
Daher ist sie in der Praxis unbrauchbar.
Alex alex schrieb:> würde für mich bei umgekehrter> Parameterbenennung (*port->*pin,pin->port) einfach mehr sinn machen.
Nö.
Ein Port hat immer die Breite von einem Byte (8Bit) bzw. bei größeren
CPUs auch 16 oder 32Bit.
Ein Pin ist aber nur ein einzelner Pin (1Bit).
Um nun aus dem Byte genau einen Pin auszuwählen braucht man eben die
Pinnummer.
(pin -> pin-number).
Peter
Karl Heinz Buchegger schrieb:> Lass dich nicht von den Bezeichnungen täuschen. Das PINB ist> konzeptionell etwas völlig anderes, als der 'pin' in der Funktion.>> PINB <==> ein kompletter Anschluss (ein Port) mit 8 Bit> pin <==> die Nummer eines Bits
Danke!
Peter Dannegger schrieb:
> Nö.> Ein Port hat immer die Breite von einem Byte (8Bit) bzw. bei größeren> CPUs auch 16 oder 32Bit.> Ein Pin ist aber nur ein einzelner Pin (1Bit).> Um nun aus dem Byte genau einen Pin auszuwählen braucht man eben die> Pinnummer.> (pin -> pin-number).
Das hilft mir! Aber ich benutzt jetzt ohnehin deine
Debouncefunktion...die kommt besser ;)
Achim Seeger schrieb:> Hallo> Habe mit der ISR so mein Problem. Habe die ISR mal dargestellt. Leider> ist der Code zu komplieziert für mich. Würde ihn sehr gerne nutzen und> ein bisschen verstehen.
Kann ich verstehen.
Allerdings. Der Code ist tricky. Ausnahmsweise gehe ich bei diesem Code
von meiner generellen Empfehlung ab, das man Code den man übernimmt auch
verstehen muss.
Ich denke, ich bin nicht schlecht in der Analyse von Code, na ja nach
nahezu 30 Jahren sollte ich das auch können. Aber auch ich hab bei
diesem Code mehr als 45 Minuten gebraucht um rauszuknobeln wie und warum
das funktioniert.
> Was brauch ich den eigentlich?
BIs auf die Zeile
> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload> 10ms
alles.
Das ist der Teil, der in die ISR kommt. Die ISR hast du ja schon hast
und mit 10ms passt die auch gut ins Konzept. Für den Entprellcode ist
die genaue Einhaltung der 10ms nicht wirklich wichtig. Verwendbar sind
an ISR Zykluszeiten alles von ca.5ms bis hinauf zu 50ms. Kleiner als 5ms
sollte die Zykluszeit nicht werden, weil man dann schon in die
Prellzeiten hineinkommt und eine sichere Entprellung unter allen
Umständen nicht mehr garantiert werden kann. Länger ist zwar kein
technisches Problem, aber dann kann es sein, das man als Benutzer
unangenehm merkt, dass zwischen dem Drücken einer Taste und der
Programmreaktion Zeit vergeht. Aber dazwischen ist alles was man an ISR
hat fein und kann benutzt werden.
Also:
* den Teil in die ISR kopieren
1
staticuint8_tct0,ct1,rpt;
2
uint8_ti;
3
4
i=key_state^~KEY_PIN;// key changed ?
5
ct0=~(ct0&i);// reset or count ct0
6
ct1=ct0^(ct1&i);// reset or count ct1
7
i&=ct0&ct1;// count until roll over ?
8
key_state^=i;// then toggle debounced state
9
key_press|=key_state&i;// 0->1: key press detect
10
11
if((key_state&REPEAT_MASK)==0)// check repeat function
12
rpt=REPEAT_START;// start delay
13
if(--rpt==0){
14
rpt=REPEAT_NEXT;// repeat delay
15
key_rpt|=key_state&REPEAT_MASK;
16
}
* Die globalen Variablen
1
volatileuint8_tkey_state;// debounced and inverted key state:
2
// bit = 1: key pressed
3
volatileuint8_tkey_press;// key press detect
4
5
volatileuint8_tkey_rpt;// key long press and repeat
übernimmst du so wie sie sind
* Ebenso die Funktionen
1
uint8_tget_key_press(uint8_tkey_mask)
2
uint8_tget_key_rpt(uint8_tkey_mask)
3
uint8_tget_key_short(uint8_tkey_mask)
4
uint8_tget_key_long(uint8_tkey_mask)
eventuell könnte man bei den Funktionen noch überlegen, welche Features
man zu nutzen gedenkt
* will ich lange und kurze Tastendrücke unterscheiden können?
wenn nicht, brauch ich get_key_short und get_key_long nicht
* will ich Autorepeat nutzen?
wenn nicht, ist get_key_rpt überflüssig
* Dann werden die #define übernommen und gleich konfiguriert
Am besten denkt man sich gleich mal ein paar sprechende Namen
für die Tasten aus.
Habe ich zb eine Menüsteuerung mit den Tasten 'Links' 'Rechts' und
'Enter', dann arbeite ich das gleich in die #define mit ein.
Erst mal die generelle Definitionne für den Port, an dem die Tasten
sitzen
1
#define KEY_DDR DDRB
2
#define KEY_PORT PORTB
3
#define KEY_PIN PINB
Danach die Tasten, so wie ich sie in meiner App benötige. Da benutze ich
gleich 'sprechende' Namen. Die Zahlen sind ganz einfach die Pinnummern
am Port, an dem jeweils die Taste angeschlossen ist (Die Taste 'Links'
wäre also am Pin 0 vom Port B)
1
#define KEY_LEFT 0
2
#define KEY_RIGHT 1
3
#define KEY_ENTER 2
In ALL_KEYS werden einfach alle definierten Tasten in einer einzigen
Maske zusammengefasst
Fehlt noch die Festlegung für den Autorepeat. Welche Tasten sollen
grundsätzlich mit einem Autorepeat belegbar sein und wie soll das
Zeitverhalten für den Autorepeat sein. Lässt du die Zeiten-Einstellungen
so wie im Artikel angegeben, dann ergibt sich eine brauchbare
Defaulteinstellung. Da muss also erst mal nichts angepasst werden, es
sei denn, es zeigt sich beim Testen, dass das etwas zu schnell/langsam
geht. Das kann zb sein, weil die ISR eben nicht 10ms hat sondern
weniger/mehr.
In meinem Beispiel mit der Menüsteuerung möchte ich zb auf Links/Rechts
einen Autorepeat machen können, aber nicht auf Enter. Also werden auch
nur die Tasten Links/Rechts aufgeführt. Achtung: Das heißt nicht, dass
da jetzt schon ein Autorepeat drauf liegt. Das heißt nur, dass die
Möglichkeit dazu besteht.
Also
1
#define REPEAT_MASK (1<<KEY_LEFT | 1<<KEY_RIGHT)
2
#define REPEAT_START 50 // after 500ms
3
#define REPEAT_NEXT 20 // every 200ms
Fehlt nur noch der Teil, der ins Hauptprogramm muss.
Da du ja bereits einen Timer bei dir laufen hast, interessiert der dich
nicht weiter, du brauchst nur die Portinitialisierung des Tastenports
1
...
2
intmain()
3
{
4
...
5
6
KEY_DDR&=~ALL_KEYS;// configure key port for input
7
KEY_PORT|=ALL_KEYS;// and turn on pull up resistors
8
...
9
10
sei();
11
while(1){
12
...
13
}
14
}
Tja. Das wars auch schon. Ab jetzt kannst du mit den Tastenabfragen
deine Tasten mit Leben befüllen
Hallo Karl Heinz
entschuldige, ich bin platt. Mir fehlen im Moment die Worte,um es
auszudrücken. Hatte eigentlich schon viel erwartet, aber bin einfach
platt. Werde Wochen und Monate brauchen, um damit klar zu kommen. Kann
nur meinen Hut ziehen und dir vielmals danken. Werde es ausführlich
durcharbeiten und darüber berichte wie ich es einsetze. Muss dir noch
aus einem anderen Grund danken. Du hast mir schon wiederholte male
geholfen. Denke dabei an den Timer und die LED und entschuldige meine
blöden Fragen.
achim
Achim Seeger schrieb:> Hallo Karl Heinz> entschuldige, ich bin platt. Mir fehlen im Moment die Worte,um es> auszudrücken. Hatte eigentlich schon viel erwartet, aber bin einfach> platt. Werde Wochen und Monate brauchen, um damit klar zu kommen.
Ah geh.
Da bei dir der Timer schon läuft, ist das eine Sache auf 10 Minuten und
du hast deine Tasten am laufen.
Hallo Karl Heinz
Für dich 10 Minuten, eine Traumzahl für mich. Es geht mir immer darum.
es zu verstehen. Mein Ziel ist es, dich nicht mehr mit Anfänger Fragen
zu löchern. Danke
achim
Hallo Karl Heinz
10 min sind eine Traumzahl. Habe jetzt rund 2 h gebraucht. Mit Kontrolle
für vielen kleinen Zeichen. Zum Schluss ist aber doch noch eine
Fehlermeldung gekommen. Habe sie mal kopiert:
..default/../Arbeit_Timer.c:117: undefined reference to `get_key_press'
..default/../Arbeit_Timer.c:121: undefined reference to `get_key_press'
if(get_key_press(1<<KEY_LEFT)){
// zB LED an
}
if(get_key_press(1<<KEY_RIGHT)){
// zB LED aus
}
Das sind die beiden Zeilen, in dem der Fehler stecken soll.
volatile uint8_t key_state;
volatile uint8_t key_press;
volatile uint8_t key_rpt;
uint8_t get_key_press(uint8_t key_mask);
uint8_t get_key_rpt(uint8_t key_mask);
uint8_t get_key_short(uint8_t key_mask);
uint8_t get_key_long(uint8_t key_mask);
In diesem Teil wird doch die Variable deklariert. Kann im Moment keinen
Fehler sehen.
Noch eine kleine Frage zum Timer. Habe den Text eingetragen, so wie due
es geschrieben hast. die eine Zeile ist raus. Ich hatte vorher wait10
als 10ms Zeit für mich. Welcher Teil ist den 10ms?
achim
Achim Seeger schrieb:> Hallo Karl Heinz> 10 min sind eine Traumzahl. Habe jetzt rund 2 h gebraucht. Mit Kontrolle> für vielen kleinen Zeichen. Zum Schluss ist aber doch noch eine> Fehlermeldung gekommen. Habe sie mal kopiert:>> ..default/../Arbeit_Timer.c:117: undefined reference to `get_key_press'> ..default/../Arbeit_Timer.c:121: undefined reference to `get_key_press'
Du musst dir schon die komplette Funktion von dem Link in deinen Code
einkopieren!
> uint8_t get_key_press(uint8_t key_mask);
Das ist ja noch nicht die Funktion. Da fehlt ja die Implementierung
Aus dem Link
// check if a key has been pressed. Each pressed key is reported
4
// only once
5
//
6
uint8_tget_key_press(uint8_tkey_mask)
7
{
8
cli();// read and clear atomic !
9
key_mask&=key_press;// read key(s)
10
key_press^=key_mask;// clear key(s)
11
sei();
12
returnkey_mask;
13
}
Ich sah einfach nur keinen Sinn darin, in meiner Checkliste die
Funktionen komplett einzukopieren. Deshalb hab ich ja auch keinen ; ans
Zeilenende gesetzt.
Ich hatte eigentlich schon gedacht, dass du eine Funktion erkennst, wenn
ich nur den Funktionskopf anführe.
> uint8_t get_key_rpt(uint8_t key_mask);> uint8_t get_key_short(uint8_t key_mask);> uint8_t get_key_long(uint8_t key_mask);>> In diesem Teil wird doch die Variable deklariert.
Ah geh.
Das sind doch keine Variablen. Seit wann wird denn bei einer
Variablendefinition ein ( ) Teil geschrieben.
> Noch eine kleine Frage zum Timer. Habe den Text eingetragen, so wie due> es geschrieben hast. die eine Zeile ist raus. Ich hatte vorher wait10> als 10ms Zeit für mich.
Ähm. Du hattest was?
Hast du nun einen Timer am laufen oder nicht?
wait10 klingt nicht nach einem Timer. Das klingt nach einer
Warteschleife. Und das ist unbrauchbar.
Hallo Karl Heinz
danke für deine Antwort
Habe leider den Teil zwar gesehen, aber nicht ernst genommen. Da der
Code sehr tricky ist, habe ich die Funktion noch nicht verstanden. Da
her auch übersehen. Habe einige Teile übersetzt in mein Verständnis,
fehlt aber noch einen ganze Menge. Zur Kontrolle vergleiche ich immer
mit dem ganzen Code und dabei die Stelle im Ablauf. Habe einen Timer am
laufen.
void nibo_timer2() // Timer 10ms
{ TCNT2 = 0;
OCR2=249;
TCCR2=(1<<WGM21)|(1<<CS21)|(1<<CS20);
TIMSK |= (1<<OCIE2); }
ISR (TIMER2_COMP_vect) // wait1=1ms,
{
if( wait<=9) // Takt 1ms, bei 9 sind es
10ms
{ wait++; } // erhöht
else // wenn dann ...
{ wait=0; // setzt wait auf 0
wait10=0xFF; // Signal alle 10ms wait10
}
}
Ich habe ihn mit der ganzen ISR kopiert. Im Moment nutze ich ihn für den
Aufbau einer Warteschleife. Zur Kontrolle schalte ich zur Zeit nur LED.
Daher auch der Name.
achim
Achim Seeger schrieb:> Hallo Karl Heinz> danke für deine Antwort> Habe leider den Teil zwar gesehen, aber nicht ernst genommen. Da der> Code sehr tricky ist, habe ich die Funktion noch nicht verstanden.
Entdschuldige, aber das ist jetzt eine faule Ausrede.
Der ISR-Teil ist von der Funktion her tricky. Der Rest ist klar wie
Klossbrühe. Insbesondere wenn es nur darum geht: was brauch ich und was
nicht.
Da werden Funktionen aufgerufen, also muss es die Funktionen auch
irgendwo geben.
> mit dem ganzen Code und dabei die Stelle im Ablauf. Habe einen Timer am> laufen.
Na dann passt es doch.
Den ISR-Code mit in die ISR aufnehmen und gut ists.
>> void nibo_timer2() // Timer 10ms> { TCNT2 = 0;> OCR2=249;> TCCR2=(1<<WGM21)|(1<<CS21)|(1<<CS20);> TIMSK |= (1<<OCIE2); }>> ISR (TIMER2_COMP_vect) // wait1=1ms,> {> if( wait<=9) // Takt 1ms, bei 9 sind es> 10ms> { wait++; } // erhöht> else // wenn dann ...> { wait=0; // setzt wait auf 0> wait10=0xFF; // Signal alle 10ms wait10> }> }>> Ich habe ihn mit der ganzen ISR kopiert. Im Moment nutze ich ihn für den> Aufbau einer Warteschleife. Zur Kontrolle schalte ich zur Zeit nur LED.> Daher auch der Name.
Ja, ok.
Und jetzt kopierst du da noch den ISR Code mit rein, so dass er alle
10ms ausgeführt wird. Bei dir ist es der else Teil, der alle 10ms
ausgeführt wird. Also kommt der Code in diesen Zweig rein.
Wo ist das Problem?
1
ISR(TIMER2_COMP_vect)// wait1=1ms,
2
{
3
staticuint8_tct0,ct1,rpt;
4
uint8_ti;
5
6
if(wait<=9)// Takt 1ms, bei 9 sind es 10ms
7
{
8
wait++;
9
}// erhöht
10
else// wenn dann ...
11
{
12
wait=0;// setzt wait auf 0
13
wait10=0xFF;// Signal alle 10ms wait10
14
15
i=key_state^~KEY_PIN;// key changed ?
16
ct0=~(ct0&i);// reset or count ct0
17
ct1=ct0^(ct1&i);// reset or count ct1
18
i&=ct0&ct1;// count until roll over ?
19
key_state^=i;// then toggle debounced state
20
key_press|=key_state&i;// 0->1: key press detect
21
22
if((key_state&REPEAT_MASK)==0)// check repeat function
23
rpt=REPEAT_START;// start delay
24
if(--rpt==0){
25
rpt=REPEAT_NEXT;// repeat delay
26
key_rpt|=key_state&REPEAT_MASK;
27
}
28
}
fertig ist der Einbau in die ISR.
Auch wenn du die Funktion des Codes nicht verstehen musst, mitdenken
musst du schon. Das Ganze ist schon so aufbereitet, dass man es leicht
übernehmen und einbauen kann. Dazu muss man nicht verstehen, wie der
Code im Detail arbeitet. Aber das entbindet nicht, das Prinzip zu
verstehen, wie die Einzelteile zusammenarbeiten. Und das ist wie immer:
In der ISR findet die Entprellung statt und der Code hinterlässt in den
globalen Variablen das was er rausgefunden hat, bzw. benutzt die als
sein Zwischenspeicher. Die restlichen Funktionen holen sich die
Ergebnisse aus diesen globalen Variablen.
@ Achim Seeger
Ein Tipp: mach mal um deine Codeabschnitte sowas (ohne die Leerzeichen
in den []:
[ c ]
#define dein C-Code
static int rpt; // das hier ist ein unglaublich in die Breite
gezogener Kommentar
char i;
if(wait<9)
wait++;
else
wait=0;
[ /c ]
Und heraus kommt sowas:
1
#define dein C-Code
2
staticintrpt;// das hier ist ein unglaublich in die Breite gezogener Kommentar
Hallo Karl Heinz
Habe den Code endlich Fehlerfrei abgetippt und kopieren können. Wie
üblich liegt der Teufel im Detail. Es ist manchmal kaum sichtbar,
welcher Punkt oder Klammer was ist. Es geht jetzt. Nach deinem Code
liegt die Tastenerkennung in diesen Zeilen
1
#define KEY_DDR DDRB // Defi an welchem Port die Tasten sitzen
2
#define KEY_PORT PORTB
3
#define KEY_PIN PINB
4
5
#define KEY_LEFT 0 // Taste links
6
#define KEY_RIGHT 1 // Taste rechts
7
#define KEY_ENTER 2 // Taste Enter
Dabei wird der ganze Port (PORTB) verwendet. Da ich im Moment nur 1
Taste verwende, kann ich es so nicht schreiben. Verwende jetzt beim
Atmega 128 PD4 und später noch PD6, PD5 und PD3.
Ansonsten warte ich noch auf die Erleuchtung durch Tommy. Sorry, bin
kein Profi. Versuche es mir doch mal zu erklären. Danke Tommy
achim
Der Code ist kommentiert und ist auch nicht obfuscated, was kann ich
hier mehr schreiben als schon im Code steht? Und es werden ja auch nur
ganz einfache Operationen verwendet, in kurzen Anweisungen.
Am besten gehst du den Code mit für verschiedene Szenarios durch, also
für einen Taster und dann Taste gedrückt, Taste los gelassen, Taste
nicht gedrückt, etc.
So mache ich das auch. Mit Übung gehts natürlich entsprechend schneller.
Hallo Tommy
Ich bereits einiges probiert. z.B. Eingabe der Adressen, veränderte PINS
usw. Leider kam es nicht zu einem Ergebnis. So viel ich gesehen habe,
geht das das über das gesamze Port mit entspr, Breite. Wenn ich nur
einen brauche kann ich es über Bitmanipulatiin machen. Es wir aber dabei
der gesamte Port angegeben. Das ist leider die Stelle wo ich schwimme.
Werde es weiter testen. Es muss gehen, ist mir klar.
achim
Tommy schrieb:> Der Code ist kommentiert und ist auch nicht obfuscated, was kann ich> hier mehr schreiben als schon im Code steht? Und es werden ja auch nur> ganz einfache Operationen verwendet, in kurzen Anweisungen.>
Du 'erklärst' den COde nicht. Du liest ihn nur vor. Das hat mit erklären
nichts zu tun.
Wenn ich jemandem den Code erkläre, dann muss ich ihm die Idee
vermitteln, die dahintersteckt. Und ich muss ihm zeigen, wie sich diese
Idee in den Zeilen des Codes wiederfindet.
> Am besten gehst du den Code mit für verschiedene Szenarios durch, also> für einen Taster und dann Taste gedrückt, Taste los gelassen, Taste> nicht gedrückt, etc.
Das hilft zwar. Aber ohne die generelle Idee herausgefunden zu haben,
wirst du nicht oder nur sehr schwer ergründen können, welche Rolle die
Variablen ct0 und ct1 spielen. Und ein Neuling findet das erst recht
nicht raus, dass sich in diesen 2 Variablen im Prinzip 8 Stück 2-Bit
Zähler verbergen. Aber sag mal nach deiner 2 Minuten Analyse: zählen die
Zähler rauf oder runter?
>#defineKEY_DDRDDRB// Defi an welchem Port die Tasten sitzen
2
>#defineKEY_PORTPORTB
3
>#defineKEY_PINPINB
4
>
5
>#defineKEY_LEFT0// Taste links
6
>#defineKEY_RIGHT1// Taste rechts
7
>#defineKEY_ENTER2// Taste Enter
8
>
>> Dabei wird der ganze Port (PORTB) verwendet. Da ich im Moment nur 1> Taste verwende, kann ich es so nicht schreiben. Verwende jetzt beim> Atmega 128 PD4 und später noch PD6, PD5 und PD3.
Dann konfigurierst du das eben nach deinen Bedürfnissen.
Deine Tasten liegen am Port D. Also
1
#define KEY_DDR DDRD // Defi an welchem Port die Tasten sitzen
2
#define KEY_PORT PORTD
3
#define KEY_PIN PIND
und dort liegt die Taste am Pin 4. Also
1
#define KEY_1 4
es schadet auch nichts, wenn du gleich mal alle Tasten angibst. Solange
die Hardwaremässig nicht vorhanden sind, wertest du sie halt einfach
nicht aus
Also
1
#define KEY_1 4
2
#define KEY_2 3
3
#define KEY_3 5
4
#define KEY_4 6
Ich versteh jetzt ehrlich gesagt nicht, was (überhaupt nachdem ich dich
weiter oben durch den Prozess geführt habe) daran jetzt besonders schwer
sein soll.
Jeder der schon mal ein Blinkprogramm an seine speziellen Verhältnisse
angepasst hat, an dem die LED eben an einem anderen Pin sitzt als in der
Vorlage, muss doch erkennen und wissen, was es mit den 'Wörtern' DDRB,
PORTB und PINB auf sich hat, und dass er die eben gegen die 'Wörter'
austauscht, die in seiner speziellen Hardwaresituation vorliegen.
> Ansonsten warte ich noch auf die Erleuchtung durch Tommy.
Auf die warte ich auch. Die 2 Minuten sind schon lange um.
Guten Morgen,
ich hatte mal ein lauffähiges Programm mit Peters Routinen
veröffentlicht.
Vielleicht hilft es bel Querlesen und Verstehen.
Beitrag "Re: 3fach Timer (einschaltverzögerung)"
siehe c-src/tools/..
Achim Seeger schrieb:> So viel ich gesehen habe,> geht das das über das gesamze Port mit entspr, Breite.
Genau das ist der Trick. Um den Code zu verstehen, reicht es, nur ein
Bit (Taste) je Variable zu betrachten, z.B. Bit 0.
Und wenn man dann die Funktion verstanden hat, ist es für Bit 1 .. 7
genau gleich. Die Logikoperatoren verknüpfen ja immer nur Bits an
gleicher Stelle, die Nachbarbits beeinflussen sie nicht.
Achim Seeger schrieb:> Wenn ich nur> einen brauche kann ich es über Bitmanipulatiin machen.
Genau das macht der Code. Für eine Taste könnte man es auch anders lösen
bei fast gleichem Aufwand.
Aber schon bei 2 Tasten ist der Aufwand anderer Lösungen doppelt so hoch
und bei 8 Tasten sogar 8-fach.
Peter
Hallo Peter
eigentlich hast du mein Hauptproblem gefunden. Ich arbeite in der
Grundversion nur mit einer Taste. Wenn ich mir den Code anshe, so ist er
riesengross. Anders betrachtet, wenn ich mit einem Timer arbeite und für
jede Funktion nur so viel Zeit nehme wie es unbedingt notwendig ist,
habe ich mit den "normalen" Abfragen ein Problem. Es geht nicht so.
Damit sind wir wieder bei deinem Taster. Habe das mit dem Verstehen erst
mal vertagt. Wenn ich mehr in C stecke, sieht es anders aus. Wie kann
ich es vernünftig gestalten, erst mal ein PIN abzufragen mit der Option
auf weitere PINs. Dabei ist die Sache mit den Botschieben an dieser
Stelle unklar. Du verwendest DDR, PORT, PIN. Bei einem PIN klar. Wie ist
es mit unterschiedlichen?
achim
Achim Seeger schrieb:> Hallo Peter> eigentlich hast du mein Hauptproblem gefunden. Ich arbeite in der> Grundversion nur mit einer Taste. Wenn ich mir den Code anshe, so ist er> riesengross.
Nicht wirklich.
Der Teil in der ISR übersetzt sich zu einer Handvoll Assembler
Anweisungen. Das ist nichts was dich beunruhigen müsste. Lass dich nicht
vom C-Code ins Boxhorn jagen. Entscheidend ist, was der Compiler in der
Maschinensprachenversion daraus macht. Und da bleibt nicht viel übrig.
> Anders betrachtet, wenn ich mit einem Timer arbeite und für> jede Funktion nur so viel Zeit nehme wie es unbedingt notwendig ist,> habe ich mit den "normalen" Abfragen ein Problem.
Wetten nicht.
Wenn dein Code so zeitkritisch ist, dass dir 10 oder 15 Taktzyklen (bei
1Mhz Taktfrequenz sind das ~15 MYKRO-Sekunden) in einer Timer-ISR, die
alle 10 Millisekunden aufgerufen wird, tatsächlich weh tun, dann stimmt
schon an ganz anderer Stelle etwas nicht.
(ich geh jetzt mal davon aus, dass du NICHT ein Videosignal erzeugst,
bei dem tatsächlich jede µs wichtig ist. Oder irgendetwas anderes, bei
dem ähnliche enge zeitliche Vorgaben unabdingbar sind)
Dieser Code wird von tausenden Programmierern problemlos eingesetzt, im
Moment sehe ich keinen wirklichen Grund, warum das bei dir nicht auch
klappen sollte.
Und um ehrlich zu sein: Nachdem was ich in anderen Threads von dir so
gesehen habe, bezweifle ich ganz massiv, dass du die Situation überhaupt
korrekt und vernünftig einschätzen kannst.
Hallo Karl Heinz
du hast recht. Der Code geht. Habe ihn ja nach einiger Zeit so weit zum
laufen bekommen. Eines der Problem ist dabei sicher die Anpassung an den
128 und dem bisher verwendeten Code. Für mich ist der Code schon bei
dieser Länge unübersichtlich. Daher versuche ich viel in einzelne Teile
zu zerlegen. Dabei kommt auch der Gedanke der weiteren Verwendung. Habe
festgestellt, das manchmal nur Kleinigkeiten angepasst werden müssen.
Das gefällt mir. Nach dem Artikel"Warten verboten" kam ja erst der
Gedanke mit dem Timer und die entsprechende Verarbeitung und Ablauf. Da
ich delay nicht verwende soll alles schneller gehen. Eigentlich fangen
dort andere Probleme an, die ich vorher so nicht bedacht habe. Damit
sind wir wieder bei den Tasten. Ein übersetzen der bisherigen Sachen war
kaum möglich. Dazu kommt die Auswertung der Zeiten. Da nur eine Taste
vorhanden ist, muss man sich was ausdenken. Da kommt die Sache von Peter
genau richtig. Sind wir wieder bei der Anpassung.
Hallo Uwe
habe mir die Sachen geholt und bin beim lesen. Den Grundartikel kenne
ich. Dort sind einiges Datein leider nicht zu laden.
achim
Achim Seeger schrieb:> Gedanke mit dem Timer und die entsprechende Verarbeitung und Ablauf. Da> ich delay nicht verwende soll alles schneller gehen.
Noch mal
'schnell' ist in einer ISR ein relativer Begriff. 'schnell' bedeutet
nicht, dass man in einer ISR gar nichts tun darf. Man darf nur nicht
exzessiv Zeit verbrutzeln.
Dieser Code ist von 'exzessiv Zeit verbrutzeln' noch weit, weit, weit
entfernt.
> Eigentlich fangen> dort andere Probleme an, die ich vorher so nicht bedacht habe. Damit> sind wir wieder bei den Tasten. Ein übersetzen der bisherigen Sachen war> kaum möglich. Dazu kommt die Auswertung der Zeiten. Da nur eine Taste> vorhanden ist, muss man sich was ausdenken. Da kommt die Sache von Peter> genau richtig. Sind wir wieder bei der Anpassung.
Bei allem nötigen Respekt.
Aber wenn dir diese Anpassung (bei der es nur darum geht ein paar
#define auf die richtigen Werte - ja nach Port an dem die Tasten
angeschlossen sind - zu stellen, insbesondere am Code selber musst du
nichts machen, du musst nicht verstehen wie er arbeitet, du musst ihn
nur einbinden und nach Bedarf eine Funktion aufrufen) schon
Schwierigkeiten macht, dann solltest du dir mal überlegen, ob du nicht
das falsche Hobby hast, bzw. ob dir da nicht viele Grundlagen fehlen.
Hallo Karl Heinz
es fehlen Grundlagen, geb ich ehrlich zu. Mein bester Freund ist auch
das C Buch. Bin am lernen und versuche es zu begreiffen. Ist nicht
leicht. Einfache Sachen klappen schon ganz gut. Bei anderen muss ich
fragen. Bei solchen Sachen wie Tasten ergebe ich mich beim Code
verstehen. Ist auch keine Schade, zu sagen das es andere weit aus besser
können.
Falsches Hobby nicht gerade, vielleicht zu wenig Zeit und Ausdauer.
achim
Achim Seeger schrieb:> Falsches Hobby nicht gerade, vielleicht zu wenig Zeit und Ausdauer.
Ich laufe auch gerne Marathon. Habe nur zuwenig Zeit und Ausdauer. :)
Hallo Karl Heinz
habe das Wochenende damit verbracht alles über DDR, PORT und PIN zu
lesen, was ich im Netz gefunden habe. Sehe soweit durch, das ich den
Eingang und die Zuordnung der entsprechen PIN und PORTs jetzt richtig
begriffen habe. Nach dem ich die enrsprechenden Stelen alle geändert
hatte, ging es trotzdem nicht. Nach 2 weiteren Stunden hatte ich dann
die Fehler gefunden und jetzt geht es sehr gut. Möchte aber noch zum
verständnis was fragen. Es wird
1
..get_key_short(uint8_tkey_mask)
2
..get_key_press(~key_state&key_mask);
3
..get_key_long(uint8_tkey_mask)
verwendet. (short-kurz, press-? , long-lang) Ist damit die Länge des
Tastendrucks gemeint und wird diese über
Achim Seeger schrieb:> Hallo Karl Heinz> habe das Wochenende damit verbracht alles über DDR, PORT und PIN zu> lesen, was ich im Netz gefunden habe.
Ich will ja nich lästig sein, aber das sind eigentlich Dinge, die weit
vor einer Tastenentprellung kommen oder vor der Verwendung von Timern.
Du tust dir keinen Gefallen damit, wenn du die Basisdinge ignorierst
oder als so trivial ansiehst, dass du keine Übungen dazu machen musst.
Nicht umsonst ist die allererste Übung, die man überhaupt macht einfach
ein paar LED an einen Port anschliessen und dann mit den DDR Registern
bzw. den PORT Registern da erst mal ein paar LED zum leuchten zu
bringen, Muster zu erzeugen etc. Dann kommt ein Taster dazu, wobei es
nicht um Entprellung etc. geht, sondern einfach nur um 'Ist der Taster
gedrückt oder nicht'. Und so geht es weiter, Schritt um Schritt, bis man
dann nach einiger Zeit bei der Flankenanbfrage von Tastern angelangt
ist, plus zugehöriger Entprellung.
Überspringst du diese Schritte, dann ist klar, dass du an allen Ecken
und Enden Wissenslücken hast. Wie gesagt, du tust dir damit selbst
keinen Gefallen, wenn du diese Schritte überspringst.
>
1
>..get_key_short(uint8_tkey_mask)
2
>..get_key_long(uint8_tkey_mask)
3
>
>> verwendet. (short-kurz, press-? , long-lang) Ist damit die Länge des> Tastendrucks gemeint und wird diese über
Ja.
get_key_press und get_key_rpt gehören zusammen und können mitsammen
benutzt werden.
und get_key_short und get_key_long gehören zusammen und können mitsammen
benutzt werden.
Sozusagen 2 Funktionsfamilien, die jeweils miteinander können.
Allerdings kann man die Familien miteinander nicht mischen.
Hallo Karl Heinz
habe das Prg fertig. Es läuft so wie ich es mir vorstelle. Bitte
beachten, es ist für einen Nibo 2 mit Atmega 128 bei 16MHz geschrieben.
Habe viel dabei gelernt. Danke dir für deine Hilfe und Geduld.
1
/* Test Timer, Testet die Funktion Timer, ist eingestellt auf 10ms und wird
2
vom Timer gesteuert. Tasten sind nach Peter Dannegger entprellt, bis 4 Tasten
3
, S3 kurz - an, lang - aus, by h.j.seeger@web.de */
Guten Abend,
es gibt doch noch Fehler, alle Variable, die aus ISR heraus verändert
werden, benötigen ein |volatile| voran gestellt !
Achim Seeger schrieb:> uint8_t key_state;> uint8_t key_press;> uint8_t key_rpt;
1
volatile uint8_t key_state;
2
volatile uint8_t key_press;
3
volatile uint8_t key_rpt;
Das könntest Du auch in meinem Beispielcode finden.
_
Uwe S. schrieb:> es gibt doch noch Fehler, alle Variable, die aus ISR heraus verändert> werden, benötigen ein |volatile| voran gestellt !>
Finde ich jetzt nicht die beste Lösung, aber das ist mit Sicherheit die
einfachste.
Ich bevorzuge eine ordentliche critical section, die mit
Compiler-Barriers markiert wird, beim GCC mit
asm volatile ("" : : : "memory");
Warum sollte ich innerhalb der CS auf Optimierungen verzichten? Statt
sei und cei ruft man einfach ein Makro enter_critical_section und
leave_critical_section auf, das neben der jeweiligen
Interrupt-Konfiguration noch obige Compiler-Barrier enthält.
Grüße Tommy
Hallo Uwe
das ist mir bekannt. Habe einen Ausdruck da von und gelesen. Dadurch ist
ja die ganze Sache für den Atmel 128 gekommen und die Anpassung dazu.
Leider ist die Erklärung der einzelnen Funktion z.B. long, short usw
nicht vorhanden. Dadurch muss man alles ausprobieren.
achim
Hallo Karl Heinz
Danke dir für deine Info. Werde die Sache noch mal unter dem Hinweis
nachgehen. Es gibt noch einiges (viel) für mich zu klären und zu
begreiffen
achim
Hi Achim,
key_rpt wird verwendet um eine Taste zu wiederholen, so wie du am PC
auch vieeeeeeeeele "e"s schreiben kannst, wenn du die "e"-Taste länger
gedrückt hälst. Bei einem Mikrocontroller wäre Pfeil hoch/runter zum
scrollen, aber wohl ein typischerer Anwendungsfall, als die Eingabe von
Buchstaben.
Grüße Tommy
Achim Seeger schrieb:> Hallo Tommy> kann ich die Funktion auch ändern?> achim
Welche Funktion?
Wenn du einen Autorepeat haben willst, dann musst du
* im Makro da oben die Taste als potentiellen Autorepeat Kandidaten
anmelden
* im Code eine entsprechende Abfrage machen
zb
1
....
2
3
while(1)
4
{
5
6
if(get_key_press(1<<KEY_PLUS)||
7
get_key_rpt(1<<KEY_PLUS))
8
{
9
Wert++;
10
machwasmitWert
11
}
12
13
if(get_key_press(1<<KEY_MINUS)||
14
get_key_rpt(1<<KEY_MINUS))
15
{
16
Wert--;
17
machwasmitWert
18
}
19
}
Wert wären zb. die Minuten einer Uhr oder der PWM Wert für eine
LED-Dimmung. Drückst du einmal auf die Taste(n) veränderst du den Wert
um jeweils 1. Bleibst du auf der Taste drauf, dann beginnt der Wert
(durch den Autorepeat) sich automatisch schnell zu vergrößern (oder
verkleinern). Du hast also 2 Fliegen mit einer Klappe: Du kannst bei
Bedarf gezielt den Wert in kleinen Schritten einzeln
vergrößern/verkleinern. Musst du den Wert aber über eine größere
'Strecke' verändern, dann bleibst du einfach auf der Taste drauf und
tippst dir keinen Wolf. Dieses Feature hat wohl jeder schon mal gesehen.
Die meisten Nachtkästchen-Uhren haben sowas bei der Zeiteinstellung.
Oder dein Fernseher bei der Lautstärkeeinstellung.
Und durch die Trennung von normalem Tastendruck und Autorepeat
Tastendruck könnte man auch sowas programmieren
* normaler Tastendruck: Wert verändert sich um jeweils +-1
* Autorepeat: Wert verändert sich um beispielsweise +-10
Dann kann ich auch dann einen Wert in 0 komma nix auf den gewünschten
Zahlenwert einstellen, wenn der Einstellbereich zb 0 bis 65535 lautet.
Mit einem etwas längerem Autorepeat Tastendruck, der mir den Zahlenwert
um jeweils 10 erhöht, manövriere ich mich in die Umgebung der
gewünschten Zahl und die Feinjustage mach ich mit einzelnen
Tastendrücken. Das funktioniert ziemlich intuitiv. Damit kommen Menschen
sofort klar.
Die Tastenfunktionen stellen die Grundfunktion zur Verfügung. Durch die
Art wie du sie einsetzt, kannst du es deinem Benutzer angenehm oder
unangenehm machen, dein Gerät zu bedienen. Also was willst du da an den
Funktionen selber ändern? Diese Funktionen sind Bausteine! Du
entscheidest, wie du sie einsetzen willst.
Hallo Karl Heinz
danke für deine Antwort. Beim testen der einzelnen Funktionen war mir
die Anwendung der Taste mit rpt nicht ganz klar. Hatte als Anzeige auch
nur eine LED geschaltet. Dadurch war die Funktion wahrscheinlich auch
nicht erkennbar. Das mit deinen tasten gefällt mir sehr gut. Werde es
ausprobieren.
Danke
achim
Hallo
ich muss noch mal was fragen. Ich hatte bisher eine Textanzeige z.B. so
gemacht:
1
for(x=0;x<=84;x++)// Schleife
2
{
3
gfx_move(x,50);// Angabe Ort
4
gfx_print_text(" Text");// Ausgabe Text
5
delay(200);// Pause
6
}
Hat nur die Aufgabe durch for einen Punkt auf dem Display festzulegen.
Da ich jetzt mit einem Timer arbeite, kann ich delay nicht mehr nehmen.
Bei LED mache es so:
1
// LED Ansteuerung
2
if(wait10_merker==1)
3
{// Steuerung LED 5 rot
4
led5++;
5
if(led5==50)// Einstellung Zeit 10ms mal x
6
{
7
led5=0;
8
PORTE^=(1<<PE5);
9
}}
Es geht sehr gut. Ich kann delay im dem oberen Code nicht so einfach
austauschen, da dort for drin ist bis 84 und delay mit 200ms.Habe dann
versucht delay durch if und folgend auszutauschen. Leider geht dann gar
nichts mehr. Arbeite mit einme Atmega 128 16M und Timer von 10ms. Ein
anderer Code mit Timer und ISR steht weiter oben. Was mach ich wieder
falsch?
achim
Ich weiß zwar nicht warum meine Mail an dich hier im Thread erschienen
ist, aber hat sich ja inzwischen auch erledigt.
Der Timer-Interrupt wird ja alle x Milisekunden aufgerufen, im Fall des
Taster-Codes sind es alle 10ms. Statt jetzt mit einem delay von 200ms zu
arbeiten kannst du auch einfach im Interrupt-Handler eine Variable
hochzählen und immer wenn sie den Wert 20 errreicht führst du den Code
aus und setzt sie auf 0 zurück (20*10ms = 200ms).
Gruß Tommy
Hallo Tommy
das ist ok. Habe ich auch so bei den LED gemacht. Der Prg Aufbau ist
doch bei Timer ganz anders. Da der Code doch alle ca. 10ms bearbeitet
wird, werden auch alle nachfolgenden Befehle sofort ausgeführt.
Beispiel: wenn ich zwei if Befehle nehme, so wird erst die eine
abgearbeitet und wenn fertig die andere. Beim Timer muss ich doch if und
andere Sachen so setzen, das so was nicht passieren kann.
Ich arbeite auch ser gern mit void und dem Aufruf zu Unterprogrammen.
Dort schreibe ich dann wiederkehrende Funktionen oder ähnliches rein.
Das klappt auch nicht mehr. Werde mal einen Flag versuchen, um die
wiederholten Aufrufe zu unterbinden.
Habe da was angefangen, ohne auf die Durchführung zu achten. Timer haben
was an sich.
achim
Achim Seeger schrieb:> Habe da was angefangen, ohne auf die Durchführung zu achten. Timer haben> was an sich.
Das wichtigste ist:
Du musst deine Denkweise umstellen.
Bisher hast du das wahrscheinlich so gemacht
zuerst mach das
dann mach das
und wenn du damit fertig bist, kommt dieses drann
Jetzt musst du anfangen, in Ereignissen zu denken und wenn notwendig,
musst du dir eben selbst ein Ereignis generieren.
Deine Hauptschleife arbeitet sich dann der Reihe nach durch alle
Ereignisabfragen durch und wenn das entsprechende Ereignis vorliegt,
wird die zugehörige Teilaufgabe abgearbeitet.
Wenn diese Teilaufgabe beinhaltet, dass auf etwas gewartet werden muss,
dann ist das Ende der Warterei ebenfalls einfach wieder ein Ereignis.
Nämlich zb das Ereignis: Zeit abgelaufen; und wird genauso behandelt wie
alles andere auch. Die Teilaufgabe 'wartet' also nicht aktiv, sondern es
wird nur vermerkt, dass bei einem entsprechenden Ereignis in der Zukunft
etwas zu passieren hat. Wie weit in der Zukunft - das ist dann die
Wartezeit. Das was zu passieren hat, das ist dann das was nach der
Warterei in dieser Teilaufgabe weiter zu passieren hat.
Der Timer ist auch nichts anderes als ein Ereignislieferant. Das
Ereignis lautet bei dir: 10 Millisekunden sind vergangen. Und in der
Hauptschleife lautet dann die entsprechende Fragestellung: Wenn jetzt 10
Millisekunden vergangen sind, was gibt es dann genau jetzt alles zu tun.
Die Fragestellung ist nicht: was gibt es in 2 Sekunden zu tun oder in 5,
sondern genau jetzt. Was in der Zukunft passieren soll ist Sache eines
anderen Durchlaufs durch die Hauptschleife, wenn das nächste 10
Millisekunden Ereignis eintrifft.
Das ist das Um und Auf, wie man auf einem µC ein Programm schreibt,
welches scheinbar mehrere Dinge gleichzeitig macht.
Alles andere, wie zb eine Statemachine, sind nur unterschiedliche
Organisationsformen um dieses Grundprinzip zu implementieren.
Hallo Karl Heinz
du bietset mir ganz schön was an. Bin am umdenken. Und dabei stelle ich
fest, das es gar nicht so leicht ist. Muss ein grossteil meines
bisherigen denkens zum Ablauf, so in altbewährter Art mit delay,
wegschmeissen. Ist auch gleizeitig eine Herausvorderung für was neues
und dabei ist noch viel zu lernen. Ist ein Ziel, hoffentlich nicht zu
gross für mich.Werde dich bestimmt noch ein paar mal löchern müsse.
achim
Achim Seeger schrieb:> Hallo Karl Heinz> du bietset mir ganz schön was an. Bin am umdenken. Und dabei stelle ich> fest, das es gar nicht so leicht ist. Muss ein grossteil meines> bisherigen denkens zum Ablauf, so in altbewährter Art mit delay,> wegschmeissen.
Genau darum gehts.
Diese _delay_ms haben (ausser in Sonderfällen wenn es wirklich nur um
ganze kurze Zeiten geht) nichts in einem Programm verloren. Sobald du so
einen _delay_ms benutzt, hast du schon einen taktischen Fehler gemacht.
Diese delays werden konzeptionell immer gleich ersetzt:
Ein Timer erzeugt einen Basistakt von bekannter Zeitdauer und dann wird
entsprechend oft mitgezählt, wieviele derartige Ereignisse aufgetreten
sind bzw. auftreten müssen, bis dann die 'Wartezeit' abgelaufen ist und
es weitergeht.
Aber nicht so
cnt = 6000; // warte 6 Sekunden bei 10ms Basistakt
while( cnt > 0 ) {
if( wait_10ms ) {
wait10ms = 0;
cnt--;
}
}
das bringt dir nichts. Das ist wieder eine Warteschleife, nur mit
anderen Mitteln.
Sondern so:
1
intmain()
2
{
3
....
4
5
cnt=-1;// die für LED1 zuständige 'Eieruhr' ist inaktiv
6
// -1 bedeutet abgeschaltet
7
// 0 bedeutet die Uhr ist abgelaufen
8
// positive Werte die noch zu wartende Wartezeit in
9
// 10ms Einheiten
10
11
while(1){// das ist die einzige Schleife die dauernd
if(cnt==0){// 3. Ereignis: die Eieruhr ist abgelaufen
31
// d.h. die Zeit, die beim get_key_press
32
// auf die Eieruhr eingestellt wurde, ist
33
// vorbei
34
PORTB|=(1<<LED1);// Led wieder ausschalten
35
cnt=-1;// und die Eieruhr inaktiv stellen
36
}
37
38
39
wait_10ms=0;
40
}
Und das verallgemeinert jetzt auch simpel auf mehrere LED und mehrere
Taster, wobei jeder Taster 'seine' LED einschaltet, die dann nach einer
bestimmten Zeit wieder ausgeht. Unabhängig davon, wieviele Taster bzw.
LED, unabhängig davon ob die Zeiten Vielfache voneinander sind,
unabhängig davon in welcher Reihenfolge die Tasten gedrückt werden und
ob bereits eine Zeit läuft oder nicht wenn eine andere Taste gedrückt
wird.
Wird eine Taste gedrückt, dann geht die LED an und nach einer bestimmten
Zeit geht sie wieder aus. Das Konzept 'Eieruhr für die Wartezeit dieser
LED' zusammen mit dem Ereignis 'Eieruhr abgelaufen' regelt das.
Hallo Karl Heinz
du überraschst mich immer wieder. Hatt so wa ähnliche schon gemacht,
aber auf -1 bin ich nicht gekommen, um etwas auszuschalten. Die Idee ist
gut. Erinnert mich sehr an einen Flag. Kann bestimmt genommen werden um
bestimmte void .. zu schalten oder den Aufruf zu sperren. Muss
ausprobieren, wie ich es besten verwenden kann.
1
for(x=0;x<=84;x++)// Schleife
2
{
3
gfx_move(x,50);// Angabe Ort
4
gfx_print_text(" Text");// Ausgabe Text
5
delay(200);// Pause
6
}
7
8
for(x=84;x>=0;x--)// Schleife
9
{
10
gfx_move(x,50);// Angabe Ort
11
gfx_print_text(" Text");// Ausgabe Text
12
delay(200);// Pause
13
}
Im Moment bin ich noch an dieser Sache dran. Versuche gerade älter
Sachen von mr auf Timer zu übersetzen. Dabei merkt man wo die Probleme
sind.
Bei dem Code wird eine Text auf dem Bildschirn von links nach rechts und
wieder zurück geschoben (auf meinem kleinen Display). Mit delay kein
Problem. Das x gibt nur die Position an. So laufen beide Teile
nacheinander ab. Das geht nicht mehr. Ersetze delay durch ein Teil vom
Timer. Ich kann delay nicht so einfach austauschen. Der Timer läuft
drüber und schaltet beide oder gar nicht. Es beinflusst for und der
Timer. Habe die LED dann zur Kontrolle drin, ob es geht. Schalten die
LED, hat sich nichts aufgehängt.
achim
Achim Seeger schrieb:> Bei dem Code wird eine Text auf dem Bildschirn von links nach rechts und> wieder zurück geschoben (auf meinem kleinen Display). Mit delay kein> Problem. Das x gibt nur die Position an. So laufen beide Teile> nacheinander ab. Das geht nicht mehr. Ersetze delay durch ein Teil vom> Timer. Ich kann delay nicht so einfach austauschen.
Natürlich kannst du.
Der cnt, den ich da oben als 'Eieruhr' eingesetzt habe, ist ja nichts
anderes als eine Variable, die in bestimmten Zeitabständen ihren Wert
ändert.
Und für dein x gelten genau die gleichen Anforderungen. In bestimmten
Zeitabständen muss es andere Werte annehmen. Die Zahlenwerte sind
andere, aber das Prinzip ist genau das gleiche.
Die for-Schleife hingegen, brauchst du überhaupt nicht.
Die Umstellung auf Ereignisse bedeutet in letzter Konsequenz, dass viele
Schleifen ersatzlos wegfallen. Die Schleifen hast du nur gebraucht,
damit x seinen Wert ändert. Mit dem Ereignisansatz hast du einfach nur
eine andere Form gefunden, mit der Variablen zeitlich gesteuert ihren
Wert ändern können.
Hallo Karl Heinz
habe das Beispiel von dir genommen und erst mal alles raus, was nicht
rein muss. Dannhabe ich es geändert:
1
if(wait10_merker==1)
2
{
3
textr++;
4
if(textr==84)
5
{
6
gfx_move(textr,50);// Angabe Ort
7
gfx_print_text(" Text");// Ausgabe Text
8
textr=0;
9
}
10
}
Habe jetzt ein Zeit Problem. Die 84 ist der letzet Pixel auf dem
Display. Es läuft nicht mehr langsam über das Display somdern springt
gleich zum Ende.
Der Rücklauf kann erst starten wenn es am Ende ist. Komme leider nicht
klar.
achim
Hallo Karl Heinz
habe die Sache mit der Zeit und einiges anderes in den Griff bekommen.
Der Text läuft über das Display. Von links nach rechts und umgekehrt.
Was nicht kklappt ist die Gegenseitige Verriegelung. So das nur eine
Richtung freigegeben ist. Danach die andere. Habe es mit textlauf
probiert, geht aber nicht.
1
// Text 1 rechts oben
2
3
if(textlauf=1)
4
{
5
if(wait10_merker==1)
6
{
7
textro++;
8
text1o=textro/20;
9
gfx_move(text1o,45);// Angabe Ort unter Logo
10
gfx_print_text(" N I B O 2");// Ausgabe Nibo 2
11
if(textro==1680)
12
{
13
textro=0;
14
textlauf=2;
15
}
16
}
17
}
18
19
// Text 1 links oben
20
21
if(textlauf=2)
22
{
23
if(wait10_merker==1)
24
{
25
textlo--;
26
text2o=textlo/20;
27
gfx_move(text2o,45);// Angabe Ort unter Logo
28
gfx_print_text(" N I B O 2");// Ausgabe Nibo 2
29
if(textlo==0)
30
{
31
textlo=1680;
32
textlauf=1;
33
}
34
}
35
}
Zum Beginn des Prg setze ich textlauf auf 1, damit die Sache links
beginnt. Geht aber nicht so. Was mach ich falsch?
achim
Achim Seeger schrieb:> Hallo Karl Heinz> habe das Beispiel von dir genommen und erst mal alles raus, was nicht> rein muss. Dannhabe ich es geändert:>
1
>if(wait10_merker==1)
2
>{
3
>textr++;
4
>if(textr==84)
5
>{
6
>gfx_move(textr,50);// Angabe Ort
7
>gfx_print_text(" Text");// Ausgabe Text
8
>textr=0;
9
>}
10
>}
11
>
> Habe jetzt ein Zeit Problem.
Nein hast du nicht.
Du hast einen Programmfehler.
(d.h. vielleicht hast du auch ein Zeitproblem, mag sein. ABer erst mal
hast du einen Fehler)
> Display. Es läuft nicht mehr langsam über das Display somdern springt> gleich zum Ende.
Genau das was du programmiert hast.
Du gibst den Text erst dann aus, wenn die Textposition die 84 erreicht
hat.
Wenn du eine Laufschrift haben willst, dann wärs wohl niocht verkehrt,
wenn du den Text an JEDER Zwischenposition ausgeben würdest.
> if (wait10_merker ==1)> {> textr++;
gfx_move(textr,50); // Angabe Ort
gfx_print_text(" Text"); // Ausgabe Text
> if (textr == 84)> {> textr = 0;> }> }> [/c]
Und wenn das dann zu schnell läuft, dann muss man eben mit einem weitern
Zähler noch eine Verzögerung einbauen. Kommt das 'Zeitsignal' alle 10
Millisekunden, dann kann man 10 davon abzählen und hat so dann auch eine
Verzögerung von 1 Hunderstelsekunde.
Ich klink mich schön langsam aus.
Ich denke nämlich nicht, das es sinnvoll ist, da jetzt noch weiter unter
die Arme zu greifen. Du musst deine Fehler selber machen und du musst
sie auch selber beheben. Nur so lernst du. Wenn ich dir immer aus der
Patsche helfe lernst du in letzter Konsequenz nicht, wie man seinen
eigenen Fehlern auf die Spur kommt. Radfahren lernt man nur indem man
auch vom Rad fällt.
Hallo Karl Heinz
hatte mein Fehler bereits bemerkt und das Prg geändert. Habe auch noch
eine zusätzlich Verzögerung eingebaut
[c]
if (wait10_merker ==1)
{
textro++;
text1o=textro/20;
gfx_move(text1o,45); // Angabe Ort unter Logo
gfx_print_text(" N I B O 2"); // Ausgabe Nibo 2
if (textro == 1680)
{
textro = 0;
textlauf=2;
}
}
Damit kann ich die Zeit sehr gut einstellen und auch die Position stimmt
genau. Bleibt nur noch die Verriegelung. Hast noch einen Tip für mich.
Als Grundlage für Timer und Prg ist das alles sehr gut. Kann dann
weitermachen.
achim
Achim Seeger schrieb:> Hallo Karl Heinz> hatte mein Fehler bereits bemerkt und das Prg geändert. Habe auch noch> eine zusätzlich Verzögerung eingebaut> [c]> if (wait10_merker ==1)> {> textro++;> text1o=textro/20;> gfx_move(text1o,45); // Angabe Ort unter Logo> gfx_print_text(" N I B O 2"); // Ausgabe Nibo 2> if (textro == 1680)> {> textro = 0;> textlauf=2;> }> }
Die Idee ist zwar naheliegend, ist aber nicht besonders gut.
Die DIvision kostet Zeit und ausserdem gibst du den Text an eine
Position aus, an der er sowieso schon steht. D.h. du vergeudest
mutwillig Zeit
if (wait10_merker ==1)
{
verzoegerung++;
if (verzoegerung == 20 )
{
verzoegerung = 0;
textro++;
gfx_move(textro, 45); // Angabe Ort unter Logo
gfx_print_text(" N I B O 2"); // Ausgabe Nibo 2
if (textro == 84)
{
textro = 0;
textlauf=2;
}
}
}
So ist es besser. Jetzt fällt nur dann Rechenzeit an, wenn es auch
tatsächlich etwas zu tun gibt.
Hallo Karl Heinz
du hast von Zeitvergeudung gesprochen. Muss dazu noch was fragen. Habe
nach meinem Code 4 x Text eingebaut. Dadurch entsteht eine sichtbare
Versammlung des gesamten Codes. Dann habe ich deine Verzögrung rein
genommen. Dadurch entsteht entweder Fehlermeldung, keine Funktion oder
die Anzeige springt. Hängt da von ab, ob es bei + oder - drin ist.
Versteh den Zusammenhang nicht.
achim
Hallo Karl Heinz
habe alles programmieren können. Es geht jetz die Verzögerung, links und
rechtslauf, die Zeiten der LED stimmen, die Umkehrung am Ende der Zeile
und eine Auswahl ob zuerst links oder rechts anfangen soll bei 2 Zeilen,
die Display Anzeige ist korrekt. Alles im Timerbetrieb und ohne delay.
Hast recht, Übung machts. Danke für deine Hilfe. Falls jemand Interesse
am Code hat, stelle ich ihn rein. Ist aber für den Atmega 128 beim Nibo
geschrieben.
achim
Hallo Karl Heinz
keine Angst, ich will dich nicht mit irgendwelchen Codes ärgern. Habe
aber ein Problem, wo die Antwort in keinem Buch steht.
Da ich den Code zur Enrprellung verwende, habe ich insgesamt 4 Taster
zur Verügung. Diese setze ich ein um ein Menu zu steuern, (Start, auf,
ab, Enter). Diese Schalterauswertung geht anschlessend in ein void
(Unterprg) und führt entsprechende Aufgaben aus. Ist die Sache mit
diesen UPrg sinnvoll oder braucht es wieder zu viel Zeit. In den Unterrg
verwende ich ebenfalls kein delay. Bestimte Oprationen führe ich aus
oder nicht, in dem ich diese mit einem Flag sperre oder zulasse. Welche
Alternativen kennst du noch zu diesem Ablauf (bei Timer Betrieb)?
Kannman das so machen?
achim
Hallo Karl Heinz
habe deine Eieruhr programmiert. Leider klapp es bei der Zeit nicht.
Habe die entsprechenden Stellen rausgesucht:
1
if(get_key_press(1<<KEY_1))// Abfrage nach S3 neu
2
{
3
flagstart=1;// LED an
4
vzzeit=5;
5
6
}
7
8
if(flagstart==1)// Abfrage wai10 - Merker
9
{
10
verzoegerung=100;
11
nibo_startzeit(15,45);
12
}
Erst der Aufruf und dann die Ausführung
1
if(wait10_merker==1)
2
{
3
if(verzoegerung>0)
4
{
5
verzoegerung--;}
6
if(verzoegerung==0)
7
{
8
vzzeit--;
9
10
sprintf(zahlstart," %d",vzzeit);// Bildung Wert
11
gfx_move(zeitm+72,zeitn-1);// Angabe Ort
12
gfx_set_proportional(0);// Angabe Schrift
13
gfx_print_text(zahlstart);// Ausgabe Variable
14
}
15
}
16
}
Innerhalb der "verzoegerung" triit eine Zeitverzögerung auf. Kann
eigentlich gar nicht sein, da alles über einen Timer läuft. Leider ist
mir nicht klar warum. vzzeit ist mit 5 voreingestellt. Es sollen die
Zeiten dann runter gezählt werden und durch sprintf dargestellt werden.
Geht auch nicht so. was mach ich wieder falsch?
achim
Achim Seeger schrieb:> Hallo Karl Heinz> habe deine Eieruhr programmiert. Leider klapp es bei der Zeit nicht.> Habe die entsprechenden Stellen rausgesucht:
Sorry.
Aber ab gewissen Programmkomplexitäten reagiere ich auf "entsprechende
Stellen rausgesucht" nicht mehr.
Du siehst da drinn keinen Fehler, ich seh da drinn keinen Fehler. Also
wird der Fehler wohl in dem Teil liegen, den du nicht gezeigt hast. Zb
in den Teil in dem verzoegerung wieder von 0 auf einen Wert größer 0
gesetzt wird.
PS:
Wird dir das nicht langsam zu blöd, für eine Zahlenausgabe immer
denselben Code quer über dein Programm zu verstreuen? Schreib dir doch
eine Funktion dafür
1
voidgfx_print_int(intzeile,intspalte,intwert)
2
{
3
chartext[10];
4
5
sprintf(text,"%d",wert);
6
gfx_move(zeile,spalte);
7
gfx_set_proportional(0);
8
gfx_print_text(text);
9
}
und schon fallen dir in deinem eigentlichen Programm wieder erklecklich
viele Programmzeile weg.
1
...
2
if(wait10_merker==1)
3
{
4
if(verzoegerung>0)
5
{
6
verzoegerung--;
7
}
8
if(verzoegerung==0)
9
{
10
vzzeit--;
11
gfx_print_int(zeitm+72,zeitn-1,vzzeit);
12
}
13
}
Immer gleiche Operationen in einer Funktion zusammenzufassen IST
sinnvoll, weil an der Aufrufstelle unnötige Komplexität herausgenommen
wird und der Blick aufs wesentliche (die Programmlogik an dieser Stelle)
frei wird.
Hallo KH
danke für den Tip
eigentlich suche ich nach einer antwort auf eine einfache Frage:
Den Timer lasse ich in der while Schleife laufen. Wenn ich mit void
rausspringe und dort ein Unterprg ausführen lasse, geht dort auch der
Timer aufruf mit wait10_merker?
achim