Forum: Compiler & IDEs Microchip C18, umwandeln von WORD auf BYTE


von simon (Gast)


Lesenswert?

Hi,


ich dachte da an sowas:
1
    WORD Reg_Addr;
2
    BYTE lowbyte = (BYTE) Reg_Addr;
3
    BYTE highbyte = (BYTE) Reg_Addr >>8;

Da habe ich in der Header Datei aber folgendes gefunden:
1
typedef union
2
{
3
    WORD Val;
4
    BYTE v[2] __PACKED;
5
    struct __PACKED
6
    {
7
        BYTE LB;
8
        BYTE HB;
9
    } byte;
10
    struct __PACKED
11
    {
12
        __EXTENSION BYTE b0:1;
13
        __EXTENSION BYTE b1:1;
14
        __EXTENSION BYTE b2:1;
15
        __EXTENSION BYTE b3:1;
16
        __EXTENSION BYTE b4:1;
17
        __EXTENSION BYTE b5:1;
18
        __EXTENSION BYTE b6:1;
19
        __EXTENSION BYTE b7:1;
20
        __EXTENSION BYTE b8:1;
21
        __EXTENSION BYTE b9:1;
22
        __EXTENSION BYTE b10:1;
23
        __EXTENSION BYTE b11:1;
24
        __EXTENSION BYTE b12:1;
25
        __EXTENSION BYTE b13:1;
26
        __EXTENSION BYTE b14:1;
27
        __EXTENSION BYTE b15:1;
28
    } bits;
29
} WORD_VAL, WORD_BITS;

Da steht LB und HB drinne, ich glaube das die genau diesen Fall schon 
vorgesehen haben. wie kann ich mit hilfe der union ein WORD_VAL 
umwandeln auf BYTE?
1
BYTE high = WORD_VAL.HB?
2
BYTE low = WORD_VAL.LB?

von Frank K. (fchk)


Lesenswert?

1
WORD_VAL t;
2
WORD w=0x1234;
3
BYTE h,l;
4
5
// WORD w in die Union reinschreiben
6
t.Val=w;
7
// BYTEs h und l rausholen
8
h=t.byte.HB;
9
l=t.byte.LB;

von Madeyemike (Gast)


Lesenswert?

Temporär auf den Typen casten.
1
WORD w = 0x1234;
2
BYTE h, l;
3
4
h = ((WORD_VAL)w).HB;
5
l = ((WORD_VAL)w).LB;

Gruß Mike

von simon (Gast)


Lesenswert?

Danke.

von simon (Gast)


Lesenswert?

Also so ganz funzt das nicht, und ich verstehe nicht was er daran zu 
meckern hat:

xx.c:97:Error [1146] type mismatch in argument 1
1
void xx_write_SPI(WORD_VAL Reg_Addr, BYTE nr_bytes, BYTE *data)
2
{
3
4
}
5
6
xx_write_SPI(0xEBFF,1,&x);

Da ist doch ein wunderschöner WORD wert den ich da übergebe.

von Frank K. (fchk)


Lesenswert?

nein. Du übergibt die Union, nicht das WORD.

So ist es richtig:
1
void xx_write_SPI(WORD RegAddrW, BYTE nr_bytes, BYTE *data)
2
{
3
  WORD_VAL Reg_Addr;
4
  Reg_Addr.Val=RegAddrW;
5
6
}
7
8
xx_write_SPI(0xEBFF,1,&x);

von simon (Gast)


Lesenswert?

Also irgendwie funzt das nicht Anständig.

Ich habe es mal ohne WORD_VAL gemacht:
1
    WORD w = 0;
2
    w = (GAIN_PGA1_1 | GAIN_PGA2_2 | GAIN_PGA3_16 );
3
    bufferX[0] = 'X';
4
    bufferX[1] = ( (BYTE) (w>>8) );
5
    bufferX[2] = ( (BYTE) (w) );
6
    bufferX[3] = 'X';
7
    bufferX[4] = '\0';
8
    putsUART((char*)bufferX);
Ergebnis: er verschluckt das zweite X was angezeigt werden soll. und die 
Werte davor passen auch nicht.


Das ganze mal am PC mit gcc ( funzt out of the box):
1
  unsigned int w = (GAIN_PGA1_1 | GAIN_PGA2_2 | GAIN_PGA3_16 );
2
  char lb = (char) w;
