Datum:
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
Datum:
Es fehlt die Einrichtung des PORTD als Ausgabeport. Es muss eine Zeile mit DDRD in den Code...
Datum:
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
Datum:
> 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):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)
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.
Datum:
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
Datum:
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
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*/ } // end if return 0; } // end while } // 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?
Datum:
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 ;-)
int main(void) { uint8_t t, i; DDRB &= ~(1 << PB0); /*Pin PB0 auf Eingang(Taster)*/ PORTB &= ~(1 <<PB0); /*PullUp Widerstand aus,da extern*/ DDRD |= (1 << PD6); /*PD6 als Ausgang*/ // 10 mal blinken mit 0,5s LED AN + 0,5s LED AUS for (i = 0; i < 10; i++) { PORTD |= (1 << PD6); for (t = 0; t < 50; t++) _delay_ms(10); PORTD &= ~(1 << PD6); for (t = 0; t < 50; t++) _delay_ms(10); } while (1) { if (warte(&PINB, PB0)) /*Wenn Taster an PB0 gedrückt..*/ { PORTD = PORTD ^ (1 << PD6); /*LED an Port PD6 toggeln*/ } } return 0; // <=== nicht im while-Block! } |
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)?
Datum:
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
Datum:
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
Datum:
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.
Datum:
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
Datum:
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.
Datum:
Was mit aufgefallen ist und mich beim Versuch es zu verstehen gewundert hat! Hier nochmal das Beispiel aus dem Tutorial:
#include <avr/io.h> #include <inttypes.h> #ifndef F_CPU #warning "F_CPU war noch nicht definiert, wird nun mit 3686400 definiert" #define F_CPU 3686400UL /* Quarz mit 3.6864 Mhz */ #endif #include <util/delay.h> /* bei alter avr-libc: #include <avr/delay.h> */ /* Einfache Funktion zum Entprellen eines Tasters */ inline uint8_t debounce(volatile uint8_t *port, uint8_t pin) { if ( !(*port & (1 << pin)) ) { /* Pin wurde auf Masse gezogen, 100ms warten */ _delay_ms(50); // Maximalwert des Parameters an _delay_ms _delay_ms(50); // beachten, vgl. Dokumentation der avr-libc if ( *port & (1 << pin) ) { /* Anwender Zeit zum Loslassen des Tasters geben */ _delay_ms(50); _delay_ms(50); return 1; } } return 0; } int main(void) { DDRB &= ~( 1 << PB0 ); /* PIN PB0 auf Eingang Taster) */ PORTB |= ( 1 << PB0 ); /* Pullup-Widerstand aktivieren */ ... if (debounce(&PINB, PB0)) { /* Falls Taster an PIN PB0 gedrueckt */ /* LED an Port PD7 an- bzw. ausschalten: */ PORTD = PORTD ^ ( 1 << PD7 ); } ... } |
Bei Betrachtung des Tutorialbeispiels sind die Parameter der Funktion "debounce()" PORT-Zeiger und PIN-Kopie.
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin) |
Bei dem Einsatz in der Schleife werden die Parameter aber genau andersherum übergeben.
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!;)
Datum:
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.
Datum:
Sie ist schon syntaktisch richtig (pin = Bitnummer 0..7), aber sie funktioniert nicht. Peter
Datum:
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?
Datum:
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
Datum:
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.
Datum:
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.
Datum:
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
Datum:
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 ;)
Datum:
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.
ISR( TIMER0_OVF_vect ) // every 10ms
{
static uint8_t ct0, ct1, rpt;
uint8_t i;
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload
10ms
i = key_state ^ ~KEY_PIN; // key changed ?
ct0 = ~( ct0 & i ); // reset or count ct0
ct1 = ct0 ^ (ct1 & i); // reset or count ct1
i &= ct0 & ct1; // count until roll over ?
key_state ^= i; // then toggle debounced
state
key_press |= key_state & i; // 0->1: key press detect
if( (key_state & REPEAT_MASK) == 0 ) // check repeat function
rpt = REPEAT_START; // start delay
if( --rpt == 0 ){
rpt = REPEAT_NEXT; // repeat delay
key_rpt |= key_state & REPEAT_MASK;
}
}
Was brauch ich den eigentlich?
achim
Datum:
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
static uint8_t ct0, ct1, rpt; uint8_t i; i = key_state ^ ~KEY_PIN; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } |
* Die globalen Variablen
volatile uint8_t key_state; // debounced and inverted key state: // bit = 1: key pressed volatile uint8_t key_press; // key press detect volatile uint8_t key_rpt; // key long press and repeat |
übernimmst du so wie sie sind * Ebenso die Funktionen
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 ) |
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#define KEY_DDR DDRB #define KEY_PORT PORTB #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)
#define KEY_LEFT 0 #define KEY_RIGHT 1 #define KEY_ENTER 2 |
In ALL_KEYS werden einfach alle definierten Tasten in einer einzigen Maske zusammengefasst
#define ALL_KEYS (1<<KEY_LEFT | 1<<KEY_RIGHT | 1<<KEY_ENTER) |
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
#define REPEAT_MASK (1<<KEY_LEFT | 1<<KEY_RIGHT) #define REPEAT_START 50 // after 500ms #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
... int main() { ... KEY_DDR &= ~ALL_KEYS; // configure key port for input KEY_PORT |= ALL_KEYS; // and turn on pull up resistors ... sei(); while( 1 ) { ... } } |
Tja. Das wars auch schon. Ab jetzt kannst du mit den Tastenabfragen deine Tasten mit Leben befüllen
int main() { ... while( 1 ) { if( get_key_press( 1 << KEY_LEFT ) ) mach irgendwas, zb eine LED einschalten if( get_key_press( 1 << KEY_RIGHT ) ) mach was anderes, zb die LED wieder abschalten } } |
Datum:
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
Datum:
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.
Datum:
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
Datum:
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
Datum:
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 // only once // uint8_t get_key_press( uint8_t key_mask ) { cli(); // read and clear atomic ! key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) sei(); return key_mask; } |
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.
Datum:
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
Datum:
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?
ISR (TIMER2_COMP_vect) // wait1=1ms, { static uint8_t ct0, ct1, rpt; uint8_t i; 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 i = key_state ^ ~KEY_PIN; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } |
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.
Datum:
@ 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:
#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; |
Datum:
Danke für deine Hilfe. Das grösste Problem, was ich habe, sitzt wahrscheinlich vor dem Monitor! achim
Datum:
Ist das hier alles gruselig. Ich kriege schon ne Gänsehaut.
Datum:
Was kann man an dem 10-Zeiler bitte nicht verstehen? Und wofür soll man da mehr als 2min zum Verständnis brauchen?
Datum:
Dann las uns doch an deiner Weisheit teil haben. Bin ganz begierig auf deine Erklärung zu meiner Erleuchtung.
Datum:
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
#define KEY_DDR DDRB // Defi an welchem Port die Tasten sitzen #define KEY_PORT PORTB #define KEY_PIN PINB #define KEY_LEFT 0 // Taste links #define KEY_RIGHT 1 // Taste rechts #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
Datum:
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.
Datum:
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
Datum:
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?
Datum:
Achim Seeger schrieb: >
> #define KEY_DDR DDRB // Defi an welchem Port die Tasten sitzen > #define KEY_PORT PORTB > #define KEY_PIN PINB > > #define KEY_LEFT 0 // Taste links > #define KEY_RIGHT 1 // Taste rechts > #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. Dann konfigurierst du das eben nach deinen Bedürfnissen. Deine Tasten liegen am Port D. Also
#define KEY_DDR DDRD // Defi an welchem Port die Tasten sitzen #define KEY_PORT PORTD #define KEY_PIN PIND |
und dort liegt die Taste am Pin 4. Also
#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
#define KEY_1 4 #define KEY_2 3 #define KEY_3 5 #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.
Datum:
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/..
Datum:
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
Datum:
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
Datum:
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.
Datum:
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
Datum:
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.
Datum:
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
Datum:
Achim Seeger schrieb: > Falsches Hobby nicht gerade, vielleicht zu wenig Zeit und Ausdauer. Ich laufe auch gerne Marathon. Habe nur zuwenig Zeit und Ausdauer. :)
Datum:
Alles zurück. Habe einen Text übersehen von KH. Glaube diese Stelle hat im Kopf gefehlt. achim
Datum:
@Achim, check mal deine Mailbox.
Datum:
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
.. get_key_short(uint8_t key_mask) .. get_key_press(~key_state & key_mask); .. get_key_long(uint8_t key_mask) |
verwendet. (short-kurz, press-? , long-lang) Ist damit die Länge des Tastendrucks gemeint und wird diese über
#define REPEAT_START 50 // after 500ms #define REPEAT_NEXT 20 // every 200ms |
eingestellt? achim
Datum:
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. >
> .. get_key_short(uint8_t key_mask) > .. get_key_long(uint8_t key_mask) > |
> > 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.
Datum:
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.
/* Test Timer, Testet die Funktion Timer, ist eingestellt auf 10ms und wird vom Timer gesteuert. Tasten sind nach Peter Dannegger entprellt, bis 4 Tasten , S3 kurz - an, lang - aus, by h.j.seeger@web.de */ #include <nibo/niboconfig.h> #include <nibo/iodefs.h> #include <nibo/leds.h> #include <avr/interrupt.h> #include <avr/io.h> #include <nibo/gfx.h> #include <nibo/display.h> #include <nibo/pwm.h> #include <nibo/bot.h> #include <nibo/delay.h> #include <stdint.h> #define KEY_DDR DDRD // Datenrichtungsregister #define KEY_PORT PORTD // Datenregister #define KEY_PIN PIND // PIN D #define KEY_1 4 // #define KEY_2 3 // #define KEY_3 5 // #define KEY_4 6 // #define ALL_KEYS (1<<KEY_1|1<<KEY_2|1<<KEY_3|1<<KEY_4) #define REPEAT_MASK (1<<KEY_1|1<<KEY_2) #define REPEAT_START 50 // after 500ms #define REPEAT_NEXT 20 // every 200ms uint8_t key_state; uint8_t key_press; uint8_t key_rpt; uint8_t wait10 = 0; // ISR 10ms uint8_t wait10_merker = 0; // ISR 10ms uint8_t wait = 0; // Timer 1ms uint8_t led1 = 0; // Variable für LED 1 uint8_t led2 = 0; // Variable für LED 2 uint8_t led3 = 0; // Variable für LED 3 uint8_t led4 = 0; // Variable für LED 4 uint8_t led5 = 0; // Variable für LED 5 uint8_t led6 = 0; // Variable für LED 6 uint8_t wait_led = 0; uint8_t tast; ISR (TIMER2_COMP_vect) // wait1=1ms, { static uint8_t ct0,ct1,rpt; uint8_t i; if( wait<=9) // Takt 0,5s, bei 9 sind es 10ms { wait++; } // erhöht else // wenn dann ... { wait=0; // setzt wait auf 0 wait10=0xFF; // Signal alle 10ms wait10 i=key_state ^~KEY_PIN; ct0=~(ct0&i); ct1=ct0^(ct1&i); i&=ct0&ct1; key_state^=i; key_press|=key_state&i; if((key_state & REPEAT_MASK)==0) rpt=REPEAT_START; if(--rpt==0){ rpt=REPEAT_NEXT; key_rpt|=key_state & REPEAT_MASK; } }} uint8_t get_key_press(uint8_t key_mask) { cli(); key_mask &=key_press; key_press^=key_mask; sei(); return key_mask; } uint8_t get_key_rpt(uint8_t key_mask) { cli(); key_mask &=key_rpt; key_rpt^=key_mask; sei(); return key_mask; } uint8_t get_key_short(uint8_t key_mask) { cli(); return get_key_press(~key_state & key_mask); } uint8_t get_key_long(uint8_t key_mask) { return get_key_press(get_key_rpt(key_mask)); } void nibo_timer2() // Timer 10ms { TCNT2 = 0; OCR2=249; TCCR2=(1<<WGM21)|(1<<CS21)|(1<<CS20); TIMSK |= (1<<OCIE2); } int main(){ // Start sei(); // Freigabe Interrups display_init(); pwm_init(); gfx_init(); bot_init(); leds_init(); leds_set_displaylight(800); // setzt Displaylicht nibo_timer2(); // Aufruf Timer 2 einmalig KEY_DDR&=~ALL_KEYS; KEY_PORT|=ALL_KEYS; gfx_fill(0x00); // löscht Schirm gfx_move(5, 5); // Angabe Ort gfx_set_proportional(0); // Angabe Schrift gfx_print_text("Testprogramm Nibo 2"); // Ausgabe Text gfx_move(8, 15); // Angabe Ort gfx_set_proportional(1); // Angabe Schrift gfx_print_text("Test Timer LED HJS 2012"); // Ausgabe Text gfx_move(0,25); // Angabe Ort gfx_hline(128); // Ausgabe Strich gfx_move(25, 35); // Angabe Ort gfx_set_proportional(1); // Angabe Schrift gfx_print_text("Funktion mit Timer"); // Ausgabe Text while(1) { // Beginn Timer Schleife if (wait10 == 0xFF) // Abfrage wai10 - Merker { wait10_merker=1; wait10=0; } if(get_key_short(1<<KEY_1)) { // LED an leds_set_status(1,2); } if(get_key_long(1<<KEY_1)) { // LED aus leds_set_status(0,2); } // LED Ansteuerung 6 if (wait10_merker == 1) { // Steuerung LED 6 rot led5++; if ( led5 == 50 ) // Einstellung Zeit 10ms mal x { led5 = 0; PORTE ^= (1<<PE6); } } // LED Ansteuerung 7 if (wait10_merker == 1) { // Steuerung LED 7 grün led6++; if ( led6 == 100 ) // Einstellung Zeit 10ms mal x { led6 = 0; PORTC ^= (1<<PC7); } } // LED Ansteuerung 5 grün Blinklicht // mit Abfrage aus if (wait10_merker == 1) { led4++; if (led4 == 35 ) // Einstellung Zeit 10ms mal x { led4 = 0; if(!(PINC&(1<<PC5))) // Abfrage PortC PC5 LED 5 Grün aus? leds_set_status(1,5); // schaltet LED 5 Grün auf ein else leds_set_status(0,5); // schaltet LED 5 Grün auf aus } } wait10_merker = 0; // Wait10_merker auf 0 setzen } // while return 0; } // main |
Viel Spass an alle Nutzer achim
Datum:
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;
volatile uint8_t key_state; volatile uint8_t key_press; volatile uint8_t key_rpt; |
Das könntest Du auch in meinem Beispielcode finden. _
Datum:
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
Datum:
Hallo Uwe wo finde ich deinen Beispielcode? achim
Datum:
Guten Morgen Achim Seeger schrieb: > Hallo Uwe > wo finde ich deinen Beispielcode? > achim Einfch dem Link in diesem Beitrag folgen: Beitrag "Re: AVR-GCC Tutorial Taster entprellen"
Datum:
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
Datum:
Achim Die Tastenentprellung ist genau dieselbe. Nur hat Uwe den Code aufgeräumt und so zerlegt, dass er ihn leicht einbinden kann.
Datum:
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
Datum:
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
Datum:
Hallo Tommy kann ich die Funktion auch ändern? achim
Datum:
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
.... while( 1 ) { if( get_key_press( 1 << KEY_PLUS ) || get_key_rpt( 1 << KEY_PLUS ) ) { Wert++; mach was mit Wert } if( get_key_press( 1 << KEY_MINUS ) || get_key_rpt( 1 << KEY_MINUS ) ) { Wert--; mach was mit Wert } } |
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.
Datum:
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
Datum:
Hallo ich muss noch mal was fragen. Ich hatte bisher eine Textanzeige z.B. so gemacht:
for (x=0;x<=84;x++) // Schleife { gfx_move(x,50); // Angabe Ort gfx_print_text(" Text"); // Ausgabe Text delay(200); // Pause } |
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:
// LED Ansteuerung if (wait10_merker == 1) { // Steuerung LED 5 rot led5++; if ( led5 == 50 ) // Einstellung Zeit 10ms mal x { led5 = 0; PORTE ^= (1<<PE5); }} |
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
Datum:
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
Datum:
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
Datum:
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.
Datum:
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
Datum:
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:
int main() { .... cnt = -1; // die für LED1 zuständige 'Eieruhr' ist inaktiv // -1 bedeutet abgeschaltet // 0 bedeutet die Uhr ist abgelaufen // positive Werte die noch zu wartende Wartezeit in // 10ms Einheiten while( 1 ) { // das ist die einzige Schleife die dauernd // läuft ... if( get_key_press( .... ) ) // 1. Ereignis - Tastendruck { cnt = 6000; // Lade die Eieruhr, die dann // runterzählt PORTB &= ( 1 << LED1 ); // und schalte eine LED ein } if( wait_10ms ) { // 2. Ereignis: 10ms sind vergangen if( cnt > 0 ) // Ereignis 2a. Die Eieruhr // ist eingeschaltet, also 10ms runterzaehlen cnt--; } if( cnt == 0 ) { // 3. Ereignis: die Eieruhr ist abgelaufen // d.h. die Zeit, die beim get_key_press // auf die Eieruhr eingestellt wurde, ist // vorbei PORTB |= ( 1 << LED1 ); // Led wieder ausschalten cnt = -1; // und die Eieruhr inaktiv stellen } wait_10ms = 0; } |
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.
Datum:
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.
for (x=0;x<=84;x++) // Schleife { gfx_move(x,50); // Angabe Ort gfx_print_text(" Text"); // Ausgabe Text delay(200); // Pause } for (x=84;x>=0;x--) // Schleife { gfx_move(x,50); // Angabe Ort gfx_print_text(" Text"); // Ausgabe Text delay(200); // Pause } |
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
Datum:
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.
Datum:
Hallo Karl Heinz habe das Beispiel von dir genommen und erst mal alles raus, was nicht rein muss. Dannhabe ich es geändert:
if (wait10_merker ==1) { textr++; if (textr == 84) { gfx_move(textr,50); // Angabe Ort gfx_print_text(" Text"); // Ausgabe Text textr = 0; } } |
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
Beitrag #2679218 wurde vom Autor gelöscht.
Datum:
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.
// Text 1 rechts oben if (textlauf=1) { 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; } } } // Text 1 links oben if (textlauf=2) { if (wait10_merker ==1) { textlo--; text2o=textlo/20; gfx_move(text2o,45); // Angabe Ort unter Logo gfx_print_text(" N I B O 2"); // Ausgabe Nibo 2 if (textlo == 0) { textlo = 1680; textlauf=1; } } } |
Zum Beginn des Prg setze ich textlauf auf 1, damit die Sache links beginnt. Geht aber nicht so. Was mach ich falsch? achim
Datum:
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: >
> if (wait10_merker ==1) > { > textr++; > if (textr == 84) > { > gfx_move(textr,50); // Angabe Ort > gfx_print_text(" Text"); // Ausgabe Text > textr = 0; > } > } > |
> 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.
Datum:
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
Datum:
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.
Datum:
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
Datum:
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
Datum:
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
Datum:
Hallo Karl Heinz habe deine Eieruhr programmiert. Leider klapp es bei der Zeit nicht. Habe die entsprechenden Stellen rausgesucht:
if(get_key_press(1<<KEY_1)) // Abfrage nach S3 neu { flagstart=1; // LED an vzzeit=5; } if (flagstart==1) // Abfrage wai10 - Merker { verzoegerung=100; nibo_startzeit(15,45); } |
Erst der Aufruf und dann die Ausführung
if (wait10_merker==1) { if (verzoegerung>0) { verzoegerung--;} if(verzoegerung==0) { vzzeit--; sprintf(zahlstart, " %d", vzzeit); // Bildung Wert gfx_move(zeitm+72, zeitn-1); // Angabe Ort gfx_set_proportional(0); // Angabe Schrift gfx_print_text(zahlstart); // Ausgabe Variable } } } |
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
Datum:
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
void gfx_print_int( int zeile, int spalte, int wert) { char text[10]; sprintf( text, "%d", wert ); gfx_move( zeile, spalte ); gfx_set_proportional( 0 ); gfx_print_text( text ); } |
und schon fallen dir in deinem eigentlichen Programm wieder erklecklich viele Programmzeile weg.
... if (wait10_merker == 1) { if (verzoegerung > 0) { verzoegerung--; } if(verzoegerung == 0) { vzzeit--; gfx_print_int( zeitm+72, zeitn-1, vzzeit ); } } |
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.
Datum:
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
