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 ;)
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
@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... :(
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
@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
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
Dennis, schau mal nach ijmp und icall... ...
@...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
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...
@...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.
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
@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
@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
>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ß.
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
>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 ;-).
@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
@all, funktioniert super!!! ;) 1.) Einfacher wie Switch / Case 2.) Schneller Nur noch eine frage warum eigendlich ein ret nach dem icall ? Gruß Dennis
weil call ein unterprogrammaufruf ist und sonst jedesmal 2 bytes für die rücksprungadresse auf dem stack blieben?
hmm... mach aber doch schon ein ret in meinen unterroutinen... ;)
Jedes Call (call, rcall, icall) braucht sein Return, sonst ist der Stack nicht ausgeglichen. ...
@...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
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. ...
@...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...) ...
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? ...
@...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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.