Hallo,
ich habe gerade auf einem Atmega 169 (avr-gcc) ein (m.M. nach)
merkwürdiges Verhalten festgestellt.
Ich versuche 2 byte (low/high) in ein word (16bit) zu wandeln. Das mache
ich mit folgendem Code:
1
uint8_tlow=0x94;
2
uint8_thigh=0x38;
3
4
uint16_tword=low|(high<<8);
Erwarten würde ich:
1
word=0x3894
jedoch sehe ich
1
word=0x0194
Dabei ist es egal, welchen Wert high annimmt, nach der Wandlung steht im
high-byte immer 0x01.
Verwende ich uint32_t anstelle uint16_t funktioniert die Wandlung wie
ich es erwarte. Das lässt vermuten, dass die 1 im Ergebnis das
carry-flag ist. Dagagen spricht jedoch, das auch 0x00 -> 0x01 wird.
Interessanterweise funktioniert die Wandlung, wenn ich sie auf meinem
(32 bit PC) compiliere so wie ich's erwarte.
Hat jemand eine Idee, was das Problem ist?
Gr.
Es gibt beim gcc eine (nicht empfohlene) option, die die Standardgröße
eines Integers auf 8 bit setzt. In dem Fall würde ich zwar eher 0x0038
erwarten, aber prüfe mal, ob du die vielleicht gesetzt hast.
Das geht ja flink...
Ich habe mein erstes posting etwas kurz gehalten... ich habe alles
mögliche bereits probiert (casten, maskieren vor und nach dem shiften
etc.) das hat alles nichts gebracht...
-mint8 benutze ich nicht...
Gruß & Dank..
Wird bei einem C Compiler nicht erst die rechte Seite ausgerechnet, mit
den dortigen Datenbreiten, und dann versucht, das Ergebnis auf die linke
Seite zuzuweisen?
Das würde das Verhalten erklären...
> Wird bei einem C Compiler nicht erst die rechte Seite ausgerechnet, mit> den dortigen Datenbreiten, und dann versucht, das Ergebnis auf die linke> Seite zuzuweisen?> Das würde das Verhalten erklären...
Ich verstehe nicht so genau, was du meinst (rechte/linke Seite). Meinst
dur rechts/links vom = ?
Wenn ja, dann habe ich doch jeweils 16 bit breite Zahlen, oder?
> besser> uint16_t word;> word = low | (high << 8);
Wozu soll das gut sein? Außerdem erklärt es nicht, warum der selbe Code
auf einer 32-bit Maschine compiliert ein anderes Ergebnis liefert als
auf einer 8-Bit Maschine (beide male gcc).
Gr & Dank..
> Das funktioniert nur bei little endian.
Nein, es funktioniert eben nicht, außerdem wird die Endianess bei mir
durch "<< 8" festgelegt, bei dir durch "*0x100", wirkt aber jeweils auf
das gleiche byte, wo ist der Unterschied?
Gruß und Dank.
Thomas Klähn schrieb:> Nein, es funktioniert eben nicht, außerdem wird die Endianess bei mir> durch "<< 8" festgelegt, bei dir durch "*0x100", wirkt aber jeweils auf> das gleiche byte, wo ist der Unterschied?
Ich sagte nicht, es wäre die Lösung für dein Problem, sondern, dass ein
solches Vorgehen nur bei Little Endian funktionieren kann. Für portablen
Code ist das Gift. Das wollte ich damit sagen.
Versuchs mal so:
word = (uint16_t)low + (uint16_t)0x100*high;
Oliver J. schrieb:> Ich sagte nicht, es wäre die Lösung für dein Problem, sondern, dass ein> solches Vorgehen nur bei Little Endian funktionieren kann. Für portablen> Code ist das Gift. Das wollte ich damit sagen.
Das ist nicht richtig.
word = low | (high << 8); funktioniert auf High und Little Endian
Systemen.
Wie das System das im Speicher ablegt ist an dieser Stelle nicht
relevant.
high bezieht sich hier auf die Bits 8-15 und nicht auf die Adresse.
Naja, compilieren tut's ja bei mir auch, nur das high byte ist hinterher
immer 0x01.
Ich überprüfe das folgendermaßen:
1
staticuint8_tbuffer[0xFF];
2
3
...
4
5
uint16_tw=buffer[i]|(buffer[i+1]<<8);
6
uart_put_char(buffer[i]);
7
uart_put_char(buffer[i+1]);
8
uart_put_char(w&0x00FF);
9
uart_put_char(((w&0xFF00)>>8));
10
uart_put_char('\n');
wobei
1
buffer[i]=0x0C;
2
buffer[i+1]=0x94;
Im Terminal habe ich dann immer folgende Ausgabe:
0C 94 0C 01 0A
Wenn ich w als uint32_t definiere, dann funktionierts...
Gruß & Dank
P.S. sorry, falls ich hier irgentwie "blasiert" klinge, ich suche nur
nach 'nem Problem, dass ich noch nicht verstehe.
Thomas Klähn schrieb:> ich suche nur nach 'nem Problem, dass ich noch nicht verstehe.
Davon hast Du ausreichend Auswahl :D
uart_put_char wird wohl, wie's die Funktion so sagt, ein char und nicht
ein 16Bit int erwarten:
> uart_put_char(w & 0x00FF)
Verwendest Du keinen Simulator ? Dort geht das Zusammensetzen zu einem
int16 auch in der Originalversion, auch wenn ich's sicherheitshalber so
schreiben würde:
uint16_t word = ((uint16_t) high << 8) | low;
kommt immer
0C 94 0C 94 0A,
also genau was ich erwarte. Die ganze Caste-un Maskiererei ist also
nicht das Problem. bei der originären Zuweisung scheint das uint16_t
überzulaufen, warum ist mir auch vollkommen unklar...
> warum ist mir auch vollkommen unklar...
Mir auch!
Bei mir geht's gut, ich gucke mir dabei den generierten ASM-Code an.
Vermutlich liegt bei das Problem an einer anderen Stelle, aber wenn Du
nicht alle relevanten Informationen gibst, kann das niemand
nachvollziehen:
=> Welche IDE, (Version?)
=> Welcher Compiler, (Version)
=> Komplettes und kompilierbares Programmbeispiel
=> Projekt/Compiler-Einstellungen?
=> Makefile?
=> Build-Output? (Warnings?)
Fragen über Fragen...
Oliver J. schrieb:> Versuchs mal so:> word = (uint16_t)low + (uint16_t)0x100*high;
Das ist die richtige Lösung, alternativ auch
word = ((uint16_t)high <<8) | low;
Der Compiler liest die Zeile von rechts nach links. Nachdem er alle den
ersten Operanden und die zugehörigen Daten gefunden hat, benutzt er
immer den größten Datentypen, den er vorgefunden hat, in dem Fall also
ein 8-Bit-Typ. Es muss also bei einer solchen Operation mindestens ein
Typecast auf den Zieldatentyp erfolgen, sonst ist nicht mehr gesichert,
dass der Compiler das richtig umsetzt.
Frank Bär schrieb:> benutzt er> immer den größten Datentypen, den er vorgefunden hat, in dem Fall also> ein 8-Bit-Typ.
ich dachte ein C-Compiler MUSS minimum mit 16bit Rechnen. (wenn er 8bit
verwendet muss zumindes das gleiche Ergebniss rauskommen wie bei 16bit,
sonst ist es ein fehler)
Peter II schrieb:> Frank Bär schrieb:>> benutzt er>> immer den größten Datentypen, den er vorgefunden hat, in dem Fall also>> ein 8-Bit-Typ.>> ich dachte ein C-Compiler MUSS minimum mit 16bit Rechnen. (wenn er 8bit> verwendet muss zumindes das gleiche Ergebniss rauskommen wie bei 16bit,> sonst ist es ein fehler)
Mal ganz ehrlich gefragt: warum SOLLTE er das auf einer 8-Bit-Maschine?
16Bit-Operationen auf einer 8Bit-CPU kosten Rechenzeit, also darf der
Compiler das auf dem Mikrocontroller streng genommen gar nicht einfach
so machen, wenn du ihm keinen zwingenden Grund gibst. Der Grund ist dann
entweder eine Operation mit einem 16Bit-Wert, oder ein Typecast.
Der Compiler macht bei dieser Zuweisung nämlich folgendes.
Ausgehend von word = low + (high << 8);
sieht er drei Operationen:
1. high <<8 - das ist eine 8-Bit-Operation. Das einzige, was davon übrig
bleibt, ist ein gesetztes Carryflag.
2. low + Carryflag - Das Carryflag deutet auf einen 16-Bit-Wert hin,
daher wird ab hier mit 16 Bit weitergerechnet. Steht also: 0x0100 + low.
3. word = ...
Frank Bär schrieb:> Mal ganz ehrlich gefragt: warum SOLLTE er das auf einer 8-Bit-Maschine?> 16Bit-Operationen auf einer 8Bit-CPU kosten Rechenzeit, also darf der> Compiler das auf dem Mikrocontroller streng genommen gar nicht einfach> so machen, wenn du ihm keinen zwingenden Grund gibst. Der Grund ist dann> entweder eine Operation mit einem 16Bit-Wert, oder ein Typecast.>> Der Compiler macht bei dieser Zuweisung nämlich folgendes.>> Ausgehend von word = low + (high << 8);>> sieht er drei Operationen:>> 1. high <<8 - das ist eine 8-Bit-Operation. Das einzige, was davon übrig> bleibt, ist ein gesetztes Carryflag.> 2. low + Carryflag - Das Carryflag deutet auf einen 16-Bit-Wert hin,> daher wird ab hier mit 16 Bit weitergerechnet. Steht also: 0x0100 + low.> 3. word = ...
Sorry, aber ist schlicht falsch.
Der C-Standard schreibt vor, dass die Berechnung mindestens in int zu
erfolgen hat. Der Compiler darf daraus nur dann eine reine
8-Bit-Rechnung machen, wenn dabei garantiert immer das gleiche raus
kommt, wie wenn die Rechnung in int gemacht werden würde. Ist hier aber
nicht der Fall.
Und noch was: bei einer 8-Bit-Rechung wäre das Ergebnis 0x0094, nicht
0x0194. Die Sache mit dem Carry-Flag ist Unsinn.
Stefan Ernst schrieb:> Sorry, aber ist schlicht falsch.> Der C-Standard schreibt vor,
Welcher C-Standard?
Der Atmega169 hat eine Registerbreite von 8 Bit. Die wird auch vom
AVR-GCC benutzt.
ARM-GCC rechnet standardmäßig in 32Bit. Was "der" C-Standard
vorschreibt, ist gut und schön, orientiert sich aber alles an
32Bit-CPUs. Für eine 8Bit-CPU sind solche Standards nur bedingt gültig.
Man kann eben nicht einfach so mal was in C für den Mikrocontroller
programmieren, ohne die Hardware dahinter zu kennen.
> dass die Berechnung mindestens in int zu> erfolgen hat. Der Compiler darf daraus nur dann eine reine> 8-Bit-Rechnung machen, wenn dabei garantiert immer das gleiche raus> kommt, wie wenn die Rechnung in int gemacht werden würde. Ist hier aber> nicht der Fall.
Grau ist alle Theorie. ARM-GCC meint, in einigen Versionen einen
Unterschied zwischen (var & 1) und (var % 1) erkennen zu können.
> Und noch was: bei einer 8-Bit-Rechung wäre das Ergebnis 0x0094, nicht> 0x0194. Die Sache mit dem Carry-Flag ist Unsinn.
Schieben einer Zahl größer 0 um 8 Bit nach links erzeugt einen Überlauf,
also wird das Carryflag gesetzt.
Aber wenn mein Ablauf falsch ist, wie kommt dann deiner Meinung nach
0x0194 zustande? Prozessoren würfeln nicht.
Frank Bär schrieb:> Welcher C-Standard?
Jeder.
Frank Bär schrieb:> Für eine 8Bit-CPU sind solche Standards nur bedingt gültig.
Der Standard ist immer gültig. Wenn sich ein Compiler dafür entscheidet,
sich nicht daran zu halten, ist das eine andere Frage. Der avr-gcc hält
sich bei der fraglichen Zeile aber dran und rechnet in int.
Frank Bär schrieb:> Schieben einer Zahl größer 0 um 8 Bit nach links erzeugt einen Überlauf,> also wird das Carryflag gesetzt.
Nein, das Schieben ergibt schlicht 0, und ein Carryflag gibt es in C
nicht. Und 0x94 verodert mit der 0 ergibt dann wieder 0x94.
Frank Bär schrieb:> Aber wenn mein Ablauf falsch ist, wie kommt dann deiner Meinung nach> 0x0194 zustande?
Keine Ahnung. Aber sich irgendeinen Unsinn zusammen zu reimen, ist auch
keine Lösung.
Denke mal die ganze Diskussion ist für die Katz', zumindest ich schaffe
es nicht den Fehler so wie im ersten Post angegeben herbeizuführen.
1
uint8_tlow=0x94;
2
uint8_thigh=0x38;
3
uint16_tword=low|(high<<8);
Egal mit welchem Optimierungslevel, es kommt immer das gewünschte
Ergebnis raus.
Denke mal das hängt eher damit zusammen, wie die Werte ausgegeben /
betrachtet werden.
Disassemblat ist auch einwandfrei, bis auf so etwas bei -O0:
> +0000004C: 2F88 MOV R24,R24
Bei Bedarf mal ein kleine .hex hier einstellen, an dem der Fehler
auftritt, dann kann man schlüssig sagen, wo's hakt.
Evtl. der klassische bug-in-front-of-the-screen Fehler.
Sorry, 'war am Nachmittag "verhindert"...
Ich habe einen Woraround gefunden. Der Sieht so aus:
1
uint16_tlow=(uint16_t)buffer[i];
2
uint16_thigh=(uint16_t)(buffer[i+1]&0x00FF);
3
high*=256;
4
uint32_tlw=(high+low);
5
uint16_tw=(uint16_t)lw;
und funktioniert.
Meine Überlegung geht auch in die Richtung von f-baer (Carry /
c-standart), by the Way - ich compiliere mit -Os und -std=gnu99...
Gruß & Dank.
Stefan Ernst schrieb:> Frank Bär schrieb:>> Welcher C-Standard?>> Jeder.>> Frank Bär schrieb:>> Für eine 8Bit-CPU sind solche Standards nur bedingt gültig.>> Der Standard ist immer gültig. Wenn sich ein Compiler dafür entscheidet,> sich nicht daran zu halten, ist das eine andere Frage. Der avr-gcc hält> sich bei der fraglichen Zeile aber dran und rechnet in int.
Der OP schreibt doch klar, dass er mit GCC auf dem PC und mit AVR-GCC
auf dem Atmega unterschiedliche Ergebnisse bekommt, obwohl der Code
identisch ist. -mint8 hat er nicht aktiviert, also bleiben nicht mehr
allzu viele Alternativen. Scheint wohl, dass sich der AVR-GCC in der
verwendeten Version nicht daran hält. Interessant wäre ein Disassembly
des Codes um das abschliessend zu klären.
> Frank Bär schrieb:>> Schieben einer Zahl größer 0 um 8 Bit nach links erzeugt einen Überlauf,>> also wird das Carryflag gesetzt.>> Nein, das Schieben ergibt schlicht 0, und ein Carryflag gibt es in C> nicht. Und 0x94 verodert mit der 0 ergibt dann wieder 0x94.
Der Controller, auf dem das Programm läuft, kennt ein Carry-Flag. Wenn
der Controller Operationen durchführt, dann arbeitet er auch immer mit
dem Carryflag.
Ob die Programmiersprache C nun Flags kennt oder nicht, ist dem
Controller und seiner ALU dabei völlig schnuppe. Wenn das Carryflag
gesetzt ist, dann wertet der Controller es auch aus.
MWS schrieb:> Disassemblat ist auch einwandfrei, bis auf so etwas bei -O0:>> +0000004C: 2F88 MOV R24,R24
Da darfst du aber auch keine Ansprüche stellen, denn mit -O0 sagst du
explizit, dass der Compiler nichts an Optimierungen vornehmen darf.
Walter schrieb:> das ist doch ein würgaround,> zeig doch endlich mal das assemblerlisting
Oder zumindest das vollständige C Programm, nicht nur Schnipsel.
Frank Bär schrieb:> Das ist die richtige Lösung, alternativ auch>> word = ((uint16_t)high <<8) | low;
Und das stand schon in einem der allerersten Postings, der OP hatte nur
offensichtlich keinen Bock das auszuprobieren.
Laßt ihn doch schmoren... :-(
Ganz im Gegenteil, der OP hatte bock das auszuprobieren, und es auch
getan, jedoch hat es nicht das gewünschte Ergebnis gezeigt...
Ich packe es kurz und poste dann mein projekt. bis gleich...
Hallo, da bin ich wieder...
im Anhang ist das Projekt. Es handelt sich um einen Bootloader (noch
nicht ganz fertig...), der über SPI (CC1100 von TI) ein Quasi-ihex file
(ihex ohne : und 2Byte ASC-> 1Byte hex codiert) empfängt. Radio
funktioniert mittlerweile einwandfrei, worum es geht ist in "update.c"
in der Funktion "program_page" ab Zeile 125.
Ach ja, zum bauen ins Verzeichnis source/app_bootloader und dort "make
all".
Out-Verzeichnis ist im Folder release/execute. Map-File im release/map
und Listing in release/lst.
Gruß & Dank.
Hallo,
die Idee mit der Union finde ich klasse, 'werde das gleich 'mal
probieren.
Ansonsten benutze ich avr-gcc 4.5.3 und gcc 4.6.1 (jedoch mit
unterschiedlichen CFLAGS ;) (eclipse managed make auf dem pc, bzw.
manual make für den avr - siehe __bootloader.zip
Gr. Thomas
Frank Bär schrieb:> Der OP schreibt doch klar, dass er mit GCC auf dem PC und mit AVR-GCC> auf dem Atmega unterschiedliche Ergebnisse bekommt, obwohl der Code> identisch ist. -mint8 hat er nicht aktiviert, also bleiben nicht mehr> allzu viele Alternativen. Scheint wohl, dass sich der AVR-GCC in der> verwendeten Version nicht daran hält.
Es haben doch nun schon mehrere Personen hier geschrieben, dass sie es
selbst ausprobiert und das richtige Ergebnis bekommen haben. Also ist es
kein grundsätzliches Problem. Der OP hat sehr wahrscheinlich irgendwo
anders ein Fehler/Problem.
Frank Bär schrieb:> Der Controller, auf dem das Programm läuft, kennt ein Carry-Flag. Wenn> der Controller Operationen durchführt, dann arbeitet er auch immer mit> dem Carryflag.> Ob die Programmiersprache C nun Flags kennt oder nicht, ist dem> Controller und seiner ALU dabei völlig schnuppe. Wenn das Carryflag> gesetzt ist, dann wertet der Controller es auch aus.
Kein Hardware-Flag hat Einfluss darauf, wie das Ergebnis einer C-Zeile
auszusehen hat. Der vom Compiler generierte Code hat dafür Sorge zu
tragen, dass immer das gleiche (richtige) Ergebnis herauskommt,
unabhängig davon, welche Flags vorhanden und wie und wann gesetzt
werden. Und mal so ganz nebenbei, beim AVR wäre das Carry-Flag nach dem
Schieben 0 (nicht 1), weil zuletzt eine 0 "herausgeschoben" wurde.
Thomas Klähn schrieb:> die Idee mit der Union finde ich klasse, 'werde das gleich 'mal> probieren.> Ansonsten benutze ich avr-gcc 4.5.3 und gcc 4.6.1 (jedoch mit> unterschiedlichen CFLAGS ;) (eclipse managed make auf dem pc, bzw.> manual make für den avr - siehe __bootloader.zip
Die Methode sieht nur schön aus, sollte man sich allerdings nicht
angewöhnen. Dass die Union-Methode funktioniert ist eher ein Zufall in
diesem Falle. Auf 32 Bit Systemen wirste ganz schnell Probleme mit
Padding und solchen Sachen bekommen.
In diesem Falle ist das mit dem Zusammenschieben der Bytes der
"richtige" Weg.
> Die Methode sieht nur schön aus, sollte man sich allerdings nicht> angewöhnen. Dass die Union-Methode funktioniert ist eher ein Zufall in> diesem Falle. Auf 32 Bit Systemen wirste ganz schnell Probleme mit> Padding und solchen Sachen bekommen.
Andererseits konnte ich "sogar" 'nen Blackfin mit
Das ist allerdings
a) nicht portabel (zu anderen Compilern). Könnte problematisch sein für
Manche.
b) auf manchen Plattformen nicht möglich, da keine unaligned-Zugriffe
erlaubt sind.
c) gibt sicher noch mehr.
Die Schiebevariante ist üblicherweise problemloser.
Was ist das eigentlich für eine sch... Sprache, in der man ein Dutzend
Seiten diskutieren kann, wie man zwei Byte zusammenprökelt.
Geht das nicht einfach als union?
Beobachter schrieb:> Geht das nicht einfach als union?
DAs wurde schon um 20:16 Uhr beantwortet.
Walter schrieb:> ist das denn egal ob big oder little endian,> sprich: ist das in C genormt wie die Bytes abgelegt werden?
Nein, es ist nicht genormt. Das ist Systemabhängig.
Und es gibt auch Middle-Endian:
http://en.wikipedia.org/wiki/Endianness#Middle-endian
Und die PDP-11 hat bei der Entstehung von C mitgeholfen.
Thomas Klähn schrieb:>> besser>> uint16_t word;>> word = low | (high << 8);> Wozu soll das gut sein? Außerdem erklärt es nicht, warum der selbe Code> auf einer 32-bit Maschine compiliert ein anderes Ergebnis liefert als> auf einer 8-Bit Maschine (beide male gcc).
Das Auseinanderhalten von Typdefinitionen, Variablendeklarationen und
Anweisungen dient der Klarheit und der Sauberkeit eines Quellcodes.
Viele C-Programmierer sind schlicht zu stinkefaul und möchten deshalb
immer alles in einen Klumpen schreiben, aber das ist einfach nur
schlechter Stil.
In besagtem Beispiel besteht der Unterschied wohl darin, daß auf der
erwähnten 32 Bit-Maschine grundsätzlich 32 bittig gerechnet wird, weil
diese Maschine eben nur 32 Bit breite Register hat und garnicht anders
kann. Wie schön für den schlampigen Programmierer!
Wenn man Bytes in was Breiteres schieben will, dann soll man sich nicht
darauf verlassen, grundsätzlich ne 32 Bit Maschine zu haben sondern man
sollte es besser so schreiben:
myLong = highByte;
myLong = (myLong << 8) | lowByte;
Das klappt immer und auf allen Maschinen und die Optimierung überlasse
man besser dem Compiler. Bei einer lokalen Variable myLong wird diese
sowieso in einem Register gehalten, weswegen die scheinbar überflüssige
Zuweisung garnicht überflüssig ist.
W.S.
> Das Auseinanderhalten von Typdefinitionen, Variablendeklarationen und> Anweisungen dient der Klarheit und der Sauberkeit eines Quellcodes.> Viele C-Programmierer sind schlicht zu stinkefaul und möchten deshalb> immer alles in einen Klumpen schreiben, aber das ist einfach nur> schlechter Stil.
Dann werfe ich mal meine Version in die Runde:
1
constuint16_tw=((uint16_t)high<<8)|low;
Jawoll, ich bin faul. Und das ist auch gut so.
Es gibt Metriken, die den Abstand zwischen Deklaration und Verwendung
messen. Je länger, desto schlechter ("variable spans").
Es gibt sehr gute Gründe, const Variablen zu verwenden. Das
dokumentiert, dass der Wert nicht mehr verändert wird. Und es wird vom
Compiler auch geprüft, wodurch der Code sicherer wird. Vielleicht kann
er sogar besser optimieren. Manche Konventionen sagen, wenn eine
Variable nicht const ist, dann wird sie auch noch verändert.
Also irgendwie finde ich den ganzen Thread merkwürdig. Wer auf einem
8bit mc schreibt:
uint8_t low = 0x94;
uint8_t high = 0x38;
uint16_t word = low | (high << 8);
und denkt das geht gut, hat den Schuss nicht gehört. Da dem gcc was
unterschieben zu wollen ist fast Frechheit.
Beobachter schrieb:> Was ist das eigentlich für eine sch... Sprache, in der man ein Dutzend> Seiten diskutieren kann, wie man zwei Byte zusammenprökelt.
Soviel sei mal gesagt: Es liegt NICHT an der Sprache....
Allerdings stimme ich dir in soweit zu dass der Thread hier mehr als
lächerlich ist.
@Roland:
Genauso mache ich es auch. Sauber, verständlich und kompakt.
Eine anfängertaugliche Lösung zum näherbringen des Problems ist
folgende. Da sie weder Casts noch bitweise Operatoren verwendet.
Hallo,
ich würde das hier vielleicht ganz gern beenden und mich bei allen, die
versucht haben mir zu helfen, bedanken. Schade finde ich, dass so etwas
irgentwie immer ausarten muss.
Ich hatte mich ursprünglich nur gewundert, dass eine verhältnismäßig
trivial scheinende Operation nach meiner Interpretation fehlschlägt. Mit
> Ich habe mein erstes posting etwas kurz gehalten... ich habe alles> mögliche bereits probiert (casten, maskieren vor und nach dem shiften> etc.) das hat alles nichts gebracht...
hatte ich versucht anzudeuten, dass operationen wie
1
uint8_tlow=0x94;
2
uint8_thigh=0x38;
3
uint16_tword=low|(high<<8);
oder
1
uint8_tlow=0x94;
2
uint8_thigh=0x38;
3
uint16_tword=(uint16_t)low|(uint16_t)(high<<8);
oder
1
uint8_tlow=0x94;
2
uint8_thigh=0x38;
3
uint16_tword=(uint16_t)low|(uint16_t)(high*256);
oder ...
oder daraus abgeleitete Operationen mit Bitmaskierungen bei mir ALLE zum
gleichen, falschen Ergebnis führen und aus irgend einem Grund, der mir
weiterhin unklar ist, ich die Ergebnisvariable 32 bit breit machen muss,
um zu einem korrekten Ergebnis zu kommen.
Jedoch verstehe ich nicht, wie man sich zu Äusserungen wie
> ... und denkt das geht gut, hat den Schuss nicht gehört.> Da dem gcc was unterschieben zu wollen ist fast Frechheit.
hinreißen lassen kann. Wer unterschiebt hier was? Ich habe lediglich ein
Phänomen beschrieben, dass ich mir nicht erklären kann und darum
gebeten, dass man mir hinsichtlich dessen doch nach Möglichkeit Licht
ans Fahrrad machen möge.
Gruß Thomas
Thomas Klähn schrieb:> Hallo,> ich würde das hier vielleicht ganz gern beenden und mich bei allen, die> versucht haben mir zu helfen, bedanken. Schade finde ich, dass so etwas> irgentwie immer ausarten muss.
Was heißt "ausarten"? Es ist ein Forum, und da wird nun mal diskutiert.
Wäre doch schlimm, wenn jeder Thread nach maximal 3 Posts beendet wäre.
> hatte ich versucht anzudeuten, dass operationen wieuint8_t low = 0x94;> uint8_t high = 0x38;> uint16_t word = low | (high << 8);> oder> uint8_t low = 0x94;> uint8_t high = 0x38;> uint16_t word = (uint16_t)low | (uint16_t)(high << 8);> oder> uint8_t low = 0x94;> uint8_t high = 0x38;> uint16_t word = (uint16_t)low | (uint16_t)(high * 256);> oder ...> oder daraus abgeleitete Operationen mit Bitmaskierungen bei mir ALLE zum> gleichen, falschen Ergebnis führen und aus irgend einem Grund, der mir> weiterhin unklar ist, ich die Ergebnisvariable 32 bit breit machen muss,> um zu einem korrekten Ergebnis zu kommen.
Diese Varianten sind alle nach ISO C korrekt und müßten mit GCC
funktionieren. Einen Fehler im Compiler selbst kann ich mir aber nicht
so recht vorstellen. Der wäre bei so einer einfachen und häufig
auftretenden Operation schon viel früher aufgefallen. Ist an deiner
Compiler-Installation irgendwas besonderes?
> Jedoch verstehe ich nicht, wie man sich zu Äusserungen wie>> ... und denkt das geht gut, hat den Schuss nicht gehört.>> Da dem gcc was unterschieben zu wollen ist fast Frechheit.> hinreißen lassen kann.
Diese Aussage ist auch vollkommener Unsinn. gcc hält sich so weit wie
möglich an ISO C, auch auf AVR, und dazu gehört auch die "integer
promotion", die dafür sorgt, daß alle Berechnungen mindestens mit int
durchgeführt werden. Das ist eine ziemlich grundlegende Eigenschaft von
C, und dem gcc unterschieben zu wollen, daß er sich an solch
grundlegende Regeln nicht hält, könnte man auch als "Frecheit"
bezeichnen.
> Einen Fehler im Compiler selbst kann ich mir aber nicht> so recht vorstellen. Der wäre bei so einer einfachen und häufig> auftretenden Operation schon viel früher aufgefallen. Ist an deiner> Compiler-Installation irgendwas besonderes?
Ich glaube auch nicht an einen Compilerfehler.
Ich benutze den avr-gcc aus den ubuntu-paketquellen - also nicht mal
selbstverpfriemelt. Ich gehe davon aus, dass die Ursache irgentwo in
meiner Configuration/in meinem Code liegt.
Gruß
Thomas Klähn schrieb:> Ich gehe davon aus, dass die Ursache irgentwo in> meiner Configuration/in meinem Code liegt.
Ich hatte doch schon vorgeschlagen den erzeugten .hex-Code anzusehen,
sinnvoll wäre dabei ein minimiertes Beispiel mit allen Sourcen, welches
den Fehler zeigt. Alles was dann kam war der vollständige Code Deines
Projektes, aber eben mit Deiner Murkslösung.
Wenn Dir zehn Leute sagen, bei ihnen geht's, aber bei Dir geht's nicht,
wäre das der Zeitpunkt um in die Gänge zu kommen. Denn wenn's wirklich
ein Fehler wäre, dann müsste die Zuverlässigkeit der ganzen
Programmierumgebung angezweifelt werden.
Irgendwie beschleicht mich das Gefühl, dass Du kein ernstes Interesse an
der Lösung hast und lieber weiterbastelst. Nur frag' ich mich dann,
warum Du das Forum fragst, wenn Du Deinen Teil zur Lösung nicht
beitragen willst ?
Thomas Klähn schrieb:> bei mir ALLE zum gleichen, falschen Ergebnis führen
Bin nach wie vor der Meinung, dass nicht die Rechnung falsch ist,
sondern das Rechenergebnis falsch angezeigt wird.
Könnte es vielleicht sein, dass im kompletten Programm irgendwo der
Stack überläuft und der Wert dadurch verändert wird?
Bei den "kurzen" Testprogrammen funktioniert es daher einwandfrei.
@Thomas Klähn
Schon einmal ein "kurzes" Testprogramm ausprobiert?
Und bitte wenn es nicht funktioniert, poste den resultierenden
Assembler-Code. Da kann man wirklich sehen, was passiert.
> Bin nach wie vor der Meinung, dass nicht die Rechnung falsch ist,> sondern das Rechenergebnis falsch angezeigt wird.
Nein, da das falsche Ergebnis sowohl ausgegeben, als auch in den flash
geschrieben wird bin ich mir relativ sicher, dass die Wandlung falsch
ist.
Ich habe ein kleines Testprogramm geschrieben, das so arbeitet, wie man
es erwartet:
Das Ergebnis ist in dem Fall:
0C 94 0C 94 2E 00 2E 00 0C 94 0C 94 DD
03 DD 03 0C 94 0C 94 4B 00 4B 00 0C 94
0C 94 4B 00 4B 00 CC 00 CC 00
Vor und nach der Wandlung habe ich also gleiche Werte.
Das Listing (nur main) dazu sieht folgendermaßen aus:
Der Inhalt des Puffers ist:
0C 94 2E 00 0C 94 DD 03 0C 94 4B 00 0C 94 4B 00 CC
das Ergebnis (Ausgabe) ist in dem Fall:
0C 94 0C 01 2E 00 2E 01 0C 94 0C 01 DD
03 DD 01 0C 94 0C 01 4B 00 4B 01 0C 94
0C 01 4B 00 4B 01 CC 0C CC 01
Also wieder das Problem mit der 1.
Der Auschnitt des Listings (ich hoffe, dass der Ausschnitt nicht zu kurz
ist, für alle Fälle habe ich das vollständige Linting angehängt - mein
Ausschnitt beginnt an Zeile 699) für den Fehlerfall:
Ich würde jetzt interpretieren, dass der 2. Operand garnicht geladen
wird, oder?
Beide Programme sind auf dem selben Computer, mit dem selben Compiler,
mit identischen Compiler/Linker Flags - mit den selben Makefiles erzeugt
worden.
Gruß & Dank, Thomas
Thomas Klähn schrieb:> Ich würde jetzt interpretieren, dass der 2. Operand garnicht geladen> wird, oder?
Ja. Das Resultat ist Blödsinn, es wird Puffer[i] zum Highbyte der
Pufferadresse (0x01) gezählt, daher resultiert auch die 0x01.
Was ist das für 'ne GCC-Version ?
Stell' mal die Sourcen zu zweitem fehlerhaften Beispiel ein, damit man
es compilieren kann.
Wie ich weiter oben schrieb ist das avr-gcc 4.5.3 (aus den
Ubuntu-Paketquellen - also nicht selbstkompiliert.
Anbei die Quellen. Ggf. durch die Quellen im Verzeichnis
source/app_bootloader/core (__bootloader.zip - post vom 28.11.2011
19:44)
ersetzen um es mit meinen Einstellungen /Makefiles zu übersetzen.
Ein Hinweis sei mir noch gestattet - ich versuche hier einen Bootloader
zu bauen - ich verschiebe deshalb die text section an Adresse 0x3800
Mithilfe des Linkerflags "-Ttext=0x3800".
P.S. Die Funktion, um die es geht ist in update.c - program_page an
Zeile 115
Gruß & Dank Thomas.
Diese Dateien fehlen:
srv_data_storage.h
eeprom_defs.h
Hab' sie auskommentiert, war dann fehler- und warnungsfrei zu
compilieren, das erzeugte Compilat ist in Ordnung.
Details:
ATM169
Optimierung -Os
AVR-Studio4
WinAVR-20090313
Das:
> Diese Dateien fehlen:> srv_data_storage.h> eeprom_defs.h
Sorry, hab ich vergessen rauszunehmen, momentan werden die aber auch
nicht benötigt.
> MOVW R30,R14> LDD R24,Z+0> LDD R11,Z+1> CLR R10> MOVW R16,R10> ADD R16,R24> ADC R17,R1
sorry, wenn ich so doof frage, aber
1
ADC R17,R1
bedeutet doch soviel wie "R17 = R17 + R1" (mit carry), oder? Wo wird
denn R1 vorbelegt?
> Keine gute Idee.> Das Warum hatte ich mal im Wiki etwas ausführlicher ausgeführt:> http://www.mikrocontroller.net/articles/AVR_und_Li...
Bedeutet das nun, das der von mir verwendete Compiler tatsächlich 'nen
"Treffer" hat?
Ich werde dann wohl 'mal 'nen anderen ausprobieren.
Vielen Dank für eure Mühe.
Thomas Klähn schrieb:> bedeutet doch soviel wie "R17 = R17 + R1" (mit carry), oder? Wo wird> denn R1 vorbelegt?
R1 enthält laut WinAVR Konvention immer den Wert 0.
Thomas Klähn schrieb:> uint16_t word = (uint16_t)low | (uint16_t)(high << 8);
Diese Casts sind entweder sinnlos oder falsch. Sinnlos für den Fall
Standard-C (int mindestens 16 Bit). Falsch für den Fall, in dem avr-gcc
mit 8 Bit ints läuft (Option -mint8, glaube ich). Denn dann werden alle
Bits des 8 Bit Werts high um 8 Bits nach links raus geschoben, was eine
0 ergibt, und erst dieses konstante Ergebnis dann zu einem 16 Bit Wert
gecastet.
Thomas Klähn schrieb:> siehe da - das listing sieht wie folgt aus:> Wo wird denn R1 vorbelegt?
Na, dann ist das Problem ja endlich erledigt ;-)
Das eor ist übrigens kein Problem, ein eor auf sich selbst entspricht
einem clr, und das mit R1 wurde von Karl Heinz schon gesagt.
W.S. schrieb:> Das Auseinanderhalten von Typdefinitionen, Variablendeklarationen und> Anweisungen dient der Klarheit und der Sauberkeit eines Quellcodes.> Viele C-Programmierer sind schlicht zu stinkefaul und möchten deshalb> immer alles in einen Klumpen schreiben, aber das ist einfach nur> schlechter Stil.
Ach, dann hat man in C++ wohl einen Schritt rückwärts gemacht. Dort ist
das nicht nur üblich, es ist auch noch an jeder Stelle im Programm
möglich.
Nils S. schrieb:> <troll>> OO ist definitiv ein Schritt rückwärts, egal was man da sonst noch> Hässliches oder Falsches mit macht.> </troll>
Juhu endlich gehts los. Strukturierte Programmierung gegen OO, jetzt
fehlen nur noch die Funktionalen dann kann es so richtig häßlich werden.
Stallman (seines Zeichens FSF Fascho) ist übrigens auch gegen OO, nur
mal so zum anheizen der Stimmung... Wer traut sich das Thema Closures
anzuschneiden?
@ Thomas Klähn:
Meines Erachtens habe ich mit meiner Antwort (1. Antwort überhaupt), die
Lösung geliefert die sollte so funktionieren.
uint16_t word;
word = ((uint16_t)high << 8) | low;
Beachte der Typecast vor dem high, steht DIREKT vor dem high mit IN der
Klammer. Das ist auch der Unterschied zu Deinem Typecast Thomas. Bei dir
castest du das Ergebnis von (high << 8) und schiebst die 8Bit immer noch
in Leere.
Bei mir wird erst der Typecast auf die 8Bit Variable high beachtet, so
daß dann das Schieben im "Hintergrund" in einer 16Bit Variable passiert.
W.S. schrieb:> Das Auseinanderhalten von Typdefinitionen, Variablendeklarationen und> Anweisungen dient der Klarheit und der Sauberkeit eines Quellcodes.> Viele C-Programmierer sind schlicht zu stinkefaul und möchten deshalb> immer alles in einen Klumpen schreiben, aber das ist einfach nur> schlechter Stil.
Über schlechten Stil lässt sich streiten.
Fakt ist, dass ich nicht dauerd hoch und runter scrollen will (mein
Bildschirm ist leider breiter als hoch), da ich dauernd halbleere Zeilen
mit Variablendefinitionen sehen möchte. Ich habe kein Problem mit int
abc = 2+4; Dass hat auch nichts mit Faulheit zu tun, wenn du das nicht
sehen kannst ok - ich mag deine Variante auch nicht, die Lesbarkeit
verschlechtert sich jedoch (meiner Meinung nach).
Decius schrieb:> Meines Erachtens habe ich mit meiner Antwort (1. Antwort überhaupt), die> Lösung geliefert die sollte so funktionieren.>> uint16_t word;> word = ((uint16_t)high << 8) | low;>> Beachte der Typecast vor dem high, steht DIREKT vor dem high mit IN der> Klammer. Das ist auch der Unterschied zu Deinem Typecast Thomas. Bei dir> castest du das Ergebnis von (high << 8) und schiebst die 8Bit immer noch> in Leere.
Das wahr eine verbastelte Toolchain, ich hatte den Fehler auch
simuliert, mit -mint8 in den Compilerdirektiven, da fiel das geschobene
high-Byte einfach raus, weil der Compiler erkannte, daß es nur 0x00 sein
kann.
Jedoch passierte in keinem Fall das hier: