Question

I am working on a project with a TFT touch screen, with this screen there is an included library. But after some reading, i still dont get something. In the library there are some defines regarding colors:

/* some RGB color definitions                                                 */
#define Black           0x0000      /*   0,   0,   0 */
#define Navy            0x000F      /*   0,   0, 128 */
#define DarkGreen       0x03E0      /*   0, 128,   0 */
#define DarkCyan        0x03EF      /*   0, 128, 128 */
#define Maroon          0x7800      /* 128,   0,   0 */
#define Purple          0x780F      /* 128,   0, 128 */
#define Olive           0x7BE0      /* 128, 128,   0 */
#define LightGrey       0xC618      /* 192, 192, 192 */
#define DarkGrey        0x7BEF      /* 128, 128, 128 */
#define Blue            0x001F      /*   0,   0, 255 */
#define Green           0x07E0      /*   0, 255,   0 */
#define Cyan            0x07FF      /*   0, 255, 255 */
#define Red             0xF800      /* 255,   0,   0 */
#define Magenta         0xF81F      /* 255,   0, 255 */
#define Yellow          0xFFE0      /* 255, 255,   0 */
#define White           0xFFFF      /* 255, 255, 255 */
#define Orange          0xFD20      /* 255, 165,   0 */
#define GreenYellow     0xAFE5      /* 173, 255,  47 */
#define Pink                        0xF81F

Those are 16 bit colors. But how do they go from: 0,128,128(dark cyan) to 0x03EF. I mean, how do you convert a 16bit color to a uint16? this doesn't need to have a anwser in code, because i just want to add some collors in the library. A link to a online converter (wich i could not find) would be okay as well :)

Thanks

Était-ce utile?

La solution

From these one can easily find out the formula:

#define Red             0xF800      /* 255,   0,   0 */  
#define Magenta         0xF81F      /* 255,   0, 255 */
#define Yellow          0xFFE0      /* 255, 255,   0 */

F800 has 5 MSB bits set and FFE0 has 5 LSB not set. 0xF81F has obviously both 5 LSB's and 5 MSB's set, which proves the format to be RGB565.

The formula to convert a value 173 to Red is not as straightforward as it may look -- you can't simply drop the 3 least significant bits, but have to linearly interpolate to make 255 to correspond to 31 (or green 255 to correspond to 63).

NewValue = (31 * old_value) / 255;

(And this is still just a truncating division -- proper rounding could be needed)

With proper rounding and scaling:

Uint16_value = (((31*(red+4))/255)<<11) | 
               (((63*(green+2))/255)<<5) | 
               ((31*(blue+4))/255);

EDIT Added parenthesis to as helpfully suggested by JasonD.

Autres conseils

You need to know the exact format of the display, just "16-bit" is not enough.

There's RGB555, in which each of the three components get 5 bits. This drops the total color space to just 32,768 colors, wasting one bit but it's very simple to manage since the there's the same number of shades for each component.

There's also RGB565, in which the green component is given 6 bits (since the human eye is more sensitive to green). This might be the format you're having, since the "dark green" example is 0x03e0 which in binary is 0b0000 0011 1110 0000. Since there's 6 bits set to 1 there, I guess that's the total allocation for the green component and showing it's maximum value.

It's like this, then (with spaces separating every four bits and re-using the imaginary 0b prefix):

0bRRRR RGGG GGGB BBBB

Of course, the bit ordering can differ too, in the word.

The task of converting a triplet of numbers into a bit-packed word is quite easily done in typically programming languages that have bit manipulation operators.

In C, it's often done in a macro, but we can just as well have a function:

#include <stdint.h>

uint16_t rgb565_from_triplet(uint8_t red, uint8_t green, uint8_t blue)
{
  red   >>= 3;
  green >>= 2;
  blue  >>= 3;
  return (red << 11) | (green << 5) | blue;
}

note that the above assumes full 8-bit precision for the components, so maximum intensity for a component is 255, not 128 as in your example. If the color space really is using 7-bit components then some additional scaling would be necessary.

It looks like you're using RGB565, first 5 bits for Red, then 6 bits for Green, then 5 bits for Blue.