3
  char hb = (char) (w>>8);

Ausgabe:
 Integer: [337] -> Hex: 0x151 -> Bin: 101010001
 Integer: [1] -> Hex: 0x1 -> Bin: 1
 Integer: [81] -> Hex: 0x51 -> Bin: 1010001


In beiden fällen die defines dazu:

#define GAIN_PGA1_1  (0x0001)
#define GAIN_PGA1_2  (0x0002)
#define GAIN_PGA1_4  (0x0003)
#define GAIN_PGA1_8  (0x0004)
#define GAIN_PGA1_16 (0x0005)

#define GAIN_PGA2_1  (0x0001<<3)
#define GAIN_PGA2_2  (0x0002<<3)
#define GAIN_PGA2_4  (0x0003<<3)
#define GAIN_PGA2_8  (0x0004<<3)
#define GAIN_PGA2_16 (0x0005<<3)

#define GAIN_PGA3_1  (0x0001<<6)
#define GAIN_PGA3_2  (0x0002<<6)
#define GAIN_PGA3_4  (0x0003<<6)
#define GAIN_PGA3_8  (0x0004<<6)
#define GAIN_PGA3_16 (0x0005<<6)



Was ich mich immernoch Frage was die sache mit "char" soll.
Worin unterschiedet sich Char von Byte?
1
typedef char                    CHAR8;
2
typedef unsigned char           UCHAR8;
3
typedef unsigned char           BYTE;

Also scheinbar nur im Vorzeichen, nur wozu brauche ich bei einem char 
ein Vorzeichen?

von Fabian O. (xfr)


Lesenswert?

simon schrieb:
> Ergebnis: er verschluckt das zweite X was angezeigt werden soll. und die
> Werte davor passen auch nicht.

Dann schreib doch wenigstens, was er ausgibt. Und poste bitte den ganzen 
Code. Dann spart man sich viel Rätselraten. Man sieht z.B. nicht, 
welchen Typ bufferX hat. Und wenn eines der Bytes 0 ist, hört die 
Ausgabe natürlich an der Stelle auf, weil das Byte dann als 
String-Ende-Zeichen erkannt wird.

simon schrieb:
> Was ich mich immernoch Frage was die sache mit "char" soll.
> Worin unterschiedet sich Char von Byte?
> typedef char                    CHAR8;
> typedef unsigned char           UCHAR8;
> typedef unsigned char           BYTE;
>
> Also scheinbar nur im Vorzeichen, nur wozu brauche ich bei einem char
> ein Vorzeichen?

Wenn Du mit kleinen, vorzeichenbehafteten Werten (-128 bis 127) rechnen 
willst, nimmst Du ein signed char. Wenn Deine Werte von 0 bis 255 gehen, 
nimmst Du ein unsigned char. Wenn Du Buchstaben ausgeben willst, nimmst 
Du ein char. Eigentlich ganz einfach.

Die Typen CHAR8, UCHAR8 und BYTE hat Dein Compilerhersteller definiert, 
weil er sie anscheinend schöner fand. Ich würde die nicht in eigenem 
Code nehmen. Dann lieber <stdint.h> einbinden und Du hast für kleine 
Zahlen diese eindeutigen Typen:
1
uint8_t = unsigned char
2
 int8_t = signed char
Entsprechend für 16 Bit:
1
uint16_t = unsigned short
2
 int16_t = signed short
etc.

von simon (Gast)


Lesenswert?

Der Buffer:

BYTE bufferX[10];

von simon (Gast)


Lesenswert?

Also ich habe jetzt mal printf genommen, das auch nix schief gehen kann 
beim umwandeln.
1
#define GAIN_PGA1_1  (0x0001)
2
#define GAIN_PGA2_2  (0x0002<<3)
3
#define GAIN_PGA3_16 (0x0005<<6)
4
5
    WORD w = (GAIN_PGA1_1 | GAIN_PGA2_2 | GAIN_PGA3_16 );
6
    x = (BYTE) (w >> 8);
7
    y = (BYTE) w;
8
  
9
    printf("X: %#x\n", x);
10
    putsUSART( buffer );
11
12
    printf("Y: %#x\n", y);
13
    putsUSART( buffer );
Ergebnis:
X:0x0
Y:0x51


Also ich kann mir nicht erklären wie der auf X: 0x0 kommt.



