Forum: Mikrocontroller und Digitale Elektronik Taster Abrage mit gedrückt halten


von Hunter (Gast)


Lesenswert?

Hallo,
ich wollte einen Zähler programmieren,
der bei gedrückt gehaltenem Taster weiterhochzählt,
jedoch funktioniert das Gedrückthalten nicht richtig.
Den Code habe ich im Laufe des AVR ASM Taster Tutorial
geschrieben.

Vielleicht findet ihr ja meinen Denkfehler:
1
.def  iwr0        = R1
2
.def  iwr1        = R2
3
4
.def  key_state      = R4
5
.def  key_press      = R5
6
.def  key_rep_press    = R6
7
.def  key_rep        = R16
8
9
.def  temp1        = R17
10
.def  temp2        = R18
11
.def  temp3        = R19
12
.def  counter        = R20
13
14
.equ  KEY_PORT      = PORTB
15
.equ  KEY_DDR        = DDRB
16
.equ  KEY_PIN        = PINB
17
.equ  LED          = 1
18
.equ  BTN1        = 0
19
.equ  BTN2        = 1
20
.equ  KEY_REPEAT_START  = 40
21
.equ  KEY_REPEAT_NEXT    = 15
22
23
.equ  PRESCL_1024      = 1<<CS00 | 1<<CS02
24
25
.equ  XTAL        = 8000000
26
27
.org 0x0000
28
    rjmp  init
29
30
.org OVF0addr
31
    rjmp  timer_overflow0
32
33
timer_overflow0:
34
    push  R0
35
    in    R0, SREG
36
    push  R0
37
38
    push  R16
39
    ldi    R16, -( XTAL / 1024 * 10 / 1000 + 1 )
40
    out    TCNT0, R16
41
    pop    R16
42
43
get8key:
44
    in    R0, KEY_PIN
45
    com    R0
46
    eor    R0, key_state
47
    and    iwr0, R0
48
    com    iwr0
49
    and    iwr1, R0
50
    eor    iwr1, iwr0
51
    and    R0, iwr0
52
    and    R0, iwr1
53
    eor    key_state, R0
54
    and    R0, key_state
55
    or    key_press, R0
56
    tst    key_state
57
    breq  get8key_rep
58
    dec    key_rep
59
    brne  get8key_finish
60
    mov    key_rep_press, key_state
61
    ldi    key_rep, KEY_REPEAT_NEXT
62
    rjmp  get8key_finish
63
64
get8key_rep:
65
    ldi    key_rep, KEY_REPEAT_START
66
67
get8key_finish:
68
    pop    R0
69
    out    SREG, R0
70
    pop    R0
71
    reti
72
  
73
.include "lcd-routines.asm"
74
75
init:
76
    ldi    temp1, LOW(RAMEND)
77
    out    SPL, temp1
78
    ldi    temp1, HIGH(RAMEND)
79
    out    SPH, temp1
80
81
    ldi    temp1, 1<<BTN1 | 1<<BTN2
82
    out    KEY_PORT, temp1
83
84
    ldi    temp1, PRESCL_1024
85
    out    TCCR0B, temp1
86
    ldi    temp1, 1<<TOIE0
87
    sts    TIMSK0, temp1
88
89
    rcall  lcd_init
90
    rcall  lcd_clear
91
92
    clr    key_state
93
    clr    key_press
94
    clr    key_rep_press
95
    clr    key_rep
96
97
main:
98
    ldi    counter, 0
99
    rjmp  update_lcd
100
101
loop:  
102
    ; normaler Tastendruck
103
    cli
104
    mov    temp1, key_press
105
    clr    key_press
106
    sei
107
    cpi    temp1, BTN1+1
108
    breq  increment
109
    cpi    temp1, BTN2+1
110
    breq  decrement
111
112
    ; Tastendruck gedrückthalten
113
    cli    
114
    mov    temp1, key_rep_press
115
    clr    key_rep_press
116
    sei
117
    cpi    temp1, BTN1+1
118
    breq  increment
119
    cpi    temp1, BTN2+1
120
    breq  decrement
121
122
    rjmp  loop
123
124
increment:
125
    inc    counter
126
    rjmp  update_lcd
127
128
decrement:
129
    dec    counter
130
131
update_lcd:
132
    mov    temp1, counter
133
    rcall  lcd_clear
134
    rcall  lcd_number
135
    rjmp  loop
