Forum: Mikrocontroller und Digitale Elektronik Menü mit Sprungtabelle funktioniert nicht


von Philipp (Gast)


Lesenswert?

Hallo zusammen,
ich habe in Assembler für den ATMEGA 8 testweise mal ein Programm 
entworfen, das auf einem LCD Display ein Menü darstellen soll.
Leider funktiniert das ganze noch nicht wie es soll. Wie es aussieht 
wird das Programm immer wieder durchlaufen, ich sehe auf dem LCD nur 
filmmerndes Schwarz.
Kann mir vielleicht jemand meinen Fehler nennen?

Beim Drücken einer bestimmten Taste wird ein Interrupt ausgelöst, das 
Register r20 wird je nach Taste erhöht oder erniedrigt.
Diese Zahl im Register wird im Z-Pointer zu der Startadresse einer 
Sprungtabelle addiert und somit kann ich mit dem Befehl icall direkt 
dorthin springen. Mit dem nun angesprungenen Programm gebe ich dann auf 
dem Bildschirm den Name des Menüs auf dem Display aus.

Vielen Dank schonmal im Voraus für eure Hilfe

gruß Philipp
1
.org 0x0000
2
        rjmp main        ; Reset Handler
3
.org INT0addr
4
         rjmp Erhoehen
5
.org INT1addr
6
         rjmp Erniedrigen
7
main:
8
ldi temp, LOW (RAMEND)  ; Stack initialisieren
9
out SPL, temp
10
11
ldi temp, HIGH (RAMEND)
12
out SPH, temp
13
14
ldi temp, 0b00000000  ; Taster Pins als Eingang
15
out DDRD, temp
16
17
ldi temp, 0b00001111  ; Interrupt bei steigender Flanke
18
out MCUCR, temp
19
20
ldi temp, 0b11000000  ; Interrupt auf Port INT0, INT1 (PD2,PD3)
21
out GICR, temp
22
23
sei
24
25
rcall lcd_init
26
rcall lcd_clear
27
28
ldi ZL,low(Sprungtabelle)
29
ldi ZH,high(Sprungtabelle)
30
31
Pointer:  ; Z-Pointer Sprungtabellenanfang + Zählvariablenwert
32
add ZL,r20
33
push ZH    
34
push ZL
35
icall    ; Zu aktuellem Z-Pointer Stand springen
36
37
warte:
38
pop ZL  ; richtigen Z-Pointer Stand wiederherstellen
39
pop ZH
40
reti
41
bleib:  ; warten auf nächstes Interrupt
42
rjmp bleib
43
44
erhoehen:  ; Zählvariable erzöhen
45
inc r20
46
rjmp Pointer
47
48
erniedrigen:  ; Zählvariable erzöhen
49
dec r20
50
rjmp Pointer
51
52
Programme:
53
Prog1:        ; Menüname 1 ausgeben
54
ldi ZL, low(menu1*2)
55
ldi ZH, high(menu1*2)
56
rProg1:
57
lpm temp1, Z+
58
cpi temp1,0
59
breq warte
60
rcall lcd_data
61
rcall rProg1
62
Prog2:        ; Menüname 2 ausgeben
63
ldi ZL, low(menu2*2)
64
ldi ZH, high(menu2*2)
65
rProg2:
66
lpm temp1, Z+
67
cpi temp1,0
68
breq warte
69
rcall lcd_data
70
rcall rProg2
71
Prog3:        ; Menüname 3 ausgeben
72
ldi ZL, low(menu3*2)
73
ldi ZH, high(menu3*2)
74
rProg3:
75
lpm temp1, Z+
76
cpi temp1,0
77
breq warte
78
rcall lcd_data
79
rcall rProg3
80
81
Sprungtabelle:
82
rjmp Prog1
83
rjmp Prog2
84
rjmp Prog3
85
86
menu1:
87
  .db "Menüpunkt1 ",0
