Forum: Mikrocontroller und Digitale Elektronik 2 byte >> 1 word


von Thomas K. (blackfinn)


Lesenswert?

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_t low = 0x94;
2
uint8_t high = 0x38;
3
4
uint16_t word = 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.

von Decius (Gast)


Lesenswert?

versuch mal das:

uint16_t word;
word = ((uint16_t)high << 8) | low;

von Klaus T. (gauchi)


Lesenswert?

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.

von Thomas K. (blackfinn)


Lesenswert?

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

von Random .. (thorstendb) Benutzerseite


Lesenswert?

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

von Peter S. (psavr)


Lesenswert?

halte die Deklaration und Zuweisung auseinander:

statt
1
uint16_t word = low | (high << 8);

besser
1
uint16_t word;
2
word = low | (high << 8);

von Thomas K. (blackfinn)


Lesenswert?

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

von Oliver J. (skriptkiddy)


Lesenswert?

Thomas Klähn schrieb:
> uint16_t word = low | (high << 8);
Das funktioniert nur bei little endian. Besser ist:
1
word = low + 0x100*high;

von Thomas K. (blackfinn)


Lesenswert?

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

von Oliver J. (skriptkiddy)


Lesenswert?

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;

von DirkB (Gast)


Lesenswert?

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.

von Peter S. (psavr)


Lesenswert?

Bei mir funktioniert es prima...
1
#include <avr/io.h>
2
3
volatile uint16_t MyDummy
4
5
int main(void)
6
{
7
  uint8_t low = 0x94;
8
  uint8_t high = 0x38;
9
  uint16_t word = low | (high << 8);
10
  MyDummy=word;
11
}

von Oliver J. (skriptkiddy)


Lesenswert?

DirkB schrieb:
> Das ist nicht richtig.

Natürlich. Hast recht. Da habe ich mich wohl geirrt.

Gruß Oliver

von Thomas K. (blackfinn)


Lesenswert?

Naja, compilieren tut's ja bei mir auch, nur das high byte ist hinterher 
immer 0x01.
Ich überprüfe das folgendermaßen:
1
static uint8_t buffer[0xFF];
2
3
...
4
5
uint16_t w = 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.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Versuchs mit sprintf(buffer, "%04x", var16); uart_puts(buffer);, so 
schliesst du erstmal Fehler in deinen Schiebereien bei der Ausgabe aus.

von MWS (Gast)


Lesenswert?

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;

von Thomas K. (blackfinn)


Lesenswert?

hallo,

mit:
1
uint32_t dw = buffer[i] | (buffer[i + 1] << 8);
2
uint16_t w = (uint16_t)(dw & 0x0000FFFF);
3
uart_put_char(buffer[i]);
4
uart_put_char(buffer[i + 1]);
5
uart_put_char(w & 0x00FF);
6
uart_put_char(((w & 0xFF00) >> 8));
7
uart_put_char('\n');
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...

von Peter S. (psavr)


Lesenswert?

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

von Frank B. (f-baer)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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)

von Frank B. (f-baer)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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.

von Uwe (Gast)


Lesenswert?

union conv
 {
   int8_t Byte[2];     //2 Byte
   uint16_t Word; //1 Word
 }z;

conv convert;

main()
{
convert.Byte[0]=0x1A;
convert.Byte[1]=0x9E;
int a=convert.Word;

}


Funktioniert in beide Richtungen !

von Frank B. (f-baer)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Walter (Gast)


Lesenswert?

warum schaust du dir nicht mal das Assembler listing an bevor du das 
ganze Forum mit halben Informationen hinhältst?

von MWS (Gast)


Lesenswert?

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_t low = 0x94;
2
uint8_t high = 0x38;
3
uint16_t word = 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.

von Thomas K. (blackfinn)


Lesenswert?

Sorry, 'war am Nachmittag "verhindert"...