136
137
; Left Scroll Display
138
lcd_lscroll:
139
    push  temp1
140
    ldi    temp1, 0b00011000
141
    rcall  lcd_command
142
    rcall  delay5ms
143
    pop    temp1
144
    ret
145
146
; Right Scroll Display
147
lcd_rscroll:
148
    push  temp1
149
    ldi    temp1, 0b00011100
150
    rcall  lcd_command
151
    rcall  delay5ms
152
    pop    temp1
153
    ret
154
; Move Cursor Left
155
lcd_lmove_cursor:
156
    push  temp1
157
    ldi    temp1, 0b00010000
158
    rcall  lcd_command
159
    rcall  delay5ms
160
    pop    temp1
161
    ret
162
; Move Cursor Right
163
lcd_rmove_cursor:
164
    push  temp1
165
    ldi    temp1, 0b00010100
166
    rcall  lcd_command
167
    rcall  delay5ms
168
    pop    temp1
169
    ret
170
171
text:  .db    "Start Counter", 0

von Klaus R. (klara)


Lesenswert?

Gibt es denn keinen Debugger?
mfg Klaus

von Hunter (Gast)


Lesenswert?

Nein, leider habe ich keinen Debugger parat.

von Stefan F. (Gast)


Lesenswert?

Das gute(?) alte AVR Studio 4.19 enthält sowohl einen Simulator als auch 
einen Debugger, der für diese Aufgabe sicher gut geeignet ist.

von Hunter (Gast)


Lesenswert?

Ich habe jetzt mit Atmel Studio 7 mein Programm gedebuggt,
aber da hat alles so funktioniert wie es soll.
An der Hardware kann es aber eigentlich auch nicht liegen,
weil der normale Tastendruck auch funktioniert,
nur der gehaltene Tastendruck funktioniert nicht.

von expliziter Klammersetzer (Gast)


Lesenswert?

Hunter schrieb:
> ldi    R16, -( XTAL / 1024 * 10 / 1000 + 1 )

Setze mal explizit Klammern um die Rechenoperationen. Dann versteht der 
Assembler, wie du den Timer oldschool vorladen möchtest.

von Hunter (Gast)


Lesenswert?

In etwa so?
1
ldi    R16, -( (((XTAL / 1024) * 10) / 1000) + 1 )

Leider erkennt das Programm immer noch nicht den langen Tastendruck.

von M.K. B. (mkbit)


Lesenswert?

Ich habe mal drübergeschaut ohne das Programm genau zu verstehen. 
Außerdem habe ich schon lange keinen Assembler mehr programmiert.

Könnte es sein, dass get8key_rep immer aufgerufen wird und damit der 
Zähler nie ablaufen kann.

von Hunter (Gast)


Lesenswert?

Der get8key_rep Abschnitt wird doch nur aufgerufen,
wenn die Zero-Flag durch tst gesetzt wurde im
Status Register, also wenn keine Taste gedrückt wird.

Ich denke das geht schon so, da auch der Debugger im Simulator
aus der Schleife ausbrechen konnte.

von Mario M. (thelonging)


Lesenswert?

In key_state sind nicht genutzte Pins ständig auf 1 gesetzt. Dadurch ist 
Repeat ständig aktiv und Du bekommst in key_rep_press nie die erwarteten 
Werte. Also musst Du vor "tst key_state" alle nicht genutzten Bits 
maskieren und darfst später auch nur die genutzten Bits in key_rep_press 
übernehmen.

von Hunter (Gast)


Lesenswert?

Aber die Pins an dem der Taster nicht gedrückt wird sind doch im Zustand 
1
und diese werden doch dann durch den Befehl com zu 0?

von S. Landolt (Gast)


Lesenswert?

Um welchen Controller geht es eigentlich?

von Mario M. (thelonging)


Lesenswert?

Die nicht genutzten Pins sind Ausgänge, stehen auf 0 und werden damit zu 
Einsen. Wenn Du die Pins nicht anderweitig brauchst, kannst Du als bösen 
Hack den KEY_PORT mit 0xff initialisieren.

von Hunter (Gast)


Lesenswert?

Ich verwende den Atmega328.

von Hunter (Gast)


Lesenswert?

Mario M. schrieb:
> Die nicht genutzten Pins sind Ausgänge, stehen auf 0 und werden damit zu
> Einsen.

Aber sind die nicht genutzten Pins nicht auf Eingang geschalten?