88
menu2:
89
  .db "Menüpunkt2 ",0
90
menu3:
91
  .db "Menüpunkt3 ",0

von Michael U. (amiga)


Lesenswert?

Hallo,

hab nur kurz rübergeschaut:

add ZL,r20
push ZH
push ZL

wer garantiert Dir, da0 es keinen Übertrag ins H-Byte gibt?
clr r19  ; oder was frei ist...
add ZL,r20
adc ZH,r19

weiter: Du weißt, daß Tasteb prellen?
Wenn Du da einmal drückst gibt es vermutlich ein Dutzend Interrupts 
hintereinander...
Stichwort: Tasten entprellen

Gruß aus Berlin
Michael

von Philipp (Gast)


Lesenswert?

Danke für deine Tips.

Mit dem Übertrag hast du Recht, daran habe ich gar nicht gedacht, dass 
wenn die Sprungtabelle eine höhere Z-Pointer Adresse hat eventuell ein 
Übertrag entstehen kann.

Auf das Tasten entprellen verzichte ich, da ich mit dem Prellen noch nie 
probleme hatte. Habe vorher schon ein einfaches Programm geschrieben, 
mit dem man mit 2 Tasten entweder eine Zahl hoch oder runterzählen 
konnte, das hat auch wunderbar ohne entprellen geklappt.

Mein eigentliches Problem bleibt aber leider nach wie vor bestehen. Wäre 
nett wenn jemand mal drüberschauen könnte.

von Philipp (Gast)


Lesenswert?

Noch ein kleiner Nachtrag: Jetzt ist die Schrift auf dem Display schon 
viel deutlicher zu lesen, offensichtlich werden jetzt in einer 
Endlosschleife alle 3 Menünamen hintereinander ausgegeben.

Bei der Simulation mit dem AVR Studio ist mir aufgefallen, dass mit dem 
Befehl reti in der Warteschleife zur Ausgabe des 2. Menüpunktes 
gesprungen wird. Ich habe eigentlich gedacht, reti wäre dazu da, um 
Interrupts wieder freizugeben, ist dem nicht so?

von Oliver (Gast)


Lesenswert?

>weiter: Du weißt, daß Tasteb prellen?
>Wenn Du da einmal drückst gibt es vermutlich ein Dutzend Interrupts
>hintereinander...

>Auf das Tasten entprellen verzichte ich, da ich mit dem Prellen noch nie
>probleme hatte.

>offensichtlich werden jetzt in einer
>Endlosschleife alle 3 Menünamen hintereinander ausgegeben.

Na dann...


>Bei der Simulation mit dem AVR Studio ist mir aufgefallen, dass mit dem
>Befehl reti in der Warteschleife zur Ausgabe des 2. Menüpunktes
>gesprungen wird.

reti sollte dahin zurückspringen, wo das Programm beim Auslösen des 
Interrupts gerade war. Tut es das nicht, hast du irgendwo zwischendrin 
den Stack versemmelt.

>Ich habe eigentlich gedacht, reti wäre dazu da, um
>Interrupts wieder freizugeben, ist dem nicht so?

Doch. Reti setzt das I-Flag.

Oliver

von Peter D. (peda)


Lesenswert?

Philipp schrieb:
> Auf das Tasten entprellen verzichte ich, da ich mit dem Prellen noch nie
> probleme hatte.

Jetzt halten sich 99% der Leser vor Lachen den Bauch.
Du wirst schon merken, daß diese Einstellung grundfalsch ist, wenn Deine 
Programme erstmal größer werden.

Du hast warscheinlich entprellt, ohne es zu wissen, durch lange Delays 
(z.B. LCD-Clear).
Aber Delays fressen Dir kostbare CPU-Zeit auf und die fehlt dann in 
größeren Projekten.


Peter

von kbuchegg (Gast)


Lesenswert?

