Forum: Mikrocontroller und Digitale Elektronik Funktionsauswahl / Menü - Wie realisiert man sowas ?


von Dennis Brücke (Gast)


Lesenswert?

Hi @all,

nach dem nun meine Drehzahlberechnung & Temperaturanzeige hinhaut hab
ich mal wieder die nächste Frage...

Da das Display beide Sachen nicht gleichzeitig anzeigen kann währe es
halt schön wenn man sich "durchblättern" könnte.

Jetzt hab ich mir in etwa das ganze so Gedacht:

1 Register hat z.B. Bit 0 auf 1
wenn ich jetzt eine Taste Drücke rolle ich das ganze nach Links
wenn ich eine andere Taste drücke rolle ich das ganze nach Rechts

im Hauptprogramm kopiere ich mir dieses Register nach r16 (z.b.) rolle
es einmal nach rechts wenn Carry 1 dann Menue1 ansonsten weiter rollen
testen... usw...

Aber so hab ich ja nur die möglichkeit 8 Menues zu bauen...

wie macht man so was üblicherweise ?

Gruß Dennis

CPU:ATMega8
Coding in ASM
Status: Anfänger ;)

von Tobi (Gast)


Lesenswert?

warum unbedingt bitrollen? zähl doch hoch und runter, dann hast du 256
möglichkeiten. das ganze kann man dann noch als index für eine
sprungtabelle nehmen, damit immer gleich die richtige routine für die
gewünschte anzeige kommt

von Dennis Brücke (Gast)


Lesenswert?

@Tobi,

ok das mit dem Hoch & Runter rollen klingt logisch ;) Nur ab dem Teil
mit der Sprungtabelle komme ich jetzt gerad mit den Gedanken nicht
mit... :(

von avusgalaxy (Gast)


Lesenswert?

Hi Dennis, vielleicht so:

Vergleiche ein Register mit einer Zahl, und leiß dann die Tabelle aus.

Menü:
    cpi muster, 0x00
    brne muster00
    cpi muster, 0x01
    brne muster01
    cpi muster, 0x02
    brne muster02
    cpi muster, 0x03
    brne muster03
    cpi muster, 0x04
    brne muster04
muster00:
       ldi ZL, LOW(m00*2)
        ldi ZH, HIGH(m00*2)
    rjmp musterende

muster01:
       ldi ZL, LOW(m01*2)
        ldi ZH, HIGH(m01*2)
    rjmp musterende

muster02:
       ldi ZL, LOW(m02*2)
        ldi ZH, HIGH(m02*2)
    rjmp musterende

muster03:
       ldi ZL, LOW(m03*2)
        ldi ZH, HIGH(m03*2)
    rjmp musterende

muster04:
       ldi ZL, LOW(m04*2)
        ldi ZH, HIGH(m04*2)
    rjmp musterende
musterende:
ret



;Menüs
m00:
.db "Menü0",0

m01:
.db "Menü1",0

m02:
.db "Menü2",0

m03:
.db "Menü3",0

m04:
.db "Menü4",0

von Dennis Brücke (Gast)


Lesenswert?

@avusgalaxy,

Ok soweit verstanden nur muss das brne doch breq sein oder ?
mit der Sprungtabelle m02 ist jetzt nur der Text gemeint Richtig ?!

patsch

jetzt muss ich nur noch nen weg finden wie ich bei Tastendruck inc /
dec durchführe... dazu könnte ich doch in Timer0 z.b. die Entprellung
vornehmen und wenn ich wieder in Main bin Taste auswerten und inc oder
dec machen ?!

Gruß Dennis

von avusgalaxy (Gast)


Lesenswert?

Ups, natürlich breq...

In m02, m03 ist dann der Menütext gespeichert, den du haben willst.

Inc oder dec kannst ja schon im Timer machen und dann

Menü:
    cpi muster, 0x00
    breq muster00
machen.

Wenn das Register "Muster" 0 ist, dann soll es "Drehzahl:" sein.
Wenn es 0x01 ist, dann "Temperatur:"
Wenn es 0x02 ist, dann "Uhr:"

Gruß Avus

von ...HanneS... (Gast)


Lesenswert?

Dennis, schau mal nach ijmp und icall...

...

von Dennis Brücke (Gast)


Lesenswert?

@...HanneS...

hab mir mal ijmp & icall angeschaut, wenn ich es richtig verstanden
habe, kann ich damit direkt zu adress springen welche ich im Z Register
angebe. Aber muss ich da nicht genau wissen, an welcher stelle welche
routine liegt ? Wenn ja, wie springt man dann wieder raus ?

Gruß Dennis

von ...HanneS... (Gast)


Lesenswert?

Hi Dennis...

Du kannst z.B. deine Variable (Menüpunkt) als Index auf eine Tabelle
nutzen, in der die Adressen der Routinen stehen. Diese Tabelle legst du
mit .dw und dem Namen (Label) der Routine an. Damit kannst du sogar mit
verschiedenen Indizies aud gleiche Routinen verweisen.

Also:
- Z-Pointer auf Tabellenanfang,
- Index (doppelt) draufaddieren
- zwei Bytes (Adresse der Routine) aus Tabelle auslesen (lpm)
- die beiden Bytes in den Z-Pointer kopieren
- mit ijmp springen (zurück mit rjmp mainloop)
  oder mit icall springen, mit ret zurück

Es geht vermutlich auch bedeutend eleganter, aber ich habe mich mit
dieser Möglichkeit noch nicht näher beschäftigt.
Jedenfalls ist es bedeutend effektiver als eine Latte
cpi/brne/rjmp-Abfragen.

Viel Erfolg...
...HanneS...

von Dennis Brücke (Gast)


Lesenswert?

@...HanneS...,

klingt auf jedenfall sehr Interessant, allerdings wohl ein wenig
übertrieben für 2-4 Menü´s (Programmfunktionen) ist ja wie mit Kanonen
auf Spatzen zu schiessen... ;)