Mein erwartetes Ergebnis wäre:
 X: 0x1
 Y: 0x51

von simon (Gast)


Lesenswert?

kleiner Fehler, so:
1
    char buffer[20];
2
    WORD w = (GAIN_PGA1_1 | GAIN_PGA2_2 | GAIN_PGA3_16 );
3
    BYTE x = (BYTE) (w >> 8);
4
    BYTE y = (BYTE) w;
5
  
6
    sprintf(buffer, "X: %#x\n", x);
7
    putsUSART( buffer );
8
9
    sprintf(buffer, "Y: %#x\n", y);
10
    putsUSART( buffer );

Denoch gleiches Ergebnis.

von Karl H. (kbuchegg)


Lesenswert?

simon schrieb:

> Denoch gleiches Ergebnis.

Und welchen Wert hat w?

von simon (Gast)


Lesenswert?

#define GAIN_PGA1_1  (0x0001)
#define GAIN_PGA2_2  (0x0002<<3)
#define GAIN_PGA3_16 (0x0005<<6)

WORD w = (GAIN_PGA1_1 | GAIN_PGA2_2 | GAIN_PGA3_16 );

von Karl H. (kbuchegg)


Lesenswert?

simon schrieb:
> #define GAIN_PGA1_1  (0x0001)
> #define GAIN_PGA2_2  (0x0002<<3)
> #define GAIN_PGA3_16 (0x0005<<6)
>
> WORD w = (GAIN_PGA1_1 | GAIN_PGA2_2 | GAIN_PGA3_16 );


Nein.
Ich meine an der Ausgabe. Lass es dir mal mit einem printf ausgeben. Die 
0 für x muss ja irgendwo herkommen. Wenn das High-Byte in w schon 0 ist, 
dann geht die Suche weiter, warum das High-Byte 0 ist und nicht warum 
bei der Zerlegung was schief geht.

Daher sollte man mal klären, ob die 0 schon im w drinnen steckt oder 
nicht.

von simon (Gast)


Lesenswert?

sprintf(buffer, "W: %#i\n", w);
    putsUSART( buffer );

    W: 81

da müsste Eigentlich 337 erscheinen.

 Integer: [337] -> Hex: 0x151 -> Bin: 101010001
 Integer: [81] -> Hex: 0x51 -> Bin: 1010001
 Integer: [1] -> Hex: 0x1 -> Bin: 1

von simon (Gast)


Lesenswert?

Also ich komme zum verrecken nicht drauf was hieran falsch sein soll?

Es funktioniert ja mit gcc am PC wunderbar.

Was macht der Microchip XC16 anders?

von Fabian O. (xfr)


Lesenswert?

Probier mal
1
#define GAIN_PGA3_16 (0x0140)
und
1
#define GAIN_PGA3_16 (0x0005UL<<6)
und
1
#define GAIN_PGA3_16 ((WORD)0x0005<<6)

von simon (Gast)


Lesenswert?

> #define GAIN_PGA3_16 (0x0140)

Funzt Perfekt.

Mal so zum verstehen, was ist hier den genau schief gelaufen?

Der Preprozessor hat das als größere oder kleinere Variable gesehen?

von simon (Gast)


Lesenswert?

Gibt es hier eine Einfache Möglichkeit das WORD in w dieser Funktion zu 
übergeben?

Die Funktion erwartet (Gain addresse, anzahl bytes, pointer auf byte)
1
w = (WORD) (GAIN_PGA1_1 | GAIN_PGA2_2 | GAIN_PGA3_16 );
2
3
xx_write_SPI((WORD)GAIN,2,(BYTE*)&w); // Ist es auch ok hier ein WORD zu nehmen?
4
5
void xx_write_SPI(WORD Reg_Addr, BYTE nr_bytes, BYTE *data)
6
   BYTE i;
7
    for(i=0;i<nr_bytes;i++) {     // Write Data
8
        spi(*data);
9
        data++;
10
    }

von Karl H. (kbuchegg)


Lesenswert?

simon schrieb:
>> #define GAIN_PGA3_16 (0x0140)
>
> Funzt Perfekt.
>
> Mal so zum verstehen, was ist hier den genau schief gelaufen?

Ist das wieder so ein Müll, dass der Compiler entgegen dem C-Standard 
ein

    5 << 6