Wenn ich mir dein Programm so ansehe, denke ich, dass dir der 
Unterschied zwischen einem Sprung (JUMP) und einem Unterprogrammaufruf 
(CALL) nicht klar ist.

Mit einem Jump geht die Programmausführung ganz einfach an einer anderen 
Stelle weiter. Mehr passiert nicht.

Mit einem Call hingegen ist die Situation eine andere. Wenn der Call 
ausgeführt wird, wird die momentane Programmadresse auf dem Stack 
abgelegt. Die Programmausführung geht dann an der im Call angegebenen 
Stelle weiter. Und zwar solange, bis ein Ret (Return from Call) 
angetroffen wird. Dann wird die im Stack zwischengespeicherte Adresse 
geholt und die Programmausführung geht dann unmittelbar nach dem 
aufrufenden Call weiter.

Bei einem Call wird also abgespeichert, von wo der Call gekommen ist und 
mit einem Ret kommt man an genau diese Stelle wieder zurück.

Weiters ist es wichtig, dass
* der Stack bei vielen Calls nicht übergeht. Also nicht zuviel am Stack 
zwischengespeichert wird
* Alles was nach dem Call zb durch Push auf den Stack geschoben wird, 
vor dem Ret per Pop wieder vom Stack entfernt wird.
Der µC führt nicht Buch, wo am Stack die beim Call gespeicherte 
Returnadresse liegt. Der holt sich einfach vom Stack die nächsten beiden 
Bytes (oder wie lange eine Adresse im System ist) und benutzt die als 
Returnadresse.


Und nein. Der Hauptzweck eines reti ist es, aus dem aufgerufenen 
Unterprogramm wieder zur Aufrufstelle zurückzukommen. So wie das ein 
normaler ret auch macht. Nur werden zusätzlich die Interrupts wieder 
freiggeben.

Warum ich auf den Gedanken komme, dass dir der Unterschied zwischen jump 
und call nicht klar ist. Zb. Hier:

hier macht der call absolut keinen Sinn. Du willst hier nicht ein 
'Unterprogramm' namens .... aufrufen. DU willst einfach nur, dass die 
Programmausführung bei .... weiter geht.

von Karl H. (kbuchegg)


Lesenswert?

Sorry. War nicht angemeldet und wollte die Details im vorhergehenden 
Post noch ergänzen.

Also nochmal:

Warum ich auf den Gedanken komme, dass dir der Unterschied zwischen jump
und call nicht klar ist. Zb. Hier:
1
rProg3:
2
        lpm    temp1, Z+
3
        cpi    temp1,0
4
        breq   warte
5
        rcall  lcd_data
6
        rcall  rProg3

hier macht der call absolut keinen Sinn. Du willst hier nicht ein
'Unterprogramm' namens rProg3 aufrufen. Du willst einfach nur, dass die
Programmausführung bei rProg3 weiter geht, weil du eine Schleife formen 
willst.
1
rProg3:
2
        lpm    temp1, Z+
3
        cpi    temp1,0
4
        breq   warte
5
        rcall  lcd_data
6
        rjmp   rProg3

an dieser Stelle war der rcall völlig kontraproduktiv. Bei jedem rcall 
wurde wieder eine Rücksprungadresse auf dem Stack abgelegt.

Es gibt noch mehr solcher Stellen in deinem Code.

von Philipp (Gast)


Lesenswert?

Vielen Dank für deine Hilfe. Du hattest natürlich Recht, der Unterschied 
zu "call" und "jump" war mir nicht so ganz bewusst. Darauf habe ich gar 
nicht geachtet. Jetzt wo du es sagst wird mir mein Fehler natürlich 
klar.

Nachdem ich meinen Fehler verbessert habe, funktioniert das Programm 
auch fast.

