www.mikrocontroller.net

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


Autor: Philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
.org 0x0000
        rjmp main        ; Reset Handler
.org INT0addr
         rjmp Erhoehen
.org INT1addr
         rjmp Erniedrigen
main:
ldi temp, LOW (RAMEND)  ; Stack initialisieren
out SPL, temp

ldi temp, HIGH (RAMEND)
out SPH, temp

ldi temp, 0b00000000  ; Taster Pins als Eingang
out DDRD, temp

ldi temp, 0b00001111  ; Interrupt bei steigender Flanke
out MCUCR, temp

ldi temp, 0b11000000  ; Interrupt auf Port INT0, INT1 (PD2,PD3)
out GICR, temp

sei

rcall lcd_init
rcall lcd_clear

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

Pointer:  ; Z-Pointer Sprungtabellenanfang + Zählvariablenwert
add ZL,r20
push ZH    
push ZL
icall    ; Zu aktuellem Z-Pointer Stand springen

warte:
pop ZL  ; richtigen Z-Pointer Stand wiederherstellen
pop ZH
reti
bleib:  ; warten auf nächstes Interrupt
rjmp bleib

erhoehen:  ; Zählvariable erzöhen
inc r20
rjmp Pointer

erniedrigen:  ; Zählvariable erzöhen
dec r20
rjmp Pointer

Programme:
Prog1:        ; Menüname 1 ausgeben
ldi ZL, low(menu1*2)
ldi ZH, high(menu1*2)
rProg1:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rcall rProg1
Prog2:        ; Menüname 2 ausgeben
ldi ZL, low(menu2*2)
ldi ZH, high(menu2*2)
rProg2:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rcall rProg2
Prog3:        ; Menüname 3 ausgeben
ldi ZL, low(menu3*2)
ldi ZH, high(menu3*2)
rProg3:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rcall rProg3

Sprungtabelle:
rjmp Prog1
rjmp Prog2
rjmp Prog3

menu1:
  .db "Menüpunkt1 ",0
menu2:
  .db "Menüpunkt2 ",0