werd es in diesem Projekt erstmal mit dem Hochzählen und Comparen
machen. Welches auch im moment funktioniert ;)

Jetzt muss ich mich nur noch mit 2 Dingen beschäftigen, und dann denke
ich das ich fertig bin...
Mittelwertbildung & Laufschrift auf einer Anzeige mit 8 byte (byte für
byte im SRAM abgelegt)

Aber dafür ist es glaube ich besser 2 neue Thread´s aufzumachen...
(keine Sorge habe schon viel gesucht, finde bloß nicht so wirklich was
mir hilft)

Gruß Dennis

P.S: Ich weis das solche sachen bestimmt leichter in C gehen, aber das
ist eine Sprache mit der ich mich im Zusammenhang mit µC einfach nicht
anfreunden kann.

von Andi (Gast)


Lesenswert?

Ach Dennis!

Willst Du denn bei Deinen nächsten Projekten immer noch wegen
Banalitäten, wie einer Auswahl in einer 16Bit-Liste für Sprungadressen,
hier einen Post schreiben und Dir alles was ungefähr paßt von anderen
zusammenkratzen?
Man muß sich auch seinen Horizont erweitern und sich die Routinen
irgend wann mal selber schreiben können.
Versuche doch mal, das von Hannes nachzubilden, also den Algorythmus
als UP schreiben.
Wenn Du das geschafft hast, fällt es Dir bei den nächsten Ideen
einfacher.
Das selbe gilt für Avus.
Das, was Hannes meinte, sind auch nicht mehr als 11 Zeilen, habs gerade
mal schnell aufgebaut und Du kannst mit einer Variablen 128
Unterprogramme aufrufen, bei leichter Änderung 256.

Gruß
Andi

von Dennis Brücke (Gast)


Lesenswert?

@Andi,

