Hallo,
erstmal kurz zur Vorgeschichte, ich baue gerade ein Anzeigeinstrument
welches verschiedene Analogwerte einliest und diese dann auf 2 (1x3 7
Segment und 1x2 7 Segement) anzeigen ausgiebt. Zudem soll über eine
Status LED jeweils angezeigt werden welcher AD Kanal gerade auf dem
jeweiligen Display dargestellt wird.
Es können so immer zwei werte Parallel angezeigt werden. Das wechseln
der Kanäle soll über einen Taster erfolgen.
Ich hab jetzt gestern mal etwas rumexperimentiert und es funktioniert
auch schon eingermassen. Es ist aber mein erstes ASM Programm mit dem
AVR und mein erstes AVR Projekt überhaupt, deshalb glaube ich das ich
sicher einiges noch eleganter lösen könnte.
Die Status LED habe ich mangels IO Ports über Logik Gatter kodiert.
Wobei zwei IO Pins geschaltet werden, das dritte Bit wird vom aktuellen
7 Segment Auswahlpin übernommen und durch ein 3fach NOR Gatter geleitet.
Dadurch unterscheide ich zwischen der ersten und der zweiten Anzeige.
Den Code habe ich beigefügt, währe cool wenn sich das jemand mal ansehen
könnte und verbesserungsvorschläge machen könnte. Besondere Probleme hab
ich noch mit dem Taster und dessen Auswertung.
Gruß
Malte.
Ziemlich spaghetti, oder? :-)
Ein Programm schreibt man nicht direkt am Computer, sondern macht sich
zumindest auf einem Blatt Papier einen groben PAP.
Das ergibt dann im Idealfall ein lineares Hauptprogramm in einer
Endlosschleife, die nacheinander alle Unterfunktionen aufruft und sich
dann ggf. eine Weile schlafen legt, um dann von vorn zu beginnen.
Also
1. Initialisierung des Controllers/Peripherie/RAM/SP
2. Main loop
call Wandler lesen
call Sensoren linearisieren
call Umrechnen in Siebensegmentcode
call Ausgabe
wait
goto main loop
Und wenn du es ganz schön machen willst, übergibst du alle Parameter an
die Unterprogramme in einem Datenstack, ebenso die Rückgabewerte. Das
ist erstmal mehr Aufwand, zwingt aber zu eiserner Disziplin und macht
die Module weiterverwendbar. Im Lauf der Zeit sammelt sich eine schöne
Anzahl von Routinen, die dann problemlos ohne Änderungen in anderen
Projekten weiterverwendbar sind. Gute Vorplanung spart min. die Hälfte
der Programmierzeit/Fehlersuche.
Trotzdem: Hut ab, wenn es dein erstes Projekt ist und es funktioniert.
Hi,
sorry das der Code so runtergeschrieben ist... mach ich normal nicht
so... Programmiere normal in Hochsprachen und hab daher nicht so viel
erfahrung mit Assembler code...
Kannst du nochmal genau beschreiben wie du die Parameterübergabe
abhandlen würdest? Wenn ich das richtig verstanden habe meinst du ich
soll alle benötigten Parameter vor dem Aufruf der Subfunktion in den
Stack Pushen und durch die Funktion dann wieder rausholen (POP xyz) ?!?
Bisher habe ich die parameterübergabe immer über vorher definierte
Register bzw. speicherinhalte deren Adresse ich via Register übergeben
habe gelöst. Aber die Stack Methode leuchtet ein das sie sinnvoller ist,
weil universeller.
Nur noch mal zur sicherheit das ich das Prinzip richtig verstanden habe.
Eine Frage hab ich trotzdem noch, was mache ich mit der Funktion für den
Schalter? Wie reagiere ich darauf, er soll ja nicht wenn ich ihn z.B. 2
Sek. lang gedrückt halte, dauernd den Wert hochzählen, sondern nur
einmal. Hat da vieleicht jemand ne Beispielfunktion o.ä.?
Gruß
Malte.
Also ich kann absolut nichts Spaghetti finden.
Der Programmierstil ist astrein, schön jede Funktion mit Header, so
wünscht man sichs.
Aber wenn Du schon Hochsprachen programmiert hast, sollte man das ja
auch erwarten können.
Die Parameterübergabe in Registern ist auch voll o.k., macht doch auch
jeder C-Compiler so.
Unnütz zu Pushen kostet nur wertvollen Flash und der ist bei den kleinen
AVRs (1...4kWorte) verdammt knapp.
Pushen muß man wirklich nur das, was man auch später noch benötigt.
Funktional habe ich mir den Kode noch nicht näher angesehen.
Trotzdem mal einige Ideen, wie ich sowas machen würde:
Die 5 7-S-Anzeigen würde ich im Multiplexbetrieb ansteuern (8+5=13
Portpins, Schaltplan anbei).
Die Tasten würde ich entprellen.
Dazu nehme ich immer einen Timerinterrupt als zentrales Arbeitspferd für
alles zeitabhängige.
D.h. der würde die Multiplexausgabe und das Entprellen übernehmen.
Zum Entprellen von bis zu 8 Tasten haben ich hier schon mal was
geschrieben:
http://www.mikrocontroller.net/forum/read-1-18592.htmlhttp://www.mikrocontroller.net/attachment.php/18783/Get8key.asm
Eine Frage zu Deinem Interrupthandler. Kann es sein, daß du die noch
anderweitig verwendeten Register sowie das SREG nicht sicherst ?
Ist aber unüblich, die komplette Mainloop unter Interruptsperre
auszuführen. Da könnten leicht Interrupts verloren gehen.
Besser ist daher, die Interrupts so zu schreiben, daß das Main an
beliebiger Stelle unterbrochen werden kann.
Peter
über den normalen Stack geht es glaube ich nicht, zumindest wird das
sehr aufwändig, und mit Interrupts gibts auch Probleme.
Aber du hast ja 3 Indexregister, ich habe immer das y-Register benutzt.
Du definierst dir einen Datenstack, irgendwo in der Mitte des RAM, wo du
den hinlegst, hängt von der RAM-Größe, Stackbedarf und Variablenmenge
ab. Der "normale" Stack wächst vom RAM-Ende nach unten, die Variablen
beginnen am RAM-Anfang. Mußt natürlich aufpassen, daß sich die Bereiche
nicht überschreiben, dann gibts i.a. einen Absturz.
Nehmen wir mal einen 2313 als Beispiel: SP=0xDF, y=0x7F
Parameterübergabe:
st -y, r11
st -y, r10
Damit hast du 2 Byte im Stack abgelegt, können natürlich mehr sein.
Im UP verfährst du analog, ein direkter Zugriff auf das Parameterarray
ist auch mit den den LDD-Befehlen möglich. Scheint alles erstmal
verwirrend, wenn man es aber verstanden hat, geht es fast von allein.
@crazy horse,
der Aufrufer muß also "st -y, r11" machen und der Aufgerufene dann
wieder "ld r11, y+" um damit arbeiten zu können.
Aber wozu soll das denn gut sein ?
Es kostet doch nur 2 unnütze Instruktionen für jedes Datenbyte.
Peter
Ich möchte die Sache mit dem Stack einsehen bei größeren Parametern
(z.B. Strings), aber ich nutze die vom Atmel zur Verfügung gestellten 32
Register weidlich aus. Bei Prozessoren wie 6502 o.ä. muß man mit den
Registern geizen - aber warum sollte man das beim Atmel tun? Es kommt
schlußendlich auch auf die Hardware an. Wenn ich einen MCS-51 mit
externem RAM verwende, haue ich da auch alles rein, bevor ich ein
Register verschwende.
der Vorteil ist einfach erklärt: die Routine wird unabhängig vom
jeweiligen Programm und kann ohne Änderungen und erneute
Prüfung/Fehlersuche in anderen Programmen weiterverwendet werden und es
gibt keinen Registerkuddelmuddel. Der Preis dafür ist einmal etwas mehr
Arbeit und etwas längere Laufzeit. Ich habe nicht gesagt, daß man daß so
machen muss - man kann.
Natürlich benutze ich auch direkte Registerübergaben, besonders bei
kleinen, speziellen Programmen, die in einem Rutsch geschrieben werden.
Mein allgemeiner Ansatz:
R0 reserviert für LPM
R1 reserviert als sreg_bak
R2-R9 oft benutzte Variablen im Hauptprogramm
R9-R17 temporäre Register für Unterprogramme, können dort frei verwendet
werden und brauchen nicht gesichert werden
R18-R25 wie R2-R9
R26-R31 reseviert
Das System halte ich ein und kann jederzeit ausgetestete Routinen
einbinden, ohne mich um irgendeine Anpassung derselben kümmern zu
müssen. Und die dann eingesparte Zeit und Fehlersuche ist es mir allemal
wert, die paar Byte Code und ein paar Takte Laufzeit zu opfern. Wie
gesagt, es muß keiner so machen, bei mir hat es sich bewährt. Sicher
ist es nichts für einen, der alle halbe Jahre mal einen MC programmiert