Осваиваем работу с цветными TFT-дисплеями на базе ST7735

25 мая 2018

Если зайти на eBay и ввести в поиске «ST7735», можно найти немало модулей с дисплеем на базе данного контроллера. Модули обычно бывают двух типов — с TFT-дисплеем диагональю 1.44" и разрешением 128x128 пикселей, а также с диагональю 1.8" и разрешением 128x160 пикселей. Последние в большинстве случаев также имеют и разъем для подключения SD-карт (но не все). Дисплеи позволят отображать 65536 цветов в палитре R5G6B5. Интересны данные модули тем, что будучи чуть-чуть дороже популярных 0.96-дюймовых OLED-экранчиков на базе SSD1306 предлагают существенно большие разрешение и диагональ, а также в ~30 тысяч раз больше цветов.

Fun fact! Еще есть шилд для Arduino от Duinopeak с 1.8-дюймовым дисплеем, джойстиком и разъемом для SD-карт, а также шапка для Raspberry Pi от WaveShare с 1.44-дюймовым дисплеем, джойстиком и тремя кнопками . Однако цена этих модулей с учетом доставки относительно высока. Кроме того, на AliExpress доступны укороченные 0.96-дюймовые модули с разрешением 80x160.

Модули питаются от 3.3 В или 5 В, имеют подсветку (которая питается только от 3.3 В, благодаря чему ее легко случайно спалить!) и используют SPI-подобный протокол. Желающие посмотреть на конкретные единички и нолики протокола, могут воспользоваться Sigrok и соответствующим .sr файлом из репозитория sigrok-dumps. Также мной был написан простенький декодер протокола ST7735 для Sigrok, но на момент написания этих строк патч еще не был вмержен в основную ветку (UPD: как оказалось, на самом деле он уже вмержен, см раз и два).

В PulseView протокол выглядит как-то так:

Протокол ST7735 в PulseView

Команды и данные передаются с помощью пинов SCLK и MOSI, с порядком бит msb-first, как в обычном SPI. Здесь байт B1 является кодом команды FRMCTR1, а байты 01, 2C и 2D — аргументами этой команды. Отличить аргументы от команды можно по пину DC (data or command), который имеет низкое напряжение для команд и высокое для данных. Также можно заметить, что CS (chip select) можно смело менять посреди передачи фрейма. Как и в традиционном SPI, чип выбран, когда напряжение на CS низкое. Наконец, пин RES позволяет сбросить состояние контроллера, подав на этот пин низкое напряжение.

Для получения приведенной выше картинки я использовал Arduino Uno и библиотеку для ST7735 от Adafruit. В версиях этой библиотеки старше 1.0.8 также вкорячили поддержку ST7789, с иерархиями классов, обмазкой всего макросами, и всяким таким. Из-за этого код библиотеки стал намного труднее для восприятия. Любопытно, что ST7789 не является слишком уж распространненым контроллером. Он похож на ST7735, но в виде модуля продается только в магазине Adafruit за сравнительно большие деньги. Модуль имеет разрешение 240x240 при диагонали 1.54". В своих проектах я бы не стал использовать этот модуль из-за небольшой диагонали, высокой стоимости и завязки на одного производителя. А для изучения работы интересного мне ST7735 я использовал тэг 1.0.8 библиотеки.

Было решено спортировать библиотеку от Adafruit на STM32. Конечно, под STM32 нашлись и готовые библиотеки. Но, во-первых, использование готового кода — это скучно :) Во-вторых, оно не приводит к появлению нормального понимания работы устройства. Наконец, в-третьих, библиотеки, которые мне удалось найти, были написаны хардкорными бородатыми эмбеддерами, привыкшими делать все напрямую через регистры и бинарные сдвиги. Не будучи хардкорным бородатым эмбеддером, я предпочитаю библиотеки без преждевременных оптимизаций, основанные на HAL, которые легче в поддержке и расширении.

Интерфейс библиотеки вышел следующим:

void init() {
    ST7735_Init();
}

void loop() {
    // Check border
    ST7735_FillScreen(ST7735_BLACK);

    for(int x = 0; x < ST7735_WIDTH; x++) {
        ST7735_DrawPixel(x, 0, ST7735_RED);
        ST7735_DrawPixel(x, ST7735_HEIGHT-1, ST7735_RED);
    }

    for(int y = 0; y < ST7735_HEIGHT; y++) {
        ST7735_DrawPixel(0, y, ST7735_RED);
        ST7735_DrawPixel(ST7735_WIDTH-1, y, ST7735_RED);
    }

    HAL_Delay(3000);

    // Check fonts
    ST7735_FillScreen(ST7735_BLACK);
    ST7735_WriteString(0, 0, "Font_7x10", Font_7x10,
                       ST7735_RED, ST7735_BLACK);
    ST7735_WriteString(0, 3*10, "Font_11x18", Font_11x18,
                       ST7735_GREEN, ST7735_BLACK);
    ST7735_WriteString(0, 3*10+3*18, "Font_16x26", Font_16x26,
                       ST7735_BLUE, ST7735_BLACK);
    HAL_Delay(2000);

    // Check color inversion
    ST7735_InvertColors(true);
    HAL_Delay(2000);
    ST7735_InvertColors(false);
    HAL_Delay(2000);

    // Display test image
    ST7735_DrawImage(0, 0, ST7735_WIDTH, ST7735_HEIGHT,
                     (uint16_t*)test_img_128x128);
    HAL_Delay(15000);
}

Пример вывода текста на дисплей с диагональю 1.44":

STM32 и ST7735: пример вывода текста

Интересно, что если в случае SSD1306 можно было спокойно хранить всю картинку в памяти и каждый раз передавать ее дисплею целиком, то в случае с ST7735 этого уже так просто сделать нельзя. Тут и цвет 16-и битный, а не монохромный, да и разрешение в 2-2.5 раза выше. Чтобы хранить картинку в памяти микроконтроллера, понадобится 32 Кб для дисплея 128x128 и 40 Кб для дисплея 128x160. Тем временем, какой-нибудь STM32F103C8T6, используемый в Blue Pill, имеет всего 20 Кб RAM. Конечно, используемый в Nucleo-F411RE микроконтроллер STM32F411RET6 имеет уже 128 Кб RAM. Но мне хотелось бы, чтобы библиотека работала на любом микроконтроллере, и желательно без хаков типа использования 4-х битной палитры, уменьшения разрешения или использования индексированных цветов. Поэтому, в отличие от библиотеки для SSD1306, библиотека для ST7735 сразу отправляет все данные дисплею и ничего не хранит в памяти.

В первом приближении, это все, о чем я хотел сегодня рассказать. Желающие ознакомиться с полной версией кода к этой заметке могут найти его на GitHub. Код работает с дисплеями, имеющими разрешение 80x160, 128x128 и 128x160 пикселей, требуется лишь немного подправить файл st7735.h в соответствии с комментариями в нем. Наиболее же полную информацию о ST7735 вы найдете в даташите [PDF].

А доводилось ли вам использовать подобные модули, и если да, то в каких проектах?

Дополнение: Вас также может заинтересовать статья Использование TFT-дисплеев на базе ILI9341 с тачскрином.

Метки: , .

Понравился пост? Узнайте, как можно поддержать развитие этого блога.

Также подпишитесь на RSS, Google+, Facebook, ВКонтакте или Twitter.