Ein Problem habe ich allerdings noch. Wie du gesagt hast springt "reti" 
zurück an den Punkt im Programm an dem das Interrupt. Ich möchte aber 
einfach nur die Interrupts wieder freigeben Dazu habe ich einfach mal 
"sei" genommen, obwohl im Tutorial steht, dass das nur zum generellen 
Interrupt freigeben am Anfang wäre. Nun wird aber überhaupt nicht mehr 
auf ein Tastendruck reagiert.
Was habe ich jetzt schon wieder falsch gemacht?
Ich fände es eigentlich besser, "reti" zu verwenden, aber damit springt 
er an den Anfang des Programmes. Eigentlich sollte er doch an die 
Stelle, an der das Interrupt aufgetreten ist, springen, also in die 
Warteschleife???
1
main:
2
ldi temp, LOW (RAMEND)  ; Stack initialisieren
3
out SPL, temp
4
5
ldi temp, HIGH (RAMEND)
6
out SPH, temp
7
8
ldi temp, 0b00000000  ; Taster Pins als Eingang
9
out DDRD, temp
10
11
ldi temp, 0b00001111  ; Interrupt bei steigender Flanke
12
out MCUCR, temp
13
14
ldi temp, 0b11000000  ; Interrupt auf Port INT0, INT1 (PD2,PD3)
15
out GICR, temp
16
17
sei
18
19
clr r19
20
clr r20
21
rcall lcd_init
22
rcall lcd_clear
23
24
ldi ZL,low(Sprungtabelle)
25
ldi ZH,high(Sprungtabelle)
26
27
Pointer:  ; Z-Pointer Sprungtabellenanfang + Zählvariablenwert
28
add ZL,r20
29
adc ZH,r19
30
ijmp    ; Zu aktuellem Z-Pointer Stand springen
31
32
warte:
33
sei
34
bleib:  ; warten auf nächstes Interrupt
35
rjmp bleib
36
37
erhoehen:  ; Zählvariable erzöhen
38
inc r20
39
rjmp Pointer
40
41
erniedrigen:  ; Zählvariable erzöhen
42
dec r20
43
rjmp Pointer
44
45
Programme:
46
Prog1:        ; Menüname 1 ausgeben
47
ldi ZL, low(menu1*2)
48
ldi ZH, high(menu1*2)
49
rProg1:
50
lpm temp1, Z+
51
cpi temp1,0
52
breq warte
53
rcall lcd_data
54
rjmp rProg1
55
Prog2:        ; Menüname 2 ausgeben
56
ldi ZL, low(menu2*2)
57
ldi ZH, high(menu2*2)
58
rProg2:
59
lpm temp1, Z+
60
cpi temp1,0
61
breq warte
62
rcall lcd_data
63
rjmp rProg2
64
Prog3:        ; Menüname 3 ausgeben
65
ldi ZL, low(menu3*2)
66
ldi ZH, high(menu3*2)
67
rProg3:
68
lpm temp1, Z+
69
cpi temp1,0
70
breq warte
71
rcall lcd_data
72
rjmp rProg3
73
74
Sprungtabelle:
75
rjmp Prog1
76
rjmp Prog2
77
rjmp Prog3
78
79
menu1:
80
  .db "Menüpunkt1 ",0
81
menu2:
82
  .db "Menüpunkt2 ",0
83
menu3:
84
  .db "Menüpunkt3 ",0

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:

> Ein Problem habe ich allerdings noch. Wie du gesagt hast springt "reti"
> zurück an den Punkt im Programm an dem das Interrupt. Ich möchte aber
> einfach nur die Interrupts wieder freigeben

Ich kann dir nur raten nach den Regeln zu spielen.
Ein Interrupt löst einen call aus. Ist die Abarbeitung des Interrupts 
abgeschlossen, wird die ISR mit einem reti beendet.

Natürlich kann man das auch anders machen, wenn man weiß was man tut und 
selbst auf dem Stack aufräumt. Aber empfehlenswert ist es am Anfang 
nicht das zu tun.

Wenn man nach den Regeln spielt, hat man auf lange Sicht immer die 
wenigsten Probleme.

