[gelöst] 3 uint16_t "zusammentackern"

Hallo zusammen,
ich habe ein Beispiel gefunden, wie man 3 uint8_t zu uint32_t “zusammentackern” kann.

Mein Sensor liefert brav die Werte für red_light, green_light und blue_light und ich kann rgb auch wieder in die Bestandteile zerlegen.

    Serial.println("Sensordaten:");
    Serial.print("Red: ");
    Serial.print(red_light);
    Serial.print(", Green: ");
    Serial.print(green_light);
    Serial.print(", Blue: ");
    Serial.print(blue_light);

    uint32_t rgb = ((red_light&0x0ff)<<16)|((green_light&0x0ff)<<8)|(blue_light&0x0ff);
    Serial.print(", RGB: ");
    Serial.println(rgb);

    int red = (rgb>>16)&0x0ff;
    int green = (rgb>>8)&0x0ff;
    int blue = (rgb)&0x0ff;
    Serial.println("Einzelteile:");
    Serial.print("Red: ");
    Serial.print(red);
    Serial.print(", Green: ");
    Serial.print(green);
    Serial.print(", Blue: ");
    Serial.println(blue);

Sensordaten:
Red: 10, Green: 6, Blue: 5, RGB: 656901
Einzelteile:
Red: 10, Green: 6, Blue: 5

aber:
Das funktioniert nur, so lange red_light, green_light und blue_light kleiner als 255 sind.

Wie kann ich 3 uint16_t Farbwerte “zusammentackern” und wieder in die Einzelteile zerlegen?

Natürlich kann das nur mit Werten <=255 funktionieren. Mehr passt in ein Byte ja auch nicht rein.
Wenn Du 3 uint16_t verketten willst, musst Du uint64_t als Resultat nehmen

uint64_t rgb = ((red_light&0x0ffff)<<32)|((green_light&0x0ffff)<<16)|(blue_light&0x0ffff);

Gruß Tommy

freddy64:
Wie kann ich 3 uint16_t Farbwerte “zusammentackern” und wieder in die Einzelteile zerlegen?

Naja, das ist die Grundlage:

uint32_t rgb = ((red_light&0x0ff)<<16)|((green_light&0x0ff)<<8)|(blue_light&0x0ff);

Nu ist der Wert nicht 8 sondern 16 breit
Damit brauchst einen Datentyp, der 64bit breit ist um das aufzunehmen.
uint64_t rgb = ((red_light&0x0ff)<<48)|((green_light&0x0ff)<<16)|(blue_light&0x0ff);

Aber willst Du das? auf einem 8bit AVR?

(Tommy56 war schneller)

letzter...

Das funktioniert nur, so lange red_light, green_light und blue_light kleiner als 255 sind.

oben schreibst du dass die drei Werte nur ein unit8_t sind ... das kann nicht größer werden als 255.

Dein eigentliches liegt imho wo anders. Du shiftest um 16 bit in eine Variable die nur 16 bit hat. Das kann eigentlich so nicht funktionieren. Das wirst vorher casten müssen. Sieht man aber nicht weil da wesentliche Zeilen fehlen.

Mach ein kompilierbares Beispiel inkl aller Variablen dann kann man dir vieleicht besser helfen.

uint64_t rgb = ((red_light&0x0ffff)<<32)|((green_light&0x0ffff)<<16)|(blue_light&0x0ffff);
funktioniert nicht.

Da spinnt der Compiler:
warning: left shift count >= width of type [-Wshift-count-overflow]
uint64_t rgb = ((red_light&0x0ffff)<<32)|((green_light&0x0ffff)<<16)|(blue_light&0x0ffff);

ColorSensor.txt (3.3 KB)

Du musst auch &0xffff bei 16 Bit machen.

Wo ist das Problem mit uint64_t die 8-Bit-AVR können problemlos damit umgehen, nur print kann es nicht.
Da braucht es dann eine kleine Erweiterung.

Gruß Tommy

Dann musst Du alle bereits in uint64_t legen.

Gruß Tommy

Das hier

(red_light&0x0ffff)

ist ja nur 16 Bit breit; deshalb die Meldung, dass es nicht um 32 Bit geshiftet werden kann.
Also sollte das zu shiftende Ding schon 64 Bit breit werden.

Ein einfacher C-style cast könnte da schon Wunder wirken:

((uint64_t)red_light&0x0ffff)

Besser wäre vermutlich ein static_cast:

(static_cast<uint64_t>(red_light)&0x0ffff)

Da spinnt der Compiler:

Nein, das tut er nicht!

Sondern:
Du hast vergessen dein neues C++ Buch aufmerksam zu lesen.


Ein einfacher C-style cast könnte da schon Wunder wirken:

Alternativ, nutzt man den impliziten Cast.

  uint64_t rgb = ((red_light&0x0ffffull)<<32)|((green_light&0x0ffffull)<<16)|(blue_light&0x0ffffull);