You should mask with 0xF800 and shift right 11 bits to get the red component (or shift 8 bits to get a value from 0-255). Mask with 0x07E0 and shift right 5 bits to get green component (or 3 to get a 0-255 value). Mask with 0x001F to get the blue component (and shift left 3 bits to get 0-255).

Your colours are in 565 format. It would be more obvious if you wrote them out in binary.

  • Blue, (0,0,255) is 0x001f, which is 00000 000000 11111
  • Green, (0, 255, 0) is 0x07e0, which is 00000 111111 00000
  • Red, (255, 0, 0) is 0xf800, which is 11111 000000 00000

To convert a 24 bit colour to 16 bit in this format, simply mask off the upper bits needed from each component, shift into position, and OR together.

The convert back into 24 bit colour from 16 bit, mask each component, shift into position, and then duplicate the upper bits into the lower bits.

In your examples it seems that some colours have been scaled and rounded rather than shifted.

I strongly recommend using the bit-shift method rather than scaling by a factor like 31/255, because the bit-shifting is not only likely to be faster, but should give better results.

The 3-part numbers you’re showing are applicable to 24-bit color. 128 in hex is 0x7f, but in your color definitions, it's being represented as 0x0f. Likewise, 255 is 0xff, but in your color definitions, it's being represented as 0x1f. This suggests that you need to take the 3-part numbers and shift them down by 3 bits (losing 3 bits of color data for each color). Then combine them into a single 16-bit number:

uint16 color = ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);

...revised from earlier because green uses 6 bits, not 5.

You need to know how many bits there are per colour channel. So yes, there are 16 bits for a colour, but the RGB components are each some subset of those bits. In your case, red is 5 bits, green is 6, and blue is 5. The format in binary would look like so:

RRRRRGGG GGGBBBBB

There are other 16 bit colour formats, such as red, green, and blue each being 5 bits and then use the remaining bit for an alpha value.

The range of values for both the red and blue channels will be from 0 to 2^5-1 = 31, while the range for green will be 0 to 2^6-1 = 63. So to convert from colours in the form of (0->255),(0->255),(0->255) you will need to map values from one to the other. For example, a red value of 128 in the range 0->255 will be mapped to (128/255) * 31 = 15.6 in the red channel with range 0-31. If we round down, we get 15 which is represented as 01111 in five bits. Similarly, for the green channel (with six bits) you will get 011111. SO the colour (128,128,128) will map to 01111011 11101111 which is 0x7BEF in hexadecimal.

You can apply this to the other values too: 0,128,128 becomes 00000011 11101111 which is 0x03EF.

Those colours shown in your code are RGB565. As shown by

#define Blue            0x001F      /*   0,   0, 255 */ 
#define Green           0x07E0      /*   0, 255,   0 */ 
#define Red             0xF800      /* 255,   0,   0 */

If you simply want to add some new colours to this #defined list, the simplest way to convert from 16bit UINT per channel is just to shift your values down to loose the the low order bits and then shift and (or) them into position in the 16bitRGB value. This could well produce banding artefacts though, and there may well be a better conversion method.

i.e.

UINT16 blue = 0xFF;
UINT16 green = 0xFF;
UINT16 red = 0xFF;


blue  >>= 11; // top 5 bits
green >>= 10; // top 6 bits
red   >>= 11; // top 5 bits

UINT16 RGBvalue = blue | (green <<5) | (red << 11)

You may need to mask of any unwanted stray bits after the shifts, as I am unsure how this works, but I think the code above should work.

Building on unwind's answer, specifically for the Adafruit GFX library using the Arduino 2.8" TFT Touchscreen(v2), you can add this function to your Arduino sketch and use it inline to calculate colors from rgb:

uint16_t getColor(uint8_t red, uint8_t green, uint8_t blue)
{
  red   >>= 3;
  green >>= 2;
  blue  >>= 3;
  return (red << 11) | (green << 5) | blue;
}

Now you can use it inline as so, illustrated with a function that creates a 20x20 square at x0,y0:

void setup() {
  tft.begin();
  makeSquare(getColor(20,157,217));
}

unsigned long makeSquare(uint16_t color1) {
  tft.fillRect(0, 0, 20, 20, color1);
}

Docs for the Adafruit GFX library can be found here

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top