Forum: Mikrocontroller und Digitale Elektronik Erfahrungen eines Einsteigers


von Peter S. (cbscpe)


Lesenswert?

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

von Sebastian (Gast)


Lesenswert?

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."
;)

von Bernd N (Gast)


Lesenswert?

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 ?

von MaWin (Gast)


Lesenswert?

> 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.

von Peter D. (peda)


Lesenswert?

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

von Marko B. (glagnar)


Lesenswert?

> Da ich beruflich lange mit Assembler (PDP-11) zu tun hatte

Du solltest Dir mal den MSP430 anschauen ...

von Oliver (Gast)


Lesenswert?

MaWin schrieb:
> Du schreibst nur Kinderprogramme ?

Nö. Ich nehm gleich den 5% schnelleren Quarz, und erspare mir damit 
50000% Zeit.

Oliver

von Bernd N (Gast)


Lesenswert?

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 :-)

von MaWin (Gast)


Lesenswert?

> 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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

von Peter S. (cbscpe)


Lesenswert?

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

von MaWin (Gast)


Lesenswert?

> 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 ?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Bernd N (Gast)


Lesenswert?

>> 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
void putchar (uint8_t c)  {
2
  while (!TI);                               // Transmitter ready/busy ?
3
  TI = 0;                                    // TI Flag löschen
4
  acc = c;                                   // Paritäts BIT prüfen mittels ACC
5
  if (P) {                                   // Parity BIT = 1 ?
6
    TB8 = 0;                                 // ODD Parity
7
  } else {
8
    TB8 = 1;
9
  }
10
  SBUF = c;                                  // Ausgabe an UART
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).

von Thomas E. (thomase)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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
#define F_CPU  ((uint32_t)F_CPU_AS)


Peter

von Peter D. (peda)


Lesenswert?

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

von Bernd N (Gast)


Lesenswert?

>> 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.

von Peter S. (cbscpe)


Lesenswert?

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

von Peter S. (cbscpe)


Lesenswert?

>
>
1
> void putchar (uint8_t c)  {
2
>   while (!TI);                               // Transmitter ready/busy ?
3
>   TI = 0;                                    // TI Flag löschen
4
>   acc = c;                                   // Paritäts BIT prüfen
5
> mittels ACC
6
>   if (P) {                                   // Parity BIT = 1 ?
7
>     TB8 = 0;                                 // ODD Parity
8
>   } else {
9
>     TB8 = 1;
10
>   }
11
>   SBUF = c;                                  // Ausgabe an UART
12
> }
13
> 
14
>
>

Ja das ist zwar C aber wirklich lesbarer als eine einfache Assembler 
Routine ist es nicht.

Peter

von MaWin (Gast)


Lesenswert?

> 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.

von Bernd N (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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?

von Peter S. (cbscpe)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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).

von Peter S. (cbscpe)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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!

von Peter S. (cbscpe)


Lesenswert?

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 ;-)

von Karl H. (kbuchegg)


Lesenswert?

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?

von MaWin (Gast)


Lesenswert?

> Nö.
> In C irgendwie so
> Beitrag "Re: Inline Assembler- Bits spiegeln"

Nein, kein Mirror, sondern im 8 x 8 bitarray um 90 Grad drehen.

von Peter S. (cbscpe)


Lesenswert?

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...

von Karl H. (kbuchegg)


Lesenswert?

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.

von Valentin B. (nitnelav) Benutzerseite


Lesenswert?

...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

von MaWin (Gast)


Lesenswert?

> @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.

von Valentin B. (nitnelav) Benutzerseite


Lesenswert?

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

von Detlef _. (detlef_a)


Lesenswert?

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

von Andreas B. (Gast)


Lesenswert?

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
void test_io()
2
{
3
  OCR1A = 0xa020;
4
  OCR1B = 0xa020;
5
}
1
Disassembly of section .text:
2
3
00000000 <test_io>:
4
   0:  80 e2         ldi  r24, 0x20  ; 32
5
   2:  90 ea         ldi  r25, 0xA0  ; 160
6
   4:  9b bd         out  0x2b, r25  ; 43
7
   6:  8a bd         out  0x2a, r24  ; 42
8
   8:  99 bd         out  0x29, r25  ; 41
9
   a:  88 bd         out  0x28, r24  ; 40
10
   c:  08 95         ret

Wieviel Zyklen kann man dann noch sparen, wenn man das von Hand in 
Assembler schreibt?

von Peter S. (cbscpe)


Lesenswert?

ldi  r24,0x40
  out  0x2xb,r24
  ldi  0x07
  out  0x2a,r24
  out  0x28,r24

von Peter D. (peda)


Lesenswert?

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

von Peter S. (cbscpe)


Lesenswert?

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

von Andreas B. (Gast)


Lesenswert?

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.

von Sven S. (boldie)


Lesenswert?

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

von Peter S. (cbscpe)


Lesenswert?

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

von Andreas B. (Gast)


Lesenswert?

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.

von Peter S. (cbscpe)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Bernd N (Gast)


Lesenswert?

>> werde mal auf Grund deines Hinweises nach -O suchen

Wie steht denn derzeit der Optimizer ? mit -Os optimierst du auf Size.

von Spess53 (Gast)


Lesenswert?

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

von Peter S. (cbscpe)


Lesenswert?

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
     current_byte = message[i++];

in Assembler etwa so umgesetzt
1
  lds  r30,i  
2
  inc  r30  
3
  sts  i,r30      // Save Next Index
4
  clr  r31
5
  subi  r30,lo8(-(message))  // Point to Message Buffer +1
6
  sbci  r31,hi8(-(massage))
7
  ld  r0,-Z      // Get current Message byte
8
9
  sts  doi.current_byte,r0  // Put into current byte buffer

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 ;-)

von Peter D. (peda)


Lesenswert?

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

von Peter S. (cbscpe)


Lesenswert?

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.

von Spess53 (Gast)


Lesenswert?

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

von Peter S. (cbscpe)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

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
Noch kein Account? Hier anmelden.