Я работаю над проектом с сенсорным TFT-экраном, на этом экране есть библиотека. Но после некоторого чтения я все еще ничего не понимаю. В библиотеке есть некоторые определения относительно цветов:
/* 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
Это 16-битные цвета. Но как они идут от 0,128,128 (темно-голубой) до 0x03EF. Я имею в виду, как вы конвертируете 16-битный цвет в uint16? для этого не нужно иметь anwser в коде, потому что я просто хочу добавить несколько библиотек в библиотеку. Ссылка на онлайн-конвертер (который я не смог найти) тоже подойдет 🙂
Спасибо
Из них можно легко узнать формулу:
#define Red 0xF800 /* 255, 0, 0 */
#define Magenta 0xF81F /* 255, 0, 255 */
#define Yellow 0xFFE0 /* 255, 255, 0 */
F800 имеет 5 битов MSB, а FFE0 имеет 5 битов LSB.
Очевидно, что 0xF81F имеет набор из 5 LSB и 5 MSB, что подтверждает формат RGB565.
Формула для преобразования значения 173 в красное не так проста, как может показаться — вы не можете просто отбросить 3 младших значащих бита, но должны линейно интерполировать, чтобы сделать 255, чтобы соответствовать 31 (или зеленый 255, чтобы соответствовать 63).
NewValue = (31 * old_value) / 255;
(И это все еще только усеченное деление — может потребоваться правильное округление)
При правильном округлении и масштабировании:
Uint16_value = (((31*(red+4))/255)<<11) |
(((63*(green+2))/255)<<5) |
((31*(blue+4))/255);
РЕДАКТИРОВАТЬ Добавлена скобка, как подсказывает JasonD.
Похоже, вы используете RGB565, сначала 5 бит для красного, затем 6 бит для зеленого, затем 5 бит для синего.
Вы должны замаскировать с 0xF800 и сдвинуть вправо на 11 бит, чтобы получить красный компонент (или сдвинуть 8 бит, чтобы получить значение от 0 до 255).
Маска с 0x07E0 и сдвиг вправо на 5 бит, чтобы получить зеленый компонент (или 3, чтобы получить значение 0-255).
Маска с 0x001F, чтобы получить синий компонент (и сдвиг влево на 3 бита, чтобы получить 0-255).
Вам нужно знать точный формат дисплея, просто «16-битный» недостаточно.
Есть RGB555, в котором каждый из трех компонентов получает 5 бит. Это уменьшает общее цветовое пространство до 32 768 цветов, тратя впустую один бит, но управлять им очень просто, поскольку для каждого компонента одинаковое количество оттенков.
Также есть RGB565, в котором зеленому компоненту дается 6 бит (поскольку человеческий глаз более чувствителен к зеленому). Это может быть формат, который вы используете, так как «темно-зеленый» пример 0x03e0
который в двоичном виде 0b0000 0011 1110 0000
, Так как там есть 6 битов, равных 1, я предполагаю, что это общее распределение для зеленого компонента и показывает его максимальное значение.
Тогда это так (с пробелами, разделяющими каждые четыре бита и повторно использующими воображаемое 0b
префикс):
0bRRRR RGGG GGGB BBBB
Конечно, порядок слов тоже может отличаться.
Задача преобразования триплета чисел в битовое слово довольно легко выполняется на типичных языках программирования, которые имеют операторы битовых манипуляций.
В C это часто делается в макросе, но мы также можем иметь функцию:
#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;
}
обратите внимание, что вышеупомянутое предполагает полную 8-битную точность для компонентов, поэтому максимальная интенсивность для компонента составляет 255, а не 128, как в вашем примере. Если цветовое пространство действительно использует 7-битные компоненты, тогда потребуется дополнительное масштабирование.
Ваши цвета в формате 565. Было бы более очевидно, если бы вы записали их в двоичном виде.
Чтобы преобразовать 24-битный цвет в 16-битный в этом формате, просто замаскируйте верхние биты, необходимые для каждого компонента, сдвиньте в позицию и OR
все вместе.
Преобразуйте обратно в 24-битный цвет из 16-битного, замаскируйте каждый компонент, сдвиньте в позицию, а затем дублируйте старшие биты в младшие биты.
В ваших примерах кажется, что некоторые цвета были масштабированы и округлены, а не смещены.
Я настоятельно рекомендую использовать метод битового сдвига, а не масштабировать с коэффициентом, подобным 31/255, потому что битовый сдвиг не только будет быстрее, но и даст лучшие результаты.
Показанные вами 3-значные номера применимы к 24-битному цвету. 128 в шестнадцатеричном формате — это 0x7f, но в ваших определениях цвета он представлен как 0x0f. Аналогично, 255 — это 0xff, но в ваших определениях цвета он представлен как 0x1f. Это говорит о том, что вам нужно взять номера из 3-х частей и сдвинуть их на 3 бита (теряя 3 бита цветовых данных для каждого цвета). Затем объедините их в одно 16-битное число:
uint16 color = ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
…пересмотрено ранее, потому что зеленый использует 6 бит, а не 5.
Вы должны знать, сколько битов на цветовой канал. Так что да, для цвета есть 16 битов, но каждый компонент RGB является некоторым подмножеством этих битов. В вашем случае красный — 5 бит, зеленый — 6, а синий — 5. Формат в двоичном виде будет выглядеть так:
RRRRRGGG GGGBBBBB
Существуют другие 16-битные форматы цвета, такие как красный, зеленый и синий, каждый из которых имеет 5 бит, а затем использует оставшийся бит для значения альфа.
Диапазон значений для красного и синего каналов будет от 0
в 2^5-1 = 31
в то время как диапазон для зеленого будет 0
в 2^6-1 = 63
, Таким образом, чтобы преобразовать из цветов в виде (0->255),(0->255),(0->255)
вам нужно будет сопоставить значения от одного к другому. Например, красное значение 128
В диапазоне 0->255
будет сопоставлен с (128/255) * 31 = 15.6
в красном канале с дальностью 0-31
, Если мы округлим вниз, мы получим 15
который представлен как 01111
в пяти битах. Точно так же для зеленого канала (с шестью битами) вы получите 011111
, ТАК цвет (128,128,128)
будет отображаться в 01111011 11101111
который 0x7BEF
в шестнадцатеричном формате.
Вы можете применить это и к другим значениям: 0,128,128
становится 00000011 11101111
который 0x03EF
,
Эти цвета, показанные в вашем коде, RGB565. Как показано
#define Blue 0x001F /* 0, 0, 255 */
#define Green 0x07E0 /* 0, 255, 0 */
#define Red 0xF800 /* 255, 0, 0 */
Если вы просто хотите добавить новые цвета к этому #defined
список, самый простой способ конвертировать из 16-битного UINT на канал — просто сдвинуть ваши значения вниз, чтобы потерять младшие биты, а затем сдвинуть и (или) их в положение в 16bitRGB-значении.
Впрочем, это может привести к появлению артефактов полосатости, и вполне может быть лучший метод преобразования.
то есть
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)
Возможно, вам потребуется замаскировать любые ненужные биты после смен, поскольку я не уверен, как это работает, но я думаю, что приведенный выше код должен работать.
Основываясь на ответе раскрутки, в частности, для библиотеки Adafruit GFX с использованием Arduino 2.8 «TFT Touchscreen (v2), вы можете добавить эту функцию к вашему эскизу Arduino и использовать ее для вычисления цветов по 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;
}
Теперь вы можете использовать его встроенным образом, как показано на рисунке, с помощью функции, которая создает квадрат 20×20 в x0, y0:
void setup() {
tft.begin();
makeSquare(getColor(20,157,217));
}
unsigned long makeSquare(uint16_t color1) {
tft.fillRect(0, 0, 20, 20, color1);
}
Документы для библиотеки Adafruit GFX можно найти Вот