von Mario M. (thelonging)


Lesenswert?

Stimmt, hab Quatsch geschrieben. Die Pins sind Eingänge, es fehlt aber 
der Pullup. Egal wie, maskiere key_state entsprechend in der 
Interruptroutine, dann funktioniert es.

von Hunter (Gast)


Lesenswert?

Aber was soll ich denn da maskieren, wenn die gedrückten Tasten 1 und
die nicht gedrückten 0 sind?

von S. Landolt (Gast)


Lesenswert?

Wie Mario M. um 15:41 schrieb!

Wenn es nicht anders verstanden wird, hier im Detail für Taster auf B0 
(ziemlich wüst allerdings):
1
...
2
    or    key_press, R0
3
 ldi r24,$01
4
 and key_press,r24
5
    tst    key_state
6
.
7
.
8
.
9
    mov    key_rep_press, key_state
10
 ldi r24,$01
11
 and key_rep_press,r24
12
    ldi    key_rep, KEY_REPEAT_NEXT
13
...

Und die Zeiten sind etwas knapp bemessen.

von Mario M. (thelonging)


Lesenswert?

Als erste Abhilfe initialisiere mal KEY_PORT mit 0ffh. Wahrscheinlich 
funktioniert es dann schon.

von S. Landolt (Gast)


Lesenswert?

So ist es.
Läuft sogar besser, das Zeitenproblem ist weg.

von Hunter (Gast)


Lesenswert?

Ja, so funktioniert das gut,
aber dann kann ich den Port nicht
für andere Sachen benutzen.

Kann es vielleicht sein, dass auch
Probleme an dem Port auftauchen, weil
der Programmer und der Quartz an den PortB
angeschlossen ist?

von Mario M. (thelonging)


Lesenswert?

Guck Dir mal die C-Variante von Peter Danneggers Tastenentprellung an. 
Da sieht man, wie es richtig gemacht wird. Stichwort REPEAT_MASK.

https://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_.28nach_Peter_Dannegger.29

von Hunter (Gast)


Lesenswert?

Ich habe meinen Code jetzt mal ein wenig angepasst
an die C-Routine von Peter Dannegger.
Jetzt funktioniert eigentlich alles soweit,
jedoch kommt es ab und zu vor bei normalen Tastendrücken,
dass der nicht erkannt wird.

Hier nochmal mein aktualisierter Code:
1
.def  iwr0        = R1
2
.def  iwr1        = R2
3
4
.def  key_state      = R4
5
.def  key_press      = R5
6
.def  key_rep_press    = R6
7
.def  key_rep        = R16
8
9
.def  temp1        = R17
10
.def  temp2        = R18
11
.def  temp3        = R19
12
.def  counter        = R20
13
14
.equ  KEY_PORT      = PORTB
15
.equ  KEY_DDR        = DDRB
16
.equ  KEY_PIN        = PINB
17
.equ  LED          = 1
18
.equ  KEY0        = 0
19
.equ  KEY1        = 1
20
.equ  ALL_KEYS      = (1<<KEY0 | 1<<KEY1)
21
22
.equ  KEY_REPEAT_MASK    = (1<<KEY0 | 1<<KEY1)
23
.equ  KEY_REPEAT_START  = 50
24
.equ  KEY_REPEAT_NEXT    = 20
25
26
.equ  PRESCL_1024      = (1<<CS00 | 1<<CS02)
27
28
.equ  XTAL        = 8000000
29
30
.org 0x0000
31
    rjmp  init
32
33
.org OVF0addr
34
    rjmp  timer_overflow0
35
36
timer_overflow0:
37
    push  temp1
38
    push  R0
39
    in    R0, SREG
40
    push  R0
41
42
    push  R16
43
    ldi    R16, -( XTAL / 1024 * 10 / 1000 + 1 )
44
    out    TCNT0, R16
45
    pop    R16
46
47
get8key:
48
    in    R0, KEY_PIN
49
    com    R0
50
    eor    R0, key_state    ; key changed?
51
    and    iwr0, R0      ; count in iwr0 and iwr1
52
    com    iwr0
53
    and    iwr1, R0
54
    eor    iwr1, iwr0
55
    and    R0, iwr0
56
    and    R0, iwr1
57
    eor    key_state, R0    ;
58
    and    R0, key_state
59
    or    key_press, R0
60
    ldi    temp1, KEY_REPEAT_MASK