> Ich fände es eigentlich besser, "reti" zu verwenden, aber damit springt
> er an den Anfang des Programmes.


Ich würde mal dein Programm kräftig umstellen.
Du springst in deinem Programm hin und her. Bei manchen Sachen hab ich 
den Eindruck, die sind nur deshalb gerade an dieser Stelle im Code, weil 
der Cursor beim Tippen genau dort stand :-)

Verusch deinen Code so zu strukturieren, dass die Hauptarbeitsrichtung 
immer 'nach unten' ist. Versuch auch Sprünge (mit Ausnahme von 
Schleifenkonstrukten) nach Möglichkeit 'nach unten im Code' führen. 
Meistens ist es dann leichter den Programmfluss zu verfolgen.

von Stefan M. (celmascant)


Lesenswert?

Du Springst bei einem Interrupt von Int0 oder Int1 zu Erhöhen oder 
Erniedrigen.
Als erstes solltest du mal die Interruptroutinen nicht mitten in das 
Hauptprogramm reinschreiben. Ich schreibe sie ganz an den Anfang des 
Programmes. So wird das ganze übersichtlicher.
Dann springst du aus "Erhöhen" / "Erniedrigen" nach "Pointer".
Da das der Programmanfang ist, springt er eben immer zum Anfang.
Ersetze mal das "rjmp Pointer" durch "reti", dann macht er an der Stelle 
im Programm weiter, wo der Interrupt auftrat.
Du musst dann unter "bleib:" auswerten, ob eine Taste gedrückt wurde 
oder nicht.
Ich habe auch gerad ein Menu programmiert, dort lasse ich das Programm 
immer wieder schauen, welcher Screen ausgegeben werden muss. Wenn 
aktuell auszugebende Screen schon beim letzten mal ausgegeben wurde, 
wird er nicht ausgegeben, also die Datenübertragung ans LCD wird einfach 
übersprungen.

Gruss Stefan

Edit: Da war wohl schon jemand schneller mit einer guten Idee^^

von Philipp (Gast)


Lesenswert?

Auf die Idee, immer wieder die Zählervariable auszulesen bin ich auch 
schon gekommen. Ich habe mir aber gedacht, dass das ziemlich viel 
Rechenleistung benötigt. Wenn man das alles in einem Interrupt löst, 
wird es ja pro Tastendruck nur einmal durchlaufen.

von spess53 (Gast)


Lesenswert?

Hi

>Auf die Idee, immer wieder die Zählervariable auszulesen bin ich auch
>schon gekommen. Ich habe mir aber gedacht, dass das ziemlich viel
>Rechenleistung benötigt.

Ob dein Controller in der Warteschleife hängt, oder etwas Sinnvolles 
macht kommt auf das gleiche heraus.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:
> Auf die Idee, immer wieder die Zählervariable auszulesen bin ich auch
> schon gekommen. Ich habe mir aber gedacht, dass das ziemlich viel
> Rechenleistung benötigt. Wenn man das alles in einem Interrupt löst,
> wird es ja pro Tastendruck nur einmal durchlaufen.

Alte Regel:

Machs erst korrekt.
Dann sieh nach ob du ein Zeitproblem hast.
Dann machs schneller, wenn du ein Zeitproblem hast

von Karl H. (kbuchegg)


Lesenswert?

Sieh dir mal Folgendes an.
Eine Sammlung von Unterprogrammen die aufgerufen werden und bei denen 
jede eine eindeutige, einzige und dokumentierte Funktion ausführt.
Von jedem Unterprogramm kann man sich leicht überzeugen, dass es das 
Gewünschte macht. Kein wildes Ge-jumpe quer durch den Gemüsegarten, in 
der Hoffnung, das der ret auf den man irgendwann stossen wird, schon das 
richtige vom Stack holen wird.
Das kann man dann auch im Simulator durchsteppen, ohne nach 3 
jumps/calls den Überblick zu verlieren, wo man denn her gekommen ist, 
warum der jump jetzt genau dorthin geht etc.

