Hallo liebe Mitstreiter, habe vor ein paar Wochen mit diesem kommodigem AVR-Assembler begonnen und erwecke nun einige Hardwarekomponenten (die üblichen Verdächtigen) zum Leben. Aus der Hochsprachenprogrammierung habe ich noch die dumme Angewohnheit meinen Code auf Wiederverwendbarkeit und schlanke Schnittstellen zu trimmen, sodass ich keine Monolithen bekomme und nie mit Codekopieren arbeiten muss. Das ist in Assembler gar nicht so leicht, insbesondere wenn kein Betriebsystem und damit keine Speicherverwaltung vorhanden ist wie auf meinen ATmega-Systemen. Wenn ich nun Assembler-Dateien, z.B. für UART-Zugriff und Display-Ansteuerung für Wiederverwendbarkeit gestalte, habe ich Schwierigkeiten und erhoffe hier ein paar Hinweise von Euch zur Gestaltung von Projekten mit mehreren Dateien. Beispiel: ich habe Basisroutinen zur Ringpufferverwaltung auf die die UART-Routinen aufbauen (ich arbeite mit blockierendem Senden und gepuffertem Empfang). Die UART-Routinen sollen wiederum im Hauptprogramm verwendet werden, welches aber möglicherweise selber auch Ringpufferroutinen verwendet. In C und C++ und ähnlichen bindet jedes "Modul" das ein, was es benötigt und Präprozessor oder ein anderer Mechanismus verhindert doppeltes Kompilieren. Im Moment muss ich immer auf die Reihenfolge der includes von asm-Dateien achten und teilweise deren defs in eigene .inc-Dateien auslagern, damit ich diese .inc-Dateien oben im Hauptprogramm und die asm-Dateien weiter unten einbinden kann. Gibt es dazu Hinweise und Verfahren, die sich bewährt haben, vielleicht ähnliche Krücken wie in C mit #ifndef MEINEDATEI_H usw.? Grüße, Dank und viel Spaß
Nunja, wenn ich bisher ein Projekt in Assembler aufgesetzt habe, dann meistens immer aus dem Grund, das letzte Bisschen Geschwindigkeit und Rechenzeit herauszunehmen. Allein daher wars dann eben schon unpraktisch, 'generische' Module zu schreiben und wieder Tempo zu verschenken. Kurzun: Meistens immer ein langes ASM-Listing :->
Man kann mit include dateien arbeiten... {$include mymath.asm} oder so.
Jan Schirrmacher schrieb: > Wenn ich nun Assembler-Dateien, z.B. für UART-Zugriff und > Display-Ansteuerung für Wiederverwendbarkeit gestalte, habe ich > Schwierigkeiten und erhoffe hier ein paar Hinweise von Euch zur > Gestaltung von Projekten mit mehreren Dateien. Schau Dir mal amforth (amforth.sf.net) an, dort werden einige viele Assemblersourcen nach Bedarf zusammengestellt. Die Schnittstelle zwischen den Modulen ist relativ einfach als Stack ausgebildet. Wiederverwendung und austauschbare Module sind ebenfalls an Bord.
Wenn du in C genau das kannst, was du willst, warum schreibst du deine Programme dann stattdessen in Assembler?
Warum wird immer wie selbstverständlich die Annahme gemacht, daß man seine Code-Module von vornherein möglichst generisch gestalten und auf Wiederverwendbarkeit trimmen sollte? Steht das in irgendwelchen Lehrbüchern, die nicht hinterfragt werden? Ich halte das für einen großen Irrtum und einen Hauptgrund für aufgeblasene, fehlerträchtige und ineffiziente Software. Insofern schließe ich mich meinen Vorrednern an, gerade wenn es um Assembler-Programmierung geht. ASM-Codeschnipsel sind per definitionem weder portabel noch generisch. Was nicht heißt, daß man nicht aus einem alten Projekt bewährte Codesequenzen in ein neues kopieren und dort anpassen sollte.
Hi Hier mal mein Ansatz am Beispiel Dog-Grafik-LCDs. Für die ASM-LIB existiert eine Konfigurations-Datei in der alle notwendigen Konstanten festgelegt und die verwendeten Unterprogramme freigeschaltet werden. Diese wird in das Projektverzeichnis kopiert, konfiguriert und am Anfang des Hauptprogramms mit include eingebunden. Die ASM-Datei befindet sich in einen anderen Verzeichnis, das mit 'Additonal include path' für den Assembler sichtbar ist. MfG Spess
Rolf Magnus schrieb: > Wenn du in C genau das kannst, was du willst, warum schreibst du deine > Programme dann stattdessen in Assembler? ooch, ich finde maschinennahe Sachen sehen in Assembler besser aus. Beispielsweise sind die verschiedenen Bitroll-Befehle kaum unterstützt. Außerddem kann man in Assembler sehr wohl strukturiert programmieren. Das hängt aber ein wenig an einer eventuell vorhandenen Betriebsystemstruktur ab. Habe solche Systeme in jungen Jahren entwickelt. Aber hauptsächlich finde ich AVR so schön Back-To-The-Roots. Da passt eben Assembler. Aber dein Einwand ist natürlich prinzipiell berechtigt.
Oliver Döring schrieb: > Warum wird immer wie selbstverständlich die Annahme gemacht, daß man > seine Code-Module von vornherein möglichst generisch gestalten und auf > Wiederverwendbarkeit trimmen sollte? Steht das in irgendwelchen > Lehrbüchern, die nicht hinterfragt werden? > > Ich halte das für einen großen Irrtum und einen Hauptgrund für > aufgeblasene, fehlerträchtige und ineffiziente Software. Das steht sehr wohl in Lehrbüchern drin. Und Assembler ist nicht gleichbedeutend mit Unstrukturiert. Die Strukturen sind eben bloß nicht portierbar.
Dein Problem rührt einfach daher, dass der AVR Assembler nur ein recht einfacher Assembler ist. Mit dem AVR-GCC kann man sehr wohl mehrere Module getrennt assemblieren und dann zu einem einzigem ELF oder HEX zusammen linken. Symbole kann man dann in einem Modul (glaub per 'public') auch nach aussen sichtbar machen und von wo anders her aufrufen. Schau dir am besten mal die Man-Pages zum AVR-GCC an. Einziger Stolperstein ist hier, dass der Linker immer noch den Einsprungpunkt in der C-Startup Funktion vermutet. Aber das liess sich auch deaktivieren, müsste nochmal nachschauen wo das war. Wenn man meist auf dem selben AVR arbeitet, kann es schon Sinn machen die gut optimierten Assembler Routinen in ein Modul zu schieben und dann per #define abhängig vom konkret verwendeten AVR festzulegen welcher Code erzeugt wird. Mit jedem neuem AVR wächst dann das Modul-Listing eben etwas. Gruß, Ronny
> Warum wird immer wie selbstverständlich die Annahme gemacht, daß man > seine Code-Module von vornherein möglichst generisch gestalten und auf > Wiederverwendbarkeit trimmen sollte? Steht das in irgendwelchen > Lehrbüchern, die nicht hinterfragt werden? Na endlich spricht das mal jemand an! Die meisten Projekte haben nämlich immer wieder etwas andere Anforderungen an das konkrete Modul. Da fährt man einfach dreimal besser, wenn man den Code kopiert und nicht bloss einbindet. Insbesondere treten so in alten Projekten nicht plötzlich Bugs aufgrund von Änderungen in den Bibliotheken auf. Natürlich, wenn man am Anfang die Module perfekt durchplant und sehr flexibel gestaltet, dann sollten weder Änderungen an den Bibliotheken noch Kompatibilitätsprobleme mit älteren Projekten auftreten. Aber jeder hier kennt die Grenzen von Vorausplanung (und -sehung) sowie Bugfreiheit. Und Assemblerprojekte zu modularisieren ist sowieso ein Widerspruch in sich - wer heute auf Mikrocontrollern in Assembler programmiert, der will ja gerade maximale Effizienz und/oder exakt vorhersehbares Zeitverhalten. Das beisst sich aber mit generischen Schnittstellen ganz gewaltig.
Hi, kann auch nur WinAVR empfehlen, arbeitet mit AVR Studio zusammen (wenn man das denn will) und man kann ASM und C kombinieren. In meinem aktuellen Projekt (12x12 LED-Matrix mit Software PWM und diversen auf dem AVR selbst berechneten Effekten) habe ich im C-Programm mehr die Steuerfunktionen und Berechnungen. Im ASM Teil habe ich die Sachen, die oft aufgerufen werden und Schleifen beinhalten (PWM, kopieren von Speicherbereichen, Linien zeichnen etc.). Zwangsweise ergeben sich zum C-Programm entsprechende Schnittstellen die auch ein paar Zyklen kosten (Register auf Stack sichern etc.), auch habe ich einige Programmteile ziemlich allgemein gehalten bzw. paramtrierbar gemacht. Aber ich schätze alles in allem dürfte der Vorteil durch direkten ASM-Code noch min. Faktor 2 sein was auf einem µC schon einiges ausmacht. Ciao... Markus
Markus M. schrieb: > kann auch nur WinAVR empfehlen, arbeitet mit AVR Studio zusammen (wenn > man das denn will) und man kann ASM und C kombinieren. Sehr gut, so langsam wird mir der Hintergrund klarer. Der AVR-Assembler des AVR-Studio arbeitet ohne Linker (da er eh keinen Code zu relozieren hat) und daher funktioniert Modularisierung auf Linker-Ebene sowieso nicht. habe mich auch schon gefragt, wie man dann C und Assembler mischen kann (ausser inlne Assembler) ohne Linker. Aber der Hinweis mit WinAVR ist interessant in diesem Zusammenhang, da ich über kurz oder lang die Gurken auch mal in C programmieren werde. Dann auch für die Hinweise der anderen Poster. Die Diskussion über den Sinn des Assemblerprogrammierens ist berechtigt und ich sehe auch das Problem der Codeanpassung, z.B. an andere Port-Konfigurationen, das strukturiert nur aufwändig zu realisieren ist. Wahrscheinlich denke ich zu softwarelastig. Mir ist gerade eingefallen, dass die AVRs recht billig sind und daher eine Modularisierung auf Hardwareebene ebenfalls in Frage kommt. So könnte ich mein LCD-Modul einfach mit einem ATTiny ausstatten und ein simples SPI-Interface implementieren mit einem eigenen Softwareprotokoll und schon wäre die Sache einfacher in andere Projekte zu integrieren. Haach. Das bringt schon Spaß mit diesen kleinen Chips.
Ich finde modulare Programmierung schon sehr sinnvoll. Z.B. beim LCD möchte ich mir nicht vorschreiben lassen, welche Pins ich nehmen soll, da man dann oft die Spezialfunktionen (UART, PWM, ADC, ...) verliert. Da nehme ich einfach nen Universalcode für beliebige Pins: http://www.mikrocontroller.net/attachment/30300/lcd_drv.zip Auch wird durch die Modularisierung die Programmierung deutlich leichter und fehlersicherer. Ein einmal entwickelter Modul wird benutzt und gut is. Programme über 2kB möchte ich aber nicht mehr in Assembler proggen müssen, das ist mir deutlich zu aufwendig. C-Compiler sind garnicht so schlecht, etwa 5..20% Overhead bezüglich Größe und Geschwindigkeit ist doch kein großer Nachteil. Die 5..20% gelten aber nur für erfahrene Assemblerprogramierer. Assembleranfänger erreichen oft das Gegenteil, d.h. größeren und langsameren Code als der C-Compiler. Was nützt es, einige Befehle einzusparen um damit bezogen auf das Gesamtprogramm 0,1% CPU-Last weniger zu haben. Es ist einfach nur vergeudete Arbeitszeit. Peter
@oliver >Ich halte das für einen großen Irrtum und einen Hauptgrund für >aufgeblasene, fehlerträchtige und ineffiziente Software. Das Stimmt so nicht ganz. Aufgeblasene Software hat man doch ohnehin meist "nur" auf dem PC, wo es eben nicht auf ein Megabyte genau ankommt. Beim AVR hat man eben aber kein Megabyte. Und ich behaupte mal das wenn man sich die Schnittstellen seiner Module vernünftig zurechtlegt dann spart man doppelt und dreifache Arbeit, selbst wenn es etwas mehr Code kostet. Und wie Peter schon erwähnte, bei großen Projekten ist Assembler einfach zu aufwendig, selbst wenn man dann vllt ein paar Prozent eingespart hat. Zumal hat man bei Assembler eben nicht die Möglichkeit den Code für einen anderen Prozessor (bzw andere Prozessorfamilie) einzusetzen und muß dann wirklich alles neu schreiben. Assembler ist dann eine Alternative, wenns auf jeden Taktzyklus oder jedes Byte ankommt.
Oliver Döring schrieb: > Warum wird immer wie selbstverständlich die Annahme gemacht, daß man > seine Code-Module von vornherein möglichst generisch gestalten und auf > Wiederverwendbarkeit trimmen sollte? Steht das in irgendwelchen > Lehrbüchern, die nicht hinterfragt werden? Das zeichnet einfach die Entwickler aus, die etwas weiter als bis zum aktuellen Projekt denken. ;) Wiederverwendbarer Code spart nicht nur Arbeitszeit, er sorgt auch für bessere getestete Software und - was immer wieder vergessen wird - er ist oft deutlich besser strukturiert und damit besser wartbarer. Außerdem macht sich der Entwickler vorher eher Gedanken über einen sinnvollen Aufbau. > Ich halte das für einen großen Irrtum und einen Hauptgrund für > aufgeblasene, fehlerträchtige und ineffiziente Software. Nö. Aufgeblasen und fehlerträchig wird es erst, wenn der Entwickler nicht verstanden hat, wie weit die Wiederverwendbarkeit sinnvoll getrieben werden kann. Und auch dann, wenn sich der Entwickler keinerlei Gedanken um Wiederverwendbarkeit macht und einfach drauflos programmiert. Ich versuche meinen Code auch immer so zu strukturieren und die Schnittstellen von Funktionen so zu gestalten, dass ich die auf möglichst vielseitige Art in verschiedenen Projekten nutzen kann. Der Code ist deswegen kein bisschen aufgeblasen.
Hi >C-Compiler sind garnicht so schlecht, etwa 5..20% Overhead bezüglich >Größe und Geschwindigkeit ist doch kein großer Nachteil Ich habe hier noch keinen disassemblierten C-Code gesehen der diese Bedingung erfüllt hätte. >Programme über 2kB möchte ich aber nicht mehr in Assembler proggen >müssen, das ist mir deutlich zu aufwendig. Wenn du davon 1,5k aus der Schublade holen kannst, kein Problem. >Zumal hat man bei Assembler eben nicht die Möglichkeit den Code für >einen anderen Prozessor (bzw andere Prozessorfamilie) einzusetzen und >muß dann wirklich alles neu schreiben. Das hat sich auch erledigt, wenn du in C auf irgendwelche IO-Register zugreifst. MfG Spess
>Das hat sich auch erledigt, wenn du in C auf irgendwelche IO-Register >zugreifst. Wenn der Code wiederverwendbar werden soll benutzt man i.d.R. Makros die man bei Wiederverwendung innerhalb der selben Prozessorfamilie einfach nur anpasst. Beispiel 7-Segmentanzeige : #define LED7_DATA_PORT PORTA #define LED7_DATA_DDR DDRA #define LED7_STRB_PORT PORTD #define LED7_STRB_DDR DDRD #define LED7_STRB_PIN 4 #define LED7_SIZE 3 #define LED7_STRB_MASK 0x70 #define LED7_DATA_INIT LED7_DATA_DDR = 0xff; #define LED7_STRB_INIT LED7_STRB_DDR |= LED7_STRB_MASK; #define LED7_STRB(x) LED7_STRB_PORT = \ (LED7_STRB_PORT & ~LED7_STRB_MASK) | \ (1 << (LED7_STRB_PIN + (x))) #define LED7_DATA(x) LED7_DATA_PORT = (x) void v7SegInit (void); void v7SegDoTimer (void); void v7SegSet (char cSeg, unsigend char cDigits); So ähnlich könnte ein 7-Segment-Modul-Header aussehen. Der eigentliche Quellcode lässt sich für alle Prozessoren der selben Familie verwenden. Außerdem kann man durch Anpassung der Makros LED7_DATA und LED7_STRB die Anzeige auch über einen I2C Bus, ein Schieberegister oder dergleichen für den Anschluß realisieren. Wenn das ganze z.b. für einen ARM7 portiert werden soll, passt man einfach nur die Makros LED7_DATA, LED7_DATA_INIT, LED7_STRB und LED7_STRB_INIT an. Bei komplexeren Sachen die die direkte Hardware des Prozessors betreffen wie z.b. UART, I2C, SPI muß das Modul neugeschrieben werden. Aber bei Verwendung der gleichen Software-Schnittstellen kann darauf basierende Hardware (z.b. LCD via SPI oder I2C EEProm) bei entsprechender Kapselung (meist) ohne weiteres angesprochen werden. Alle weiteren Software-Module die nichts mit der Hardware zu tun haben, Middleware, Tools und Ablaufsteuerungen lassen sich fast gänzlich ohne Anpassungen portieren, dadurch das da eben keine Registerzugriffe gemacht werden.
Hi >Wenn der Code wiederverwendbar werden soll benutzt man i.d.R. Makros die >man bei Wiederverwendung innerhalb der selben Prozessorfamilie einfach >nur anpasst. Geht in Assembler ebenso. Sieh dir mal meinen ersten Anhang vom 17.1. an. Das Problem ist, das viele die Möglichkeiten des Assemblers/Preprozessors nicht nutzen oder gar nicht kennen. MfG Spess
spess53 schrieb: > Ich habe hier noch keinen disassemblierten C-Code gesehen der diese > Bedingung erfüllt hätte. Nimm einfach mal das oben erwähnte LCD-Beispiel und versuch es einzudampfen. Ich kann Dir auch das Assemblerlisting erzeugen (mit eingeschalteter Optimierung). Es gibt natürlich viele C-Programme, die aufgebläht sind. Sehr oft sieht man z.B. daß gedankenlos int genommen wird, obwohl uint8_t ausreicht. Klar, daß sich dann der Code verdoppeln muß, wenn man 16Bit auf nem 8Bitter macht. Peter P.S.: Hast Du ein schönes Assemblermacro, was es auch gestattet, Portpins bequem als Bitvariablen zu definieren. D.h. Bitnummmer und Adresse in einem Namen abzulegen? Das gefällt mir am GCC sehr, hat mir auch am Keil C51 schon gut gefallen.
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.