# Теория: как экран рисует картинку

## 1. Матрица пикселей: тысячи маленьких лампочек

TFT (Thin-Film Transistor) экран — это сетка из крошечных ячеек-пикселей,
каждая со своим тонкоплёночным транзистором, который управляет тем, сколько
света пропускает эта конкретная точка и какого цвета. Экран этого компонента
(чип ST7735, часто продаётся с меткой "BLACKTAB" — так называется одна из
заводских настроек, [tft_display.cpp:8](src/tft_display.cpp#L8)) имеет
разрешение **128×160 пикселей** — это 20480 отдельных управляемых точек.

Рисовать "линию" или "круг" на таком экране на самом деле означает: вычислить,
какие именно пиксели должны загореться нужным цветом, и передать эти
координаты чипу. Библиотека `Adafruit_GFX` берёт эту математику на себя
(алгоритмы вроде Брезенхэма для линий/окружностей) — наш класс `TFTDisplay`
просто передаёт ей "откуда, докуда, каким цветом"
([tft_display.cpp:40-46](src/tft_display.cpp#L40-L46)).

## 2. SPI + лишний провод DC: как чип понимает, команда это или данные

Экран подключается по SPI — тому же протоколу, что часто используют карты
памяти и некоторые датчики. Но у TFT-экранов есть контакт, которого нет у
большинства других SPI-устройств: **DC (Data/Command)**.

По SPI идёт просто поток байт — сам по себе он не говорит чипу, значит ли
очередной байт "нарисуй точку здесь" (команда) или "вот значение цвета"
(данные). Пин DC решает эту неоднозначность: пока он в одном логическом
состоянии, чип трактует входящие байты как команды, пока в другом — как
данные для уже выбранной команды. Переключение DC туда-сюда делает
`Adafruit_ST7735` автоматически на каждом вызове — этой логики нет и не
должно быть в нашем классе.

`CS` (Chip Select) — стандартный SPI-пин, который "выбирает" именно этот
чип на шине (на случай, если на одной SPI-линии несколько устройств). `RST`
(Reset) физически перезагружает чип; в конфигурации этого компонента он
почти везде равен `-1` ([tft_display.h:13-33](include/tft_display.h#L13-L33)) —
это означает "используем программный сброс вместо отдельного провода",
экран умеет сбрасываться командой по SPI, а не только по физическому пину.

## 3. Формат цвета RGB565

У этого экрана нет отдельных проводов на "красный", "зелёный", "синий" —
цвет каждого пикселя передаётся как одно 16-битное число. Формат называется
**RGB565**, потому что 16 бит разбиты неравномерно:

```
биты:  R R R R R | G G G G G G | B B B B B
        (5 бит)      (6 бит)      (5 бит)
```

Зелёному отведён лишний бит (6 вместо 5) — человеческий глаз различает
больше оттенков зелёного, чем красного или синего, поэтому такое
распределение даёт визуально более точный цвет при том же объёме данных.

Константы палитры класса ([tft_display.h:42-49](include/tft_display.h#L42-L49))
— это уже готовые 16-битные числа в этом формате, например
`COLOR_RED = 0xF800` — это `11111 000000 00000` в двоичном виде: все 5 бит
красного включены, зелёный и синий — нули.

## 4. Почему нельзя просто "перерисовать один пиксель"

Кажется естественным при обновлении текста стирать только старые пиксели, но
на практике проще и быстрее залить область целиком новым фоном и нарисовать
заново — именно так устроен `showMessage()`
([tft_display.cpp:19-33](src/tft_display.cpp#L19-L33)): каждый вызов сначала
чистит весь экран (`clear(COLOR_BLACK)`), а потом рисует текст поверх.
Частичное обновление (только изменившихся пикселей) возможно, но требует
дополнительной логики отслеживания "что было нарисовано раньше" — этот
компонент, ориентированный на понятность для школьников, такой сложности
намеренно избегает.

## 5. Почему у класса нет прямого доступа к Adafruit

`Adafruit_ST7735` — это уже полноценный, хорошо протестированный драйвер
самого чипа: он умеет говорить с ним по SPI, переключать DC, знает все
команды инициализации конкретной модели матрицы. Класс `TFTDisplay` не
переписывает эту работу — он использует `Adafruit_ST7735` как приватное поле
([tft_display.h:81](include/tft_display.h#L81)) и добавляет поверх него свой,
более простой прикладной API: `showMessage`/`drawLine`/`fillCircle`/`printAt`.

Важно, что этот API **не пробрасывает** оригинальный объект Adafruit наружу
(в отличие от более раннего варианта этого компонента, где был метод
`getDriver()`) — весь доступ идёт через методы `TFTDisplay`. Это тот же
принцип, что у `NrfLink` в компоненте nRF24l01: библиотека-транспорт остаётся
внутри класса, наружу выходит только осмысленный прикладной интерфейс.
