Hallo, ich habe ein Problem mit einer Subroutine in meinem Programm für eine Uhr Also: Ich wollte meine Uhr manuell stellen. --> Das funktioniert! Ich wollte, dass das Stellen der Uhr erst aktiviert wird, wenn ich zuerst mindestens 2mal Taster 2 und danach 2 Taster 1 gedrückt habe. --> Das funktioniert auch1 Zu letzterem wollte ich nun eine Erweiterung machen, die quasi dafür sorgt, dass die Abfolge "2mal Taste2 dann 2mal Taste1" innerhalb von 2Sekunden geschehen muss. Dazu habe ich die angefügte Subroutine geschrieben. Der Beginn der 2 Sekunden wird durch das erste Drücken von Taster 2 markiert. --> Bit 3 in "ind_flag2" wird gesetzt (habe ich überprüft wird auch tatsächlich gesetzt) Dann soll wenn Bit 2 von "ind_flag2" nicht gesetzt ist (wenn es gesetzt ist, ist die Uhr bereits im "Stellmodus") und Bit 1 in "ind_flag" gesetzt ist. (wird von einer ISR alle 10ms gesetzt und später von einer folgenden Subroutine zurückgesetzt) ein Zähler zu laufen beginnen, der nach 2 Sekunden: - Bit3 von "ind_flag2" zurücksetzt (damit der zähler nicht nochmal zählt) - 2 Zustandszähler zurücksetzt (für die Subroutine mit dem 2mal Taste2 dann 2mal taste1) - und die Zustandszähler dieser Subroutine zurücksetzt ("reg100_2sec_reset" wird mit 100 initialisiert und "reg2_2sec_reset" wird mit 2 initialisiert) Leider funktioniert das nicht? Ich kann leider keine Fehler in der Routine entdecken. Stimmt da was mit den Zustandszählern nicht?
Olli R. schrieb: > Ich wollte, dass das Stellen der Uhr erst aktiviert wird, wenn ich > zuerst mindestens 2mal Taster 2 > und danach 2 Taster 1 gedrückt habe. Das ist ja ne höllisch komplizierte Bedienung. Damit stellst Du sicher, daß kein anderer außer Dir das Gerät bedienen kann. Ich mach das so, daß man eine Taste lange drücken muß, um in den Stellmodus zu kommen. Das reicht völlig, um eine Fehlbedienung zu vermeiden. Außerdem kann man ja den Stellmodus verlassen, ohne eine Ziffer zu ändern. Ansonsten würde ich Dir raten, erstmal in Worten einen Programmablaufplan zu schreiben und nicht so drauflos zu programmieren. Du hast zwar jede Zeile kommentiert, aber ich kann nirgends einen roten Faden erkennen. Ich sehe da jedenfalls nicht durch. Peter
Olli R. schrieb: > Zu letzterem wollte ich nun eine Erweiterung machen, die quasi dafür > sorgt, dass die Abfolge "2mal Taste2 dann 2mal Taste1" innerhalb von > 2Sekunden geschehen muss. Also 120 Anschläge pro Sekunde? Dafür musst Du nicht unbedingt Sekretärin sein, aber doch ne ordentliche Tastatur haben. Lass ne LED die Wartezeit anzeigen und schau, ob Du die letzte Taste noch rechtzeitig drückst.
Peter Dannegger schrieb: > Ich mach das so, daß man eine Taste lange drücken muß, um in den > Stellmodus zu kommen. Das reicht völlig, um eine Fehlbedienung zu > vermeiden. Außerdem kann man ja den Stellmodus verlassen, ohne eine > Ziffer zu ändern. ich nutze ja im wesentlichen deine Routine zum Einlesen der Taster! Beitrag "Taster entprellen funktion "get8key" ?" deswegen ist es ja nicht so ohne weiteres mögliche in den Stellmodus zu wechseln, indem man nur eine Taste lange (z.B. 3s lang) gedrückt hält! Denn diese Routine gibt mir ja ein byte "key_press" und das zur Taste gehörige Bit wird in diesem Byte gesetzt, wenn die Taste "einmal" gedrückt wurde. Ein Halten der Taste kann ich also gar nicht so ohne weiteres Erkennen? oder doch?
Hi Klar kannst du erkennen, wie lange eine Taste edrückt ist! Wie ich sehe, programmierst du in asm. also versuch ich mal, dir auf die Sprünge zu helfen... Deklariere eine Variable In_Byte ( z.B.) und lese diese als erstes in deiner Programmschleife ein, getreu nach EVA. ("E"inlesen, "V"erarbeiten, "A"usgeben) Dann baust du dir eine Timer-ISR und in dieser prüfst du, ob dein Eingang "gesetzt" ist. Ist dies der FAll, zälst du eine Variable hoch. Wenn du das alle 100mSek. tust, hast du bei 20 die 2 Sek. Haltezeit. Ist das Signal "nicht gesetzt", setzt du den Zähler einfach auf 0
1 | Timer_ISR: |
2 | LDS Reg_A, msek_0 ; Auflösung Millisekunden |
3 | INC Reg_A |
4 | STS mSek_0, Reg_A ; zurückspeichern |
5 | CPI Reg_A, 10 |
6 | BRLO End_ISR ; prüfen ob kleiner 10 |
7 | ; ------- 10 mSek |
8 | CLR Reg_A ; wenn nicht , zurücksetzen |
9 | STS mSek_0, Reg_A ; und zurückspeichern |
10 | LDS Reg_A, msek_1 ; nun Millisekunden x 10 |
11 | INC Reg_A |
12 | STS mSek_1, Reg_A ; zurückspeichern |
13 | CPI Reg_A, 10 |
14 | BRLO End_ISR ; prüfen ob kleiner 10 |
15 | ; ------- 100 mSek |
16 | CLR Reg_A ; wenn nicht , zurücksetzen |
17 | STS mSek_1, Reg_A ; und zurückspeichern |
18 | |
19 | RCALL Press_Event_Time ; hier rufst du deine Routine auf, die |
20 | ; die Länge des Tastendrucks prüft |
21 | |
22 | LDS Reg_A, msek_2 ; nun Millisekunden x 100 |
23 | INC Reg_A |
24 | STS mSek_2, Reg_A ; zurückspeichern |
25 | CPI Reg_A, 10 |
26 | BRLO End_ISR |
27 | ; ------- Sekunden |
28 | etc....... |
29 | |
30 | |
31 | RETI |
32 | |
33 | Press_Event_Time : |
34 | Push Reg_A ; Register sichern, weil Aufruf aus ISR |
35 | LDS Reg_A, In_Byte ; Eingelesenen Wert laden |
36 | ANDI Reg_A, 0b00010000 ; Relevanten Eingang ausmaskieren |
37 | BREQ Time_Cnt_Res ; wenn nicht, Zeitzähler rücksetzen |
38 | LDS Reg_A, Time_Cnt_x ; zugehörigen Zeitzähler laden |
39 | Inc Reg_A |
40 | CPI Reg_A, 20 ; 2 Sek. erreicht |
41 | BRLO Noch_Warten ; |
42 | LDI Reg_A, 20 |
43 | STS Time_Cnt_x, Reg_A ; auf Wert 20 halten |
44 | LDS Reg_A, Prg_Ctrl ; Ein Controllbyte laden |
45 | ORI Reg_A, 0b00000001 ; Bearbeitungsbit setzen |
46 | ; Wird im Programm abgefragt und bearbeitet |
47 | STS Prg_Ctrl, Reg_A |
48 | RJMP End_Events |
49 | Noch_Warten: |
50 | STS Time_Cnt_x, Reg_A |
51 | End_Events: |
52 | POP Reg_A ; Register wieder herstellen |
53 | RET ; kein RETI, da nur Unterprogram |
Natürlich mußt du in der Timer-ISR auch noch die Register sichern, die du brauchst, auch das Statusregister, aber das findest du hier in den Tutorials über den Timer. Sicherlich ist dieses noch ausbaufähig, aber soll dir ja auch nur einen möglichen Weg zeigen. Gruß oldmax
Olli R. schrieb: > ich nutze ja im wesentlichen deine Routine zum Einlesen der Taster! http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten#Tastenentprellung.2C_Abfrage_und_Autorepeat Ich programmiere jetzt nur noch in C. Da kann man sich voll auf die eigentliche Aufgabe konzentrieren und muß sich nicht mehr Push/Pop, Parameterübergabe, Variablenverwaltung und Rechenroutinen abplagen. Peter
Hi @Peter Hast diesbezüglich recht, dafür aber auch woanders Probleme. Ich hab, was C betrifft, so gut wie gar keine Ahnung und Versuche, einzusteigen, sind am "holperigen" Syntax gescheitert. Ich werd in meinem Alter vermutlich auch nicht mehr so einfach einsteigen können. Was Push und POP angeht, na ja, solche Routinen schreibst du einmal, danach brauchts auch keine großartige Überlegung mehr, weil du ja das Rad nicht dauernd neu erfindest. Und so oft ruf ich aus einer ISR auch nicht grad Unterprogramme auf. Gruß oldmax
oldmax schrieb: > Dann baust du dir eine Timer-ISR und in dieser prüfst du, ob dein > Eingang "gesetzt" ist. Ist dies der FAll, zälst du eine Variable hoch. > Wenn du das alle 100mSek. tust, hast du bei 20 die 2 Sek. Haltezeit. Ist > das Signal "nicht gesetzt", setzt du den Zähler einfach auf 0 ja natürlich daran habe ich ja gar nicht mehr gedacht. Ich hab mich zu sehr einfach auf die bereits implementierte Taster einlesen-Routine berufen, dass ich ich gar nicht daran gedacht habe, den "Port" der Tasten nochmal einzulesen (sollte ja sogar weniger aufwendig sein als eine Routine, die die Taster entprellt!) Ich werd mal schauen (natürlich mit vorherigem, genauem Überlegen) wie ich das hinkriege. Peter Dannegger schrieb: > Ich programmiere jetzt nur noch in C. OK an C werde ich ohnehin nicht ganz vorbei kommen. Das werde ich dann mal bei einem neuen Projekt in Angriff nehmen. Zur Übersichtlichkeit: Also zu Anfang habe ich doch recht strukturiert gearbeitet. Dann kamen allerdings immer mehr Erweiterungen hinz zu, die ich "mal eben" implementieren wollte. Ich hätte nicht gedacht, wie schnell einem dann das Ganze fast über den Kopf wachsen kann! Aber ich denke, nachdem ich den aktuellen Schritt (aktivieren des Stellen nach 3s Taster 1 drücken) umgesetzt habe, werde ich erst mal Ordnung schaffen und die schlimmsten (Unübersichtlichkeiten) ausmerzen. Fürs nächste Projekt weiss ich jetzt wenigstens, das auch hinter vermeintlich geringfügingen Erweiterungen (z.B. bezüglich Einlesen und Verarbeitung von Tastern) ne Menge hintersteht und solche Dinge gleich in einem Ablaufplan aufnehmen.
Hi >ja natürlich daran habe ich ja gar nicht mehr gedacht. >Ich hab mich zu sehr einfach auf die bereits implementierte Taster >einlesen-Routine berufen, dass ich ich gar nicht daran gedacht habe, den >"Port" der Tasten nochmal einzulesen (sollte ja sogar weniger aufwendig >sein als eine Routine, die die Taster entprellt!) So hatte ich es eigentlich nicht gemeint.... Also, du hast eine Programmschleife, die nach "EVA" funktionieren sollte Einlesen Verarbeiten Ausgeben Am Anfang deiner Schleife liest du die Daten ein. Dazu baust du dir eine Routine Read_IO und schreibst die gelesenen Werte in ein Byte Bspw. "New_In". Hier kannst du deine IO-Pins nun im gesamten Programm abfragen, denn genau für einen Zyklus sind sie gültig. Liest du deine IO's mehrfach ein, so mußt du bei einer Änderung immer im gesamten Programm nach den IO Aufrufen suchen. Mit diesem kleinen Unterprogramm weißt du aber, wo alles steht und du kannst gezielt deine Ändeungen vornehmen. Wenn du mal ein wenig suchst, findest du "OpenEye". Ich hab das mal vor einiger Zeit hier im Forum veröffentlicht. Dieses Programm liest über RS 2323 die Variablen aus und gibt deren Inhalte wieder. Ist sehr hilfreich, wenn man nicht anders debuggen kann. An meinem Pollin-Board ist auf ISP der Progger und der serielle Anschluß auf dem Com-Port. So kann ich sehr schnell Fehler in meinem Programmablauf erkennen. Also: nicht im Programm verteilt einlesen, sondern in einer Speicherzelle den gelesenen Wert für einen Zyklus halten. So kannst du dir die Bits auch zusammenstellen und dokumentieren zB. New_In: .Byte 1 ; Bit 0 = PortD.4 "Taster 1" ; Bit 1 = PortD.5 "Taster 2" ; Bit 2 = PortC.0 "Taster 3" usw. Gruß oldmax
Olli R. schrieb: > Ich hab mich zu sehr einfach auf die bereits implementierte Taster > einlesen-Routine berufen, dass ich ich gar nicht daran gedacht habe, den > "Port" der Tasten nochmal einzulesen (sollte ja sogar weniger aufwendig > sein als eine Routine, die die Taster entprellt!) Nicht den Port. Nimm key_state, daß ist schon entprellt. Olli R. schrieb: > Also zu Anfang habe ich doch recht strukturiert gearbeitet. Dann > kamen allerdings immer mehr Erweiterungen hinz zu, die ich "mal eben" > implementieren wollte. Das wichtigste ist, modular zu programmieren. Wenn Du etwas hinzufügen willst, verwebe es nicht mit dem bestehenden Code, sondern erstelle eine Unterfunktionen. Der AVR ist ja schließlich kein PIC, wo der Call-Stack auf 2 Ebenen limitiert ist. Für die Verwendung von Unterfunktionen mußt Du Dir aber erstmal ein paar Regeln festlegen. Also in welchen Registern werden Argumente übergeben, welche Register enthalten Returnwerte und welche Register sind Scratchpad (kann die Funktion zerstören ohne umständlich push/pop). Es ist sinnvoll, sich 4..8 Sctratchpadregister zu definieren, Du wirst staunen, wieviel push/pop man dadurch einspart. Denn oftmals übergibt man Parameter an eine Funktion, die dann hinterher garnicht mehr gebraucht werden. Wann immer möglich sollte man mit lokalen Variablen (Register) arbeiten, globale Variablen so wenig wie möglich. Es hilft Dir, die Übersicht zu behalten, Du weißt, daß lokale Variablen nach Funktionsende ungültig sind, es kann also keine Seiteneffekte mit anderen Programmteilen geben. Ein C-Compiler übernimmt das Erstellen solcher Regeln automatisch für Dich. Peter
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.