menu3:
  .db "Menüpunkt3 ",0

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: kbuchegg (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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:
rProg3:
        lpm    temp1, Z+
        cpi    temp1,0
        breq   warte
        rcall  lcd_data
        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.
rProg3:
        lpm    temp1, Z+
        cpi    temp1,0
        breq   warte
        rcall  lcd_data
        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.

Autor: Philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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???
main:
ldi temp, LOW (RAMEND)  ; Stack initialisieren
out SPL, temp

ldi temp, HIGH (RAMEND)
out SPH, temp

ldi temp, 0b00000000  ; Taster Pins als Eingang
out DDRD, temp

ldi temp, 0b00001111  ; Interrupt bei steigender Flanke
out MCUCR, temp

ldi temp, 0b11000000  ; Interrupt auf Port INT0, INT1 (PD2,PD3)
out GICR, temp

sei

clr r19
clr r20
rcall lcd_init
rcall lcd_clear

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

Pointer:  ; Z-Pointer Sprungtabellenanfang + Zählvariablenwert
add ZL,r20
adc ZH,r19
ijmp    ; Zu aktuellem Z-Pointer Stand springen

warte:
sei
bleib:  ; warten auf nächstes Interrupt
rjmp bleib

erhoehen:  ; Zählvariable erzöhen
inc r20
rjmp Pointer

erniedrigen:  ; Zählvariable erzöhen
dec r20
rjmp Pointer

Programme:
Prog1:        ; Menüname 1 ausgeben
ldi ZL, low(menu1*2)
ldi ZH, high(menu1*2)
rProg1:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rjmp rProg1
Prog2:        ; Menüname 2 ausgeben
ldi ZL, low(menu2*2)
ldi ZH, high(menu2*2)
rProg2:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rjmp rProg2
Prog3:        ; Menüname 3 ausgeben
ldi ZL, low(menu3*2)
ldi ZH, high(menu3*2)
rProg3:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rjmp rProg3

Sprungtabelle:
rjmp Prog1
rjmp Prog2
rjmp Prog3

menu1:
  .db "Menüpunkt1 ",0
menu2:
  .db "Menüpunkt2 ",0
menu3:
  .db "Menüpunkt3 ",0

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan M. (celmascant)
Datum:

Bewertung
0 lesenswert
nicht 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^^

Autor: Philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

.include "m8def.inc"

.def  temp = r16
.def  temp1 = r17

.org 0x0000
        rjmp main        ; Reset Handler
.org INT0addr
        rjmp Erhoehen
.org INT1addr
        rjmp Erniedrigen

;******************************************************
Erhoehen:  ; Zählvariable erhöhen
        inc r20
        rcall CallFunc  // Funktion entsprechend der Nummer aufrufen
        reti


;******************************************************
Erniedrigen:  ; Zählvariable erniedrigen
        dec   r20
        rcall CallFunc  // Funktion entsprechend der Nummer aufrufen
        reti

;******************************************************
main:
        ldi temp, LOW (RAMEND)  ; Stack initialisieren
        out SPL, temp

        ldi temp, HIGH (RAMEND)
        out SPH, temp

        ldi temp, 0b00000000  ; Taster Pins als Eingang
        out DDRD, temp

        ldi temp, 0b00001111  ; Interrupt bei steigender Flanke
        out MCUCR, temp

        ldi temp, 0b11000000  ; Interrupt auf Port INT0, INT1 (PD2,PD3)
        out GICR, temp

        rcall lcd_init
        rcall lcd_clear

        clr r19
        clr r20
        rcall CallFunc        ; erste Funktion aufrufen

        sei

forever:
        rjmp forever

;******************************************************
; ruft die Funktion auf, deren Nummer in r20/r19 steht
CallFunc:
        ldi ZL,low(Sprungtabelle)
        ldi ZH,high(Sprungtabelle)

        add ZL,r20
        adc ZH,r19
        icall           ; entsprechende Funktion aufrufen
        ret

;******************************************************
Prog1:        ; Menüname 1 ausgeben
        ldi   ZL, low(menu1*2)
        ldi   ZH, high(menu1*2)
        call  lcd_string
        ret
       
;******************************************************
Prog2:        ; Menüname 2 ausgeben
        ldi   ZL, low(menu2*2)
        ldi   ZH, high(menu2*2)
        call  lcd_string
        ret

;******************************************************
Prog3:        ; Menüname 3 ausgeben
        ldi   ZL, low(menu3*2)
        ldi   ZH, high(menu3*2)
        rcall lcd_string
        ret

;******************************************************
; gibt einen String aus.
; Z-Pointer enthält die Startadresse des Strings
; es werden solange Zeichen ausgegeben, bis ein 0 Byte erkannt wird
lcd_string:
        lpm  temp1, Z+
        cpi  temp1, 0
        breq finished
        rjmp lcd_string
finished:
        ret

;******************************************************
; Hilfsfunktionen um Assemblieren zu können
lcd_data:  ret
lcd_init:  ret
lcd_clear: ret

;******************************************************
Sprungtabelle:
        rjmp Prog1
        rjmp Prog2
        rjmp Prog3

menu1:  .db "Menüpunkt1 ",0
menu2:  .db "Menüpunkt2 ",0
menu3:  .db "Menüpunkt3 ",0

Autor: Philipp (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?
;**************Interruptvektoren***************
.org 0x0000
        rjmp main        ; Reset Handler
.org INT0addr
         rjmp Erhoehen
.org INT1addr
         rjmp Erniedrigen

;***************Vorkonfiguration****************
main:
ldi temp, LOW (RAMEND)  ; Stack initialisieren
out SPL, temp

ldi temp, HIGH (RAMEND)
out SPH, temp

ldi temp, 0b00000000  ; Taster Pins als Eingang
out DDRD, temp

ldi temp, 0b00001111  ; Interrupt bei steigender Flanke
out MCUCR, temp

ldi temp, 0b11000000  ; Interrupt auf Port INT0, INT1 (PD2,PD3)
out GICR, temp

sei
;**************************
clr r19
clr r20
rcall lcd_init
rcall lcd_clear

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

;**************** Sprung zum richtigen Menüname*****
Pointer:  ; Z-Pointer Sprungtabellenanfang + Zählvariablenwert
add ZL,r20
adc ZH,r19
push ZL
push ZH
ijmp    ; Zu aktuellem Z-Pointer Stand springen
;*******************Warteschleife********************
warte: 
pop ZH    ; Z-Pointer wiederherstellen
pop ZL
brie bleib  ; wenn nicht durch Interrupt ausgelöst dann zu bleib
reti
bleib:  ; warten auf nächstes Interrupt
rjmp bleib
;**************Zählvable bei Interrupt erhöhen***********
erhoehen:  ; Zählvariable erhöhen
inc r20
rjmp Pointer
;***********Zählvariable bei Interrupt erniedrigen**************
erniedrigen:  ; Zählvariable erniedrigen
dec r20
rjmp Pointer
;***************Sprungtabelle*********
Sprungtabelle:
rjmp Prog1
rjmp Prog2
rjmp Prog3
;****************Bildschirmausgabeprogramme
Programme:
Prog1:        ; Menüname 1 ausgeben
ldi ZL, low(menu1*2)
ldi ZH, high(menu1*2)
rcall lcd_clear
rProg1:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rjmp rProg1
Prog2:        ; Menüname 2 ausgeben
ldi ZL, low(menu2*2)
ldi ZH, high(menu2*2)
rcall lcd_clear
rProg2:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rjmp rProg2
Prog3:        ; Menüname 3 ausgeben
ldi ZL, low(menu3*2)
ldi ZH, high(menu3*2)
rcall lcd_clear
rProg3:
lpm temp1, Z+
cpi temp1,0
breq warte
rcall lcd_data
rjmp rProg3
;***************Textdaten**************
menu1:
  .db "Menüpunkt1 ",0
menu2:
  .db "Menüpunkt2 ",0
menu3:
  .db "Menüpunkt3 ",0

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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:
;******************************************************
; ruft die Funktion auf, deren Nummer in r20/r19 steht
CallFunc:
        cpi r20, Sprung_tab_end - Sprungtabelle
        brcc _CallFunc_to_far

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

        add ZL,r20
        adc ZH,r19
        icall           ; entsprechende Funktion aufrufen
        ret
_CallFunc_to_far:
                        ; insert error handling
        ret

;******************************************************
;******************************************************
Sprungtabelle:
        rjmp Prog1
        rjmp Prog2
        rjmp Prog3
Sprung_tab_end:



Peter

Autor: Z8 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Z8 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
völliger Quatsch! nehme Frage zurück! sorry Z8

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.