PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 16-bit int in 2 8-bit int zerlegen



Christoph2
21.02.2009, 21:03
Hallo!

Ich muss einen 16 Bit int-werte in 2 8-Bit werte zerlegen, damit ich diese über i2c an meinen motorcontroller schicken kann.

Gibt es dazu in avr-gcc eine funktion?

Ich hab mir auch schon selber ein programm überlegt, nur weiß ich nicht wie die vorzeichen sein müssen. Sollen die dann zum höherwertigen byte oder zum niederwertigeren?


int8_t int16to8_high(uint16_t variable)
{ variable=variable/128;

//vorzeichen??

return(uint8_t) variable;
}

int8_t int16to8_low(uint16_t variable)
{ variable=variable & 0b1000000001111111;
return (uint8_t) variable;
}

Also hier ist mein programm, gibt es von avr-gcc auch eins?

lg christoph

Dirk
21.02.2009, 21:52
Wahrscheinlich stellt sich deine Frage so gar nicht. Wenn du einen Integer-Wert (var) in 2 Bytes verschicken must, kannst du das einfach so machen:
LByte = var;
HByte = (var >> 8 );

Auf der anderen Seite wird der Wert wieder hergestellt:
var = ((HByte << 8 ) | LByte);

Oder habe ich deine Frage nicht verstanden?

Gruß Dirk

Christoph2
21.02.2009, 22:05
Nein du hast das richtig verstanden, danke.
Ich hab an das verschieben garnicht gedacht, immer nur an dividieren... aber eigentlich ist das ja das selbe...

wenn ich so mache:

variable8bit = (uint8_t) variable16bit;

Dann fallen einfach die höheren 8 bit der 16-bit variable weg oder?


lg Christoph

Dirk
21.02.2009, 22:09
Ja genau.

Gruß Dirk

Besserwessi
21.02.2009, 22:39
Man kann auch direkt auf die einzelnen Bytes zugreifen, wenn man die Variable als union definiert.

Es könnte sein dass GCC das per Optimierung auch so hinkriegt. Manchmal geht die Optimierung ja sehr weit, einige einfach aushende Dinge klappen aber auch nicht.

Christoph2
22.02.2009, 17:52
Hi!

Danke, ich hab jetzt ein programm zum umwandeln von 16 bit in zwei 8 Bit werte, es funktioniert super.

Jetzt brauche ich einens, dass es umgekehrt macht, von zwei 8 bit werten in eine 16 bit variable.

Ich wollte das so machen:


int16_t int8to16(int8_t high, int8_t low)
{ uint16_t out;

out = (high<<8);
out = out | low;

return out;
}


Es funktioniert aber leider nicht. Es kommen irgendwelche zufallszahlen heraus, ich vermute einen vorzeichenfehler. aber ich kann nicht erkennen wo da ein fehler sein soll.

Könnt ihr euch das mal ansehen?

lg christoph

McJenso
22.02.2009, 18:25
Hallo,

hast du mal Beispielzahlen. low darf natürlich nur unsigned sein. Was soll sonst z.B. high = 16 und low -5 ergeben?
Was meinst du mit Zufallszahlen? Bei gleichen Parametern verschiedene Ergebnisse?

Gruß

Jens

Dirk
22.02.2009, 18:42
Hallo Christoph2,

in deiner Funktion müssen die 8-Bit-Variablen uint8_t sein und die out-Variable natürlich int16_t.

Gruß Dirk

Christoph2
22.02.2009, 19:41
ahhh danke!!
Es funktioiert!!

Ich hab aber nur die low variable uint8_t gemacht, die high hab ich int8_t gemacht, sonst hab ich keine vorzeichen bei der int16_t.

lg und danke
Christoph

Dirk
22.02.2009, 20:08
... die high hab ich int8_t gemacht, sonst hab ich keine vorzeichen bei der int16_t.
Wenn du die Integer so sendest, wie oben weiter gezeigt, dürfte das nicht stimmen!
Beide 8-Bit-Variablen müssen uint8_t sein.

Gruß Dirk

Christoph2
22.02.2009, 20:39
Ja, stimmt, ich hab die ausgabevariable unsigned gemacht, hab ich übersehen.

Es kommt aber auf das selbe, weil das höchstwertige bit auf jeden fall ein vorzeichenbit ist.

