Eine hohe Anforderung an ein User-Interface ist jene, dass eine möglichst regelmäßige Aktualisierung der Anzeigen stattfinden soll. In vielen Situation ändern sich quasi zugleich 2 Zeilen der LCD-Anzeige ständig, was nach einer hohen ,,Refresh-Rate'' verlangt.
Ein zu häufiges Ansteuern der Anzeige, die sehr oft mit Display- und Businitialisierungen verbunden ist, kann allerdings dazu führen, dass auf Grund der ,,Trägheit'' des LCD-Displays die Anzeige noch nicht fertig aufgebaut ist, wenn der neue, eventuell geänderte Wert bereits wieder ausgegeben wird. Dies führt zu einem ,,Verblassen'' der LCD-Anzeige bzw. zu einem unruhigen ,,Flackern'' das die Lesbarkeit deutlich vermindert.
Um dies zu verhindern, wurde eine einfache Zeitsteuerung über den 8-Bit Timer/Counter0 realisiert (Scheduling [7] S. 188). Dieser niedrigst-priorisierte Timer-Interrupt wird dazu folgendermassen ,,programmiert'':
; ... from inc/constants.inc
; 4000000/256=15625 ~ 16kHz/TCNT0
.equ Timer0Div = 0b00000100 ; cpuClk/256
.equ Timer0Val = -128 ; 15625/128~120Hz
; ...
; level0_value = 4000000/256/128 ~ 8.2msec
; -----------------------------
.equ schedLevel1Val = 0x10 ; 16*level0_value ~ 7.5Hz
.equ schedLevel2Val = 0x80 ; 128*level0_value ~ 0.9Hz
; ... from lib/systimer.lib
; Timer0 clock(io)/64
OUTB TCCR0, Timer0Div ; prescaler value
; Timer0 oreset value (-128)
OUTB TCNT0, Timer0Val ; set new init value
Der daraus resultierende Grundtakt (z. Zt. ca. 120 Hz) wird nun in
drei Schedulerringen mit unterschiedlichen Teilern verwendet, damit
ist es möglich, drei unabhängige Zeit-Zyklen (Ringe) zu verwalten.
Die konkrete Implementierung verwendet dazu die Timer/Counter0 Overflow Interrupt Service Routine, die drei Schalter (Flags) verwendet, die im jeweiligen Ring nach Erreichen eines vordefinierten Timerwertes auf ,,aktiv'' gesetzt werden.
Jede Service-Routine, die einen dieser Ringe verwendet, muss das Flag auswerten und die Abarbeitung abbrechen wenn der Wert ,,inaktiv'' ist. Falls das Flag aber ,,aktiv'' ist, wird diese Serviceroutine ausgeführt und am Ende der Routine wird das Flag von der Service-Routine wieder auf ,,inaktiv'' gesetzt, danach beginnt der Zyklus wieder von vorne.
Anmerkung:Abb. 7.13 zeigt ein konkretes Beispiel, das die Steuerung der Anzeigenhäufigkeit (auto_update_display) im automatischen Positioniermodus (siehe 7.8.1) kontrolliert. Dieses Service wird mit mittlerer Priorität ausgeführt.
Im Gegensatz zum herkömmlichen Scheduling Begriff, der den einzelnen Services eine garantierte Ausführungszeit zusichern soll, zielt diese Methode darauf ab, für ein Service eine kürzestmögliche Wiederholzeit festzulegen, unabhängig davon, wie oft der Programmteil aufgerufen wird.
; ---------------------------------
; scheduler controlled display update (can be called always)
; ---------------------------------
auto_update_display: ; 3 rcall overhead
lds sched_temp, level1_waiting ; 2 memory access
tst sched_temp ; 1 test function
brne auto_update ; 1 (2 if true) branch
ret ; 4 - sum=11 (2.75 usecs)
; ...
; do display update ...
auto_update:
PUSHW REG_Z
push ltmp2
push temp1
rcall powr_funcmode_1 ; update power value display
rcall angl_funcmode_2 ; update angle value display
pop temp1
pop ltmp2
POPW REG_Z
; ---------------------------------
; reset scheduler values for a new timer cycle
clr sched_temp
sts level1_waiting, sched_temp ; reset scheduler flag
ret
In diesem Projekt wird Ring 1 zur Steuerung der Anzeigenhäufigkeit
im automatischen Positioniermodus (siehe 7.8.1)
und Ring 2 zur Aktualisierung der Temperaturanzeige im ,,Idle''
Modus (in der Datei lib/mainmenu.lib) verwendet, Ring 0 wird
in der aktuellen Implementierung nicht benutzt.
Alle Timer-Initialiserungen und Timer-Services sind in lib/systimer.lib definiert.
gerhard.reithofer@tech-edv.co.at