61
    and    key_state, temp1
62
    tst    key_state
63
    breq  get8key_rep
64
    dec    key_rep
65
    brne  get8key_finish
66
    mov    key_rep_press, key_state
67
    ldi    key_rep, KEY_REPEAT_NEXT
68
    rjmp  get8key_finish
69
70
get8key_rep:
71
    ldi    key_rep, KEY_REPEAT_START
72
73
get8key_finish:
74
    pop    R0
75
    out    SREG, R0
76
    pop    R0
77
    pop    temp1
78
    reti
79
  
80
.include "lcd-routines.asm"
81
82
init:
83
    ldi    temp1, LOW(RAMEND)
84
    out    SPL, temp1
85
    ldi    temp1, HIGH(RAMEND)
86
    out    SPH, temp1
87
88
    ldi    temp1, ALL_KEYS
89
    com    temp1
90
    out    KEY_DDR, temp1
91
92
    ldi    temp1, ALL_KEYS
93
    out    KEY_PORT, temp1
94
95
    ldi    temp1, PRESCL_1024
96
    out    TCCR0B, temp1
97
    ldi    temp1, 1<<TOIE0
98
    sts    TIMSK0, temp1
99
100
    rcall  lcd_init
101
    rcall  lcd_clear
102
103
    clr    key_state
104
    clr    key_press
105
    clr    key_rep_press
106
    clr    key_rep
107
108
main:
109
    ldi    counter, 0
110
    rjmp  update_lcd
111
112
loop:  
113
    rcall  get_short_key
114
    cpi    temp1, 1<<KEY0
115
    breq  increment
116
    cpi    temp1, 1<<KEY1
117
    breq  decrement
118
119
    rcall  get_long_key
120
    cpi    temp1, 1<<KEY0
121
    breq  increment
122
    cpi    temp1, 1<<KEY1
123
    breq  decrement
124
125
    rjmp  loop
126
127
increment:
128
    inc    counter
129
    rjmp  update_lcd
130
131
decrement:
132
    dec    counter
133
134
update_lcd:
135
    mov    temp1, counter
136
    rcall  lcd_clear
137
    rcall  lcd_number
138
    rjmp  loop
139
140
; normaler Tastendruck
141
get_short_key:
142
    cli
143
    mov    temp1, key_press
144
    clr    key_press
145
    sei
146
    ret
147
148
; Tastendruck gedrückthalten
149
get_long_key:
150
    cli    
151
    mov    temp1, key_rep_press
152
    clr    key_rep_press
153
    sei
154
    ret
155
156
; Left Scroll Display
157
lcd_lscroll:
158
    push  temp1
159
    ldi    temp1, 0b00011000
160
    rcall  lcd_command
161
    rcall  delay5ms
162
    pop    temp1
163
    ret
164
165
; Right Scroll Display
166
lcd_rscroll:
167
    push  temp1
168
    ldi    temp1, 0b00011100
169
    rcall  lcd_command
170
    rcall  delay5ms
171
    pop    temp1
172
    ret
173
; Move Cursor Left
174
lcd_lmove_cursor:
175
    push  temp1
176
    ldi    temp1, 0b00010000
177
    rcall  lcd_command
178
    rcall  delay5ms
179
    pop    temp1
180
    ret
181
; Move Cursor Right
182
lcd_rmove_cursor:
183
    push  temp1
184
    ldi    temp1, 0b00010100
185
    rcall  lcd_command
186
    rcall  delay5ms
187
    pop    temp1
188
    ret
189
190
text:  .db    "Start Counter", 0

von Mario M. (thelonging)


Lesenswert?

In Peters Routine wird im Interrupt key_state nicht geändert und es 
werden auch nur die repetierenden Bits zurück gegeben:
1
    mov    temp1, key_state
2
    andi   KEY_REPEAT_MASK
3
    breq   get8key_rep
4
    dec    key_rep
5
    brne   get8key_finish
6
    mov    key_rep_press, temp1
7
    ldi    key_rep, KEY_REPEAT_NEXT
8
    rjmp   get8key_finish

von Mario M. (Gast)


Lesenswert?

Korrektur:
1
 ANDI temp1, KEY_REPEAT_MASK

von Hunter (Gast)


Lesenswert?

Ich bedanke mich bei euch für eure große Hilfe.
Das Problem scheint nun gelöst zu sein.

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
Noch kein Account? Hier anmelden.