Ich habe einen Woraround gefunden. Der Sieht so aus:
1
uint16_t low = (uint16_t)buffer[i];
2
uint16_t high = (uint16_t)(buffer[i + 1] & 0x00FF);
3
high *= 256;
4
uint32_t lw = (high + low);
5
uint16_t w = (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.

von Frank B. (f-baer)


Lesenswert?

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.

von Andreas B. (andreas_b77)


Lesenswert?

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.

von Walter (Gast)


Lesenswert?

Thomas Klähn schrieb:
> Ich habe einen Woraround gefunden. Der Sieht so aus:

das ist doch ein würgaround,
zeig doch endlich mal das assemblerlisting

von asd (Gast)


Lesenswert?

Walter schrieb:
> das ist doch ein würgaround,
> zeig doch endlich mal das assemblerlisting

Oder zumindest das vollständige C Programm, nicht nur Schnipsel.

von Martin (Gast)


Lesenswert?

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

von Thomas K. (blackfinn)


Lesenswert?

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

von Thomas K. (blackfinn)


Angehängte Dateien:

Lesenswert?

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.

von Thomas K. (blackfinn)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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.

von Thomas K. (blackfinn)


Lesenswert?

> 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
1
#pragma packed (1)
davon überzeugen die Bytes "aneinanderzureihen"

von Simon K. (simon) Benutzerseite


Lesenswert?

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.

von Thomas K. (blackfinn)


Lesenswert?

> Die Schiebevariante ist üblicherweise problemloser.
Das hab' ich ja auch geglaubt...

von Walter (Gast)


Lesenswert?

eine Frage zum Vorschlag union:

ist das denn egal ob big oder little endian,
sprich: ist das in C genormt wie die Bytes abgelegt werden?

von MWS (Gast)


Lesenswert?

Das ist ein merkwürdiges Rumgestopsele, Du willst nicht wirklich sagen, 
daß solche Verrenkungen notwendig sind um aus 2 uint8 ein uint16 zu 
machen...
1
        uint16_t low = (uint16_t)m_page_buffer[i];
2
        uint16_t high = (uint16_t)(m_page_buffer[i + 1] & 0x00FF);
3
        high *= 256;
4
5
        uint32_t lw = (high + low);
6
        uint16_t w = (uint16_t) lw;
Wie sagte schon Igor: Meister, Meister, es lebt...

von Beobachter (Gast)


Lesenswert?

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?

von DirkB (Gast)


Lesenswert?

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.

von MWS (Gast)


Lesenswert?

Das:
1
w = (m_page_buffer[i+1] << 8) | m_page_buffer[i];
erzeugt mit -Os das:
1
LDD       R19,Z+1
2
LDI       R18,0x00
3
LD        R24,Z+  
4
LDI       R25,0x00
5
OR        R24,R18
6
OR        R25,R19
Und das ist völlig einwandfrei, in R24/25 steht das korrekte Ergebnis.

von W.S. (Gast)


Lesenswert?

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.

von Roland H. (batchman)


Lesenswert?

> 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
const uint16_t w = ( (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.

von Jürgen (jliegner)


Lesenswert?

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.

von Cyblord -. (cyblord)


Lesenswert?

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.
1
uint8_t low;
2
uint8_t hi;
3
uint16_t w=hi;
4
w <<=8;
5
w+=low;

von Thomas K. (blackfinn)


Lesenswert?

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_t low = 0x94;
2
uint8_t high = 0x38;
3
uint16_t word = low | (high << 8);
oder
1
uint8_t low = 0x94;
2
uint8_t high = 0x38;
3
uint16_t word = (uint16_t)low | (uint16_t)(high << 8);
oder
1
uint8_t low = 0x94;
2
uint8_t high = 0x38;
3
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.
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

von Rolf Magnus (Gast)


Lesenswert?

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.

von Thomas K. (blackfinn)


Lesenswert?

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

von MWS (Gast)


Lesenswert?

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.

von Christian H. (netzwanze) Benutzerseite


Lesenswert?

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.

von Thomas K. (blackfinn)


Angehängte Dateien:

Lesenswert?

> 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:
1
static const uint8_t buffer[] = {0x0C, 0x94, 0x2E, 0x00, 0x0C, 0x94, 
2
                                 0xDD, 0x03, 0x0C, 0x94, 0x4B, 0x00, 
3
                                 0x0C, 0x94, 0x4B, 0x00, 0xCC};
4
5
int main(void) {
6
    init_uart();
7
    for(uint8_t i = 0; i < sizeof(buffer); i += 2) {
8
        uint16_t w=(uint16_t)(buffer[i])+(uint16_t)(buffer[i+1]<<8);
9
10
        uart_put_char(buffer[i]);
11
        uart_put_char(buffer[i + 1]);
12
        uart_put_char(w);
13
        uart_put_char(w >> 8);
14
    }
15
16
    return 0;
17
}
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:
1
0000388a <main>:
2
388a:  df 92         push  r13
3
388c:  ef 92         push  r14
4
388e:  ff 92         push  r15
5
3890:  0f 93         push  r16
6
3892:  1f 93         push  r17
7
3894:  cf 93         push  r28
8
3896:  df 93         push  r29
9
3898:  0e 94 71 1c   call  0x38e2  ; 0x38e2 <init_uart>
10
389c:  c0 e0         ldi  r28, 0x00  ; 0
11
389e:  d1 e0         ldi  r29, 0x01  ; 1
12
38a0:  88 81         ld  r24, Y
13
38a2:  d9 80         ldd  r13, Y+1  ; 0x01
14
38a4:  1d 2d         mov  r17, r13
15
38a6:  00 e0         ldi  r16, 0x00  ; 0
16
38a8:  78 01         movw  r14, r16
17
38aa:  e8 0e         add  r14, r24
18
38ac:  f1 1c         adc  r15, r1
19
38ae:  0e 94 7f 1c   call  0x38fe  ; 0x38fe <uart_put_char>
20
38b2:  8d 2d         mov  r24, r13
21
38b4:  0e 94 7f 1c   call  0x38fe  ; 0x38fe <uart_put_char>
22
38b8:  8e 2d         mov  r24, r14
23
38ba:  0e 94 7f 1c   call  0x38fe  ; 0x38fe <uart_put_char>
24
38be:  8f 2d         mov  r24, r15
25
38c0:  0e 94 7f 1c   call  0x38fe  ; 0x38fe <uart_put_char>
26
38c4:  22 96         adiw  r28, 0x02  ; 2
27
38c6:  81 e0         ldi  r24, 0x01  ; 1
28
38c8:  c2 31         cpi  r28, 0x12  ; 18
29
38ca:  d8 07         cpc  r29, r24
30
38cc:  49 f7         brne  .-46       ; 0x38a0 <main+0x16>
31
38ce:  80 e0         ldi  r24, 0x00  ; 0
32
38d0:  90 e0         ldi  r25, 0x00  ; 0
33
38d2:  df 91         pop  r29
34
38d4:  cf 91         pop  r28
35
38d6:  1f 91         pop  r17
36
38d8:  0f 91         pop  r16
37
38da:  ff 90         pop  r15
38
38dc:  ef 90         pop  r14
39
38de:  df 90         pop  r13
40
38e0:  08 95         ret

Kommen wir nun zu meinem Bootloader. Hier habe ich den (fast) gleichen 
Code ausgeführt:
1
for (i = 0; i < SPM_PAGESIZE; i += 2) {
2
    uint16_t w=
3
       (uint16_t)m_page_buffer[i]+(uint16_t)(m_page_buffer[i+1]<<8);
4
    uart_put_char(m_page_buffer[i]);
5
    uart_put_char(m_page_buffer[i + 1]);
6
    uart_put_char(w);
7
    uart_put_char(w >> 8);
8
}
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:
1
3dc6:  00 00         nop
2
3dc8:  cf b6         in  r12, 0x3f  ; 63
3
3dca:  f8 94         cli
4
3dcc:  01 e4         ldi  r16, 0x41  ; 65
5
3dce:  11 e0         ldi  r17, 0x01  ; 1
6
3dd0:  f8 01         movw  r30, r16
7
3dd2:  80 81         ld  r24, Z
8
3dd4:  e0 e0         ldi  r30, 0x00  ; 0
9
3dd6:  ef 01         movw  r28, r30
10
3dd8:  7e 01         movw  r14, r28
11
3dda:  e8 0e         add  r14, r24
12
3ddc:  f1 1c         adc  r15, r1
13
3dde:  0e 94 37 1f   call  0x3e6e  ; 0x3e6e <uart_put_char>
14
3de2:  f8 01         movw  r30, r16
15
3de4:  81 81         ldd  r24, Z+1  ; 0x01
16
3de6:  0e 94 37 1f   call  0x3e6e  ; 0x3e6e <uart_put_char>
17
3dea:  8e 2d         mov  r24, r14
18
3dec:  0e 94 37 1f   call  0x3e6e  ; 0x3e6e <uart_put_char>
19
3df0:  8f 2d         mov  r24, r15
20
3df2:  0e 94 37 1f   call  0x3e6e  ; 0x3e6e <uart_put_char>
21
3df6:  0e 5f         subi  r16, 0xFE  ; 254
22
3df8:  1f 4f         sbci  r17, 0xFF  ; 255
23
3dfa:  f1 e0         ldi  r31, 0x01  ; 1
24
3dfc:  01 3c         cpi  r16, 0xC1  ; 193
25
3dfe:  1f 07         cpc  r17, r31

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

von MWS (Gast)


Lesenswert?

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.

von Thomas K. (blackfinn)


Angehängte Dateien:

Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

Thomas Klähn schrieb:
> aus den
> Ubuntu-Paketquellen

Keine gute Idee.
Das Warum hatte ich mal im Wiki etwas ausführlicher ausgeführt:
http://www.mikrocontroller.net/articles/AVR_und_Linux#Toolchain_beschaffen

von MWS (Gast)


Lesenswert?

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:
1
uint16_t w = (uint16_t)m_page_buffer[i] + (uint16_t)(m_page_buffer[i + 1] << 8);
wird zu:
1
MOVW      R30,R14
2
LDD       R24,Z+0
3
LDD       R11,Z+1
4
CLR       R10    
5
MOVW      R16,R10
6
ADD       R16,R24
7
ADC       R17,R1
Ist so völlig in Ordnung.

von Thomas K. (blackfinn)


Lesenswert?

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

von Thomas K. (blackfinn)


Lesenswert?

Hallo,
 ich habe jetzt 'mal ein gcc-compilat von 
http://www.mikrocontroller.net/articles/AVR_und_Li... verwendet - und 
siehe da - das listing sieht wie folgt aus:
1
movw  r30, r14
2
ld  r24, Z
3
ldd  r11, Z+1  ; 0x01
4
eor  r10, r10
5
movw  r16, r10
6
add  r16, r24
7
adc  r17, r1

von Karl H. (kbuchegg)


Lesenswert?

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.

von Andreas B. (andreas_b77)


Lesenswert?

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.

von MWS (Gast)


Lesenswert?

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.

von Kernie (Gast)


Lesenswert?

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.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

<troll>
OO ist definitiv ein Schritt rückwärts, egal was man da sonst noch 
Hässliches oder Falsches mit macht.
</troll>

von Cyblord -. (cyblord)


Lesenswert?

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?

von Decius (Gast)


Lesenswert?

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

von Sam .. (sam1994)


Lesenswert?

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

von MWS (Gast)


Lesenswert?

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:
1
ldi   r16, 0x41   
2
ldi   r17, 0x01     <-- ; Adresse high byte in R17
3
movw  r30, r16      <-- ; R17/18 nach R30/31, R31 = 0x01
4
ld    r24, Z
5
ldi   r30, 0x00     <-- ; high byte der Adresse (0x01) immer noch in R31
6
movw  r28, r30      <-- ; R30/31 nach R28/29 kopieren, 0x01 in R29
7
movw  r14, r28      <-- ; R28/29 nach R14/15 kopieren, 0x01 in R15
8
add   r14, r24
9
adc   r15, r1       <-- ; R1 0x00 wird mit Carry zu R15 0x01 addiert = Murks
Da hätte Dein Vorschlag gar nichts geholfen.

von MWS (Gast)


Lesenswert?

MWS schrieb:
> movw  r30, r16      <-- ; R17/18 nach R30/31, R31 = 0x01

Korrektur, muss heißen:
1
movw  r30, r16      <-- ; R16/17 nach R30/31, R31 = 0x01

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.