lg
Christoph

Dirk
22.02.2009, 21:09
Hallo Christoph2,

Es kommt aber auf das selbe, weil das höchstwertige bit auf jeden fall ein vorzeichenbit ist.
Bei der ganzen Geschichte hier geht es ja nur um das Aufsplitten von einer 16-Bit-Variable (egal ob mit Vorzeichen oder ohne) in zwei 8-Bit-Variablen, um sie über I2C versenden zu können.

Dabei werden einfach die 16 Bits der Ausgangsvariable in 2x 8 Bit aufgeteilt, gesendet und dann beim Empfänger wieder zusammengesetzt.

Das Vorzeichen einer Integer spielt beim byteweisen Versenden gar keine Rolle, denn da werden einfach 2x 8 Bit übertragen. Da könnten auch z.B. Pixel eines Bildes drin sein oder eine Word-Variable, ganz egal.

So erklärt sich, dass auf der "Transportseite" (2 Bytes werden über I2C versendet) die beiden High-/Low-Bytes uint8_t sind:
Jedes Byte transportiert ja nur die 8 Bits und ist daher vorzeichenfrei.

Deutlicher würde das Ganze, wenn man den Vorschlag von Besserwessi mit der Union umsetzt:
Da würde man eine int16_t (oder uint16_t egal) definieren und an derselben Speicherstelle zwei Bytes (uint8_t). Man kann dann an dieser Stelle eine Integer ablegen und direkt als 2 Bytes weiterverarbeiten (z.B. senden).

Puh ...