Gerade in Assembler ist Organisation das halbe Leben. Und ja: dazu 
gehört auch eine etwas ansprechende Source Code Gestaltung. In so einer 
Assemblerwurst, die sich über 2 Seiten hinzieht, und bei der man nicht 
weiß wo man mit dem Stochern anfangen soll, findet sich kein Mensch 
zurecht. Auch du nicht, nachdem 2 Wochen ins Land gezogen sind.

1
.include "m8def.inc"
2
3
.def  temp = r16
4
.def  temp1 = r17
5
6
.org 0x0000
7
        rjmp main        ; Reset Handler
8
.org INT0addr
9
        rjmp Erhoehen
10
.org INT1addr
11
        rjmp Erniedrigen
12
13
;******************************************************
14
Erhoehen:  ; Zählvariable erhöhen
15
        inc r20
16
        rcall CallFunc  // Funktion entsprechend der Nummer aufrufen
17
        reti
18
19
20
;******************************************************
21
Erniedrigen:  ; Zählvariable erniedrigen
22
        dec   r20
23
        rcall CallFunc  // Funktion entsprechend der Nummer aufrufen
24
        reti
25
26
;******************************************************
27
main:
28
        ldi temp, LOW (RAMEND)  ; Stack initialisieren
29
        out SPL, temp
30
31
        ldi temp, HIGH (RAMEND)
32
        out SPH, temp
33
34
        ldi temp, 0b00000000  ; Taster Pins als Eingang
35
        out DDRD, temp
36
37
        ldi temp, 0b00001111  ; Interrupt bei steigender Flanke
38
        out MCUCR, temp
39
40
        ldi temp, 0b11000000  ; Interrupt auf Port INT0, INT1 (PD2,PD3)
41
        out GICR, temp
42
43
        rcall lcd_init
44
        rcall lcd_clear
45
46
        clr r19
47
        clr r20
48
        rcall CallFunc        ; erste Funktion aufrufen
49
50
        sei
51
52
forever:
53
        rjmp forever
54
55
;******************************************************
56
; ruft die Funktion auf, deren Nummer in r20/r19 steht
57
CallFunc:
58
        ldi ZL,low(Sprungtabelle)
59
        ldi ZH,high(Sprungtabelle)
60
61
        add ZL,r20
62
        adc ZH,r19
63
        icall           ; entsprechende Funktion aufrufen
64
        ret
65
66
;******************************************************
67
Prog1:        ; Menüname 1 ausgeben
68
        ldi   ZL, low(menu1*2)
69
        ldi   ZH, high(menu1*2)
70
        call  lcd_string
71
        ret
72
       
73
;******************************************************
74
Prog2:        ; Menüname 2 ausgeben
75
        ldi   ZL, low(menu2*2)
76
        ldi   ZH, high(menu2*2)
77
        call  lcd_string
78
        ret
79
80
;******************************************************
81
Prog3:        ; Menüname 3 ausgeben
82
        ldi   ZL, low(menu3*2)
83
        ldi   ZH, high(menu3*2)
84
        rcall lcd_string
85
        ret
86
87
;******************************************************
88
; gibt einen String aus.
89
; Z-Pointer enthält die Startadresse des Strings
90
; es werden solange Zeichen ausgegeben, bis ein 0 Byte erkannt wird
91
lcd_string:
92
        lpm  temp1, Z+
93
        cpi  temp1, 0
94
        breq finished
95
        rjmp lcd_string
96
finished:
97
        ret
98
99
;******************************************************
100
; Hilfsfunktionen um Assemblieren zu können
101
lcd_data:  ret
102
lcd_init:  ret
103
lcd_clear: ret
104
105
;******************************************************
106
Sprungtabelle:
107
        rjmp Prog1
108
        rjmp Prog2
