Hi Leutz, ausgehend von diesem Thread: Beitrag "Re: Welcher Cortex M3?" wo es über die CMSIS heiss her ging, möchte ich "euch" in diesem Thread die Gelegenheit geben, über den GNU Support der CMSIS zu diskutieren. Sicherlich ist die CMSIS auch ein politisches Projekt (sind Standards immer), aber sofern es sinnvoll ist, können wir diese sehr flexibel Erweitern und updaten. Während der Entwicklung der Core Funktionalität gab es recht wenig Support seitens der GNU Gemeinde, von daher war die Implementierung nicht so leicht. Daher würde ich mich über Anregungen und Kritik (bitte keine Beleidigungen!) freuen, und diese ggf. umsetzen. Die CMSIS gibtz zum Download bei www.onARM.com --- Der Ansatz des CMSIS-Core (mein Aufgabengebiet) war es, eine superschmale HAL zu schaffen, mit der sich die Registerprogrammierer anfreunden können. So ist z.B. der NVIC des CM3 ein sehr komplexes Peripheral, wer es verstanden hat, darf sich glücklich schätzen (wohl die wenigsten ^^) Die inline Funktionen in der core_cm3.h bilden exakt das ab, was man in seinen Source schreiben müsste, um einen Interrupt zu behandeln. Mit Ausnahme sehr weniger Funktionen (NVIC_SetPriorityGrouping) ergibt sich somit also kein Overhead. Was sich damals aus compilertechnischen Gründen (z.B. armcc V3, gcc) nicht in static inline verpacken liess, wurde dann - nach langen recherchen - grummelnd zu einer core_cm3.c verpackt, ein Konstrukt, was notwendig war, aber das asbald möglich verschwinden soll. --- Die Sache bleibt überschaubar, und grenzt sich somit massivst ab von den grossen DriverLibs, die mit massig parametern die HW versuchen zu erschlagen. Das Problem, was ich damals dabei sah, ist, dass DLs meist nicht die komplette Funktionsweise eines Peripherals abbilden können, und immer wieder Handarbeit notwendig ist. Ausserdem blähen sie idR. den Code auf. --- Ohne jetzt eine Politische Diskussion vom Stapel reissen zu wollen; wenn ihr Anregungen habe oder Fehler findet, dann bitte her damit. Schliesslich profitiert auch die GNU Gemeinde davon, denn CMSIS ist frei und Tool-unabhängig. Mit der CMSIS hat auch der absolute Neuling, egal in welcher Umgebung, eine reelle Chance, ohne viel Recherche seinen Cortex-M3 zum Laufen zu bekommen (z.B. Umsteiger von AVR -> ARM). VG, /th.
>Details? >z.B. hier, da ist nix drin von wegen parameter und return, da ich vom >AAPCS ausgehen kann:__ASM uint32_t __get_PRIMASK(void) >{ > mrs r0, primask > bx lr >} Das funktioniert aber nur wenn eine echte Funktion daraus wird, als inline wird's eine Bombe. Wenn man das in der GCC-typischen Art realisiert, dann kann es zwar vorkommen, dass im ASM Statement mrs r1, primask generiert wird, und der Compiler die Funktion (non-inlined) danach mit mov r0, r1 bx lr beendet, aber das ist ja auch nicht falsch.
Ok - hier in diesem Thread über CMSIS weiter. Ich halte das CMSIS Projekt für eine sehr gute Sache, die sich wahrscheinlich durchsetzen wird. Als Vergleich die alten DOS Programme wo jeder einen eigenen Dialog zum Öffnen einer Datei gebraucht hat und jeder für jeden Drucker einen eigenen Treiber usw. Dann kam Windows und plötzlich konnte man die passende Klasse auswählen und auch für den Anwender sahen (fast) alle Datei Öffnen Dialoge gleich aus. Aus diesem Grund hat sich das durchgesetzt und zwischenzeitlich ist mein Projekt auch so weit fortgeschritten, daß ich meine daß es keine Fehlentscheidung war sich auf CMSIS einzulassen. Aber: Die Driver Lib hat sich in den letzten 3 Versionen jährlich grundlegend geändert. Zuletzt mit der Einführung von CMSIS und das ist der Verbreitung nicht gerade zuträglich. Bleibt trotzdem zu hoffen, dass sich CMSIS durchsetzt und auch damit irgendwann TCP/IP Stacks und Filesysteme zu einer einfachen Angelegenheit bei uC werden. Zum ersten Problem: Alle Startup Assemblerfiles für den GCC haben bei mir eine falsche Adresse im Reset Vektor gelinkt welche das T-Bit nicht gesetzt hat. Dies führt zu einem Hard Fault beim ersten Befehl und ohne Debugger gibt es für Anfänger kaum eine Chance da drauf zu kommen. Mein erster Lösungsansatz war, Symbol+1 zu schreiben damit das geht. Sodann habe ich gesehen, dass eine Pseudo Anweisung .thumb_func das gleiche macht. Möglicherweise gibt es auch irgendwo einen Kommandozeilenschalter wo das Gleiche macht, der dann aber nicht Default ist und den ich auch nicht kenne. Zum zweiten Problem: Das assert_param Makro hat bei mir zu einem Linkerfehler geführt, weil es der Compiler als Implicit Declaration ohne Prototyp (=Warnung) gesehen hat. In der CMSIS Doku von STM gibt es eine farbige Grafik welche im übrigen Schreibfehler bei den Dateinamen hat. Alle stm32f10x_ppp.c Dateien benutzen assert_param welches als Makro in conf.h definiert ist. Alle ppp.c haben aber als Include nur ppp.h und dortselbst ist conf.h auch nicht drinnen. Dafür aber stm32f10x.h und deshalb habe ich das assert_param von conf.h in stm32f10x.h verschoben um übersetzen zu können. Die Pfeilrichtungen im CMSIS Diagramm mit der Header Hierarchie sind mir übrigens auch noch ein Rätsel. Zum dritten Problem: was eigentlich nur ein Schönheitsfehler mit einer unnötigen Neudefinition von Registeradressen ist, siehe hier: http://www.st.com/mcu/forums-cat-9101-23.html Der tiefere Grund für solchen Frevel hat sich mir jedenfalls noch nicht erschlossen. Das vierte Problem ist natürlich der Speicherbedarf. Da wäre mit einem Übersetzen als Archiv wahrscheinlich schon viel gespart. Ganz toll wäre es natürlich, wenn man die Symbole aus den Headern auch im Assembler benutzen könnte.
CMSIS ist sinnvoll, keine Frage. Vor allem wenn darin auch die Periphieriedefinitionen des jeweiligen Herstellers drin sind, denn das war nicht grad selbstverständlich. Auf die Support-Funktionen kann ich verzichten, aber die Strukturen und Bits sollten drin sein. Was mich an den Periphieriedefinitionen beispielsweise vom STM32 allerdings durchaus nervt, das ist die Deklaration der Registerbits als Maske. Zwar kann man dann elegenat UNIT.REGISTER = ENABLE | DISABLE | EXPLODE; schreiben, aber der elegante Weg ins doch ziemlich praktische Bitbanding ist damit erst einmal verbaut. Lieber wäre mir da UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE; So musste ich eben in meine Bitbanding-Makros eine Hilfsfunktion einbauen, die aus einer Maske eine Bitnummer macht.
J. V. schrieb: > ist natürlich der Speicherbedarf. Da wäre mit einem Übersetzen als > Archiv wahrscheinlich schon viel gespart. Geht bei GCC auch einfacher: --function-sections --data-sections im Compiler, und beim Linken dann -gc-sections (oder so ähnlich). Da fliegen alle Funktionen und Daten raus, die nirgends referenziert werden. Sämtliche Interrupt-Handler inklusive ;-), weshalb man diese mit __attribute__((used)) verzieren sollte.
A. K. schrieb:
> UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE;
PS: Das hat auch den Vorteil, dass man zur besseren Dokumentation der
Einstellungen
UNIT.REGISTER = 1<<ENABLE | 0<<DISABLE | 0<<EXPLODE;
schreiben kann. Also auch Bits explizit reinschreiben kann, die man
gezielt nicht setzen will.
> UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE;
Ist auch mein genereller Ansatz, aber so:
UNIT->REGISTER = (1<<ENABLE) | (1<<DISABLE) | (1<<EXPLODE);
oder mit
bit_on(UNIT->REGISTER, ENABLE);
was meine bevorzugte Variante ist. Eventuell etwas länger, dafür
supereinfach pflegbar!.
Findet sich auch im core wieder (ersteres).
Viele Hersteller nehmen aber gerne - warum auch immer - defines, in
denen das '1<<' schon mit drinsteckt, ka, why.
Warscheinlich ist das der Unterschied zwischen Register-Usern, die in
Bits denken, und DriverLib-Usern, die in Values denken.
VG,
/th.
>Zwar kann man dann elegenat > UNIT.REGISTER = ENABLE | DISABLE | EXPLODE; >schreiben genau das geht eben nicht immmer und ob es geht, kriegt man eigentlich nur darüber raus, indem man im Quellcode nachschaut, ob es sich um zwei Bits handelt die im gleichen Register mit gemeinsamem Zugriff liegen. So hatte ich zum Beispiel bereits ziemlich Pech mit NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn | DMA1_Channel1_IRQn; NVIC_Init(&NVIC_InitStructure);
A. K. schrieb: > schreiben, aber der elegante Weg ins doch ziemlich praktische Bitbanding > ist damit erst einmal verbaut. Lieber wäre mir da > UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE; > > So musste ich eben in meine Bitbanding-Makros eine Hilfsfunktion > einbauen, die aus einer Maske eine Bitnummer macht. Wo ist denn hier der Vorteil des Bitbanding? Statt einem Speicherzugriff hast Du nun sechs. Bitbanding ist insgesamt ziemlich unnütz zum Zugriff auf Peripherie Register. Die µC Hersteller haben die ursprünglichen Probleme mit atomaren Zugriffen seit Jahren gelöst. Bitbanding hat aber durchaus andere Anwendungsgebiete. Gruß Marcus http://www.doulos.com/arm/
Diese Funktionen verwende ich schon der darin enthaltenen Verschleierung der realen Vorgänge wegen nicht selbst, allenfalls als Demo wie andere das gemacht haben (NB: die diesbezügliche Baudratenrechnung bei der UART vom STM32 ist eher ein Beispiel dafür, wie man es nicht machen sollte). Ausserdem ist mir der Weg über Speicherstrukturen zu aufwendig.
Marcus Harnisch schrieb: > Wo ist denn hier der Vorteil des Bitbanding? Statt einem Speicherzugriff > hast Du nun sechs. Für 3 Bits ist das meist Unsinn, klar. Bei einem einzelnen Bit nicht. > Bitbanding ist insgesamt ziemlich unnütz zum Zugriff auf Peripherie > Register. Ganz im Gegenteil. Wenn Interrupts oder ein RTOS im Spiel sind, dann können die atomaren Zugriffe des Bitbandings von entscheidender Bedeutung sein. Wenn also ein Register sowohl im Hauptprogramm als auch im Interrupt-Handler modifiziert wird, was durchaus nicht selten ist und nicht nur GPIO-Ports betrifft. > Die µC Hersteller haben die ursprünglichen Probleme mit > atomaren Zugriffen seit Jahren gelöst. Nicht wirklich, d.h. oft nicht vollständig. Bei den GPIO Ports beispielsweise nur, wenn der Hersteller der Port-Unit intelligent genug war, bidirektionale Open Drain Ports zuzulassen, oder auch für die Richtungsregister bitweise Operationen vorgesehen hat. Was auf STM32 und SAM7 zutrifft, auf STR7/STR9/LM3 (die Primecell) und LPC2000 jedoch nicht. Bei diesen muss man dafür über das nicht bitweise ansprechbaren Richtungsregister arbeiten und kriegt allzu leicht ein Problem mit nicht atomaren Zugriffen. Es gibt sehr gute Gründe, weshalb ARM das Bitbanding eingebaut hat.
> das gemacht haben (NB: die diesbezügliche Baudratenrechnung bei der UART > vom STM32 ist eher ein Beispiel dafür, wie man es nicht machen > sollte). Immerhin tut sie solange man draufkommt wo man was definieren muß wenn man was anderes wie einen 8 Mhz Quarz verwendet. Dass dabei zur Laufzeit ein Haufen unnützer Code entsteht liegt klar an der Implementierung. Möglicherweise könnte man das auch mit einem Preprozessor Makro erschlagen. > Ausserdem ist mir der Weg über Speicherstrukturen zu aufwendig. Aus diesem Grund wurde das beim STM8 so nicht realisiert sondern mit direkter Parameterübergabe vereinfacht. Für STM8 gibts übrigens Compiler von Cosmic und die können im Assembler auch C und Bitsymbole nutzen.
J. V. schrieb: > Immerhin tut sie solange man draufkommt wo man was definieren muß wenn > man was anderes wie einen 8 Mhz Quarz verwendet. Dass dabei zur Laufzeit > ein Haufen unnützer Code entsteht liegt klar an der Implementierung. > Möglicherweise könnte man das auch mit einem Preprozessor Makro > erschlagen. Wenn's denn sein muss. Aber m.E. hatte der Programmierer nicht viel von dem verstanden was er da realisiert hat, auch die Hex-Konstanten dezimaler Zahlen sprechen eher für Blindflug bei Übernahme eines disassemblierten Vorbilds. Diese ganze höchst umständliche Rechnung darin kondensiert nämlich zu sowas Ähnlichem wie usart.baudratenregister = clock / rate; wenn man (a) sie verstanden hat und (b) einen Fehler von maximal einem halben letzten Bit des Nachkommaanteils zulässt, was wohl nie ein Problem für die Baudrate sein wird. Aber auch mit Rundung wird das kaum komplizierter. > Aus diesem Grund wurde das beim STM8 so nicht realisiert sondern mit > direkter Parameterübergabe vereinfacht. Na also.
A. K. schrieb: > Marcus Harnisch schrieb: >> Bitbanding ist insgesamt ziemlich unnütz zum Zugriff auf Peripherie >> Register. > > Wenn Interrupts oder ein RTOS im Spiel sind, dann können die > atomaren Zugriffe des Bitbandings von entscheidender Bedeutung sein. Grundsätzlich habe ich auch nichts gegen das Bit Banding an sich. Gerade für Peripheriezugriffe ist es aber nicht das ideale Mittel, um Designprobleme der Hardware zu lösen (s.u.). > Wenn also ein Register sowohl im Hauptprogramm als auch im > Interrupt-Handler modifiziert wird, was durchaus nicht selten ist > und nicht nur GPIO-Ports betrifft. ...und sich die Modifikation auf wenige Bits beschränkt, und der Zugriff auf die Speicherzelle frei von Nebeneffekten ist (clear-on-access, etc.) In anderen Fällen ist es dann meist besser, den Register-/Speicherzugriff mit einer Semaphore zu schützen. Die wiederum kann man übrigens hervorragend mittels Bit Banding implementieren (und natürlich auch mit LDREX/STREX). > Nicht wirklich, d.h. oft nicht vollständig. In diesen Fällen bietet Bit Banding dann aber auch keine Universallösung. > Bei den GPIO Ports beispielsweise nur, wenn der Hersteller der Port-Unit > intelligent genug war, bidirektionale Open Drain Ports zuzulassen, oder > auch für die Richtungsregister bitweise Operationen vorgesehen hat. > [...] > Bei diesen muss man dafür über das nicht bitweise ansprechbaren > Richtungsregister arbeiten und kriegt allzu leicht ein Problem mit > nicht atomaren Zugriffen. Und mit Bit Banding hast Du das potentielle Problem, dass die Richtungsänderung nicht gleichzeitig erfolgt. Du wirst immer jemanden finden, der in seiner Anwendung was zu meckern hat. Diese Probleme können nur auf Peripherieebene gelöst werden. Ob die Hersteller das machen, steht auf einem anderen Blatt. > Es gibt sehr gute Gründe, weshalb ARM das Bitbanding eingebaut hat. Hauptsächlich Marketing fürchte ich. Andere µC Architekturen unterstützen Bitzugriffe (wenn auch in anderer Form) nativ. ARM musste in der feature list dagegenhalten, um dem 8-bit Markt etwas bieten zu können. Jemand der es wissen sollte, hat meinen diesbezüglichen Kommentar jedenfalls nicht dementiert :-) Außerdem ist die Implementierung nicht besonders aufwändig. Bit Banding ist zweifellos eine gute Sache aber keineswegs das silver bullet, als das es uns präsentiert wird. Der tatsächliche Nutzen für Peripheriezugriffe ist auf weniger Anwendungsfälle beschränkt als man uns glauben machen möchte. Gruß Marcus http://www.doulos.com/arm/
Marcus Harnisch schrieb: > Und mit Bit Banding hast Du das potentielle Problem, dass die > Richtungsänderung nicht gleichzeitig erfolgt. Bei parallen Bussen geht das nicht, klar. Mein "Benchmark" dafür ist allerdings sowas wie 1-Wire oder I2C, d.h. wie aufwändig lässt sich das in Software mit Portpins implementieren. Gleichzeitigkeit ist da kein Problem, atomarer Zugriff hingegen schon. Ob das nun über dne Portkonstruktion realisiert wird, oder indem dortigen Löcher über Bitbanding gestopft werden, ist mir letztlich egal. Mit der ARM Primecell für GPIO ist aber ohnehin das Kind im Brunnen. Ansonsten betrifft das vor allem allerlei Interrupt-Flags/Enables etc, die gerne im Hauptprogramm und im Handler verwendet werden. Das sind praktisch immer einzelne Bits. Das dies kein silver bullet ist, das ist mir klar. Aber Werbeschmarrn interessiert mich ohnehin nicht, ich beziehe meine Information nicht aus PR-Veranstaltungen sondern aus Handbüchern.
Nur noch mal zum Abschluss, bevor wir uns bestätigen dass wir mehr oder weniger der gleichen Meinung sind: A. K. schrieb: > Mit der ARM Primecell für GPIO ist aber ohnehin das Kind im Brunnen. Die Primecells sind nicht für Mikrocontroller entworfen worden, sondern für SoC (ASSP). Wenn da jemand I²C haben möchte, dann baut er sich entsprechendes IP ein und bastelt da nichts in Software. Das Luminary seinerzeit die Primecells eingesetzt hat lag wohl eher an der speziellen Beziehung zu ARM. Ein großer Aufwand bei der Entwicklung eines µC ist es, herauszufinden wie man es möglichst vielen Anwendern recht machen kann. Das überlässt man bei ARM dann doch lieber den µC Herstellern. Die Primecells haben nicht den Anspruch möglichst universell zu sein. Dafür sind sie meist extrem einfach zu programmieren. Ein PL081 Timer z.B. kann so gut wie nichts außer zählen und einen Interrupt generieren -- und das ist auch gut so. Wenn ich in meinem SoC irgendwelche bizarren PWM Modi brauche, dann baue ich mir spezielle Hardware dafür, die für diesen Zweck dann ähnlich einfach anzusprechen ist. Niemand, der ein ASSP entwickelt würde auf die Idee kommen, einen dieser funktional völlig überladenen µC Timer zu verwenden. Ähnlich ist das mit GPIO, UART, etc. Um ein paar LEDs am Handy anzusteuern, ist der PL061 GPIO prima. Gruß Marcus http://www.doulos.com/arm/
Warum wurden viele Defines in die C Files anstelle der Header gemacht ? Bsp. Timer: Hier sind die defines für alle Register Bitmasken im C File. Möchte eine Anwendung den Ausgang von TIM3 abschalten, so ist hier offensichtlich der Aufruf von TIM_CCxCmd(TIM3, TIM_Channel3, TIM_CCxDisable); vorgesehen. Wie bei fast allen Lib Funktionen hat die Portabilität dabei ein Code und Lauzeit Overhead von wenigstens 100% Deshalb könnte eine Anwendung draufkommen, den Ausgang selbst zu schalten. TIM3->CCER &= CCER_CC3E_RESET Das geht aber nicht, weil die defines nicht im Timer Header drinnenstehen sondern im C Quellfile (wo sie nicht hingehören?) Es bleibt also nichts anderes übrig, als ein Define aus der Bibliothek noch mal zu definieren weil es nicht gefunden wird ...
janvi schrieb: > Das geht aber nicht, weil die defines nicht im Timer Header > drinnenstehen sondern im C Quellfile (wo sie nicht hingehören?) In stm32f10x.h stehen m.W. alle Bits drin. Also: TIM3->CCER &= TIM_CCER_CC3E; Mich stört eher, dass Felder aus mehreren Bits nur einzelbitweise enthalten sind, nicht in verständlich übersetzter Form. So wünschte ich mir TIM_CR2_MMS_PULSE statt des unverständlichen Ungetüms (TIM_CR2_MMS_1|TIM_CR2_MMS_0).
Und so entstehen eben inkrementell meine privaten Zusatz-Includes für die Units. Beispiel anbei.
So war's natürlich gemeint: TIM3->CCER &= ~TIM_CCER_CC3E; Wobei ich da auch ganz gerne schreibe: BBPeriphMask(TIM3->CCER, TIM_CCER_CC3E) = 0;
Besten Dank für den Tipp. Für ein Bit sind also definiert: TIM_CCER_CC3E CCER_CC3E_RESET CCER_CC3E_SET und das verteilt über mehrere Files. Die Applikation soll Defines ohne Set/Reset und Timer Nr. nutzen und Set/Reset mit Timer Nr. ist CMSIS Angelegenheit weil die Nutzung der Inverter Tilde dem Anwender versteckt werden soll.
@janvi: >Zum ersten Problem: >Alle Startup Assemblerfiles für den GCC haben bei mir eine falsche >Adresse im Reset Vektor gelinkt welche das T-Bit nicht gesetzt hat. >Dies führt zu einem Hard Fault beim ersten Befehl und ohne Debugger gibt >es für Anfänger kaum eine Chance da drauf zu kommen. Mein erster >Lösungsansatz war, Symbol+1 zu schreiben damit das geht. Sodann habe ich >gesehen, dass eine Pseudo Anweisung .thumb_func das gleiche macht. >Möglicherweise gibt es auch irgendwo einen Kommandozeilenschalter wo das >Gleiche macht, der dann aber nicht Default ist und den ich auch nicht >kenne. Kannst du das bitte genauer erläutern? Ich stehe nämlich vor genau dem Problem. Allerdings komm ich nicht auf die Lösung.
janvi schrieb: > CCER_CC3E_RESET > CCER_CC3E_SET Diese beiden sollten schon deshalb nicht in dieser Form global definiert werden, weil sie nicht eindeutig dem Timer zuordnet sind. Gegen explizite Benennung beider Varianten habe ich bei der Definition als Maske nichts einzuwenden, wie du auch in meiner Version erkennen kannst. Allerdings nicht um die Tilde einzusparen, sondern um explizit in die Zeile reinschreiben zu können, dass ich das Bit nicht setzen will. Erspart die sattsam bekannte Code/Kommentar-Schere. Denkbar wäre, dass die Peripherie-Lib völlig unabhängig vom CMSIS stm32f10x.h entstand. Jedenfalls sehe ich in diesen beiden Ansätzen ohnehin Antagonisten. Entweder man verwendet die Lib in aller Zeilen fressenden Umständlichkeit, oder man geht ans Eingemachte, an die Register. > Die Applikation soll Defines ohne > Set/Reset und Timer Nr. nutzen und Set/Reset mit Timer Nr. ist CMSIS > Angelegenheit weil die Nutzung der Inverter Tilde dem Anwender versteckt > werden soll. Bringt das was? Immerhin muss der Anwender auch zwischen &= und |= unterscheiden und wenn er vor oder nach dem STM32 auch mal was anderes programmiert, dass wird ihm die Notation x &= ~y; durchaus vertraut sein. Denn wenn nicht, dann sind bit_set(register,mask) und bit_reset(register,mask) weitaus sinnvoller.
>> CCER_CC3E_RESET >> CCER_CC3E_SET >Diese beiden sollten schon deshalb nicht in dieser Form global definiert >werden, weil sie nicht eindeutig dem Timer zuordnet sind. eigentlich sind sie sogar Timer Nr. 3 zuzuordnen und CCER gibts nur für Timer. Dagegen ist der globale Define nur irgendeinem Timer zuzuordnen. Man hat in diesem Spezialfall halt Glück hat, daß alle 8 möglichen Timer die gleiche Registerbit Anordnung haben. >Denkbar wäre, dass die Peripherie-Lib völlig unabhängig vom CMSIS >stm32f10x.h entstand. Das ist mit Sicherheit so, denn es gab auch schon V1 und V2 der Lib und das vor CMSIS. Siehe noch die alten ST Datentypen "vu" u.a. welche noch aus Kompabilitätsgründen mitgeschleppt werden. >Jedenfalls sehe ich in diesen beiden Ansätzen ohnehin Antagonisten. Darüber kann man streiten. Ich habe schon mehrfach CanOpen und USB Stacks in verschiedenen Assembler und C geschrieben aber an ein Filesystem oder Ethernet habe ich mich bislang nicht getraut. Da hoffe ich, daß CMSIS künftig etwas hilfreich sein könnte. Wahrscheinlich ist das auch selbst gut machbar und normal bin ich auch Fan von kompaktem Code. Es gibt verdammt gut optimierende Compiler - GCC gehört leider nicht dazu - aber beim Codeoverhead kommts halt auch drauf an wie der Programmierer eine Sprache einsetzt. Leute die vom PC kommen sehen das leider kaum ein. Bei Ethernet und Multimedia wirds sowieso anders. Ein http Bildchen mit 256x256 Pixel bei 8 bit Farbtiefe belegt schon mal 64k und was soll man dagegen noch an Code mit paar lächerlichen Bytes wegoptimieren? Summa Summarum hat die Portabilität ganz klar seinen Preis und deshalb sollte der Overhead hier kein Argument gegen CMSIS sein. Nichtsdestotrotz könnte man viel Code bei konsequenter Implementierung über Makros einsparen. >Entweder man verwendet die Lib in aller Zeilen >fressenden Umständlichkeit, oder man geht ans Eingemachte, an die >Register. Momentan mache ich das Erste und mein Code der eigentlich noch kaum was macht, belegt (ohne Optimierung) bereits über 50kbyte. Später wenn alles tut, werde ich das mal von Hand optimieren, vielleicht auch in Assembler. Beim Abschalten des Timers war ich jetzt halt drauf angewiesen, im passenden Moment zwischen den Flanken abzuschalten und nicht irgendwann. Die gesamte Lib lässt sich sowieso allermeist auf Register=Konstante eindampfen weil die ganze Rechnungen und Bitschieberei zur Laufzeit gar nicht gefragt sind. Die graphischen Beans (Motorola Codewarrier) und Infineons Dave sind im Overhead übrigens auch nicht viel besser. Darüber hinaus hat es noch nie geschadet, wenn man das Datenblatt gelesen hat.
J. V. schrieb: > eigentlich sind sie sogar Timer Nr. 3 zuzuordnen, Nicht dem Timer 3 sondern dem Compare Channel 3. > Code. Es gibt verdammt gut optimierende Compiler - GCC gehört leider > nicht dazu So schlecht ist er nicht. > Momentan mache ich das Erste und mein Code der eigentlich noch kaum was > macht, belegt (ohne Optimierung) bereits über 50kbyte. Du hattest meine Notiz mit -ffunction-sections gelesen? > Overhead übrigens auch nicht viel besser und es hat noch nie geschadet, > wenn man das Datenblatt gelesen hat. Ist exakt mein Ansatz. Verstehen erspart Blindflug.
J. V. schrieb:
> aber an ein Filesystem oder Ethernet habe ich mich bislang nicht getraut.
Hehe, bei mir ist es eher umgekehrt.
CanOpen und Ethernet Stacks wohlmöglich mit TCP/IP sind aber auch eine
ganz andere Liga als die real existierende Peripherielib. So puristisch
bin ich nun auch wieder nicht. Auch Codegeneratoren sind ok, wenn der
Kram funktioniert und ich in dem entstehenden Codesalat nicht rumferkeln
muss.
Durchdachte Libs sind schon ok, wenngleich ich da vielleicht etwas eigen
bin. Nur sehe ich nicht ein, weshalb ich für die Definition eines
GPIO-Pins 8 Zeilen struct Gefummel schreiben muss, wenn das auch in
einer einzigen geht:
gpio_config(GPIOA, 1<<9, GPIO_Mode_Output2MHz | GPIO_AltOut_PushPull);
Frischling schrieb >Kannst du das bitte genauer erläutern? Beim Cortex M3 gibt es ein T-Bit. Dieses ist offensichtlich eine ARM Altlast, welches zur Umschaltung beim Interwork Code benutzt wird. D.h. Wechsel von 32 bit Arm Opcodes auf 16 bit Thumb(2) Opcode werden vom T-Bit angezeigt. Siehe Arm Architecture Manuals beim T-Bit. Dies wird dadurch gemacht, daß alle 16 bit Befehle (32 auch) immer auf geraden Adressen ausgerichtet sind. Das T-Bit wird nun gesetzt, indem ein Sprung nicht auf die genaue Zieladresse zeigt, sondern auf die ungerade Zieladresse+1. Der Arm holt sowieso das ganze Wort ab abgerundeter gerader Adrese und der Opcode Decoder weis dann, daß es sich um einen 16 bit breiten Thumb Befehl handelt. Das T-bit ist beim STM32 etwas bescheiden beschrieben, weil man es sowieso nicht braucht. Wenn dem anders sei, mögen mich die ARM Spezialisten bitte korrigieren. Im allen startup.s steht nun ein Reset Vektor: g_pfnVectors: .word _estack .word Reset_Handler .... usw. Der Reset_Handler zeigt nun auf den Programmanfang zum Umkopieren von Initialsierten Daten die in der Lib so aussehen: .section .text.Reset_Handler .weak Reset_Handler .type Reset_Handler, %function .thumb_func Reset_Handler: /* Copy the data segment initializers from flash to SRAM */ movs r1, #0 b LoopCopyDataInit Der GCC Linker setzt nun die Adresse von Reset_Handler artig und genau passend bei .word Reset_Handler ein. Der Cortex M3 holt den Reset Vektor und versucht den dort stehenden Opcode zu dekodieren. Weil das eine gerade Adresse ist, wird das T-Bit nach Reset nicht gesetzt und die Dekodierung endet mit einem Hard Fault wegen unbekanntem Opcode. Es wirkt hier Wunder, indem man die "falsche" (richtige) Adresse mit .word Reset_Handler+1 einträgt. Der Vektor zeigt auf die ungerade Adresse, das T-Bit wird gesetzt und alles ist ok. Eine andere Möglichkeit den GNU Linker automatisch dazu zu bringen die Adresse passend einzutragen ist diejenige, daß der Reset_Handler mit .thumb_func verziert wird. Warum die GNU Toolchain das nicht bereits bei .thumb oder .cortex-m3 richtig machen kann bleibt mir auch unbekannt. Die Compilerhersteller wissen das und drücken den .weak Reset_Handler mit einem eigenen korrekten Reset_Handler beiseite. Ein nackter GCC macht das nicht und deshalb grinsen sich die Damen und Herren Lizenzverkäufer einen ab, wenn andere damit auf die Fresse fliegen, mit GCC keinen Support haben bzw. selber nicht schnallen was fehlt. Hoffe das war ausführlich genug ...
J. V. schrieb: > Beim Cortex M3 gibt es ein T-Bit. Dieses ist offensichtlich eine ARM > Altlast, welches zur Umschaltung beim Interwork Code benutzt wird. Die grösseren Grosshirne (A8 und so) haben den ARM instruction set mit an Bord, also nix Altlast.
J. V. schrieb: > .thumb_func verziert wird. Warum die GNU Toolchain das nicht bereits bei > .thumb oder .cortex-m3 richtig machen kann bleibt mir auch unbekannt. Vielleicht weil nicht alles was in der text section steht auch Code ist, und bei Datenadressen wärs mit gesetzten Bit 0 etwas peinlich. Und dass die Tools für ARVv7-M gegenüber ARMv7 keine Extrawurst braten ist nachvollziehbar, die müssen immer noch beide Varianten im Auge haben.
A. K. schrieb: > Und dass die Tools für ARVv7-M gegenüber ARMv7 keine Extrawurst braten ist > nachvollziehbar, die müssen immer noch beide Varianten im Auge haben. Die RealView tools haben damit aber keine Probleme. Wenn ich sage, dass mein Code für CM3 übersetzt werden soll, dann ist Thumb implizit aktiv und das LSB ist bei Funktionsadressen immer gesetzt. Gruß Marcus http://www.doulos.com/arm/
Cortex soll ja eine CPU sein, bei welcher man keinen Assembler mehr braucht. Faktor 2 traue ich mir gegenüber dem GCC aber allemal in vielen Fällen zu. Ein ausserordentlicher Spaß ist natürlich den in ASM vorliegenden Startup in C umzuschreiben um zu sehen ob GCC alles richtig macht. Tut tatsächlich: void (* const Interrupts[])(void) _attribute_ ((section("vector"))) = { (void (*)(void))((unsigned long)RAMStack + sizeof(RAMStack)), Reset, NMI, HardFault, usw... void Reset(void) {unsigned long *Src, *Dest; Src = &_etext; for(Dest = &_data; Dest < &_edata; ) *Dest++ = *Src++; for(Dest = &_bss; Dest < &_ebss; ) *Dest++ = 0; main(); } > Die RealView tools haben damit aber keine Probleme deshalb leben die Hersteller ja auch trotz GCC noch ganz gut. Wenn du das ReaView Teil am Start hast, wäre übrigens interessant ob es mit solchen Sitationen klarkommt: Beitrag "Computed Jump im DWARF2 Debugger Format"
>Nur sehe ich nicht ein, weshalb ich für die Definition eines >GPIO-Pins 8 Zeilen struct Gefummel schreiben muss, das Struct Gefummel kann portabel bleiben wenn ein anderer Cortex ganz andere IO Ports implementiert hat die vielleicht sogar nur ein Subset sind. > wenn das auch in einer einzigen geht: > gpio_config(GPIOA, 1<<9, GPIO_Mode_Output2MHz | GPIO_AltOut_PushPull); In deiner Anwendung mag das besser sein, nicht jedoch wenn du einen Protokoll Stack schreiben willst, welcher später auf allerlei anderen (CMSIS kompatiblen) Zielsystemen laufen soll. Das ist der Preis aber auch die große Chance von CMSIS.
J. V. schrieb: > das Struct Gefummel kann portabel bleiben wenn ein anderer Cortex ganz > andere IO Ports implementiert hat die vielleicht sogar nur ein Subset > sind. Bislang hatte ich CMSIS als eine Sammlung von Definitionen und Hilfsfunktionen für (a) den implementierungsübergreifenden Core und (b) die Peripherieeinheiten der jeweiligen Implementierung gesehen. Compilerübergreifend ja, stellt es aber eine 1:1 Abbildung der Register und Bits der konkreten Implementierung der Peripherieeinheiten dar. Dass CMSIS davon abstrahierende will, das ist mir neu. Die aktuelle Version 1.30 jedenfalls vereinheitlicht nur die den Core betreffenden Aspekte. Wenn sich CMSIS zu einem Layer entwickelt sollte, mit dem eine Anwendung für GPIO, SPI, CAN, die für STM32 enwickelt wurde, ohne grössere Änderungen auf Stellaris oder LPC1700 portierbar sei, dann Hut ab. Aber da weisst du offenbar mehr als ich, denn nichts an dem was ich davon bisher gesehen haben deutet darauf hin.
Das Gefummel hat auch nichts mit CMSIS zu tun, sondern betrifft die davon unabhängig entstandene Peripherielib von ST. Und dort hätte ST schon ein bischen üben können, wenn sie das gewollt hätten, nämlich zwischen den einzelnen bestehenden Varianten wie STR7, STR9 und STM32, um nur die 32-Bit Versionen zu nennen. Indes sind die entsprechenden Libs von STR9 und STM32 abgesehen von der mir schwer zugänglichen Vorliebe für structs noch deutlich inkompatibler zueinander als die zugrunde liegende Hardware. Was ich als Erstes für Controller zusammenbaue sind eben solche Abstraktionen. Nicht für GPIO, das halte ich für ziemlich sinnlos, sondern für komplexe Module wie beispielsweise UART und CAN, die bei allem Unterschied in der Hardware nach oben hin durchaus ähnlich sein können. CMSIS hilft mir dabei, das CAN-Modul für STM32 von GCC ggf. auf IAR portieren zu können, aber eine Implementierung für STR9 ist notwendigerweise völlig anders.
A. K. schrieb: > Dass CMSIS davon abstrahierende will, das ist mir neu. Die aktuelle > Version 1.30 jedenfalls vereinheitlicht nur die den Core betreffenden > Aspekte. Wie man bei CMSIS lesen kann: The Middleware Access Layer is adapted by the Silicon Vendor for the device specific peripherals used by middleware components. The middleware access layer is currently in development and not yet part of this release. > Wenn sich CMSIS zu einem Layer entwickelt sollte, mit dem eine Anwendung > für GPIO, SPI, CAN, die für STM32 enwickelt wurde, ohne grössere > Änderungen auf Stellaris oder LPC1700 portierbar sei, dann Hut ab. Aber > da weisst du offenbar mehr als ich, denn nichts an dem was ich davon > bisher gesehen haben deutet darauf hin. Vielleicht sollten wir mal den STM8 genauer anschauen anstelle STM32 mit Arm7+9 zu vergleichen. Hier gibt es Compiler von Cosmic welche als gut otpimierend bekannt sind. Der STM8 ist eigentlich ein alter Motorola HC05 aber mit ausgerechnet fast der gleichen Peripherie wie der STM32. Damit kopiert STM das Controller Continuum von Freescale auf die eigenen Produkte. Oder wieso sollte ST neben den kleinen STM32 sonst noch eine neue 8 bit Familie auf den Markt bringen? Mit weniger als 2k Ram kann sich die Implementation die Structs nicht mal auf dem Stack erlauben und auch ein C Compiler braucht Qualitäten die aus einer Zeit stammen wo Großrechner ähnliche RAM Größen mit handgewickelten Ferritkernen hatten. Trotzdem scheint der STM8 gleich ein nicht-ARM zu sein, welcher von der CMSIS Idee profitiert. Was dabei von ARM kommt, bzw. CMSIS ist und was von STM kommt verschwimmt da natürlich etwas weil man nicht weis, was sich STM selbst ausgedacht hat und was von CMSIS vorgeschrieben wurde wie es zu machen ist.
J. V. schrieb: > The Middleware Access Layer is adapted by the Silicon Vendor for the > device specific peripherals used by middleware components. Da bin ich mal gespannt. Ein herstellerübergreifender Standard für zig völlig verschiedene Varianten von I/O-Modulen? Sowas klingt für mich irgendwie nach jahrelangen Komiteesitzungen und monströsem Ergebnis. Oder ein eher lose definiertes Framework, in den jeder Hersteller seine chipspezifische und damit eben nicht portable Variante einigermassen stilkonform einbringen kann?
J. V. schrieb: > Cortex soll ja eine CPU sein, bei welcher man keinen Assembler mehr > braucht. Faktor 2 traue ich mir gegenüber dem GCC aber allemal in > vielen Fällen zu. Nicht überraschend. > Ein ausserordentlicher Spaß ist natürlich den in ASM vorliegenden > Startup in C umzuschreiben um zu sehen ob GCC alles richtig > macht. Dass der CMSIS startup code nicht tool-unabhängig in C geschrieben wurde halte ich für eine verpasste Chance. > void (* const Interrupts[])(void) _attribute_ ((section("vector"))) = Wobei gerne vergessen wird (so auch bei CMSIS), dass man durchaus auch mehrere Vector Tables aufsetzen kann. Also bitte in ein typedef packen. Und dann das Alignment nicht vergessen:
1 | /* Vector tables for Cortex-M3 must be aligned. The minimum alignment
|
2 | * is 32 words. The alignment of the vector table is calculated from
|
3 | * its size rounded up to the next power of two.
|
4 | */
|
5 | #define _vec_tbl_align(a) (((16+(a) <= 32) ? 32 :
|
6 | ((16+(a) <= 64) ? 64 : |
7 | ((16+(a) <= 128) ? 128 : 256))) * 4) |
8 | |
9 | __attribute__ ((aligned( _vec_tbl_align(NUM_INT) ))) ... |
Gruß Marcus http://www.doulos.com/arm/
>Dass der CMSIS startup code nicht tool-unabhängig in C geschrieben >wurde halte ich für eine verpasste Chance. das wird wahrscheinlich schwierig weil viel Symbole an dieser Stelle aus den Linkerfiles kommen oder irgendwo in den Quellen absolut passend zu den Speichergrößen festgenagelt sind. (Stacktop muss übrigens +2 heissen weil sonst wegen predekrement die oberste RAM Stelle nicht genutzt wird). Es würde dabei dann aber nicht schaden bei CMSIS zumindest für GCC ein verbindliches Makefile mitzuliefern welches die entsprechende Nutzung mit passendem Linker File demonstriert. Wenn dieses dann für Keil, IAR und andere nicht passt, sollen diese Hersteller fürs Geld was tun und ein entsprechendes makefile bzw. Linkerfile selbst anpassen und bereitstellen.
J. V. schrieb: >>Dass der CMSIS startup code nicht tool-unabhängig in C geschrieben >>wurde halte ich für eine verpasste Chance. > > das wird wahrscheinlich schwierig [...] So? Siehe auch cmsis_v1p20\Core\CM3\startup\{gcc,iar,tasking} Gruß Marcus http://www.doulos.com/arm/
Marcus Harnisch schrieb: > J. V. schrieb: >>>Dass der CMSIS startup code nicht tool-unabhängig in C geschrieben >>>wurde halte ich für eine verpasste Chance. Tool-unabhängig war zugegebenermaßen nicht der richtige Ausdruck. Assembler-unabhängig wäre wohl korrekter. Oder /weitestgehend tool-unabhängig/. Man wird tool-abhängig natürlich die Linker Defined Symbols auflösen müssen. Davon ist allerdings ein großer Teil des Startup codes nicht betroffen. Dass der Startup code für einen Core, der damit beworben wird, dass er die Verwendung von Assembler unnötig macht, gerade in Assembler implementiert wird, entbehrt nicht einer gewissen Ironie. Gruß Marcus http://www.doulos.com/arm/
> Du hattest meine Notiz mit -ffunction-sections gelesen?
ja klar, besten Dank. Leider habe ich --print-gc-sections nicht zum
laufen gekriegt weshalb ich dann etwas vorsichtiger File für File den
Parameter dazugesetzt habe. Hier das Resultat:
Ohne Optimierungen: 47732 Bytes
adc 45372 Bytes
dma 45092 Bytes
flash 42895 Bytes
gpio 41672 Bytes
rcc 40808 Bytes
spi 39100 Bytes
tim 39092 Bytes
uart 30440 Bytes
misc 30246 Bytes
Man sieht, dass der Uart (wahrscheinich wegen der meist unbenutzten
Synchron Modis) am meisten ausmacht. Die Anwendung passt jetzt in ein
32k Low Density, was anderes habe ich in meinem Zielsystem nicht und es
funktioniert noch immer alles prima.
Natürlich bin ich mit der Codegröße noch nicht zufrieden und ich schätze
mal in Assembler hätte ich das in vielleicht 2 kbyte geschrieben. Also
ziehen wir mal am Optimierer die Register. Von -O0 auf -Os bei allen
Files ergibt das noch eine Gesamtgröße von 26016 Bytes. Leider
funktioniert meine Anwendung jetzt auch nicht mehr. Eine grobe Analyse
hat gezeigt, dass ein Interrupt zuviel kommt und anschliessend
unpassende DMA Vorgänge daran schuld sind. Sehr schnell stellt sich
stm32F10x_tim.c als Übeltäter heraus. Wenn ich hier was anderes als -o0
nehme geht nix mehr. Das riecht danach, dass irgendwo ein volatile
vergessen wurde oder GCC wegen der DMA auf die Fresse fliegt. Bei
Gelegenheit guck ich vielleicht mal genauer. Ohne optimierten Timer gibt
die Version mit rund 26kbyte aber noch immer ein erbärmliches Bild.
Das kommt davon, weil GCC für ein sprintf in der libc von malloc bis
fseek in die Vollen langt. Derweil hat Hitex bei meiner Version
offensichtlich überhaupt nichts an der lib optimiert. Der Versuch printf
auf putchar umzuleiten endet in isatty mit einem Software Breakpoint.
Eine eigenes bin2dez reduziert die Codegröße nun auf
(Trommelwirbel) gesamt: 6648 Bytes und es tut noch immer alles.
Es wäre interessant was Codesourcery, Rowley, Raisonance und so an libs
mit kleinen printf können. Beim ST7 hatte ich mal einen kleinen printf
von Cosmic der das Meiste konnte (keine float) und grad mal 2k Code
gemacht hatte. Wäre also nett, was ein typischer printf mit %x oder %d
bei den Anderen so an Code beiträgt und ob die gebührenpflichtigen
Vollversionen der GCC vielleicht sogar mit Lib Quellen daherkommen?
J. V. schrieb: > Das kommt davon, weil GCC für ein sprintf in der libc von malloc bis > fseek in die Vollen langt. Weshalb ich für solche Fälle meine eigene portable etwas vereinfachte und float-freie Version von printf() habe, die mir den ganzen platzraubenden newlib-Kram erspart. > Es wäre interessant was Codesourcery, Rowley, Raisonance und so an libs > mit kleinen printf können. Für Crossworks kann ich das mal nachsehen. Bei Codesourcery tippe ich auf die Newlib, und die ist alles andere als platzsparend. Crossworks hat für's Debugging eine eigene debug_stdio, in der die kompletten Funktionen in den PC ausgelagert sind (quasi per RPC) und die dementsprechend platzsparend ist. Funktioniert natürlich nur, wenn der Debugger die Kontrolle hat.
Crossworks V2 (GCC 4.4.2): Ohne printf 1208 Bytes debug_printf 1269 Bytes eigenes rprintf 2212 Bytes Crossworks printf 3116 Bytes -Os, ohne float-support und nur ein Minimum an UART-Funktion ohne Interrupt. Mein rprintf braucht 748 Bytes, der Rest der 1K Differenz sind ein paar Runtime-Funktionen für 64-Bit Arithmetik und Strings und der UART-Code.
> Mein rprintf braucht 748 Bytes
Ich weiss natürlich nicht, was das alles kann :-)
Im Anhang mal meine Version, die ich mir mal ausgedacht hab. Wollte
wissen, wie va_args funktionieren, und wie klein ich das krieg. Ca.
200Bytes mit armcc auf -O3.
Kann:
- printf formatstring
- %i, %c, %s, führende nullen bis max. 8
- \t, \r, \n
Kann nicht:
- Noch kein Hex (kommt noch)
- Kaffee kochen
Bis auf HEX also das wichtigste drin.
---
Könnt ihr mit spielen, zerreissen, kaputtdiskutieren, selbst verwenden,
erweitern, ..., was auch immer.
Konstruktive Vorschläge bzgl. Codegrössenverkleinerung oder
Erweiterungen stets willkommen :-)
VG,
/th.
In ChaNs Beispielen zu seiner FAT-library findet man monitor.c/monitor.h. Dieses "rprintf" (er nennt es xprint) nutze ich gerne und oft. Bietet aber auch kein %f und %lf. (http://elm-chan.org/fsw/ff/ffsample.zip) Weiterhin ganz schick ist "stdio.c" aus den Software-Beispielen zu AT91 (UVZ utils) von Atmel. Lässt sich auch gut portieren und "überlagert" printf der libc (newlib bei CS G++, Yagarto, DevkitARM, WinARM etc.). Link-Reihenfolge beachten... Spart #defines beim "Vorentwickeln" von C-Code auf PC und späterer Anwendung auf µC. Speicherplatzbedarf für beide habe ich bis dato nicht ermitteln müssen.
Hättet Ihr diese Diskussion nicht etwas früher führen können ? (grins) Nach diversen Frusterlebnissen mit der newlib (ich sehne mich nach der avr-libc zurück....), habe ich: http://www.sparetimelabs.com/tinyprintf/index.html gefunden und bei mir implementiert. Funktioniert bis jetzt gut. Hat auch keine Floatsupport, aber das stört mich nicht, macht man ja eh. nicht habe ich von Euch gelernt ;-) Daniel
>gerne und oft. Bietet aber auch kein %f und %lf. >(http://elm-chan.org/fsw/ff/ffsample.zip) der Link geht leider grade nicht. Die Variadic Version von Thorsten scheint mir aber ziemlich ausgekocht zu sein. Va_args Makros sind schon in meinem ersten C Buch von 1987 drinnen aber ich habe sie noch nie benutzt. Ich habe den Flush weggelassen und fputc durch ein eigenes Putchar ersetzt. fputc aus der Lib tut bei mir sowieso nicht und es wird dann nicht jedesmal ein Filpointer stdout auf den Stack geschoben. Mit -Os komme ich auf 440 Bytes und mit -O0 auf 988 Bytes was ich vor allem gegenüber der newlib als sehr ordentlich gelten lassen möchte. Besten Dank an Thorsten - die 200 Bytes scheinen ohne Literal Pool gezählt zu sein ? Auf der Wunschliste stehen natürlich noch die rechtsbündige Ausrichtung mit Leerzeichen anstelle führender Nullen, (evtl. auch Linksbündig) und zum Debuggen eben %x bzw. %X >Nach diversen Frusterlebnissen mit der newlib (ich sehne mich nach der >avr-libc zurück....) Klar kann man sagen, daß die Lib Sache der Compilerbauer ist. Newlib wurde halt für RAM Maschinen mit MMU und so gemacht und da hat eine Reentrant und über File-Write voll gepufferte Ausgabe schon seine Berechtigung die vielleicht auch noch bei einem Cortex-A passen mag. Beim Cortex-M kocht dagegen jetzt wieder jeder sein eigenes Süppchen was wegen mehr oder weniger Salz letztendlich wieder inkompatibel ist. Vielleicht ist es ja eine Überlegung wert, ob CMSIS dabei nicht doch naheliegende Äquivalente zur avr-libc sozusagen als cm3-libc definieren kann, wobei dann die Compilerhersteller noch immer irgendwas eigenes machen könnten wenn sie meinen dies besser zu können. Über stdout kann man letztendlich ebenso wie beim Startup streiten, ob es besser beim Compiler oder einer CMSIS HAL aufgehoben ist.
J. V. schrieb: >>gerne und oft. Bietet aber auch kein %f und %lf. >>(http://elm-chan.org/fsw/ff/ffsample.zip) > > der Link geht leider grade nicht. Ja. ChaN mag offentsichtlich keine "deep links". Über die Homepage gehen: http://elm-chan.org dort gibt es derzeit einen direkten Link (update FatFs Module, dann "Samples") ansonsten über "Software" durchhangeln.
A. K. schrieb: > A. K. schrieb: > >> UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE; > > PS: Das hat auch den Vorteil, dass man zur besseren Dokumentation der > Einstellungen > UNIT.REGISTER = 1<<ENABLE | 0<<DISABLE | 0<<EXPLODE; > schreiben kann. Also auch Bits explizit reinschreiben kann, die man > gezielt nicht setzen will. Das könnte man auch bei Masken ähnlich erreichen:
1 | UNIT.REGISTER = 1*ENABLE | 0*DISABLE | 0*EXPLODE; |
J. V. schrieb: > Die Variadic Version von Thorsten > scheint mir aber ziemlich ausgekocht zu sein. Va_args Makros sind schon > in meinem ersten C Buch von 1987 drinnen aber ich habe sie noch nie > benutzt. Ich habe den Flush weggelassen und fputc durch ein eigenes > Putchar ersetzt. fputc aus der Lib tut bei mir sowieso nicht und es wird > dann nicht jedesmal ein Filpointer stdout auf den Stack geschoben. Mit > -Os komme ich auf 440 Bytes und mit -O0 auf 988 Bytes was ich vor allem > gegenüber der newlib als sehr ordentlich gelten lassen möchte. Besten > Dank an Thorsten - die 200 Bytes scheinen ohne Literal Pool gezählt zu > sein ? > > Auf der Wunschliste stehen natürlich noch die rechtsbündige Ausrichtung > mit Leerzeichen anstelle führender Nullen, (evtl. auch Linksbündig) und > zum Debuggen eben %x bzw. %X > Hi, vielen Dank für's Lob! Dadurch, dass der C Code dabei ist, kannste das printf ja etwas erweitern. Die 200Bytes (oder genauer: 190) waren mit Cortex-M3 und armcc V3.x (Keil uVision 3.x) und -O3, allerdings fehlte der Support für die führenden nullen im thprintf. --- Die "Gemeinheit" hat wohl keiner entdeckt **grins** Ich verwende einen Pointer zum zwischenspeichern einer Zahl. Dies tut man normalerweise nicht, ist hier aber ok, da nicht schreibend drauf zugegruffen wird. Ansatz war - neben va_args - wie man das möglichst klein kriegt in C :-) --- Verwendung frei, wenn es irgendwo auftaucht würde ich mich über einen Hinweis a'la der Kommentare sehr freuen. VG, /th.
> Die Variadic Version von Thorsten > scheint mir aber ziemlich ausgekocht zu sein. In der Tat steckt da doch einiges an Zeit drin. > Ich habe den Flush weggelassen und fputc durch ein eigenes > Putchar ersetzt. Passt. Ich selbst habe ein "tierisches", OS like retargeting laufen, was ganz unten an der stdio anflanscht. Zusammen mit einem (eigenen) virtuellen Dateisystem a'la /dev/hw_driver. Daher gehen alle höheren Funktionen den standard Weg, da sie dann immer richtig rauskommen. Mein eigendliches putchar auf UART0,1,2,3, ITM, LCD, GLCD, whatEver kommt dann ein paar ebenen tiefer im Gerätetreiber :-) Dies wird dann per f(re)open gesteuert. Ist sehr angenehm/flexibel, wenn auch auf dem µC alles OS like machen kann, z.B. Cmd> echo "Hallo, Welt" > /dev/glcd0 VG, /th.
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.