Hallo, nach langer Zeit habe ich mich endlich dazu entschlossen doch noch in die Mikrokontrollerprogrammierung einzusteigen. Dabei war diese Web-Site nicht ganz unschuldig, dank den vielen Beiträgen und Tutorials. Vielen Dank einmal. Ich wollte hier zuerst nur mal meine persönlichen Erfahrungen und meine ersten Erfolge und Misserfolge weitergeben, vielleicht helfen meine Erkenntnisse anderen beim Einstieg in die wirklich faszinierende Welt der Mikrokontroller. Da ich beruflich lange mit Assembler (PDP-11) zu tun hatte und auch zu Zeiten des Apple II vor allem Assembler benutzte war es für mich am einfachsten auch hier mit Assembler zu beginnen. Auch wenn meine Erfahrungen mit Assembler sehr lange her sind, aber in Assembler programmieren ist wie Schwimmen oder Fahrrad fahren, man vergisst es nie mehr. Zuerst startete ich mit dem Evaluations Board 2.01 von Pollin und AVR Studio. Vielleicht mag es für viele ein günstiger Einstieg sein, aber das ewige hin und herschalten zwischen AVR Studio und Pony konnte mich nicht überzeugen. Da der erste ISP MKII den ich bestellte nie den Weg zu mir gefunden hat und der "noname" ISP MKII aus Hong-Kong von AVR Studio nicht erkannt wurde (Schuld daran hatte aber wie sich später zeigte nicht das "noname" Geräte, sondern es lag entweder an der alten AVR Studio Version oder an meiner Schusslichkeit im Umgang mit Windows, denn wie sich kürzlich zeigte erkennt AVR Studio 4.18 den ISP und konnte ihn problemlos auf den neusten Stand bringen) habe ich mich dann nach einer Frustpause von fast einem halben Jahr entschlossen doch noch etwas Geld in die Hand zu nehmen und mir ein STK500 gekauft. Damit bin ich nun sehr zufrieden und denke es ist die einfachste und unkomplizierteste Einstiegsplattform, wenn auch nicht die günstigste. Als erstes startete ich wie gesagt mit dem AVR Assembler und hatte auch schnell Erfolge (Knight Rider LED, UART Programmierung, etc. bis zu meinem ursprünglichen Grund für den Einstieg in die Mikrokontrollerwelt, ein PS/2 Interface für meinen alten Apple als alternative zur original Tastatur, da ich immer noch gerne mit Apple II spiele). Da aber sehr viele Beispiele im Internet in C geschrieben sind habe ich begonnen mich mit C auseinanderzusetzen. Nur kann mich C im Zusammenhang mit hardwarenaher Programmierung einfach nicht überzeugen. Da habe ich mir gedacht, am besten wäre es wenn man beides haben könnte. Also C dort zu brauchen wo es der Übersichtlichkeit und dem Verständnis und Wartbarkeit dient und die hardwarenahen Routinen und die ISR in Assembler weil hier manchmal jeder uSekunde zählt. Damit haben aber die Probleme erst richtig begonnen. Der gnu Assembler hat natürlich eine ganz andere Syntax als der AVR Assembler. Es ist alles anders auch wenn in AVR Studio gnu Assembler unterstützt wird (in der Version 4.18 sogar richtig gut, viel Handarbeit wie es früher anscheinend nötig war wie ich im Internet gesehen habe war nicht nötig) ist natürlich nichts so wie in einem reinen C oder Assembler Projekt. Inline Assembler war mir zu mühsam und auch nicht das was mir vorschwebte. Die Dokumentation von gas gibt da auch nicht viel her. Also war Probieren angesegt. Die folgenden Aktionen haben mir dann aber zum Erfolge verholfen Man muss in den Assembler Sourcen #include <avr/io.h> das generische Header File inkluden und ja keines der üblichen. Header Files muss man 2-teilen eines für Assembler und eines für C Source. Wenn man die IO Register symbolisch anspricht muss man jeweils das Macro _SFR_IO_ADDR bemühen, das jeweils 32 von den Definitions in avr/io.h abzieht, damit es stimmt. Also etwa out _SFR_IO_ADDR(OCR1BL),r24 Aber der grösste Frust war F_CPU. AVR Studio übergibt den Wert den man in den Konfigurations Optionen angibt mit -DF_CPU=16000000UL, nur kann der Assembler mit dem Wert 16000000UL gar nichts anfangen. Nach langem suchen bin ich dann per Zufall über folgenden Beitrag im Internet bei AVR Freaks gestossen. AVR GCC forum - How to get a constant from C to Asm Und dort hat dann jemand ohne das es direkt das Ursprungstopik betraff folgende Antwort auf mein Problem gehabt .set MY_FREQ,0 //init to 0 .irpc param,F_CPU //go through all 'characters' in F_CPU .ifnc \param,U //if not a 'U' .ifnc \param,L //and not an 'L' .set MY_FREQ,MY_FREQ*10+\param //left shift,then add .endif .endif .endr der die Definition von F_CPU mit abschliessendem UL in einen Wert ohne UL am Ende umwandelt der dann vom Assembler wieder als Zahl verstanden wird. Netterweise generiert AVR Studio 4.18 ein Listing auch wenn der Suffix des Assembler Sourcfiles .s und nicht .S (im vom AVR Studio generierten Makefile steht ASMFLAGS += -x assembler-with-cpp -Wa,-gdwarf2) So das wollte ich mitteilen, auch deshalb weil ich zwar im Zusammenhang mit den Fragen die ich hatte immer wieder mal via Google auf dieses Forum gestossen bin aber im Zusammenhang mit gemischten C/Assembler Projekten keine Antworten auf die Fragen bekam, sogar wenn Benutzer vor langer Zeit die gleichen Fragen wie ich hatte fand ich im Thread keine Antwort. Vielleicht hilft es dem Einen oder Anderen oder kann mir sonst nocht Tipps zu gemischten Projekten geben. Gruss Peter
Gast
#1979646
Die Hinweise zur Kombination von Assembler und C können sicher dem einen oder anderen Forumsteilnehmer nützen. Aber, ohne irgendwie den AVR geringschätzen zu wollen... von jemandem, der PDP-11 Erfahrung hat, hätte ich eigentlich erwartet, mit dem MSP430 einzusteigen. Warum? Zitat aus der englischen Wikipedia: "The architecture dates from the 1990s and is reminiscent of the DEC PDP-11." ;)
Gast
#1979650
Es erschließt sich mir bis auf den heutigen Tag nicht warum man C und ASM mischen muß. Ich programmiere, wie du auch, in beiden Sprachen aber nie gemischt. Auch wo ich hier solche Konstrukte gesehen habe stellt sich am Ende heraus das eine Mixtur aus beiden Sprachen nicht nötig ist. Kannst du mal ein Beispiel geben ?
Gast
#1979664
> Es erschließt sich mir bis auf den heutigen Tag > nicht warum man C und ASM mischen muß. Du schreibst nur Kinderprogramme ? Da, wo's schnell sien muss Assembler, da, wo's komplex wird C. Wenn man die KOnstante nicht als DEFINE braucht, sondern nur als konstanten Wert, kann man sie einfach zuweisen und in Assembler verwenden.
Bernd N schrieb: > Es erschließt sich mir bis auf den heutigen Tag nicht warum man C und > ASM mischen muß. Ich programmiere, wie du auch, in beiden Sprachen aber > nie gemischt. Auch wo ich hier solche Konstrukte gesehen habe stellt > sich am Ende heraus das eine Mixtur aus beiden Sprachen nicht nötig ist. Dem kann ich mich nur uneingeschränkt anschließen. Es sind eigentlich nur Anfänger, die meinen, etwas unbedingt in Assembler schreiben zu müssen. Und laufen damit in einen Haufen Probleme. Die Fortgeschrittenen wüßten zwar, wie es gehen könnte. Aber sie machen alles in C und haben keine Lust sich diese unnötige Arbeit aufzubürden. Peter
> Da ich beruflich lange mit Assembler (PDP-11) zu tun hatte Du solltest Dir mal den MSP430 anschauen ...
Gast
#1979709
MaWin schrieb: > Du schreibst nur Kinderprogramme ? Nö. Ich nehm gleich den 5% schnelleren Quarz, und erspare mir damit 50000% Zeit. Oliver
Gast
#1979712
Lieber MaWin, ich denke dir fehlt da noch einiges an Erfahrung. >> Wenn man die KOnstante nicht als DEFINE braucht, >> sondern nur als konstanten Wert, kann man sie >> einfach zuweisen und in Assembler verwenden. Das ist in C nicht anders, zeig mal ein Beispiel :-)
Gast
#1979754
> Das ist in C nicht anders, zeig mal ein Beispiel :-) Scherzkeks, du hast überhaupt nicht begriffen daß die Konstante bereits in C vorliegt. > Lieber MaWin, ich denke dir fehlt da noch einiges an Erfahrung. Dir fehlt VERDAMMT VIEL an Erfahrung. Und ja, ich schreibe öfters mal ein Programm, welches Assemblerbefehle einsetzt die so in C gar nicht machbar wären (Hauptsächlich die geschickte Verwendung des Carry), und bei denen diese Form der Programmierung entscheidend ist damit das Programm schnell genug wird, denn leider gehen die üblichen Microcontroller nur in die Megahertz und nicht in die Gigahertz. Dafür hab ich kein Problem mit einem üblichen 16MHz AVR auf 16 x 16 RGB LEDs helligkeitsanimierte Filme ablaufen zu lassen, ohne Hilfschips.
MaWin schrieb: > Programmierung entscheidend ist damit das Programm schnell genug wird, > denn leider gehen die üblichen Microcontroller nur in die Megahertz und > nicht in die Gigahertz. Was für dich die Regel ist, muss für andere noch lange nicht der Normalfall sein. Und der NOrmalfall ist nun mal, dass in >90% aller Fälle, in denen Neulinge denken sie bräuchten unbedingt Assembler, dieser Sachverhalt ganz einfach nicht zutrifft. Das es Fälle gibt, in denen jeder Taktzyklus zählt, ist unbestritten. Aber im Regelfall ist das nicht der Fall.
MaWin schrieb: > Dir fehlt VERDAMMT VIEL an Erfahrung. > > > > Und ja, ich schreibe öfters mal ein Programm, welches Assemblerbefehle > > einsetzt die so in C gar nicht machbar wären (Hauptsächlich die MaWin ist heute aber nicht sehr entspannt. Was soll so eine blöde Anmache? mfg.
Ja die gute alte PDP-11. Ich habe sogar noch eine PDP-11/23, aber leider keine RSX-11M Lizenz, da viel zu teuer. Tja, Bernd ich habe nicht gesagt du musst, und ich musste auch nicht ich wollte einfach. Es ging um ein Projekt mit Hauptprogramm, dass via UART Befehle entgegennahm, den Input überprüfen, interpretieren und übersetzen musste um danach Befehle in eine Warteschlange zu stellen die von einer Interruptroutine in regelmässigen Abständen (im Moment 50 uSekunden) abarbeiten muss. Da ich einerseits immer mehr Logik in das Hauptprogramm packen muss und ich auch den Zyklus verkleinern will (weil dann die Schrittmotoren besser, d.h. sanfter und geräuschloser, arbeiten) kam ich langsam an die Grenzen der Performance des AVR. Eine kleine Analyze zeigte, der AVR verbraucht ganz schön Zyklen in der in C gehaltenen Interrupt routine. In Assembler übersetzt braucht die ISR noch 60% der Zyklen und ist erst noch übersichtlicher geworden. C ist eben nicht für alles brauchbar. Andererseits wäre der Aufwand das Hauptprogram in Assembler zu transferieren viel zu gross. Das mit #define F_CPU 16000000UL , ich brauche den Wert um Konstanten umzurechnen und da will gas keine U und L am Ende von Zahlen. Etwa #define t_period_1_l lo8(F_CPU_AS * PERIOD_1 1000000 2) und das geht mit F_CPU nicht. Peter du sagts es und um es mit deinen eigenen Worten zu sagen. Ich habe keine Lust mir diese unnötige Arbeit aufzubürden alles in C zu schreiben, wenn es nur darum geht mit Bits herumzuschaufeln. In Assembler gelingt mir das schneller, sicherer und mit viel weniger Fehlern. Ich kann da nur für mich sprechen und es liegt sicher auch daran das ich als Echtzeitprogrammierer auf der PDP-11 einiges an Erfahrung im Schreiben von Interrupt Routinen und Hardwaretreibern sammeln konnte die mir nun das Leben wesentlich mehr erleichtern als meine aktuellen Kenntnisse in C. Und glaube mir die Debugmöglichkeiten auf einer PDP-11 waren lange nicht so ausgereift wie das was heute als selbstverständlich vorausgesetzt wird. Und so wie es scheint gibt es auch andere Programmierer die in einem gemischten Projekt Vorteile sehen ;-). Gruss Peter
Gast
#1979802
> Was soll so eine blöde Anmache?
Die, die Bernd N an mich geschrieben hat ?
Gute Frage, ihm fehlt halt offensichtlich Erfahrung,
die Erfahrung, daß das was ich schriebe schon richtig
ist und Hand und Fuss hat.
Aber was interessiert dich das ? Hast du womöglich das
geschrieben was unter Bernd N erschien und kommst mit
deinen Pseudonymen durcheinander ?
Peter Schranz schrieb: > uSekunden) abarbeiten muss. Da ich einerseits immer mehr Logik in das > Hauptprogramm packen muss und ich auch den Zyklus verkleinern will (weil > dann die Schrittmotoren besser, d.h. sanfter und geräuschloser, > arbeiten) kam ich langsam an die Grenzen der Performance des AVR. Eine > kleine Analyze zeigte, der AVR verbraucht ganz schön Zyklen in der in C > gehaltenen Interrupt routine. Du hast doch nicht die Analyse in die ISR gepackt, oder? > In Assembler übersetzt braucht die ISR > noch 60% der Zyklen und ist erst noch übersichtlicher geworden. Bist du sicher, dass du den Optimizer eingeschaltet hast? > C ist > eben nicht für alles brauchbar. Doch ist es. So ziemlich. TV-Signal Timing geht nicht mehr 100% aber ansonsten geht fast alles. Nur dazu muss man C können und der grundsätzliche Progammaufbau muss stimmen. Wenn ich nach dem bischen gehe, was ich deiner Beschreibung entnehmen kann, klingt das für mich alles nach: Du hast deinen Programmaufbau vermurkst und jetzt muss es Assembler richten.
Gast
#1979810
>> Tja, Bernd ich habe nicht gesagt du musst, und ich musste auch nicht ich >> wollte einfach. Das laß ich gelten :-) Solche Dinge lassen sich nur Anhand konkreter Beispiele diskutieren und ich hab halt Freude daran entwickelt es genau umgekehrt zu handhaben. Man kann auch in C auf einen Akku zugreifen wenn man den Compiler und die Architektur gut kennt. Kleines Beispiel für nen 8x51:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
Das ist eigentlich Assembler :-) und ich kann dir viele fiese Beispiele zeigen. Ich würde hier gerne am konkreten Beispielcode diskutieren statt mich beleidigen zu lassen (MaWin).
MaWin schrieb: > Gute Frage, ihm fehlt halt offensichtlich Erfahrung, > > die Erfahrung, daß das was ich schriebe schon richtig > > ist und Hand und Fuss hat. Du solltest dich in Gott umbenennen. Aber vorher einen Deutschkurs machen.
Peter Schranz schrieb: > Das mit #define F_CPU 16000000UL , ich brauche den Wert um Konstanten > umzurechnen und da will gas keine U und L am Ende von Zahlen. Das L kannst Du einfach weglassen. Da 16000000 nicht mehr in ein int16_t paßt, ist es implizit int32_t. Das U kannst Du auch weglassen, wenn Zwischenrechnungen nicht Werte >2147483647 ergeben. Du kannst aber auch einfach umgekehrt definieren: -DF_CPU_AS=16000000 Und im C-Header:
1 | |
Peter
Peter Schranz schrieb: > von einer Interruptroutine in regelmässigen Abständen (im Moment 50 > uSekunden) abarbeiten muss. Das sind bei 16MHz dann 800 Zyklen. Ein Interrupt in C hat bei mir im Schnitt 50..200 Zyklen, klingt also problemlos in C machbar. Allerdings muß ich zugeben, daß der AVR-GCC es einem nicht gerade leicht macht, optimiert zu schreiben. Viele Sachen macht er von Haus aus 16-bittig. Also immer schön darauf achten, daß Variablen und Returnwerte uint8_t sind, wenn ausreichend. Von daher ist es vorteilhaft, Assembler zu verstehen und mal näher ins Listing zu schauen, warum er manchmal etwas umständlich macht. Peter
Gast
#1979862
>> Was soll so eine blöde Anmache? >> Die, die Bernd N an mich geschrieben hat ? Sollte ich dich beleidigt haben dann entschuldige ich mich. Es gibt unzählige Threads C vs ASM, ich mag hier den Thread nicht mißbrauchen aber am Beispiel zu diskutieren hilft. Wie man klar sehen kann rollen die ersten Tips ein und ich denke das Peter Danegger sowie Karl Heinz eine Menge Erfahrung haben, du brauchst dich also nicht auf meine Aussagen zu verlassen. Nach mehr als 20 Jahren ASM und ca. 5 Jahren C Erfahrung traue ich mir allerdings ein bischen was zu.
Hallo Peter, ja ich kann das L und U schon einfach weglassen da hast du natürlich recht. Aber AVR Studio übergibt es dem avr-gcc mit U und L daran bin ich gescheitert. -DF_CPU=16000000UL auch wenn ich in den Configuration Options zum Projekt bei Frequency nur 16000000 eingebe. Ich könnte es natürlich auch dort weglassen und in einem globalen Header File mit #define F_CPU 16000000 definieren. Das wäre auch gar nie das Problem gewesen, hätte ich von Anfang an gewusst, dass das UL an den Assembler Fehlermeldungen schuld war ../timer1isr.s: Assembler messages: ../timer1isr.s:111: Error: `)' required ../timer1isr.s:111: Error: garbage at end of line ../timer1isr.s:149: Error: `)' required ../timer1isr.s:149: Error: garbage at end of line Aber eben ohne dieses Wissen bin ich gar nicht erst darauf gekommen F_CPU selbst im Header File ohne UL zu deklarieren. Gruss Peter
> >
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
>
Ja das ist zwar C aber wirklich lesbarer als eine einfache Assembler
Routine ist es nicht.
Peter
Gast
#1979883
> Ich würde hier gerne am konkreten Beispielcode diskutieren > statt mich beleidigen zu lassen (MaWin). Du beleidigst, Bernd, denn es bist du der sich nicht vorstellen kann, was anderen klar ist. Einfaches drehen von bits: unsigned char bisher[8]; unsigned char neu[8]; Was jetzt in einem byte steckt, soll in bit 0 von neu 0..7 verteilt werden. if C irgendwie so: for(i=0;i<8;i++) { for(j=0;j<8;j++,bisher[i]>>=1) { neu[j]=(neu[j]<<1)|(bisher[i]&1); } } und vergleiche die Ausgabe deines Compilers mit irgendeinem Assembler, ob 6502, PIC oder AVR ROR bisher0 ROL neu0 ROR bisher1 ROL neu0 ROR bisher2 ROL neu0 ROR bisher3 ROL neu0 ROR bisher4 ROL neu0 ROR bisher5 ROL neu0 ROR bisher6 ROL neu0 ROR bisher7 ROL neu0 8 x mit oder ohne Schleife. Wer wie Peter Schranz zu Apple ][ Zeiten programmiert hat, hat ein gutes Gefühl für Microcontroller, schliesslich war die 6502 damals ähnlich schnell wie Microcontroller heute. Man konnte (siehe Apple Graphik Spiele) damals viel machen, aber damit es ausreichend schnell wurde war war Assembler angesagt. Das begrifft nicht nur Graphik, auch Mathematik wird ohne Assembler öde langsam. Ein guter Teil der C-Standardfunktionen die du benutzt aus der Standardlibrary wird übrigens in Assembler geschrieben worden sein, also benutzt du auch unter C stets Assemblerstücke.
Gast
#1979892
Also ich beleidige :-) >> Ein guter Teil der C-Standardfunktionen die du benutzt aus >> der Standardlibrary wird übrigens in Assembler geschrieben >> worden sein, also benutzt du auch unter C stets Assemblerstücke. Da wäre ich nicht drauf gekommen.
Peter Schranz schrieb: > Aber AVR Studio übergibt es dem avr-gcc mit U und L daran bin ich > gescheitert. Ich muß zugeben, ich benutze kaum AVRStudio. Ich nehme meistens ne Batch. Hier kannst Du mal sehen, wie ich das mache und was dabei rauskommt: Beitrag "mehrere MC seriell über Datenbus verbinden (1Draht)" Es ist ein SW-Protokoll und damit auch ein bischen "zeitkritisch". Die Interrupts sind also möglichst optimiert geschrieben. Peter
MaWin schrieb: > if C irgendwie so: > > for(i=0;i<8;i++) > { > for(j=0;j<8;j++,bisher[i]>>=1) > { > neu[j]=(neu[j]<<1)|(bisher[i]&1); > } > } Nö. In C irgendwie so Beitrag "Re: Inline Assembler- Bits spiegeln" > > ROR bisher0 > ROL neu0 > ROR bisher1 > ROL neu0 > ROR bisher2 > ROL neu0 > ROR bisher3 > ROL neu0 > ROR bisher4 > ROL neu0 > ROR bisher5 > ROL neu0 > ROR bisher6 > ROL neu0 > ROR bisher7 > ROL neu0 > > 8 x mit oder ohne Schleife. macht 16 Zyklen versus 20. So what?
Hallo Karl-Heinz, nein der Programaufbau ist nicht vermurkst. Wie gesagt läuft es ja auch in der ursprünglichen Version in C, mit 50us Interval und dem aktuellen Befehlssatz den das Hauptprogram interpretieren muss ohne Probleme. 50us sind zwar bei 16MHz 800 Zyklen, aber das ist auch nicht das Problem der ISR, sie verbrauchte nicht 800 Zyklen. Ich möchte nur dem Hauptprogram auch noch ein bisschen Zeit gönnen. Und jetzt stellte sich eben die Aufgabe, ich will dem Hauptprogram auf alle Fälle genügend Zeit lassen und den Zyklus würde ich gerne auf 25 oder 20us verkleinern. Einem C Progammierer mit mehr Erfahrung wäre es sicher auch gelungen die ISR in C zu beschleunigen. Aber dazu habe ich zuwenig Erfahrung in C. Ich hatte ja auch nie wirklich Probleme mit dem Program sondern es waren mehr ärgerliche Problemchen mit der IDE. Gruss Peter
Peter Schranz schrieb: > sind zwar bei 16MHz 800 Zyklen, aber das ist auch nicht das Problem der > ISR, sie verbrauchte nicht 800 Zyklen. Ich möchte nur dem Hauptprogram > auch noch ein bisschen Zeit gönnen. Und jetzt stellte sich eben die > Aufgabe, ich will dem Hauptprogram auf alle Fälle genügend Zeit lassen > und den Zyklus würde ich gerne auf 25 oder 20us verkleinern. Einem C > Progammierer mit mehr Erfahrung wäre es sicher auch gelungen die ISR in > C zu beschleunigen. Was genau machst du alles in der ISR? Das ist der springende Punkt! In deiner Problemstellung bieten sich 2 ISR an. Die eine die 1 Zeichen von der UART holt und in eine FIFO stellt, von wo aus es dann gemütlich im Hauptprogramm geholt werden kann, wenn Zeit ist. Die andere ISR ist der Basistakt, mit dem die Schrittmotoren angesteuert werden. Und mit so einem Aufbau kann ich mir ehrlich gesagt nicht wirklich vorstellen, warum die Zykluszeit der Hauptschleife ein Problem darstellt (im Rahmen gesehen natürlich).
Es ist schon alles so wie du gesagt hast. Eine ISR für den UART mit FIFO zum Hauptprogram und eine ISR die zyklisch die Schrittmotoren steuert. Wenn das Hauptprogram nicht nachkommt setzt die UART ISR das CTS und wenn es dumm kommt, dann hat die Schrittmotoren ISR nicht genügend Aufträge mehr und die Maschine wird langsamer. Das ist nicht schlimm aber erhöht die Verarbeitungszeit. Kam aber schon in der ursprünglichen Version ab und zu vor. Das wolle ich vermeiden. Und das Hauptprogram hat doch einiges zu tun, vor allem wenn ich Kurven fahren will.
OK. Das klingt ja schon mal vernünftig. Und wie bzw. wo sparst du jetzt in der ISR durch die Verwendung von Assembler 60% Rechenzeit ein? Das klingt nämlich überhaupt nicht mehr vernünftig. Entweder ist dein C-Code extrem ineffizient programmiert oder die ISR ist so kurz, dass der Callframe Overhead schon signifikant wird. Das man mit C ein paar Abstriche machen muss ist klar. Aber 60% ist schon extrem viel. Edit: Tschuldigung. Umgekehrt. Es waren nur 40%. Ist aber immer noch viel!
Nein nicht 60% eingespart, sondern er braucht noch 60% der Zyklen. Also ich habe 40% gespart. Der Original Source Code in C stammt nicht von mir, ich darf ihn nur brauchen und modifizieren aber nur privat. Auch habe ich mich jetzt mal auf Grund der vielen Hinweise bezüglich Effizienz von C nochmals daran gesetzt (die Erfahrungen die ich hier festgehalten habe sind ja auch schon eine Zeit her) und mir einfach mal mit -S zeigen lassen was denn gcc aus dem Original Code macht. Was mir so auf die schnelle auffällt. Wenn ich OCR1A und OCR1B mit den gleichen Werten bestücke (kommt in diesem Program öfters vor) lädt gcc zweimal die Register r24 und r25 mit dem gleichen Konstanten bevor er OCR1A und danach OCR1B schreibt, in Assembler macht man das intuitiv nicht. Auch memcpy scheint in einer ISR sehr schlecht zu sein, dadurch werden ziemlich viele Register in der ISR auf den Stack gelegt. Da die Anzahl der verschobenen bytes jeweils klein ist und auch nicht variabel ist schreibe ich in Assembler meist eine Kette von lds und sts und brauche genau ein Register. D.h. ich mache nicht mal in Assembler eine Schlaufe, Zeit war ja das Kriterium nicht Platz. Im Moment ist der ganze Code 28kbytes, da habe ich noch einiges bis der ATMEGA32 ausgereizt ist und dann kann ich ja immer noch auf dem ATMEGA64 oder so wechseln (was ich sowieso wegen dem 2ten UART mal machen will). Es scheint wohl so zu sein, man hätte mit C Erfahrung ähnlich viel herausholen können. Auf der anderen Seite muss ich natürlich sagen, mir macht Assembler Spass, hat es immer gemacht ;-)
Peter Schranz schrieb: > danach OCR1B schreibt, in Assembler macht man das intuitiv nicht. Auch > memcpy scheint in einer ISR sehr schlecht zu sein, dadurch werden > ziemlich viele Register in der ISR auf den Stack gelegt. Eine Grundregel beim GCC lautet: In einer ISR keine Funktionen aufrufen! Dadurch zieht man sich einen Rattenschwanz an Register-Sicherungen rein. > Da die Anzahl der verschobenen bytes jeweils klein ist und auch > nicht variabel ist schreibe ich in Assembler meist eine Kette von > lds und sts und brauche genau ein Register. Und warum kannst du dieselbe Strategie nicht auch in C benutzen?
Gast
#1980026
> Nö. > In C irgendwie so > Beitrag "Re: Inline Assembler- Bits spiegeln" Nein, kein Mirror, sondern im 8 x 8 bitarray um 90 Grad drehen.
Da hast du natürlich recht. Vergiss nicht, meine C Kenntnisse sind bezüglich do's und don't's in einer ISR mangels Erfahrung sehr mager. Und darum bin ich ja dazumals auch dazu übergegangen einfach das ganze in Assembler zu schreiben. In Assembler habe ich Erfahrung und auch während meiner Arbeit ziemlich viel programmiert. C kenne ich fast nur vom Hörensagen. Selbst viel programmiert habe ich in C bis jetzt nicht. Und der Erfolg den ich mit Assembler habe hat mich natürlich auch nicht bewogen mich vermehrt mit C zu befassen. Wie heisst es doch so schön, was der Bauer nicht kennt...
MaWin schrieb: >> Nö. >> In C irgendwie so >> Beitrag "Re: Inline Assembler- Bits spiegeln" > > Nein, kein Mirror, sondern im 8 x 8 bitarray um 90 Grad drehen. Ah. Jetzt versteh ichs. Ja, da gbe ich dir schon recht. Wenn man auf Bitebene runter muss, wirds in C manchmal haarig. C beginngt nun mal erst ab Bytes aufwärts, eigentlich erst so richtig ab int aufwärts und mit Bytes hat man schon zeitweise zu kämpfen. Auf Bitebene kanns dann schon mal extrem tricky werden.
...und sofort gibts wieder einen Flamewar. 2/3 dieses Threads gingen darum, ob ASM oder C BESSER sind. Dabei sind beide berechtigt am Platz. Selbst Bascom wird irgendwo seine Berechtigung haben, auch wenn Ich vielleicht noch nicht sehe wo! Es gibt auch ein wunderbares Java für die Dinger! Am besten ist es immer, alles zu mischen, solange man keinen Knoten in den Kopf kriegt! @MaWin: Wenn du so toll in ASM bist, warum machst du dann noch kein AVR-Brainf*ck? Nur um noch mal daran zu erinnern: Der TO hat nicht mal eine Frage gestellt! Mit freundlichen Grüßen, Valentin Buck
Gast
#1980081
> @MaWin: > Wenn du so toll in ASM bist, > warum machst du dann noch kein AVR-Brainf*ck? Weil > Dabei sind beide berechtigt am Platz. das meine Aussage war. Es sind Bernd und Oliver, die keinen Platz für Assembler sahen. Aber schön, daß du schon weisst, der der Böse ist, und zum persönlich angreifenden flamewar übergehst. Denn eine Entschuldung wird von dir nicht kommen.
MaWin schrieb: > Aber schön, daß du schon weisst, der der Böse ist, > und zum persönlich angreifenden flamewar übergehst. Wieso Flamewar? Ich habe dich indirekt gefragt (zugegeben, nicht im allernettesten Tonfall), warum du eher aggressiv auf die Beiträge geantwortet hast. Ich finde Kritik nicht schlimm, nur man kann sie doch auch auf verschiedene Arten ausdrücken (wie einen Schwamm!). Wenn du sie psychisch nicht handhaben kannst und nun verzweifelst, so tut es mir leid. Mensch, entspann dich! Im Grunde genommen ist alles doch nur ein Weg, um ans Ziel zu kommen. Und da kann man doch alles machen. Es ginge doch auch, alles analog zu machen, nur ist C bequem und ASM auch einfacher als das! Und meine Frage war ernst gemeint! Mit freundlichen Grüßen, Valentin Buck
Win* vs. Linux Radfahrer gegen Autofahrer Assembler vs. C Immer diegleichen Diskussionen, ich meine, das eine tun und das andere nicht lassen: - Von (AVR-) Assembler kann man problemlos auf C-Variablen zugreifen. - Man (Ich ;-/ ) ist beim Optimieren nicht viel besser als der Compiler - Wenns wirklich auf maximale Leistung bei kurzem Programmstück ankommt kann man durch Ausnutzen processorspezifischer Befehle (AVR 'swap') schon was rausholen. - Besser nen schnelleren Quartz nehmen! - Wer keine Reserven im System hat, hat falsch geplant. - Inline Assembler in gcc ist gewöhnungsbedürftig, geht aber. - Was Ernsthaftes nur in Assembler ist sehr schwer (64k Assembleroutput will debuggt sein) - Mit C kann man sich an den Assemblerstil ranrobben, umgekehrt geht das durch Makros auch. Ja, soweit mein Beitrag zur Diskussion Cheers Detlef
Gast
#1980154
Peter Schranz schrieb: > Was mir > so auf die schnelle auffällt. Wenn ich OCR1A und OCR1B mit den gleichen > Werten bestücke (kommt in diesem Program öfters vor) lädt gcc zweimal > die Register r24 und r25 mit dem gleichen Konstanten bevor er OCR1A und > danach OCR1B schreibt Nö, tut er nicht:
1 | |
2 | |
3 | |
4 | |
5 | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
Wieviel Zyklen kann man dann noch sparen, wenn man das von Hand in Assembler schreibt?
ldi r24,0x40 out 0x2xb,r24 ldi 0x07 out 0x2a,r24 out 0x28,r24
Peter Schranz schrieb: > In Assembler habe ich Erfahrung und auch > während meiner Arbeit ziemlich viel programmiert. C kenne ich fast nur > vom Hörensagen. Selbst viel programmiert habe ich in C bis jetzt nicht. Ja das kenne ich. Du wirst viele C-Programmierer finden, die auch mal Assembler heiß geliebt haben und sich nichts anderes vorstellen konnten. Ich gehöre auch dazu. Aber der Appetit kommt beim Essen. Mit steigender C-Erfahrung wirst Du Assembler immer weniger mögen und irgendwann aufgeben, ihn mit C zu mixen. C macht es auch einfacher, verschiedene AVRs zu verwenden. Man kann sich natürlich auch in Assembler Macros schreiben, die automatisch zwischen IN/LDS, OUT/STS, RCALL/CALL usw. auswählen. Aber angenehmer ists schon, sich darum nicht kümmern zu müssen. Ich schreibe auch oft Funktionen, die nur einmal aufgerufen werden. Einfach, um das Problem in Module aufzuteilen und die Übersicht zu behalten. Das merkt dann der Compiler und fügt sie inline ein. Peter
Hallo Peter, du hast ziemlich sicher recht und ich sehe das ähnlich. Ich stehe ja aber auch erst am Anfang meiner MCU und C Erfahrung. Assembler war der einfachste Einstieg für mich und half mir die MCU Welt zu erfahren. Ich war vielleicht einfach zu faul mich in C zu vertiefen nur um meine ersten Ideen umzusetzen. Ich wollte auch nie einen Glaubenskrieg zwischen den Programmiersprachen beginnen noch will ich jemanden sagen in welcher Sprache er am besten programmieren soll. Um C komme ich ja sowieso nicht herum. Das war mir schon immer klar. Nur hätte ich mir einen weicheren Übergang gewünscht. Gruss Peter
Gast
#1980267
Peter Schranz schrieb: > ldi r24,0x40 > out 0x2xb,r24 > ldi 0x07 > out 0x2a,r24 > out 0x28,r24 Die zweite Zeile sollten wohl zwei Zeilen out sein und du hast das ret vergessen. Insgesamt also genau 0 Zyklen und 0 Bytes gespart, oder wie? Davon abgesehen wollte ich ja hauptsächlich die Aussage "gcc lädt identische Konstanten doppelt" richtigstellen.
Hallo, naja, welche Sprache ist im Grunde bei Kleinigkeiten oftmals egal. Ich habe nur die Erfahrung gemacht (Programmiere fast alles in C), dass man in C das wesentlich besser verstehen und lesen kann, insbesondere, wenn das ganze eine gewisse kritische Masse überschreitet. Ich denke, das kommt einfach drauf an, was man machen will und welche Anforderungen vorhanden sind. Die ein oder andere Interruptroutine habe ich auch schon in Assembler gehackt, weil sie einfach zu viel Zeit gefresen hat, aber wie war das mit 80/20, man kann sich also 80% sparen in Assembler umzusetzen, weil es eh nicht Geschwindigkeitsrelevant ist. Ganz nebenbei, nimm mal nen Assemblercode von einem Fujitsu Controller und lass den dann auf einem AVR oder MSP430 laufen :) Da war ich schon oft froh, dass man sowas in C mit wesentlich weniger Änderungen machen kann. Viele Grüße
Hallo Andreas,
das High-Byte geht in einen zwischenspeicher und wird erst mit dem Laden
des Low-Byte in das 16-bit Register übernommen. Ok ich hatte noch einen
Typo in der 3ten Zeile
ldi r24,0x40
out 0x2xb,r24
ldi r24,0x07
out 0x2a,r24
out 0x28,r24
das erste out setzt das tmp und das wird bei beiden anderen out
mitverwendet.
Der gcc macht bei mir leider folgendes
OCR1A = F_CPU * TIMER1_0 2 1000000L; //1856
55c: 80 e4 ldi r24, 0x40 ; 64
55e: 97 e0 ldi r25, 0x07 ; 7
560: 9b bd out 0x2b, r25 ; 43
562: 8a bd out 0x2a, r24 ; 42
OCR1B = F_CPU * TIMER1_0 2 1000000L; //1856
564: 80 e4 ldi r24, 0x40 ; 64
566: 97 e0 ldi r25, 0x07 ; 7
568: 9b bd out 0x2b, r25 ; 43
56a: 8a bd out 0x2a, r24 ; 42
wenn ich nur wüsste warum, irgendwas habe ich da wohl man unbewusst
verbockt. Hat einer einen Tipp?
Gruss
Peter
Gast
#1980326
Peter Schranz schrieb: > das erste out setzt das tmp und das wird bei beiden anderen out > mitverwendet. Ich vergaß diese Möglichkeit. Dann ist natürlich auch klar, warum der Compiler das eine out nicht einsparen kann: Er weiß ja nicht, dass das TEMP Register nicht in einem Interrupt genutzt wird. In deinem Beispiel werden auch nirgends die Interrupts abgeschaltet oder sonstwie gesichert, also funktioniert dieses Fragment auch nur mit impliziten Annahmen. Peter Schranz schrieb: > wenn ich nur wüsste warum, irgendwas habe ich da wohl man unbewusst > verbockt. Hat einer einen Tipp? Optimierung einschalten vielleicht? Ich habe mein Beispiel mit -O2 übersetzt. Ohne Optimierung arbeitet der gcc halt stur nach den Anweisungen, wie sie im Quellcode vorkommen. Schön fürs Debugging, weniger für Code-Größe und Performance.
Hallo Andreas, ich muss mal nachschauen was da AVR Studio ein- ausschaltet und warum oder warum nicht, das werd ich noch herausfinden, werde mal auf Grund deines Hinweises nach -O suchen und die Doku nochmals gezielt nach Hinweisen durchforsten. Das mit meiner Annahme ist ganz einfach, das mache ich nur in der ISR, also kein Risiko für weitere Interrupts, aber sonst würde ich natürlich von solchen Tricks auch abraten. In der Zwischenzeit bekomme ich mit den Hinweisen auch mit C immer kürzeren und schnelleren Code. Nur eine Zeile macht mir echt sorgen, weil sie ist jetzt noch der Grund das der C Code viel zu viele Register auf den Stack legt. current_byte = message[i++]; daraus wird dann 59c: 80 91 bf 00 lds r24, 0x00BF 5a0: e8 2f mov r30, r24 5a2: f0 e0 ldi r31, 0x00 ; 0 5a4: e1 54 subi r30, 0x41 ; 65 5a6: ff 4f sbci r31, 0xFF ; 255 5a8: 93 81 ldd r25, Z+3 ; 0x03 5aa: 90 93 c0 00 sts 0x00C0, r25 5ae: 8f 5f subi r24, 0xFF ; 255 5b0: 80 93 bf 00 sts 0x00BF, r24 Es soll mir einfach nur nie mehr jemand sagen C sei viel einfacher als Assembler, beides hat wohl seine Tücken. Gruss Peter
Peter Schranz schrieb: > Nur eine Zeile > macht mir echt sorgen, weil sie ist jetzt noch der Grund das der C Code > viel zu viele Register auf den Stack legt. > > current_byte = message[i++]; Das sieht doch recht optimal aus. current_byte und i sind bestimmt globale Variablen, also muß er sie aus dem SRAM holen bzw. dort ablegen. Peter
Gast
#1980471
>> werde mal auf Grund deines Hinweises nach -O suchen
Wie steht denn derzeit der Optimizer ? mit -Os optimierst du auf Size.
Gast
#1980474
Hi >Es soll mir einfach nur nie mehr jemand sagen C sei viel einfacher als >Assembler, beides hat wohl seine Tücken. Als notorischer Assemblerprogrammierer muss ich sagen, nach 5min von C erzeugten Code Ansehen brauche ich eine halbe Stunde um die gesträubten Nackenhaare wieder in Form zu bringen. MfG Spess
Geht mir manchmal genauso. Und ich kann mich nicht daran anfreunden C so zu "ordnen" und zu "verwenden" dass daraus schöner Assembler Code entsteht. dann mach ich es lieber gleich direkt in Assembler. Ich denke in den ISRs werde ich definitv bei Assembler bleiben. Bei mir würde
1 | |
in Assembler etwa so umgesetzt
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
ich verbrauche wesentlich weniger Register (wir sind hier in der ISR, daher ist jedes Register das man nicht braucht gesparte Zeit) und es ist knapp kürzer. Schlimm wird der erzeugte Code aber vor allem dann wenn es um Bitmanipulationen geht ;-)
Peter Schranz schrieb: > ich verbrauche wesentlich weniger Register Na komm, 3 ist nicht wesentlich weniger als 4. Auch ist Dein Assembler nicht identisch zu der C-Zeile. Du machst pre-increment, im C-Code hast Du aber geschrieben post-increment (i++). Der Compiler muß immer genau das tun, was Du hinschreibst. Es stimmt allerdings, beim Register-Renaming ist der AVR-GCC recht faul. Er MOVed gerne mal unnütz in seine Lieblings-Register. Peter
Ja für diese Zeile stimmts. Das mit den Registern muss man über die ganze ISR betrachten, in C wurden dort r0,r1,r18,r19,r20,r21,r24,r25,r30 und r31 gesichert, in Assembler sind es noch r0,r1,r30 und r31, aber ich denke r1 kann ich auch noch eliminieren, so dass noch r0, r30 und r31 bleiben. Und mein Assembler Code macht schon das was das C Statement macht, nämlich das Byte an der Position i nehmen und i inkrementieren.
Gast
#1984053
Hi >...in Assembler sind es noch r0,r1,r30 und r31, aber ich >denke r1 kann ich auch noch eliminieren, so dass noch r0, r30 und r31 >bleiben. In deinem Code sehe ich kein r1. Aber r0 kannst du einsparen wenn du das ld r0,-Z durch z.B. sbiw ZH:ZL,1 ld ZL,Z ersetzt. MfG Spess
Nein das r1 kommt an anderen Stellen der ISR noch vor, mein "eliminieren von r1" muss man im ganzen ISR Kontext sehen. Ja deinen Trick habe ich vergessen.
Peter Schranz schrieb: > Und mein Assembler Code macht schon das was das C Statement macht, > nämlich das Byte an der Position i nehmen und i inkrementieren. Ja aber vorher! Das i++ bedeutet aber nachher Willst Du es vorher, mußt Du ++i schreiben. Der Compiler kann nicht wissen, daß das für Dich egal ist, er muß die Reihenfolge daher beibehalten. Er darf das Ergebnis von i++ erst abspeichern, nachdem alles andere erledigt ist. Es könnte ja sein, daß der Rest des Ausdrucks das alte i noch benötigt. Peter
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.