Hi
Ich gestehe und oute mich. Ich bin ein Freund von Interrupts.
Das du gern mit Interrupt arbeitest, ist ja nicht das Problem. Aber man muss da schon unterscheiden, was ist ein Interrupt, was ist eine Programmschleife. Und da hat jeder Programmierer eigene Ansichten und ist (meistens) auch von seinem Stil überzeugt. Deswegen betrachte meine Ausführung nicht als Vorgabe, sondern bestenfalls als Denkanstoß.
Ich halte mich an EVA. Eva ist schon für vieles mißbraucht worden, aber mir gab sie immer eine Richtung. Ich meine Einlesen-Verarbeiten-Ausgeben. Grad bei Assembler ist schnell ein unübersichtliches Programmkonstrukt erstellt. Ich mach es mir da etwas einfacher. Und da du von Sekunden sprichst, ist die Vera<rbeitung in einer ISR meiner Meinung nach völlig unnütz.
Mein Weg wäre :
Eine Handvoll Variablen für Zeitflags und Jobflags sowie Zähler und was sonst noch wichtig ist.
Die Programmschleife rotiert immer, warum auch nicht, und beginnt, Eingänge zu lesen und in einer Variablen abzulegen. Damit bleiben diese an einer Stelle und für den Zyklus gültig. Genauso wird die Ausgabe in einer Variablen abgelegt und erst am Ende der Schleife zugewiesen. Dazwischen erfolgt nun die verarbeitung, indem für die verschiedenen Jobs Bits abgefragt werden.(Job- und Zeitflags)
Wenn du so ein Listing aufstellst, dann gibt es auch Stimmen dagegen: "Wozu dieses Bitgeraffel". Nun, bisher hab ich meine Programme auch nach einem halben Jahr noch pflegen können.....
.... und ich bin schon leicht verkalkt...
Nun zu den Zeiten: Die ISR setzt nur das Zeitflag und gut.In der Regel wird bis zur Zehntelsekunde bei mir in der ISR verschiedene Zeitflags gesetzt, die dann in der Programmschleife ausgewertet werden. z.B. das Flag für eine mSek. Da darf dann natürlich nicht allzuviel Code hinterstehen, aber es gibt durchaus auch dafür Verwendung.
Mit dem Flag der Zehntelsekunde leite ich dann auch schon mal umfangreiche Jobs ein. So auch das Setzen eines Flags für eine halbe Sekunde.
Beispiel
Code:
Chk_Zehntel:
LDS Reg_A. Time_Flag
ANDI Reg_A, 0b00000001 ; Flag für Zehntelsekunde
BREQ End_Zehntel
LDS Reg_A, Time_Cnt_05
Dec Reg_A
STS Time_cnt_05
BRNE weiter
LDI Reg_A, 5
STS Time_Cnt_05
LDS Reg_A,Time_Flag
ORI Reg_A,0b00000010 ; Zeitflag halbe Sekunde
Weiter:
ANDI Reg_A, 0b11111110 ; Zeitflag quittieren
STS Time_Flag, Reg_A
RCALL weitere_ZeitJobs_01 ; weitere Jobs in diesem Zeitraster
End_Zehntel:
RET
Eine solche Routine wird in der Programmschleife aufgerufen. Klar, wenn es wirklich Zeitkritisch sein sollte, dann kann man das auch etwas intelligenter lösen. Aber i.d.R. sind diese paar µSek. kein Problem.
nun zum Thema Hoch bzw. runterzählen. Wenn du eine Zeit von 23 Sekunden wünscht, dann setzt du hier einen Wert von 230 ( oder du leitest ein Zeitflag von 1 Sekunde ab und setzt dann auf 23 ) Dann das Beispiel:
Code:
Weitere Zeitjobs_01:
LDS Reg_A, My_Zeit_Cnt ; Zeitwert bekommt deinen gewünschten Wert
CMPI Reg_A, 0 ; wenn schon abgelaufen, dann keine weitere Verwendung
; oder wieder von vorn? oder neuer und anderer Wert ?
BREQ End_Job_01
DEC REG_A ; sonst runterzählen
STS My_Zeit_Cnt, Reg_A ; und abspeichern
BRNE End_Job_01 ; wenn nun nicht 0 dann Routine verlassen
;------------------------------------------------------------------------------
; hier die Aufgabe nach der gewünschten Zeit einbauen
;------------------------------------------------------------------------------
End_Job_01:
RET
Auf diese Weise erhälst du kleine überschaubare Subroutinen. Wenn du zum Beispiel einen Ausgang für eine bestimmte Zeit setzen willst, dann setzt du den Zeitwert und in der Variablen für die Ausgänge ein Bit. Ist die Zeit abgelaufen, löscht du das Bit.
Da im Programm eine Variable für die Ausgänge wie auch für die Eingänge verwendet wird, kannst du erst einmal völlig ohne Hardware arbeiten. Am Ende der Schleife setzt du dann die Ausgänge. Eine einzige Stelle, die den Aufruf "RCALL Write_IO" hält.
Somit läuft deine Programmschleife wie folgt:
Code:
MainLoop:
;------------- E --------------
RCALL Read_IO ; Eingänge lesen
RCALL Debounce ; entprellen
RCALL Event_Flags ; Flanken gültiger Statusänderung der Eingänge
;------------ V -------------
RCALL Chk_Event_Flag ; Ereignisauswertung der Eingänge
RCALL CHK_Time_Flags ; Auswertung von Zeitereignissen in Time_Flag
RCALL CHK_Job_Flags ; Bearbeiten von Jobs in Job_Flag
;------------ A ________
RCALL Write_IO ; Ausgänge beschreiben
RJMP MainLoop
ISR_TimeX:
;----------------------------------------------
; setzt die Zeitflags in Time_Flag
;----------------------------------------------
RETI
Vielleicht hilft dir das weiter
Gruß oldmax