109
        rjmp Prog3
110
111
menu1:  .db "Menüpunkt1 ",0
112
menu2:  .db "Menüpunkt2 ",0
113
menu3:  .db "Menüpunkt3 ",0

von Philipp (Gast)


Lesenswert?

Ich habe mein Programm mal etwas verbessert, inklusive Struktur.

Ich würde es aber trotzdem gerne so lassen, dass ich die komplette 
Bildschirmausgabe im Interrupt ausführe, denn das Menü ist wie gesagt 
nur Nebensache, die Hauptprozessorkapazität soll für andere 
Unterprogramme sein.
Die Warteschleife habe ich nur als Platzhalter für andere Programme 
geschrieben.

Hoffentlich ist der Quelltext jetzt etwas anschaulicher, es funktioniert 
schon besser, leider komme ich mit den Tastendrück  nicht auf die 
nächsten Menünamen, sondern es stehen immer komische Buchstaben da. 
Findet vielleicht jemand den (jetzt nur noch kleinen) Fehler?
1
;**************Interruptvektoren***************
2
.org 0x0000
3
        rjmp main        ; Reset Handler
4
.org INT0addr
5
         rjmp Erhoehen
6
.org INT1addr
7
         rjmp Erniedrigen
8
9
;***************Vorkonfiguration****************
10
main:
11
ldi temp, LOW (RAMEND)  ; Stack initialisieren
12
out SPL, temp
13
14
ldi temp, HIGH (RAMEND)
15
out SPH, temp
16
17
ldi temp, 0b00000000  ; Taster Pins als Eingang
18
out DDRD, temp
19
20
ldi temp, 0b00001111  ; Interrupt bei steigender Flanke
21
out MCUCR, temp
22
23
ldi temp, 0b11000000  ; Interrupt auf Port INT0, INT1 (PD2,PD3)
24
out GICR, temp
25
26
sei
27
;**************************
28
clr r19
29
clr r20
30
rcall lcd_init
31
rcall lcd_clear
32
33
ldi ZL,low(Sprungtabelle)
34
ldi ZH,high(Sprungtabelle)
35
36
;**************** Sprung zum richtigen Menüname*****
37
Pointer:  ; Z-Pointer Sprungtabellenanfang + Zählvariablenwert
38
add ZL,r20
39
adc ZH,r19
40
push ZL
41
push ZH
42
ijmp    ; Zu aktuellem Z-Pointer Stand springen
43
;*******************Warteschleife********************
44
warte: 
45
pop ZH    ; Z-Pointer wiederherstellen
46
pop ZL
47
brie bleib  ; wenn nicht durch Interrupt ausgelöst dann zu bleib
48
reti
49
bleib:  ; warten auf nächstes Interrupt
50
rjmp bleib
51
;**************Zählvable bei Interrupt erhöhen***********
52
erhoehen:  ; Zählvariable erhöhen
53
inc r20
54
rjmp Pointer
55
;***********Zählvariable bei Interrupt erniedrigen**************
56
erniedrigen:  ; Zählvariable erniedrigen
57
dec r20
58
rjmp Pointer
59
;***************Sprungtabelle*********
60
Sprungtabelle:
61
rjmp Prog1
62
rjmp Prog2
63
rjmp Prog3
64
;****************Bildschirmausgabeprogramme
65
Programme:
66
Prog1:        ; Menüname 1 ausgeben
67
ldi ZL, low(menu1*2)
68
ldi ZH, high(menu1*2)
69
rcall lcd_clear
70
rProg1:
71
lpm temp1, Z+
72
cpi temp1,0
73
breq warte
74
rcall lcd_data
75
rjmp rProg1
76
Prog2:        ; Menüname 2 ausgeben
77
ldi ZL, low(menu2*2)
78
ldi ZH, high(menu2*2)
79
rcall lcd_clear
80
rProg2:
81
lpm temp1, Z+
82
cpi temp1,0
83
breq warte
84
rcall lcd_data
85
rjmp rProg2
86
Prog3:        ; Menüname 3 ausgeben
87
ldi ZL, low(menu3*2)
88
ldi ZH, high(menu3*2)
89
rcall lcd_clear
90
rProg3:
91
lpm temp1, Z+
92
cpi temp1,0
93
breq warte
94
rcall lcd_data
95
rjmp rProg3
96
;***************Textdaten**************
97
menu1:
98
  .db "Menüpunkt1 ",0