zusammenkratzen ist nicht ganz richtig... :( meine Routinen für das
KS0107/8 Display hab ich mir z.b. komplett selber geschrieben.

Nur mir selber fällt es nunmal manchmal einfach schwer auf die
einfachsten Dinge zu kommen. Ich gebe offen und ehrlich zu das ich z.b.
bei der Division einfach nicht auf so banal einfaches gekommen bin wie
z.b. sub bis nicht mehr geht...

Meine Probleme liegen nicht in dem Wissen sondern beim zusammenknüpfen
solcher dinge.

Das mit dem "Horizont erweitern" ist in jedem fall richtig...
nur anefürsich bin ich mit diesem Projekt nahezu fertig, da es nicht
für mich ist möchte ich mich auch nicht noch weiter damit rumschlagen
alles auf die methodik zu ändern. Es fehlt halt einfach nur noch die
Menü geschichte (oder eher Funktionsauswahl) und der Mittelwert...

Gruß Dennis

von Dennis Brücke (Gast)


Lesenswert?

@all,

um trotzdem nochmal auf das Thema zu kommen, verstehe ich das richtig,
das man in etwa so macht (einfachstes bsp): (in r16 meinetwegen
MenueZaehlregister)

.ORG 0x0000
rjmp reset

reset:
   ldi zh,high(Sprungtabelle)
   ldi zl,low(Sprungtabelle)
   add zl, r16

   icall
rjmp reset

menue1:
   ; hier menue1
rjmp reset

menue2:
   ; hier menue2
rjmp reset

menue3:
   ; hier menue3
rjmp reset

menue4:
   ; hier menue4
rjmp reset

Sprungtabelle:
   rjmp menue1
   rjmp menue2
   rjmp menue3
   rjmp menue4


Gruß Dennis

von Santa Klaus (Gast)


Lesenswert?

>das man in etwa so macht

Fast.  So wird die Sache rund:

menu:
   ; immer zuerst "low", und dann "high"!
   ldi zl,low(Sprungtabelle)
   ldi zh,high(Sprungtabelle)
   add zl, r16  ; Überlauf nicht ausgeschlossen!
   adc zh, _0   ; Ggf. aufgetretenen Überlauf berücksichtigen
   icall
   ret

menue1:
   ; hier menue1
ret

menue2:
   ; hier menue2
ret

menue3:
   ; hier menue3
ret

menue4:
   ; hier menue4
ret

Sprungtabelle:
   rjmp menue1
   rjmp menue2
   rjmp menue3
   rjmp menue4

Dabei ist "_0" ein Register, das den Wert Null hat.
Die Zeile "adc zh, _0" sollte auf keinen Fall fehlen, weil man damit
rechnen muß, daß sich die Sprungtabelle über einen Bereich erstreckt,
in dem ein Vielfaches von 256 liegt.  Ist dies der Fall, kommt es bei
"add zl, r16" zu einem Überlauf, der berücksichtigen werden muß.

von Dennis Brücke (Gast)


Lesenswert?

Ok also eigendlich doch einfacher wie ich gedacht habe, und bei weiten
besser wie cpi & breq allerdings muss ich jetzt dennoch fragen:

durch den Verweis von ldi zl,low(Sprungtabelle) holt er sich die
Startadresse von Sprungtabelle bei der es egal ist, an welcher stelle
sie liegt richtig ?

Jetzt erhöhe ich meinen addierer (r16) oder decrementiere diesen...
beim erhöhen um 1 gelangt er jetzt also theoretisch zu:

rjmp menue1 +1 also rjmp menue2 richtig ?

wenn ich beim Addieren nicht zh auf 0x00 lege habe ich also
"theoretisch" 65536 Sprungadressen

Wenn es so ist, ist es ja schon fast einfacher wie sich nen kopf zu
machen cpi & breq.... ;)

Gruß Dennis

von Santa Klaus (Gast)


Lesenswert?

>durch den Verweis von ldi zl,low(Sprungtabelle) holt er sich die
>Startadresse von Sprungtabelle bei der es egal ist, an welcher stelle
>sie liegt richtig ?

Beide "ldi"-Befehle bewirken, daß Z (= ZH:ZL) mit der Startadresse
der Sprungtabelle geladen wird.  Wo im Flash-ROM die Sprungtabelle
letztlich zu liegen kommt - davor und danach stehen schließlich noch
hunderte Zeilen Code -, kann Dir völlig egal sein. Durch

ldi zl, low(...)
ldi zh, high(...)

ist garantiert, daß niemals etwas schiefgehen kann.

> Jetzt erhöhe ich meinen addierer (r16)

r16 ist kein "addierer", sondern ein Register, dessen Inhalt
bestimmt, welche Menü-Routine zur Ausführung gelangen soll.

>oder decrementiere diesen...
>beim erhöhen um 1 gelangt er jetzt also theoretisch zu:

> rjmp menue1 +1 also rjmp menue2 richtig ?

Ja!  rjmp zur Adresse ((Adresse von "rjmp menu1") + 1)

>wenn ich beim Addieren nicht zh auf 0x00 lege habe ich also
>"theoretisch" 65536 Sprungadressen