als 8-Bit Operation implementiert, anstatt wie es richtig wäre als int 
Operation? Bzw. der eigentliche Scheiss besteht darin, dass der COmpiler 
einen int mit 8 Bit zulässt, obwohl des gegen jeden C-Standard 
verstösst?

Ich frage deshalb, weil in einem anderen Thread genau dieser Müll mit 
dem CSS Compiler auf einem Pic aufgetreten ist.

Das solltest du unbedingt abklären, denn das ist eine wichtige 
Information für dich. Was ergibt

  int i = 5 << 6;
  sprintf(buffer, "T: %#d\n", i);
  putsUSART( buffer );

wenn da nicht 320 rauskommt, dann such nach dem Compilerschalter, wie du 
diesen Dreck abdrehen kannst. Wenn ein int nicht mehr garantiert 
mindestens 16 Bit hat, und der Compiler das auch in arithmetischen 
Ausdrücken mit den Promotionrules berücksichtigt, dann ist das keine 
Hilfe für den Programmierer sondern dann haben sie dem Compilerbauer ins 
Hirn gesch.... (wie Mundl Sackbauer sagen würde)

von Fabian O. (xfr)


Lesenswert?

simon schrieb:
> Funzt Perfekt.
>
> Mal so zum verstehen, was ist hier den genau schief gelaufen?
>
> Der Preprozessor hat das als größere oder kleinere Variable gesehen?

Was ist denn mit den anderen beiden Möglichkeiten? Funktionieren die 
auch? Wenn ja, dann hält sich Dein Compiler nicht an den C-Standard, 
weil ein int für ihn nur 8 statt mindestens 16 Bit breit ist.

Der Compiler hat also die Zahl 0x0005 als 8-Bit-Wert genommen (die 
führenden Nullen interessieren nicht) und den Linksshift um 6 Stellen 
auch nur mit 8 Bit gerechnet. Dann ist das oberste Bit rausgeflogen, 
weil es nicht mehr in 8 Bit gepasst hat.

Wie gesagt verstößt das aber gegen den C-Standard. Wenn das der Fall 
ist, solltest Du mal schauen, ob man das nicht ändern kann (per Option 
beim Aufruf des Compilers).

simon schrieb:
1
xx_write_SPI((WORD)GAIN,2,(BYTE*)&w); // Ist es auch ok hier ein WORD zu nehmen?
Das wäre sinnlos, denn die Funktion erwartete einen Zeiger auf ein BYTE. 
Das kannst Du als Aufrufer der Funktion nicht ändern. Du musst ihr das 
geben, was sie haben will. In dem Fall will sie einen Zeiger auf ein 
BYTE. &w ist aber ein Zeiger auf ein WORD. Also musst Du den BYTE* in 
einen WORD* umwandeln. Der Compiler würde das auch ohne expliziten Cast 
automatisch machen (er hat ja keine andere Wahl), aber das gibt dann 
eine Warnung.

Den ersten Cast (WORD)GAIN kannst Du Dir dagegen sparen. Die Funktion 
erwartetet ein WORD, also wird ihr der Compiler ein WORD übergeben. Auch 
wenn GAIN vielleicht kein WORD ist. Bei Integern gehen solche impliziten 
Casts ohne Warnung, da sie ständig vorkommen.

von simon (Gast)


Lesenswert?

Vielen Dank für die Tips.

1
xx_write_SPI((WORD)GAIN,2,(BYTE*)&w);

Das hier funktioniert wunderbar, nur ob das jetzt Zufall ist weisz ich 
nicht.

Mein Problem ist das ich die Funktion nicht ändern kann/sollte weil ich 
manchmal nur 1 byte schicke und manchmal auch 2, 3 oder 4 byte.

Gibt es hier den kein Trick wie ich aus dem Pointer auf mein word ein 
byte pointer machen kann?



Zu den Fragen:
Alle drei der genannten Möglichkeiten Funktionieren und bringen das 
richtige Ergebnis.
1
#define GAIN_PGA3_16 (0x0140)
2
#define GAIN_PGA3_16 (0x0005UL<<6)
3
#define GAIN_PGA3_16 ((WORD)0x0005<<6)
4
5
// Frage 2:
6
  int i = 5 << 6;
7
  sprintf(buffer, "T: %#d\n", i);
8
  putsUSART( buffer );  // Ausgabe T: 64

von Fabian O. (xfr)


Lesenswert?

