von
Hunter (Gast)
29.06.2018 18:33
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
Gibt es denn keinen Debugger?
mfg Klaus
von
Hunter (Gast)
29.06.2018 22:17
Nein, leider habe ich keinen Debugger parat.
von
Stefan F. (Gast)
29.06.2018 22:27
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)
30.06.2018 20:36
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)
30.06.2018 21:57
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)
30.06.2018 22:11
In etwa so? 1 ldi R16, -( (((XTAL / 1024) * 10) / 1000) + 1 )
Leider erkennt das Programm immer noch nicht den langen Tastendruck.
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)
30.06.2018 23:36
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.
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)
01.07.2018 19:19
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)
01.07.2018 19:34
Um welchen Controller geht es eigentlich?
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)
01.07.2018 19:40
Ich verwende den Atmega328.
von
Hunter (Gast)
01.07.2018 19:51
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?
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)
01.07.2018 20:27
Aber was soll ich denn da maskieren, wenn die gedrückten Tasten 1 und
die nicht gedrückten 0 sind?
von
S. Landolt (Gast)
01.07.2018 20:57
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.
Als erste Abhilfe initialisiere mal KEY_PORT mit 0ffh. Wahrscheinlich
funktioniert es dann schon.
von
S. Landolt (Gast)
01.07.2018 21:25
So ist es.
Läuft sogar besser, das Zeitenproblem ist weg.
von
Hunter (Gast)
01.07.2018 21:32
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?
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)
01.07.2018 23:33
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
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)
02.07.2018 09:22
Korrektur: 1 ANDI temp1, KEY_REPEAT_MASK
von
Hunter (Gast)
02.07.2018 09:40
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.