zh wird nirgendwo auf 0 gelegt. Mit "adc zh, _0" wird ein eventuell
beim Ausführen der vorangegangenen Zeile aufgetretener Überlauf
berücksichtigt.  Die Anzahl der möglichen Sprungadressen ist gleich der
Anzahl aller Werte, die r16 annehmen kann.  Das sind 256 Stück.

Falls Du mehr als 256 Sprungadressen verarbeiten willst, kannst Du
folgendermaßen auf maximal 65536 aufstocken:

add zl, r16
adc zh, r17

> Wenn es so ist, ist es ja schon fast einfacher wie sich nen kopf
> zu machen cpi & breq.... ;)

Ja, so programmiert man elegant und kompakt ein "switch... case..."
in AVR-Assembler, und dies ist praktisch auch gerade der Sinn und Zweck
des "icall"-Befehls!  Ellenlange "cpi & breq"-Kaskaden sind
dagegen... nun ja, eher etwas, womit Anfänger zu erkennen geben, daß
sie noch Anfänger sind ;-).

von Dennis Brücke (Gast)


Lesenswert?

@Santa Klaus,

hab mich in der Nachricht davor etwas blöd ausgedrückt, aber wir meinen
das gleiche ;)

Dann werd ich heute abend gleich mal meine Routine umschreiben, auch
wenn es bei 2 Switch/Case abfragen nur einen geringen Performance
Gewinn gibt ist es doch die Elegantere Art.

Gruß Dennis

von Dennis Brücke (Gast)


Lesenswert?

@all,

funktioniert super!!! ;)
1.) Einfacher wie Switch / Case
2.) Schneller

Nur noch eine frage warum eigendlich ein ret nach dem icall ?

Gruß Dennis

von Tobi (Gast)


Lesenswert?

weil call ein unterprogrammaufruf ist und sonst jedesmal 2 bytes für die
rücksprungadresse auf dem stack blieben?

von Dennis Brücke (Gast)


Lesenswert?

hmm... mach aber doch schon ein ret in meinen unterroutinen... ;)

von ...HanneS... (Gast)


Lesenswert?

Jedes Call (call, rcall, icall) braucht sein Return, sonst ist der Stack
nicht ausgeglichen.

...

von Dennis Brücke (Gast)


Lesenswert?

@...HanneS...,

das ist mir doch klar ;) ist einfach wohl nur grad nen
missverständnis,
in meinem beispiel bin ich direkt von "reset:" ausgegangen als
antwort wurde mir dann wohl ein bsp. als Unterroutine gegeben und
deswegen das ret nach icall. da dieses ja nichts mit icall zu tun hat
sondern wohl mit einem vorherigen aufruf dieser routine...

Gruß Dennis

von ...HanneS... (Gast)


Lesenswert?

So ist das, wenn man nur Programmsplitter sieht.

Wenn du schon im UP bist, dann kannst du ja auch gut mit ijmp deine
Auswahl treffen und dein ret bezieht sich auf den ersten Aufruf. Das
spart einen Rücksprung (der ja 4 Takte kostet).

Vor Call drücke ich mich sowiso etwas, das kommt aber daher, dass ich
mehr mit RAMlosen AVRs hantiere. Ich nehme Call also nur, wenn es
wirklich sein muss.

...

von Dennis Brücke (Gast)


Lesenswert?

@...HanneS...,

meine Funktionsauswahl findet direkt in Main statt (ich nenne es mal
Main) dann springe ich direkt per ijmp in die Funktion am Ende der
Funktion auch direkt wieder per rjmp main zurück. So spar ich mir die
call´s und auch mein Stack wird sich freuen... ;) Da ich in einigen
Unterroutinen vieleicht ein wenig schlampig programmiert habe und in
nahezu jeder UP alle wichtigen register sichern muss... um einfach
register zu Sparen. (welche fast voll sind...)

...

von ...HanneS... (Gast)


Lesenswert?

Das widerspricht zwar dem Konzept des modularen Programmierens, aber so
ähnlich mach' ich's auch. Beim modularen Programmieren bin ich noch
nicht angekommen. Der Tag wird kommen (oder auch nicht).

Ist Mittelwert nun klar?

...

von Dennis Brücke (Gast)


Lesenswert?

@...HanneS...,

kahm leider noch nicht dazu genau durchzugehen, muss im moment noch ein
Fahrzeug bekleben... (bei 1,5°C passt nicht in unsere Halle)

grmpf....

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.