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.
ein Status Bit setzen (Boolean). Der Schalter verriegelt sich selbst. Erst nach dem Loslassen setzt er das ST Bit zurück.
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.html http://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
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.