Ich sehe gerade, ich habe mich in meinem Beitrag verschrieben:

> In dem Fall will sie einen Zeiger auf ein
> BYTE. &w ist aber ein Zeiger auf ein WORD. Also musst Du den BYTE* in
> einen WORD* umwandeln.

Das muss richtig heißen:
In dem Fall will sie einen Zeiger auf ein BYTE. &w ist aber ein Zeiger 
auf ein WORD. Also musst Du den WORD* in einen BYTE* umwandeln.

Genau das machst Du bei dem Funktionsaufruf ja mit dem Cast:
1
(BYTE*)&w

Das ist also so gesehen schon in Ordnung. Es ist aber nicht die beste 
Methode, das zu machen. Denn es ist tatsächlich gewissermaßen "Zufall", 
dass es funktioniert. Denn diese Methode verlässt sich darauf, dass die 
beiden Bytes in der richtigen Reihenfolge (in der sie der Empfänger 
erwartet) im Speicher des WORDs stehen. Das mag bei Deinem 
Mikrocontrollertyp der Fall sein, kann auf anderen Prozessoren aber auch 
genau andersrum sein.

Deshalb wäre es besser, einen Sendepuffer anzulegen, in den Du die Bytes 
an die richtige Stelle legst:
1
WORD w = GAIN_PGA1_1 | GAIN_PGA2_2 | GAIN_PGA3_16;
2
3
BYTE buffer[2];
4
buffer[0] = w >> 8;
5
buffer[1] = w;
6
7
xx_write_SPI(GAIN, sizeof(buffer), buffer);

Falls Du die xx_write_SPI()-Funktion selber geschrieben hast, würde ich 
übrigens die Reihenfolge der Parameter nr_bytes und data umdrehen. Denn 
es ist allgemein üblich, zuerst den Zeiger und dann die Länge anzugeben. 
Alle Funktionen in der C-Standardbibliothek erwarten das so. Es ist 
irritierend, wenn das plötzlich anders ist.

von simon (Gast)


Angehängte Dateien:

Lesenswert?

Fabian O. schrieb:
> Falls Du die xx_write_SPI()-Funktion selber geschrieben hast, würde ich
> übrigens die Reihenfolge der Parameter nr_bytes und data umdrehen. Denn
> es ist allgemein üblich, zuerst den Zeiger und dann die Länge anzugeben.
> Alle Funktionen in der C-Standardbibliothek erwarten das so. Es ist
> irritierend, wenn das plötzlich anders ist.

ok, Das Beispiel was ich hatte war auch ursprünglich mal so.



Also irgendwo steckt doch da echt der wurm drinne..
1
    UINT32 x;
2
    BYTE obuf[4];
3
   char buffer[20];
4
5
    x = (UINT32) 831870;
6
    sprintf(buffer, "VL: %d \n", x); // gibt 12  aus??
7
    putsUSART( buffer );
8
9
    obuf[0] = 0;
10
    obuf[1] = ((BYTE) (x >>16));
11
    obuf[2] = ((BYTE) (x >>8));
12
    obuf[3] = ((BYTE) x);
13
14
    xx_write_SPI((WORD)VLEVEL,4,(BYTE*) &obuf[0]); // ins Register schreiben
15
    x = 0;
16
    xx_read_SPI((WORD)VLEVEL,4,(BYTE *)&x); // Zurück lesen
17
18
    sprintf(buffer, "VLEVELx: %#x\n", x); // Gibt 0x7eb1 aus, wo ict das 0C ?
19
    putsUSART( buffer );


obuf wird genau mit der spi Funktion ausgegeben, ich sehe dann auch die 
richtigen Werte am SPI.

0x00 0x0C 0xB1 0x7E = 831870;

Nur wie kann es sein das sprintf nur 12 Anzeigt?

von Josef D. (jogedua)


Lesenswert?

> sprintf(buffer, "VL: %d \n", x); // gibt 12  aus??
das %d bedeutet: betrachte x als int.

von simon (Gast)


Lesenswert?

Was sagt dann aus das x als UINT32 angesehen wird?
%i? %u?
ich kann im C18 manual nur noch x und X finden für den hex wert.

Aber selbst beim zweiten hex wert fehlt mir ein 0x0C

von Josef D. (jogedua)


Lesenswert?


von Josef D. (jogedua)


Lesenswert?

Versuch's mal mit %ld, %lx

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.