Taster mit Autorepeat
Erstellt: DL6GL, 20.01.2014, letzte Änderung 15.12.2015
« Taster kurz/lang | TOP | |
Taster mit Autorepeat (1)
Aufbauend auf der Timer Interruptroutine mit der Dannegger-Methode lässt sich auf einfache Weise auch ein Autorepeat für einzelne Taster verwirklichen, z.B. um die Zeit in einer Uhr hochlaufen zu lassen. Hierzu ist das Status-Byte "bytKey_state" im vorhergehenden Beispiel genau das Richtige. Die den einzelnen Tastern zugeordneten Bits in diesem Byte sind so lange auf 1 gesetzt, wie der zugehörige Taster gedrückt gehalten wird. So könnte das Stellen einer Uhrzeit aussehen:
Dim bytSet As Byte 'Set flag
Dim bytSec As Byte 'Seconds
Dim bytMin As Byte 'Minutes
Dim bytHou As Byte 'Hours
Do
Call ShowTime(bytHou , bytMin , 1)
If bytSet > 0 Then 'Set time
Call SetTime
Call ShowTime(bytHou , bytMin , 1)
End If
'Do something else
Loop
In der Routine "SetTime" wird die Uhrzeit gesetzt. Dabei wurden zwei Möglichkeiten vorgesehen: Uhrzeit mit Autorepeat recht flott hochlaufen lassen und mit einzelnen Tasterbetätigungen runterzählen lassen. Der Hochlauf könnte eventuell über das Ziel hinausgeschossen sein. Es werden nur Stunden und Minuten gesetzt. Danach werden die Sekunden auf 0 gesetzt.
Sub SetTime()
'Set time (hours and minutes)
If bytKey_state.Key_up = 1 Then 'Key_up was pressed
Incr bytMin
If bytMin > 59 Then
bytMin = 0
Incr bytHou
If bytHou > 23 Then
bytHou = 0
End If
End If
bytSec = 0
bytKey_press.Key_up = 0 'Reset Key_up flag
Waitms 150
End If
If bytKey_press.Key_dwn = 1 Then 'Key_dwn was pressed
'If bytKey_state.Key_dwn = 1 Then 'Key_dwn was pressed
Decr bytMin
If bytMin = 0 Then
bytMin = 59
If bytHou > 0 Then
Decr bytHou
Else
bytHou = 23
End If
End If
bytSec = 0
bytKey_press.Key_dwn = 0 'Reset Key_dwn flag
'Waitms 250
End If
End Sub
Beim Hochlaufen wird " bytKey_state.Key_up" verwendet. Dieses Bit ist 1, solange der Up-Taster gedrückt ist. So oft wird über den Aufruf in der Lo...Loop die Uhrzeit um eine Minute erhöht. Die Geschwindigkeit des Durchlaufens bestimmt das " Waitms 150". Das Herunterzählen erfolgt mit einzelnen Tastenbetätigungen, wahlweise auch mit einem (auskommentierten) etwas langsameren Autorepeat. Das "Waitms" sollte für einen gemächlicheren Durchlauf nicht zu hoch gesetzt werden. Sonst erfolgt die Reaktion auf einen kurzen Tastendruck mit einer irritierenden Verzögerung.
Der Vollständigkeit halber noch die Anzeigeroutine:
Sub ShowTime(byVal bytH As Byte , byVal bytM As Byte , byVal bytRow As Byte)
'Show Time (hh:mm) on LCD row bytRow
'Input: bytH Hour
' bytM Minute
' bytRow LCD row
Locate bytRow , 1
strTim = str(bytH)
strTim = Format(strTim , "00")
For bytTmp0 = 1 To 2
bytDis(byttmp0) = bytTim(bytTmp0) 'Hour
Next bytTmp0
bytTmp1 = 3 'Toggle ":"
If bitColon = 1 Then
bytDis(byttmp1) = ":"
Else
bytDis(byttmp1) = " "
End If
strTim = str(bytM)
strTim = Format(strTim , "00")
For bytTmp0 = 1 To 2
Incr bytTmp1
bytDis(byttmp1) = bytTim(bytTmp0) 'Minute
Next bytTmp0
LCD strDis 'Show hh:mm
End Sub
Zugehörige Deklarationen mit zwei Byte-Overlays:
Dim bytTmp0 As Byte
Dim bytTmp1 As Byte
Dim strTim As String * 2 '2 characters for time
Dim bytTim(2) As Byte At strTim Overlay 'the same as byte array
Dim strDis As String * 16 'Text to be dispayed on LCD
Dim bytDis(16) As Byte At strDis Overlay 'the same as byte array
Die Uhrzeit wird als "hh:mm" angezeigt. Als kleiner Gimmik ist ein Blinken des ":" vorgesehen, um anzuzeigen, dass die Uhr noch läuft. Hierzu eine kleine Erweiterung der Timer0_isr aus dem vorhergehenden Beispiel "Taster2_104.bas":
Dim bitColon As Bit 'Time colon on/off
Dim bytTimer0_sec As Byte 'Counter for time colon":"
Timer0_isr:
'Interrupt Service Routine for Timer0 (Keys)
'...
'...See "Taster2_104.bas
'...
bytIwr0 = bytKey_state AND bytKey_mask 'Mask out short/long keys
If bytIwr0 > 0 Then 'Short/long key still pressed
Incr bytTimer0_runs 'Clock is running...
End If
If bytTimer0_sec < 80 Then 'Bit-Toggle for time colon
Incr bytTimer0_sec
Else
Toggle bitColon
bytTimer0_sec = 0
End If
Return
Die Blinkfrequenz des ":" in der Uhrzeitanzeige wird mit " If bytTimer0_sec < 80 Then" eingestellt, hier für das Timing aus dem Beispiel "Taster2_104.bas" etwa im Sekundentakt.
Taster mit Autorepeat (2)
Taster als Simpelbauteile einer Mensch-Maschine-Kommunikation geben fast schon Stoff für eine never ending story her. In einem anderen Projekt wurde neben der Erkennung kurzer und langer Tasten-betätigungen ein Autorepeat zur Auswahl eines Wertes aus einer längeren Datenreihe erforderlich. Hierzu sollte eine dem "KeyPressedLong" ähnliche Funktion mit unveränderter Timer0-Interrupt Service Routine wie im Beispiel "Kurz/Lang-Taster mit Timer-Interrupt (2)" entstehen. Die Timer0_isr und die zugehörigen Variablen sind hier also identisch mit dem o.a. Beispiel. Die Taster sind am PortD angeschlossen. Jeder Taster ist mit einem 0,1µF-Kondensator überbrückt. Die zugehörige Konfiguration sieht dann so aus:
'Configure port D ------------------------------------------------------------
DdrD = &B00000000 'PortD = input
PortD = &B11111111 'Pullup
Key_port Alias PinD 'Input-Port Keys
Const Key_Test = 0 'Key test @ PIND.0
'Keys 1...4 are not used in this example
Const Key_Up = 1 'Key Up @ PIND.1
Const Key_OK = 2 'Key OK @ PIND.2
Const Key_Down = 3 'Key Down @ PIND.3
Const Key_ESC = 4 'Key ESC @ PIND.4
Dim bitKey_Up As Bit '=1 if key up is pressed
Dim bytKey_Test As Byte '=1 if key Test is pressed
Dim bitKey_Down As Bit '=1 if key down is pressed
Dim bytKey_OK As Byte 'Key OK pressed: 1(short), 2(long)
Dim bytKey_ESC As Byte 'Key ESC pressed: 1(short), 2(long)
Dim bytKey_mask As Byte 'Mask for short/long keys
bytKey_Mask = &B00010101 'Set 1 to enable short/long
' Keys #2 & 4 = ESC & OK
' Key #0 = test auto repeat
Der für den Autorepeat verwendete Taster ist der "Key_Test" an Port D.0. In der Bitmaske "bytKey_Mask" zur Erkennung von langen Tastenbetätigungen ist neben den hier nicht betrachteten Kurz-/Langtastern "OK" und "ESC" an den Ports D.2 und D.4 zusätzlich das Bit 0 für den Port D.0 auf 1 gesetzt (blau gekennzeichnet):
bytKey_Mask = &B00010101
Damit wird auch eine lange Tastenbetätigung am Port D.0 in der Timer0_isr erkannt, so dass der Zähler wrdTimer0_runs hochzählt, was in der Function KeyAutoRepeat ausgewertet wird.
Function KeyAutoRepeat(byVal bytKey_Rep As Byte) As Byte
'Check if key bytKey_Rep (0...7) in Key_port is pressed long for auto repeat
'Key status bytKey_state.bytKey_Rep is returned from Timer0_isr
'Input: bytKey_state.bytKey_Rep =0: Key #bytKey_Rep is not pressed
' =1: Key #bytKey_Rep is still pressed
' wrdTimer0_runs Counter returned from Timer0_isr
' Incremented per ~ 10 msec
' For different Timer0 timing check conditions
' "wrdTimer0_runs >/< 20"
'Output: KeyAutoRepeat =0: key is not pressed
' =1: key is pressed short, no auto repeat
' =2: key is pressed long, auto repeat
KeyAutoRepeat = 0
If bytKey_state.bytKey_Rep = 1 Then 'Key still pressed
If wrdTimer0_runs > 20 Then
KeyAutoRepeat = 2 'Pressed long, auto repeat
wrdTimer0_runs = 0 'Reset Timer0 counter
Waitms 100 'Step speed
End If
Else 'Key released
If wrdTimer0_runs < 20 Then '~ 250 msec
KeyAutoRepeat = 1 'Pressed short, no auto repeat
wrdTimer0_runs = 0 'Reset Timer0 counter
bytKey_press.bytKey_Rep = 0 'Reset pressed flag
End If
End If
End Function
Ausgewertet wird das Bit bytKey_state.bytKey_Rep. Dieses ist 1, so lange der zugehörige Taster gedrückt bleibt. Der Zähler wrdTimer0_runs wird erst ab einem bestimmten Stand, hier 20, betrachtet. Liegt dieser unterhalb von 20 Timer0-Durchläufen nach Tasterbetätigung, bei dem gewählten Interrupt-Timing nach ca. 250 msec, war es ein kurzer Tastendruck. Darüber war es ein langer, der nach 250 msec den Autorepeat anlaufen lässt. Mit dem Waitms 100 kann die Schrittgeschwindigkeit eingestellt werden.
Die Auswertung erfolgt wie gehabt in der Do…Loop:
Do
'Key processing
If bytKey_press > 0 Then 'Any key was pressed
'Check auto repeat key_Test
If bytKey_press.Key_Test = 1 Then 'Key Test was pressed
bytkey_Test = KeyAutoRepeat(key_Test) 'Pressed short or auto repeat
Else
bytKey_state.key_Test = 0 'Reset Key Test status flag
End If
Else 'No key was pressed
bytKey_Test = 0 'Reset key Test flag
End If
'Show key_press and key_state flags
Locate 1 , 1
LCD "Press " ; bytKey_press.bytKey_Test
Locate 1 , 10
LCD "State " ; bytKey_state.bytKey_Test
strDis = str(wrdTimer0_runs)
strDis = Format(strDis , " 0")
Locate 2 , 11
LCD strDis
If bytKey_Test > 0 Then 'Key Test is pressed
Incr bytTmp0
strDis = str(bytTmp0)
strDis = Format(strDis , " 0")
Locate 2 , 1
LCD strDis
End If
Loop
Die Überprüfung des Tasterzustandes erfolgt nur, wenn irgendein Taster am Port D gedrückt wurde. Zur Verdeutlichung wird nachfolgend nur die Auswertung des "Key-Test" gezeigt. Im unteren Teil der Do…Loop wird das Hochzählen der Variablen bytTmp0 angezeigt, entweder in Einzelschritten bei kurzem Tastendruck oder ununterbrochen während eines langen Tastendrucks.
Wird daneben auch die einfache Kurz-/Langerkennung gebraucht, ist die aus dem o.g. Beispiel ergänzend einsetzbar, etwa so:
Do
'Key processing
If bytKey_press > 0 Then 'Any key was pressed
'Check short/long key OK
If bytKey_press.Key_OK = 1 Then 'Key OK was pressed
bytKey_OK = KeyPressedLong(Key_OK) 'Pressed short or long
Else
bytKey_OK = 0 'Key OK not pressed
bytKey_press.Key_OK = 0 'Reset Key OK pressed flag
End If
'Check auto repeat key_Test
If bytKey_press.Key_Test = 1 Then 'Key Test was pressed
bytkey_Test = KeyAutoRepeat(key_Test) 'Pressed short or auto repeat
Else
bytKey_state.key_Test = 0 'Reset Key Test status flag
End If
Else 'No key was pressed
bytKey_OK = 0 'Reset all key flags
bytKey_Test = 0
End If
'Do something else handling key events
Loop
Man beachte das unterschiedliche Zurücksetzen der press/state-Flags:
Bei der einfachen Kurz/Lang-Erkennung des OK-Tasters
wird das Flag-Bit bytKey_press.Key_OK zurückgesetzt,
bei der Kurz/Autorepeat-Erkennung des Test-Tasters das Flag-Bit bytKey_state.key_Test. Das ..._press-Bit wurde bereits in der Function KeyAutoRepeat nach einem kurzen Tastendruck zurückgesetzt.
Konkrete Anwendung in True RMS-Milivoltmeter.
« Taster kurz/lang | TOP | |