Zum Schluß noch der Gag: Es funktioniert auch mit int8_t ! :-({|=

Gruß Dirk

SprinterSB
22.02.2009, 23:31
Man kann auch direkt auf die einzelnen Bytes zugreifen, wenn man die Variable als union definiert.

Es könnte sein dass GCC das per Optimierung auch so hinkriegt.

GCC kann schon lange scalar replacement of aggregates, d.h. Komponenten eines zusammengesetzten Objekts werden ebenso effizient wie Skalare bekandelt, falls sie selber welche sind.

Ausnahme sind Bitfelder, die bis dato in avr-gcc nicht sehr effizient behandelt werden. Insbesondere für einzelne Bits ist der Code deutlich schlechter, als er sein könnte. Demnägst gibt's aber vielleicht in Patch, das auch das behebt. Dann kann auf Bit ein einem Bitfeld ebenso effizient zugegriffen werden wie über das C-übliche Masken-Gefummel.

opc
15.04.2016, 10:12
Hallo Community,

ich weiß, dass der Thread >6 Jahre alt ist, aber warum einen neuen eröffnen wenn mein Problem hier 100% liegt.

mein Vorhaben ganz simpel: eine Zahl (16bit) soll auch wie hier in 2x(8bit) zerlegt werden und via UART auf einen zweiten µC gesendet und auf einer 7-Segment LED-Anzeige ausgegeben werden.
Mit einer 8-bit Zahl habe ich die Ansteuerung des 7-Segment Displays getestet um dies schonmal abzuhaken in seiner Funktion.

Problem ist, wenn ich eine 16-bit Zahl (hier 6789) sende, gibt die LED-Anzeige lauter Zufallszahlen aus und wechselt hin- und her.

Ich stehe mit meiner Programmiererfahrung noch ziemlich am Anfang und habe versucht eine Lösung zu finde, jedoch ohne Erfolg. Ich poste die wichtigen Ausschnitte meines Codes (unwichtiges rausgelöscht) was das Senden und Empfangen angeht, wäre nett wenn mir einer helfen kann (ist ja bestimmt nichts großes) :)

SENDEN



.
.
.
.
uint16_t zahl=6789;

uint8_t HByte;
uint8_t LByte;


void daten_senden (uint8_t data)
{
while (!(UCSRA & (1<<UDRE))){} //warten bis senden möglich ist

UDR = data; //Zeichen in den Ausgabepuffer schreiben
}

void usart_init (void)
{
UCSRB |= (1<<TXEN); //Daten senden
UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // Asynchron 8N1
UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;
}


int main(void)
{
usart_init();

while (1)
{

HByte = (zahl >> 8);
LByte = zahl & 0xff;

daten_senden(HByte);
daten_senden(LByte);
}
}




EMPFANGEN



.
.
.


uint16_t zahl;
uint8_t usart_empfang;

void usart_init (void)
{
UCSRB |= (1<<RXEN) |(1<<RXCIE) ; //Daten empfangen und Interrupt für Datenempfang enable
UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // Asynchron 8N1
UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;
}


ISR(USART_RXC_vect)
{
usart_empfang = UDR;
}


int main(void)
{
usart_init();

sei();

while (1)
{

uint8_t HByte = usart_empfang;
uint8_t LByte = usart_empfang;

zahl = (((uint16_t) HByte) << 8 ) | LByte;

zahl_ausgeben(zahl);


}
}

Peter(TOO)
15.04.2016, 11:38
Hallo Christoph,

ahhh danke!!
Ich hab aber nur die low variable uint8_t gemacht, die high hab ich int8_t gemacht, sonst hab ich keine vorzeichen bei der int16_t.

Ich denke du hast das mit signed und unsigned noch nicht wirklich verstanden.

Der 8-Bit-Wert 0b11111111 hat unsigned den Wert 255 und signed -1.
Das höchstwertige Bit ist das Vorzeichen 0 = "+" und 1 = "-" (zumindest beim üblichen 2er Komplement)

Wird nun unsigned auf 16-Bit erweitert ergibt dies 0b00000000'11111111, bei signed wird es 0b11111111'11111111

Der 8-Bit-Wert 0b01111111 ist unsigned und signed = 127.
Entsprechend wird unsigned und signed als 16-Bit 0b00000000'01111111

Bei der Erweiterung werden die zusätzlichen Bits bei unsigned immer mit 0en gefüllt.
Bei signed, entsprechend dem Vorzeichen-Bit mit 0en oder 1ern.

MfG Peter(TOO)

opc
15.04.2016, 11:46
Hallo Peter,
der letzte Post war von mir auf einen 6 Jahre alten Thread, aber mit neuer Ausgangslage (wie erwähnt)!

Bitte sieh dir meinen Post an.

BMS
15.04.2016, 13:42
Hallo,
ich würde in der Empfangs-Programm vor dem Zusammenbauen a=(b<<8 )|c das most significant byte erst als uint16 casten bevor es geshiftet wird.
Also a=( ((uint16_t)b) <<8 ) |c
Grüße,
Bernhard

opc
15.04.2016, 14:18
Grüß dich Bernhard,

erledigt (auch hier im Code im Forum).

Sieht nun so aus:

zahl = (((uint16_t) HByte) << 8 ) | LByte;

Leider immer noch dasselbe Problem.

Gruß
Markus

HaWe
15.04.2016, 14:20
uint8_t highbyte, lowbyte;
int16_t intval;

// bytes zu int:
intval = lowbyte + (highbyte << 8);

//int zu bytes:
lowbyte = intval & 0x00ff;
highbyte = (intval >> 8) & 0x00ff;

hoffe, das stimmt...! 8-)

opc
15.04.2016, 14:31
@HaWe: ja das ist schon richtig so, mein Fehler muss woanders liegen.

Hab dein Codeschnipsel auch testweise soeben mal eingebaut bzw. ersetzt -> führt leider zum selben Ergebnis.

Habe mal testweise ein delay (1s) nach der Zahlenausgabe aufs 7-Segment geschrieben, daran erkennt man deutlich, dass zwischen zwei völlig wirren Zahlenkombinationen hin- und hergewechselt wird, also scheint der Fehler weiterhin an der Umsetzung 16bit / 2*8bit und umgekehrt zu liegen.

Bin um jeden Tipp dankbar

HaWe
15.04.2016, 15:18
bevor nicht fertig gerechnet wurde, darfst du keine Werte ans Display senden...
... und auch natürlich nicht per UART senden!

nachdem das Int/Byte-Zerlege-Problem dann aber gelöst ist, würde ich empfehlen bei weiterhin bestehenden Problemen ein neues Topic zum Thema "UART" oder "Display" aufzumachen, je nachdem, wo das Problem jetzt liegt.

opc
15.04.2016, 15:19
wenn ich wüsste woran das Problem liegt^^

aber hast recht... ich eröffne ein neues der Übersichtlichkeit wegen....

HaWe
15.04.2016, 15:21
arbeitest du mit Arduinos oder Raspis?