Sonst habe ich hier Erics Ankündigung meist auch übersetzt, aber erstens ist sie diesmal ziemlich lang, zweitens bin ich gesundheit- lich noch nicht ganz auf dem Posten und möchte mir das daher sparen. Damit also nur mal kurz der Hinweis auf Erics originale Ankündigung in AVRfreaks: http://www.avrfreaks.net/phorum/read.php?f=2&i=11301&t=11301#11301 Wenn jemand mit irgendeiner englischen Formulierung dort ein arges Verständnisproblem hat, möge er hier fragen, und ich werde mich bemühen, das zu erläutern.
Wenn ich das richtig verstanden habe, wurde leider nur an irgendwelchem Zusatz-Schnulli rumgedoktert. Der Compiler selber wurde leider nicht angefaßt. Das Bit0-Problem ist jedenfalls immer noch da (siehe Anhang). Einen Bit-Test von 4 Byte auf 16 Byte aufzublähen sieht erstmal nicht viel aus, aber das ist ja auch nur ein minimiertes Beispiel. Über einen größeren Code läppert sich da doch ganz schön was zusammen. Überhaupt ist der WINAVR sehr 16-Bit lastig, womit man ja eine 8-Bit-Maschine doch ganz schön quält. D.h. er erweitert fast immer auf 16 Bit um schließlich das High-Byte wieder wegzuschmeißen. Auch die Bibliotheken wollen wohl die heilige 16Bit-Kuh auf gar keinen Fall schlachten, obwohl ich bezweifeln will, daß jemals einer ein AVR-UNIX bauen will. Was ich dagegen vermisse, und was wirklich sinnvoll ist, sind die generic pointer, wie z.B. beim Keil. Ist ja hübsch, daß alle Funktionen mehrfach vorhanden sind, je nachdem ob Flash, SRAM oder EEPROM. Bloß damit ist leider die ganze ANSI-Kompatibilität zum Teufel. Aber die 3-Byte Pointer beim Keil, wo dann das 3. Byte den Speichertyp definiert sind einfach absolut genial. Man hat dann wieder einen flat-memory vom Programmierer aus gesehen und kann ANSI-Code schreiben. Der Compiler legt die printf(), schanf() Formatstrings usw. automatisch ins Flash und verschwendet keine Rechenzeit + SRAM mit unnützen Kopierorgien. Was ich mir noch wünschen würde ist, daß ich für Assembler-Interrupts Register reservieren könnte, die dann der Compiler auch in Ruhe läßt. So ein PUSH/POP kostet ja schließlich 4 Zyklen und das ist bei einem Interrupt schon sehr teuer. Beim Keil geht man den Weg, daß ja in etwa 90% der Fälle nur bis zu 3 Paramter einer Funktion übergeben werden und die restlichen 10% können ja den Stack benutzen. D.h man muß doch nicht unbedingt 32 Register freihalten, für den Fall, daß einer 16 Parameter übergeben will und so unvernünftig ist, dies nicht mit einem Pointer zu machen. Beim Keil gibts da die USING-Direktive, damit kann man einen Registersatz reservieren und damit ist der dann für den Linker tabu. Ich will das jetzt nicht als Meckerei verstanden wissen, sondern ich wollte nur mal aufzeigen, wo noch massig Verbesserungspotential vorhanden ist. Beim Keil mußte ich jedenfalls mein Vorurteil, daß C wesentlich langsameren und größeren Code erzeugt, aber derart gründlich revidieren, da könnte ich mich heut noch schämen. Aber beim AVR sind da immer noch große Unterschiede, die den Assembler oftmals nur so davonpreschen lassen. Peter
Peter, Welche avrgcc Version verwendest du ? Ich kann dein Beispiel mit der aktuellen WinAVR Version 20030913 gar nicht kompilieren, wegen einer Fehlermeldung bei #include <signal.h> Beim aktuellen AVRGCC sind die AVR spezifischen Includes in "avr" Unterverzeichnis, deshalb muss die includes ändern: #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> Mit dieser Modifikation kann ich dein Beispiel mit dem Sample Makefile von WinAVR-20030913 fehlerfrei kompilieren, der Test auf Bit0 wird aber effizient durchgeführt: SIGNAL (SIG_OVERFLOW0) { ca: 1f 92 push r1 cc: 0f 92 push r0 ce: 0f b6 in r0, 0x3f ; 63 d0: 0f 92 push r0 d2: 11 24 eor r1, r1 d4: 2f 93 push r18 d6: 8f 93 push r24 d8: 9f 93 push r25 unsigned char i; i = PIND; da: 80 b3 in r24, 0x10 ; 16 if( i & 1 ){ dc: 99 27 eor r25, r25 de: 80 ff sbrs r24, 0 <- Test auf Bit 0 ! e0: 09 c0 rjmp .+18 ; 0xf4 if( i & 2 ) e2: 20 91 00 01 lds r18, 0x0100 e6: 81 ff sbrs r24, 1 e8: 02 c0 rjmp .+4 ; 0xee j--; ea: 21 50 subi r18, 0x01 ; 1 ec: 01 c0 rjmp .+2 ; 0xf0 else j++; ee: 2f 5f subi r18, 0xFF ; 255 f0: 20 93 00 01 sts 0x0100, r18 } } f4: 9f 91 pop r25 f6: 8f 91 pop r24 f8: 2f 91 pop r18 fa: 0f 90 pop r0 fc: 0f be out 0x3f, r0 ; 63 fe: 0f 90 pop r0 100: 1f 90 pop r1 102: 18 95 reti
@Peter Ich verwende schon die neue, aber ich habe alle Includes nach "include" verschoben, da ich keine Lust habe, dem AVR eine Extrawurst zu braten. Alle anderen Compiler benutzen ja auch den Standard-Pfad. Warum es bei Dir geht ist mir eine Rätsel. Ich benutze folgende Batch: avr-gcc.exe -Os -mmcu=at%mcu% -Wall -g -o main.out *.c avr-objdump.exe -t -h -S main.out >%main%.lst avr-objcopy.exe -O ihex main.out %main%.hex Benutzt Du vielleicht spezielle Compiler-Optionen ? Peter
Hi ich kann das Ergebnis von Peter F. nur bestätigen. Irgendwas machst du wohl verkehrt. Ich hab mal ein makefile angehängt dem du meine Compileroptionen entnehmen kannst. BTW: Warum alles was zum avr gehört in /avr steht hat ja Jörg schonmal erklärt. Matthias
@Matthias, danke, ich hab mir Dein Make angesehen und einen Unterschied festgestellt: Sobald ich als Quellfile "test.c" eingebe gehts. Wenn ich aber "TEST.C" eingebe kommt mein obiges Listing raus. Da muß aber erstmal einer drauf kommen. In der DOS-Box (W98SE) wird aber "*.c" grundsätzlich nach Großschrift expandiert. Weiß da jemand Rat ? Warum macht der Compiler überhaupt diesen Unterschied ? Gibts da vielleicht einen Compilerschalter ? Peter
Das WinXX-Filesystem merkt sich die Gross/Klein Schreibung bei File/Directory Namen, nur der DOS-Box ignoriert es. Der Windows-Explorer zeigt aber die Gross/Klein Schreibung von Pathnamen richtig an. Das Makefile zusammen mit Make und den avr-gcc Tools erkennen aber die Gross/Kleinschreibung von Filenamen korrekt. Deshalb sollte zum kompilieren das Makefile verwendet werden und nicht ein Batch File. Tips wie man das WinAVR Beispiel Makefile anpassen muss gibt's hier: http://www.avrfreaks.com/filednload.phpurl=/Tools/ToolFiles/376/install_config_WinAVR.pdf
Und hier der vollständige Link: http://www.avrfreaks.com/Tools/ToolFiles/376/install_config_WinAVR.pdf
Ich hab jetzt einfach in avr-gcc.exe ".C" durch ".Z" ersetzt. Nun funktioniert alles wie gewünscht. Der Grund, warum ich kein Make verwenden will ist, daß es nicht mit Platzhaltern "*.c" zurechtkommt. D.h. ich müßte jedesmal alle Source-Files manuell eintragen. Und eine Batch kann das eben. Peter
Hi das macht man einmal für das Projekt und verwaltet dann pro Projekt ein makefile. Und in einer Zeile einmal den Dateinamen einzutragen ist ja durch den Komforforteil von makefiles locker aufgewogen. Aber wir kennen ja deine Einstellung: "Was der Bauer nicht kennt das frisst er nicht" ;-) Matthias
@Matthias, "...das macht man einmal für das Projekt..." ich arbeite nach der Top-Down Methode, d.h. es entstehen viele kleine Objekte erst wärend des Programmierens. 50 Source Files sind bei mir nichts ungewöhnliches. Ich schreibe nach dem Adlersuchsystem und da ist jede Tipperei-Einsparung herzlich willkommen. "...Komforforteil von makefiles..." Ich weiß, die Batch kompiliert immer alles komplett, das fällt aber auf den heutigen PCs kaum ins Gewicht. Dafür hat die Batch eben den Vorteil der Wildcards und auch Errorlevel kann man auswerten oder Abfragen machen usw. "...Was der Bauer nicht kennt..." Ich würde eher sagen "Warum soll ich was anderes nehmen, wenn sich das bisherige bereits glänzend bewährt hat" Bestimmt würden sich auch umgekehrt ne Menge GCC-Gurus mit dem Keil schwer tun. Das ist ja nur menschlich. Und mit meinem GCC-Patch bin ich auch voll zufrieden. Peter
@Peter Danneger: Bin gerade aus dem Urlaub zurück und kann mich daher erst heute Deiner ,,Meckerliste'' widmen. Erstmal: Du hast das Prinzip von WinAVR und Opensource generell einfach noch gar nicht verstanden. WinAVR stellt lediglich existierende Sachen Windows-Nutzer-freundlich zusammen, nicht mehr, aber auch nicht weniger. Eric Weddington hat damit genügend Arbeit (da ein guter Teil dieser Software nun einmal unter Unix entstanden ist und nach wie vor vorrangig dort gepflegt wird), so daß er selbst nur an einigen Teilen aktiv mitentwickelt (namentlich avr-libc und avrdude). ,,wurde ... leider nur an irgendwelchem Zusaatz-Schnulli rumgedoktort'' zeigt, daß Dir das grundlegende Gefühl für Opensource fehlt. Es ist nicht ,,man'', der hier etwas entwickelt, sondern es sind die Leute, die es tun. Jeder Einzelne. Damit etwas passiert, benötigt es eine Triebkraft, die besteht zu einem gewissen Prozentsatz auch und natürlich aus Ehrgeiz, aber auch zu einem Gutteil daraus, daß jemand in irgendeiner Form eine Motivation besitzt. Wenn Du also irgendein Feature ganz besonders dringend benötigst, dann bist genau Du es, der diese Triebkraft besitzt. Im Gegensatz zu kommerzieller Software bezahlst Du aber niemanden dafür, daß er Dir das dann irgendwann implementiert oder auch nicht. Du kannst nun also entweder hoffen, daß irgendjemand anders das Feature auch mal für so wichtig findet, daß er dahinein Zeit investieren möchte -- oder Du fängst es selbst an. ,,Kenn ich mich ja gar nicht aus!'' ist übrigens keine akzeptable Ausrede. Das ist wie mit der Regel, daß man nur Pilze essen soll, die man kennt: zum Zeitpunkt seiner Geburt kennt man gar keine, und die Kentnis über alle anderen erwirbt man sich mit der Zeit. Wenn Dir dieses Opensource-Modell nicht gefällt, nun, dann bist Du hier einfach falsch. Dann such Dir einen kommerziellen Anbieter, kaufe sein Produkt, und benutze dessen Support. ,,WinAVR'' ist übrigens nicht ,,16-bit lastig'', kann es gar nicht sein: es ist ja nur eine Sammlung von Werkzeugen. Du meinst vermutlich den gcc (in seiner Ausprägung als avr-gcc), aber dann schreib das bitte auch. Daß `int' standardmäßig 16 bits breit ist, ist, und das sollte Dir nicht fremd sein (Du bist ja kein C Neuling) eine Forderung des C-Standards. Daß Ausdrücke in C oftmals als `int' interpretiert werden, ist ebenfalls eine Forderung des Standards. Wenn Dir das alles nicht gefällt, weil Du 5000000 Mobiltelefone im Jahr verkaufen willst und daher jeden Cent bei den Controllern sparen mußt, dann ist vermutlich C gemäß dem anerkannten Standard nicht die Programmiersprache Deiner Wahl. Daraus kannst Du Dir mehrere Auswege suchen: . Du kannst in Assembler programmieren. Hohe Softwarekosten, aber die amortisieren sich trotzdem durch die Stückzahl. . Du kannst einen Compiler für eine Programmiersprache benutzen, die besser auf die Bedürnisse von Microcontrollern ausgerichtet ist als C -- wenn Du denn einen findest, der gut genug ist, auch sonst das nötige sinnvolle Maß an Optimierungen zu bieten, an die man sich bei guten C-Compilern eben gewöhnt hat. . Du kannst versuchen, einen Kompromiß zu finden. Das soll natürlich keineswegs heißen, daß die Optimierungen des avr-gcc nicht an manchen Stellen auch noch besser sein könnten. Pathologische Fälle lassen sich ohnehin immer leicht finden, aber auch Dinge, die im sehr praktischen Betrieb nennenswertes Potential für Einsparungen bieten, sind zu finden. Es sei an die Möglichkeit einer globalen Substitution von CALL/JUMP durch RCALL/RJMP bei den MCUs > 8 KB ROM verwiesen. Wurde in der avr-gcc Liste schon als Möglichkeit diskutiert. In die dritte Variante da oben kann man -mint8 vom avr-gcc hinein sortieren. Der Compiler verläßt damit die Forderungen des C Standards, wird im Gegenzug ein wenig resourcensparender. Der Haken bei der Sache ist aber einfach, daß die Standard-C- Bibliothek in ihren Funktionsprototypen durch den Standard festgelegt ist und daß dort sehr oft ein Typ `int' vorgeschrieben ist, der von der Forderung implizit ausgeht, daß ein `int' mindestens den Wertebereich -32767 bis +32767 umfassen muß. Als Beispiel sei mal getc() genannt, das in der Lage sein muß, einen von allen legalen Zeichen-Werten verschiedenen Wert mit dem Namen EOF zurückzugeben. Diese Forderung läßt sich mit einem 8-Bit Typ `int' nicht realisieren, wenn zugleich auch die Forderung nach den legalen Zeichenwerten 0...255 gestellt wird. (Für reines 7-bit ASCII wäre es mit einem 8-bit `int' realisierbar, aber wer will das schon?) Unseren Standpunkt zur -mint8 Kompatibilität der avr-libc bzw. deren Header-Files habe ich neulich auf der avr-libc Entwicklerliste mal zusammengefaßt und dabei keinen Widerspruch seitens der anderen Entwickler geerntet: wird werden nichts tun, um das Funktionieren von -mint8 zu behindern, wir werden gemeldete Bugs nach Möglichkeit beseitigen (wenn ein Patch beiliegt, der keine weiteren Neben- wirkungen besitzt, dann steigen die Chancen für die Beseitigung logischerweise), aber die ausgelieferte libc.a ist halt ohne -mint8 compiliert und folglich zu einem mit -mint8 compilierten Objekt nicht ABI-kompatibel. Ein möglicher Ausweg daraus wäre, daß man bei -mint8 auch noch eine separat compilierte libc.a linkt (das kann der Compiler-Treiber automatisch machen), die dann selbst mit -mint8 compiliert worden ist. Aber siehe obiges Beispiel, ein Teil der derzeit in der Bibliothek enthaltenen Funktionen ist wohl damit gar nicht sinnvoll compilierbar, ein Teil wiederum wird nicht mit standardkonformen Funktionsprototypen compilierbar sein. Darum könnte man sich mit geschickten #ifdefs und cpp-Makros herummogeln, d. h. im Falle von -mint8 mutieren die Header-Files ihre Prototypen in solche, die nicht mehr standardkonform sind (stört nicht, da der ganze Compiler ja ohnehin nicht mehr konform ist) bzw. bieten keinen Prototypen für die dann nicht vorhandenen Funktionen an. Nicht zu vergessen sei die Dokumentation: für jedes Feature, jede Funktion wäre dann anzugeben, ob es bei -mint8 verfügbar ist (und ggf. der abweichende Prototyp). Das ist ein gutes Stück arbeit, aber wenn das jemand ernsthaft vorhat und es dann auch bereit ist weiter zu pflegen, wird es sicherlich keinen Widerspruch geben. Von den derzeit vorhandenen aktiven avr-libc Entwicklern hat niemand die nötige Motivation dafür. Die Argumentation ,,es solle ja kein Unix darauf laufen'' ist übrigens so ziemlich die dämlichste, die ich je gelesen habe. Erstens gibt es sicherlich eine Reihe von Mini-Betriebssystemen für MCUs, und viele davon würden vermutlich genaus übern Haufen fallen wie ein (altes) Unix, wenn `int' plötzlich nicht mehr standardkonform wäre. Zweitens sind selbst die ältesten Compiler für Mikroprozessoren, die ich so kenne, vor allem auch Turbo Pascal für CP/M (aber auch alle CP/M C-Compiler) von 16-bit integer Datentypen ausgegangen -- auf einer viel schwachbrüstigeren CPU als einem AVR. Die `generic pointer' klingen auf den ersten Augenblick ja hübsch, aber sind natürlich eine arge Pessimierung: zur Laufzeit muß dann für jeden Zeiger geprüft werden, in welchem Speichersegment er liegt -- obwohl wahrscheinlich bis auf wenige Ausnahmen der Programmierer bereits gewußt hat, in welchem Segment er liegt, und er (statt der Bibliothek) hätte die passende Funktion bereits wählen können. Aber ansonsten: nur zu, diskutiere Deine GCC Patches auf der avr-gcc Liste mit anderen Entwicklern, ich denke schon, daß Du diesen oder jenen dafür begeistern könntest... Gleiches gilt natürlich für Deine Idee, in ISRs bestimmte Register zu reservieren, wenngleich ich hier vielleicht sehe, daß Du sogar jemanden finden könntest, der die Idee aufgreift und die Implementierung vorantreibt, wenn Du es nur bei den passenden Leuten diskutierst. Allerdings solltest Du Dich natürlich vorher über die tatsächlichen Gegebenheiten bessern informieren :), so werden natürlich keinesfalls 32 Register für Parameter reserviert sondern nur 8 -- und das genügt schon dann nicht mehr wirklich, wenn da außer bißchen Klimbim vielleicht noch ein uint32_t oder float ins Spiel kommt. Der Rest wird dann dennoch in Registern übergeben, aber nicht mehr in reservierten, d. h. der Aufrufer muß mit PUSH/POP arbeiten (was offensichtlich insgesamt effektiver ist, als wenn der Aufgerufene die Parameter laufend wieder aus dem Stack popel müßte). printf()-Formatstrings kannst Du auch beim avr-gcc im ROM haben, Du hast die volle Kontroller: benutze PSTR() und die *_P() Varianten der Funktionen, und schon bist Du dabei. Der Zugriff auf die Strings ist dann naturgemäß langsamer, aber im umgekehrten Falle hättest Du wiederum ja keine Möglichkeit, den Compiler zu zwingen, die schnellere Variante (String im RAM) überhaupt zu generieren. Nachdem ich auch ein bißchen im AVR-Butterfly-Code gestochert habe (der ja für den IAR geschrieben ist, der vieles automatisch im ROM handhaben kann) bin ich mittlerweile gar nicht mehr so sehr davon überzeugt, daß diese Automatismen wirklich so gut sind, wie ich anfangs dachte... Die avr-gcc Variante (die ja mehr aus der Not geboren ist, daß der GCC sehr stark von-Neumann-lastig ist) zwingt halt den Programmierer, sich immer gleich Gedanken um das Speichermodell zu machen.
@Joerg, vielen Dank für Deine ausführlichen Worte. Da habe ich Dich wohl auf dem falschen Fuß erwischt. Die Hauptsache, das Bit0-Verhalten, das mich so gestört hatte habe ich ja lösen können. Wenngleich mir zwar nicht klar ist warum die DOS-Namen "*.C" sowas bewirken, aber egal, jetzt läufts. Ansonsten ist der WINAVR schon sehr gut benutzbar, läuft auf Anhieb. Mein allererster Versuch mit dem AVR-GCC scheiterte damals schon bei der Installation von Cygwin, was ja nun nicht mehr benötigt wird. Es ist mir auch schon kar, daß die AVR-Architektur nur sehr schwer aus C optimalen Code generieren läßt. Dazu müßte man ja funktionsübergreifend eine Art Registerrenaming durchführen und sowas stelle ich mir nicht so einfach vor. Der Herr Keil hat beim C51 bestimmt auch lange getüftelt, ehe er das effiziente Data-Overlaying hingekriegt hatte. Aber selber an sowas mitzuprogrammieren traue ich mir nicht zu. Mir haben es die kleinen MC-Programme angetan, wo ich jedes Byte noch persönlich kenne. Bei den Windows-Programmen ist mir immer wieder unheimlich, womit diese denn bloß ihre hunderte MB (bald schon GB) sinnvoll ver(sch)wenden. Ein bischen hadere ich ja immer noch mit den fehlenden Interruptprioritäten beim AVR im Vergleich zum 8051. Deshalb mein Bestreben, die Interrupts so kurz wie möglich zu halten. Da ist ja gerade auch ein Thread über Registervariablen, vielleicht kann ich so Register für Interrupts benutzen. Und irgendwo werde ich wohl auch finden, wie man Interrupts in Asssembler mit in ein C-Programm einbindet. Ich verstehe durchaus, daß Anfängerfragen nerven, die immer wieder gestellt werden. Aber manchmal sind das Manual lesen und auch verstehen verschiedene Dinge, bzw. man überliest oder findet einfach nicht die richtige Stelle. Das mit dem malloc()+devopen() habe ich immer noch nicht 100%-ig kapiert, aber egal, genug die Gurus genervt :-) Peter
Nein nein, Du hast mich überhaupt nicht auf dem falschen Fuß erwischt. ;-) Mich ärgert immer nur die häufig anzutreffende ,,Konsumenten-Mentalität'' im Zusammenhang mit Opensource. Liegt vielleicht daran, daß ich Opensource schon sehr lange kenne und dann auch schnell Spaß gefunden habe, daran mitzuarbeiten (in den verschiedensten Formen, beginnend beim einfachen Patch für den Emacs, über Mitarbeit an einem Betriebssystem in teilweise ,,leitender Rolle'', bis nun zu den AVR-Sachen). Ich empfand es immer als eine Befreiung, mit Projekten arbeiten zu dürfen, bei denen man auch den Sourcecode einsehen kann. > Wenngleich mir zwar nicht klar ist warum die DOS-Namen "*.C" > sowas bewirken, aber egal, jetzt läufts. Dazu ist sicher in bißchen Unix-Background hilfreich. Unix war wohl das erste System, das sich darauf besonnen hat, daß der ASCII-Zeichensatz ja auch Kleinbuchstaben enthält. BIS DAHIN WAR ES DURCHAUS GÄNGIG, DAß DIE PROGRAMMIERER AN IHREN COMPUTERN AUSSCHLIEßLICH ALLES IN GROßBUCHSTABEN GETIPPERT HABEN. (Implizites CAPS LOCK im Terminal eingeschaltet. Sowas war, als ich mit einem PDP-11 Clone begonnen habe, auch bei uns noch gang und gäbe. Man mußte erstmal ins Terminal-Setup und das ausschalten.) Man erkannt das an vielen Dingen: das Dateisystem unterscheidet Groß- und Kleinbuchstaben, und beide bedeuten etwas Verschiedenes. Ich kann also zwei verschiedene Dateien "Makefile" und "makefile" in einem Verzeichnis haben. Auch die Programmiersprache C war wohl die erste, bei denen der Bezeichner Foo etwas anderes als foo ist -- heute sehen wir das als völlige Normalität an. Mit dem Dateisystem, das sich Groß-/Kleinschreibung nicht nur gemerkt hat (das machen praktisch alle Dateisysteme, selbst CP/M konnte das, obwohl offiziell immer alles in Großbuchstaben konvertiert worden ist) sondern auch unterscheidet, hat sich dann auch herausgebildet, daß in bestimmten Fällen Dateinamensendungen in Groß- oder Kleinschreibung eine unterschiedliche Bedeutung haben. Anders als bei CP/M, RSX-11, VMS, MS-DOS (aber genauso wie z. B. bei RIO, einem uralten Z80-Betriebssystem) ist bei Unix der Dateinamenssuffix mitsamt dem trennenden Punkt ein ganz regulärer Namensbestandteil ohne ausgezeichnete Bedeutung im Dateisystem. Er muß nicht zwingend existieren, und es kann auch beliebig viele Punkte im Namen geben. Da Unixer besonders schreibfaul waren (was der Sage nach auf die unsäglich lahmen alten 100 Bd Fernschreiber zurückgeht, die anfangs als Terminals gedient haben) und alles sehr kurz gemacht haben (zweibuchstabige Kommandos gibt es beispielsweise für alle wesentlichen Operationen), haben sie typisch auch nur einbuchstabige Suffixe gewählt. Damit hat es sich dann eingebürgert, daß beispielsweise zwischen zwei Formen von Assemblerdateien unterschieden wird, die auf .s bzw. .S enden (das ,s' steht wohl für `source', stammt also aus einer Zeit, in der noch vorwiegend Assemblerquellen geschrieben worden sind): Dateien, die auf .s enden, sind klassische Assemblerquellen, während solche, die auf .S enden, zuerst durch den C-Präprozessor gezogen werden. Damit kann man dessen Manipulationsmöglichkeiten benuten (#include, #define usw.), was sich im Laufe der Zeit als recht praktisch erwiesen hat. Mittlerweile sind wir eigentlich an dem Punkt, da niemand mehr .s Dateien selbst schreibt sondern bestenfalls .S, während das Zwischenergebnis des C-Compilers in Dateien liegt, die auf .s enden -- diese können dann beliebig gelöscht werden, wenn sie nicht mehr gebraucht werden. Unter Unix kann man gefahrlos ein "rm *.s" machen, unter DOS würde das auch *.S mit löschen... Anyway, in gleicher Linie wurde es bei Einführung von C++ dann üblich, diese Dateien statt auf .c (C-Quelldateien) einfach auf .C enden zu lassen. Damit bin ich nun endlich wieder am Ausgangspunkt angekommen: indem Du dem Compiler-Treiber (was anderes ist das Kommando avr-gcc selbst nicht, es ruft nur die einzelnen Schritte mit passenden Parametern auf, compiliert oder linkt aber nichts selbst) die Dateinamen mit dem großen C im Suffix um den Hals gehängt hast, hat er den Compiler angewiesen, sie als C++-Quellen zu behandeln. Nun sind zwar die meisten sauber geschriebenen Standard-C-Quellen auch zugleich gültige C++-Quellen, aber die Semantik ist zuweilen eben doch verschieden... Das hat zu Deinem Bit0-Problem geführt. Du hättest das übrigens auch durch eine explizite Forderung verhindern können, die Programmiersprache als C zu betrachten: »-x c«. > Es ist mir auch schon kar, daß die AVR-Architektur nur sehr > schwer aus C optimalen Code generieren läßt. Naja, gar nicht so zwingend. Gerade beim Design des AVR hat man rechtzeitig (durch Kooperation mit IAR) darauf geachtet, einen Prozessor zu bauen, der möglichst gut auf die Bedürfnisse von Hochsprachcompilern abgestimmt ist. In irgendeinem Dokument auf der Atmel-Seite ist das ja erläutert. Allerdings sind die Vorstellung von »optimal« wohl nicht immer deckungsgleich mit Deinen Vorstellungen davon ;-), zumal es ja ohnehin immer nur einen Kompromiß zwischen Geschwindigkeit und Codegröße geben kann. Zuweilen kommen mir Deine Argumente hier auch ein bißchen »religiös« vor: Du plädierst für die generic pointers, bei denen die Entscheidung über den tatsächlichen Speicherbereich erst zur Laufzeit erfolgt (was ja auf jeden Fall Laufzeit kosten wird), andererseits stören Dich die PUSH/POPs für die in einer ISR benutzten Register, weil sie paar Taktzyklen kosten. > Bei den Windows-Programmen ist mir immer wieder unheimlich, > womit diese denn bloß ihre hunderte MB (bald schon GB) sinnvoll > ver(sch)wenden. Wäre mir auch unheimlich dabei :), zum Glück mache ich kein Windows. Ich kann Dir aber versichern, daß man sich mit der nötigen Energie in jeden ordentlichen Quellcode reinfinden kann. > Ein bischen hadere ich ja immer noch mit den fehlenden > Interruptprioritäten beim AVR im Vergleich zum 8051. Nun, ich kenne die Prioritäten zwar vom 8259 aus dem PC ein bißchen, aber in der Praxis werden die dort auch praktisch nur einmal fest vergeben und dann nie wieder angefaßt. Ich habe kein Gefühl, wie oft man die bei MCS51 wirklich benutzt hat... > Deshalb mein Bestreben, die Interrupts so kurz wie möglich zu > halten. Das ist sicherlich sowieso immer die sinnvollste Variante. Eine ISR macht ganz schnell das, was zeitkritisch zu erledigen ist, und setzt dann ein Flag, das später die asynchrone Weiterbearbeitung des Ereignisses ermöglicht. Aber Du solltest bei Deinen Betrachtungen eins nicht vergessen: Register sind das Wertvollste, was ein Prozessor zu bieten hat, ausgiebige Nutzung von Registern spart Rechenzeit und im Falle des AVR auch Code ein, insofern halte ich die derzeit vom GCC (und nicht nur von ihm) gewählte Strategie, ggf. ein paar Register durch PUSH/POP freizuräumen, für eine durchaus sinnvolle. Anders gesagt: »Never try to optimize something before you have profiled it.« Es kann gut sein, daß Du in einem Timer-Interrupt, der alle 100 CPU-Takte gerufen wird, mit allem wirklich geizen solltest und auch durch Reservierung einer fest gebundenen Registervariablen etwas gewinnst, aber wenn Du zu viele Registervariablen fest bindest, entziehst Du dem Compiler Register, die er zur Optimierung anderer Stellen benötigen würde. > Und irgendwo werde ich wohl auch finden, wie man Interrupts in > Asssembler mit in ein C-Programm einbindet. Was ist so schwierig an Assembler-Interrupts und ihrer Einbindung in C? Stell' eine konkrete Frage, und sie wird sich beantworten lassen. Aber siehe oben, ich würde mir die Mühe nur für wirklich zeitkritische Dinge machen. > Ich verstehe durchaus, daß Anfängerfragen nerven, die immer > wieder gestellt werden. Nein, ich sehe das nicht als nervige Anfängerfragen, sondern als eine sinnvolle Diskussion, die naturgemäß natürlich auch mal mit provokativen Argumenten geführt wird.
Danke für den sehr interessanten Ausflug in die Historie und hinter die Kulissen.
@Joerg, vielen Dank. Es freut mich sehr, daß Du andere (provokative) Meinungen auch als Diskussionsanregeung verstehst und nicht gleich ausfallend wirst. Das -x c hat geholfen und ich habe nun meinen Patch wieder entfernt. Steht ja auch eindeutig in der untersten Zeile des Hilfetextes. Nachdem man weiß, was der Grund ist, fällt es sofort ins Auge, aber vorher sieht man eben den Wald vor lauter Bäumen nicht. Zu den generic Pointern, das ist natürlich nur die eine Hälfte der Medaille. Beim Keil sind die Bibliotheksfunktionen (z.B. strcpy(), printf()) so geschrieben, daß sie generic Pointer bekommen, d.h. sie funktionieren auf jedem Speicherbereich. Die andere Seite sind die "memory specific pointer", z.B.: char code *foo; bezeichnet einen Pointer auf den Flash. Der Vorteil davon ist, man kann auch auf solche Pointer sämtliche C-üblichen Pointeroperationen anwenden bzw. solche Pointer an Funktionen übergeben usw. Mit: #ifdef _TURBOC_ #define char #define data #define xdata #endif kann ich diese Specifier einfach ausblenden und dann solche Routinen auch auf dem PC testen. Der Vorteil ist wie gesagt, daß der C-Compiler ohne weiteres Zutun die richtigen Zugriffsbefehle bzw. Unterfunktionen verwendet. In einer universellen Bediensoftware habe ich allein etwa 16kB an Code benötigt für verschiedenste Strukturen, um die verschiedensten Module zu installieren und zu bedienen. Das Gerät hat 16 Modulschächte und sucht in einer Liste aus den möglichen Modulkonfigurationen diejenige aus, als die es arbeiten soll. Danach werden diese Module installiert und nur die änderbaren Parameter Sollwert, Istwert) im RAM abgelegt. Alle anderen (z.B. Anzeigetext, Zahlenformat, Remotestring usw.) werden immer direkt aus dem Flash gelesen. Damit ist nur ein minimaler RAM belegt. Anbei mal eine kleine Beispielroutine daraus, die Pointeroperationen (.,->) verwendet. Memory specific Pointer können auch an Funktionen übergeben werden, die generic Pointer erwarten und werden dann entsprechend erweitert, aber nicht umgekehrt. Beim GCC habe ich den Eindruck, daß es nicht so einfach ist, Strukturen im Flash abzulegen und auf diese dann mit den entsprechenden Operatoren (.,->) zuzugreifen. Peter
Vielen Dank Joerg fuer die fundierten Aussagen. Ich bin zwar auch schon eine Weile im Geschäft, konnte dennoch genug Neues dazu lernen. Peter, an paar Worte an Dich: Unkenntniss mit deftigen Sprüchen zu ersetzen ist keine gute Wahl. Würdest Du so prgrammieren, wie hier Dein Tonfall ist, ich würde wohl Zweifel kriegen ob Deine Software was taugt. Ich kann Dir nur sagen, dass ich u.a. den Keil-Compiler kenne, den sogar sehr gut. Die Erfahrungen damit haben mir die Freude am avr-gcc mehr als verdoppelt :-) Deine Parallelen sind sachlich nicht nachzuvollziehen. Ausserdem hab ich nicht vergessen, wie es bei mir angefangen hat. Durch das Lesen von Programmen Anderer viel mir mein Unwissen auf. Also hab ich gelernt. Auch, anfangs Unverständliches zu begreifen, indem ich erst mal Hausaufgaben gemacht habe. Lies die Quellen der avr-lib und lerne... Ob Win9x eine Plattform für die Entwicklung ernsthafter Software eine gute Wahl ist, dass möchte ich arg bezweifeln. Genug gemeckert ;) Peter, Dir viel Erfolg. Jörg & co: Danke für Eure Arbeit! Weitermachen!
@Peter: > Zu den generic Pointern, das ist natürlich nur die eine Hälfte der > Medaille. Beim Keil sind die Bibliotheksfunktionen (z.B. strcpy(), > printf()) so geschrieben, daß sie generic Pointer bekommen, d.h. sie > funktionieren auf jedem Speicherbereich. Das ist aber praktisch `late binding' im OO-Sinne, d. h. es verplempert einfach Rechenzeit. Um die 4 Takte für ein paar PUSHs und POPs brauchst Du Dir dann auch keine Gedanken mehr machen. ;-) > Die andere Seite sind die "memory specific pointer", z.B.: > char code *foo; > bezeichnet einen Pointer auf den Flash. ...und verletzt den C-Standard. »code« ist ein Bezeichner, der im Namensraum der Applikation liegt, d. h. Du bist vollauf berechtigt, sowas zu schreiben: char code[] = { ... }; Sie hätten, wenn schon, dann __code usw. nehmen sollen. GCC's _attribute_ Konstrukt ist das Ganze dann in allgemeiner Form. Es ist richtig, avr-gcc kann fremde Speicher-Regionen nicht automatisch wie Variablen behandeln, d. h. Du kannst auf Zeigern dorthin keine Zeigeraroperationen direkt anwenden, sondern mußt das ,,zu Fuß'' erledigen. Das liegt im Wesentlichen daran, daß die gesamte Architektur von GCC und den begleitenden Tools auf CPUs mit von-Neumann-Architektur ausgerichtet ist, die nur einen Speicherraum kennen. Andererseits, nachdem ich den Code für AVR Butterfly gesehen habe (der für den IAR-Compiler gezimmert ist, und dieser kann mit verschiedenen Speicherbereichen umgehen) und die Verrenkungen, die dann teilweise nötig sind, um dem Compiler seinen Automatismus an irgdeiner Stelle wieder abzugewöhnen, bin ich mittlerweile gar nicht mehr so sehr davon überzeugt, daß die avr-gcc-Variante so ein schlimmer Nachteil ist wie ich anfangs dachte. Vom erzeugten Code her ist es sowieso egal, es ist nur die Bequemlichkeit für den Programmierer, die dabei etwas leidet.
@Withold, "Ich kann Dir nur sagen, dass ich u.a. den Keil-Compiler kenne, den sogar sehr gut. Die Erfahrungen damit haben mir die Freude am avr-gcc mehr als verdoppelt :-)" Du scheinst ja schlechte Erfahrungen mit dem Keil gemacht zu haben. Bei mir ist es nunmal das genaue Gegenteil. D.h. der Keil hat extrem wenig Bugs, erzeugt schnellen, kompakten und nachvollziehbaren Code und geht sparsam mit RAM um. Ich muß allerdings dazu sagen, daß ich vorher jahrelang den 8051 in Assembler programmiert habe, d.h. ich hatte daher schon gute Kenntnisse im sparsamen Umgang mit Variablen. "Deine Parallelen sind sachlich nicht nachzuvollziehen." Das ist doch gerade der Witz an C, daß Programme möglichst portabel sein sollen. Und dazu muß es nun mal erlaubt sein, zwischen den Compilern Parallelen zu ziehen und Unterschiede zu benennen. Und das hat ja nun auch zu fruchtbaren Meinungen geführt. Zumindest ich kann jetzt einige Meinungen besser nachvollziehen (ich muß sie ja nicht teilen). Peter
@Joerg, "Das ist aber praktisch `late binding' im OO-Sinne, d. h. es verplempert einfach Rechenzeit. Um die 4 Takte für ein paar PUSHs und POPs brauchst Du Dir dann auch keine Gedanken mehr machen. ;-)" Das sind aber 2 verschiedene paar Schuhe. Das eine bezog sich auf schnelle Interrupts und da würde ich nie im Traum dran denken, im Interrupt printf() zu verwenden. Im main() kann ich das in der Regel verschmerzen und habe mit dem besser lesbaren Code auch eine adäquate Gegenleistung. Ich wollte ja auch nie, daß der GCC 100% Keil kompatibel sein muß. Ich wollte doch nur mal das Konzept der generic/ memory specific Pointer zur Diskussion stellen, bzw. den nicht Keil-Kennern erläutern. "Vom erzeugten Code her ist es sowieso egal, es ist nur die Bequemlichkeit für den Programmierer, die dabei etwas leidet." Genau so isses. Natürlich bin ich derzeit immer noch in der Phase des Reinschnupperns in den AVR-GCC. Aber immerhin liefen meine Testprogramme auf dem STK500 bisher auf Anhieb. Und der ATMega8 mit 1kB RAM verzeiht es einem auch erstmal auf __LPM()-Verrenkungen zu verzichten und trotzdem kleinere Tabellen und Strukturen zu verwenden. Nochmals vielen Dank für Deine ausführlichen Informationen. Peter
@Peter, Du hast Recht, meine Erfahrungen sind das Resultat jahrelanger praktischer Arbeit. Dabei trat z.B. massiv Ärger mit C51(Keil) auf. Ich betone aber, das ich deshalb C51 nicht in Grund und Boden stampfe. Wie Du auch habe ich mit Assembler begonnen. Sehr früh begann ich nach verfügbaren Alternativen zu suchen, um genau die von Dir angesprochene Transparenz zu erreichen. Die Wege gingen über Pascal und Forth (immer für Controller). Aus meinen Jahren Praxis heraus bestätigt sich der Schluss: Es gibt keine Eierlegende-Woll-Milch-Sau. Für mich persönlich zieht ein Argument besonders: "Traue nur der Software deren Quelle Du hast". Die Vorteile liegen auf der Hand. "Das mit dem malloc()+devopen() ... (P.D.)" ist für mich ein besonders wichtiger Punkt. Damit hat die OpenSource-Gemeinde Grosses geleistet. Knie Dich mal rein! Mir fehlt im Moment die Zeit, um ein paar Erfahrungen damit zu dokumentieren. Ansonsten finde ich jede Diskussion gut, solange mir niemand seine Meinung "aufzwingen" will. Mit dieser Ausnahme ist alles nur nützlich. Frohes Schaffen ;) Withold
hmm, also von mir auch nochmal Danke für Diskussion... ich hab früher alles per Assembler programmiert auf der AVR Kiste und bin so dankbar dafür endlich nen gescheites Tool zur Hand zu haben, mit dem sich äußerst komfortabel mit dem µController arbeiten lässt. Von der Performace her kann ich mich auch nicht beschweren. Klar könnte man das eine oder andere besser machen, aber dann schaut man sich halt den erzeugten Code im Assembler an und wenn es wirklich sooo zeitkritisch ist macht man halt ein wenig inline Assembling :) Also auch nochmal von mir vielen Dank an Jörg & Co.... echt unglaublich geniale Arbeit die ihr da Leistet. Ich hoffe dass ich auch mal mir die Zeit nehmen werde mich bei OpenSource ein wenig produktiver einzusetzten... Thx a lot...
Naja, ich helfe nur ein bißchen in der avr-libc mit... Das ganze großartige Werk vor allem des GCC, aber natürlich auch all der Tools ringsum ist das Werk von mittlerweile Generationen von Freiwilligen.
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.