uint64_t rgb = ((static_cast<uint64_t>(red_light)&0x0ffff)<<32) | ((static_cast<uint64_t>(green_light)&0x0ffff)<<16) | (static_cast<uint64_t>(blue_light)&0x0ffff);

funktioniert schon mal.

Zum Print(uint64_t) habe ich hier etwas gefunden. Die Variante von robtillaart scheint mir recht sinnvoll.

Aber wie kann ich das in den Core einbinden?
Print.h und Print.cpp ändern?

combie du Sauhund :slight_smile:
funktioniert auch

freddy64:
Print.h und Print.cpp ändern?

Stand da nicht in #17 was?

Hier im Thread in #5. Als Lib hat es den Vorteil, dass man es nicht bei jedem Update im Core anpassen muss.

Gruß Tommy

combie:
Sondern:
Du hast vergessen dein neues C++ Buch aufmerksam zu lesen.

Ich arbeite dran, versprochen :slight_smile: - aber das mit dem spinnenden Compiler ist ausnahmsweise nicht auf meinem Mist gewachsen.

Impliziter Cast?
Hmm, gefällt mir optisch nicht; das 0xffffull liest sich schon komisch. 0xFFFFull ist nur unwesentlich besser.

Eigentlich müsste es doch auch reichen, nur red_light zu casten, den Rest sollte doch dann der Compiler erledigen.

suuuuper funktioniert perfekt.
auch: Serial.print(uint64_t)

combie du Sauhund :slight_smile:

Danke für die Blumen!

funktioniert auch

Ich weiß.

:smiling_imp: :smiling_imp:

Impliziter Cast?
Hmm, gefällt mir optisch nicht; das 0xffffull liest sich schon komisch. 0xFFFFull ist nur unwesentlich besser.

Hmm …
0xffffULL ist auch wohl nicht schöner?

Vielleicht dann eher sowas:
(das zeigt in aller Deutlichkeit, was man will)

constexpr uint64_t operator""_uint64_t(uint64_t value){return value;}

uint64_t rgb = ((red_light&0x0ffff_uint64_t)<<32)|((green_light&0x0ffff_uint64_t)<<16)|(blue_light&0x0ffff_uint64_t);

??

constexpr uint64_t operator""_uint64_t(uint64_t value){return value;}

Ist nicht wahr... (und Schönheit liegt im Auge des Betrachters).
Danke!

Ja, soche Schönheiten hat unser combie drauf. Irgendwie sind bei mir Operatoren mit constexpr noch nicht am verlängerten Rückenmark angelangt.
Wenn ich die von combie sehe, kommt ein aha, aber selbst komme ich da nocht nicht drauf.

Gruß Tommy

Irgendwie sind bei mir Operatoren mit constexpr noch nicht am verlängerten Rückenmark angelangt.

Da hilft nur üben!

Aus meiner Lib extrahiert:

    using Millis = decltype(millis());  // mostly 32Bit, sometimes 64Bit
    
    constexpr Millis operator"" _ms(unsigned long long int t) // milli seconds
    {
          return t;
    }
    
    constexpr Millis operator"" _sec(unsigned long long int t)  // seconds
    {
          return t * 1000_ms;
    }
    
    constexpr Millis operator"" _min(unsigned long long int t)  // minutes
    {
          return t * 60_sec;
    }
    
    constexpr Millis operator"" _hr(unsigned long long int t)  // hours
    {
          return t * 60_min;
    }
    
    constexpr Millis operator"" _dy(unsigned long long int t)  // days
    {
          return t * 24_hr;
    }

    
    constexpr Millis operator"" _sec(double long  t)  // seconds
    {
          return t * 1000_ms;
    }
    
    constexpr Millis operator"" _min(double long t)  // minutes
    {
          return t * 60_sec;
    }
    
    constexpr Millis operator"" _hr(double long t)  // hours
    {
          return t * 60_min;
    }
    
    constexpr Millis operator"" _dy(double long t)  // days
    {
          return t * 24_hr;
    }

Damit sind dann solche Dinge möglich wie:

delay(1_min + 30_sek);
//oder 
delay(1.5_min);


constexpr Millis interval {2.3_dy + 8_min - 10_sek};

So wie gezeigt, sind die "user defined literals" nur syntaktischer Zucker.
Da geht noch mehr, in Sachen Typesicherheit, wo wohl ihr Hauptzweck zu suchen ist..

Da hast Du recht. Im verlängerten Rückenmark sitzen aber immer noch 18 Jahre ANSI-C und die muss man erst mal vertreiben :wink:

Gruß Tommy

Edit: Das Problem ist nicht, das Neue zu lernen, sondern das Alte, das man im Schlaf beherrst, zu vertreiben.