99
menu2:
100
  .db "Menüpunkt2 ",0
101
menu3:
102
  .db "Menüpunkt3 ",0

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:
> Ich habe mein Programm mal etwas verbessert, inklusive Struktur.

Viel ist davon nicht zu erkennen.

> Ich würde es aber trotzdem gerne so lassen, dass ich die komplette
> Bildschirmausgabe im Interrupt ausführe, denn das Menü ist wie gesagt
> nur Nebensache, die Hauptprozessorkapazität soll für andere
> Unterprogramme sein.

Ja, und.
Das Beispiel das ich dir gegeben habe funktioniert noch immer genauso 
wie du es dir vorgestellt hast: Menüausgabe im Interrupt

> Findet vielleicht jemand den (jetzt nur noch kleinen) Fehler?

Ich mach mir jetzt nicht nochmal die Mühe und analysiere dein Programm. 
Stopf es in den Simulator rein und geh mit Einzelschritten durch was bei 
einem Interrupt passiert.

von spess53 (Gast)


Lesenswert?

Hi

>ch habe mein Programm mal etwas verbessert, inklusive Struktur.

Nicht verschlimmert?

>brie bleib  ; wenn nicht durch Interrupt ausgelöst dann zu bleib

Du willst bestimmt nicht wissen, welche Haare sich sich da alle bei mir 
stäuben.
Nimm mir es nicht übel, das ist kein Programm, das ist eine Katastrophe!
Das überhaupt etwas geht, hast du nur ein paar glücklichen Umständen zu 
verdanken.

>Hoffentlich ist der Quelltext jetzt etwas anschaulicher

Nein. Sieh dir mal das Programm von Karl Heinz an. Wenn sich schon 
jemand, der 100mal mehr vom Programmieren versteht wie du, sich die Mühe 
macht, dir ein funktionierendes Programm zu schreiben, solltest du das 
auch annehmen.

MfG Spess

von Peter D. (peda)


Lesenswert?

Philipp schrieb:
> Ich habe mein Programm mal etwas verbessert, inklusive Struktur.

Och nö.

Da hat sich der Karl Hein soviel Mühe gegeben und alles war umsonst.

Eine Sache gibts aber auszusetzen:
1
;******************************************************
2
; ruft die Funktion auf, deren Nummer in r20/r19 steht
3
CallFunc:
4
        cpi r20, Sprung_tab_end - Sprungtabelle
5
        brcc _CallFunc_to_far
6
7
        ldi ZL,low(Sprungtabelle)
8
        ldi ZH,high(Sprungtabelle)
9
10
        add ZL,r20
11
        adc ZH,r19
12
        icall           ; entsprechende Funktion aufrufen
13
        ret
14
_CallFunc_to_far:
15
                        ; insert error handling
16
        ret
17
18
;******************************************************
19
;******************************************************
20
Sprungtabelle:
21
        rjmp Prog1
22
        rjmp Prog2
23
        rjmp Prog3
24
Sprung_tab_end:


Peter

von Z8 (Gast)


Lesenswert?

Hi Peter,

muss es nicht

        ldi ZL,low(Sprungtabelle*2)
        ldi ZH,high(Sprungtabelle*2)

        add ZL,r20
        adc ZH,r19
        icall

heißen? Z8

von Z8 (Gast)


Lesenswert?

völliger Quatsch! nehme Frage zurück! sorry Z8

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.