Ili9341 datasheet на русском

Использование TFT-дисплеев на базе ILI9341 с тачскрином

Некоторое время назад мы познакомились с цветными TFT-дисплеями на базе контроллера ST7735. Дисплеи эти весьма неплохи, но не лишены недостатков. В частности, максимальная диагональ таких дисплеев составляет 1.8 дюйма, а разрешение ограничено 128 x 160 пикселями, что может подходить не для всех проектов. Поэтому сегодня речь пойдет о TFT-дисплеях на базе ILI9341, имеющих среди прочих преимуществ больший размер и большее разрешение.

Такие дисплеи, как правило, продаются в виде модулей с SPI-интерфейсом, имеющих разъем для SD-карт (полноразмерных, не MicroSD). На eBay можно найти модули с диагональю 2.2", 2.4" и 2.8". Независимо от диагонали, дисплеи на базе ILI9341 всегда имеют разрешение 240 x 320. Большинство модулей имеют резистивную сенсорную панель, но также встречаются модули и без нее. Наличие или отсутствие тачскрина несложно определить чисто визуально. Цена одного модуля составляет 7-9$.

Fun fact! На eBay также доступны модули на базе ILI9341 с диагональю 3.2". Но в рамках данной статьи они не рассматриваются. Эти модули, как правило, не разведены для работы по SPI, и я не думаю, что буду использовать их в своих проектах. Дело в том, что цена на такие модули довольно высока (16$ или больше), а разница между 2.8" и 3.2" не настолько значительна.

Протокол, по которому работают данные модули, идентичен протоколу ST7735. Отличается только последовательность команд, которую необходимо выполнить для инициализации дисплея. Я лично подглядел эту последовательность в репозитории martnak/STM32-ILI9341. Кстати, на вид это довольно адекватная библиотека для STM32 на базе HAL. Что, впрочем, не помешало мне сделать собственную библиотеку с интерфейсом, аналогичным интерфейсу моей библиотеки для ST7735. Для Arduino же можно рекомендовать библиотеку Adafruit_ILI9341, а также связку UTFT и URTouch. Библиотека от Adafruit работает заметно быстрее UTFT. Но в отличие от UTFT, она написана специально для модулей производства Adafruit и, похоже, несовместима с тачскринами китайских модулей.

Важно! ILI9341 не понимает 5-и вольтовую логику от слова совсем. Если вы собираетесь использовать его из 5-и вольтовой Arduino, используйте ковертер уровня, например на базе TXS0108E (даташит [PDF]). Иначе ничего не будет работать. STM32 использует 3.3 вольтовую логику, поэтому для этих микроконтроллеров ковертер уровня не нужен.

Контроллер тачскрина на имеющихся у меня модулях имеет маркировку HR2046. Он также работает по SPI, притом протокол весьма незамысловат. Есть две команды — read X (0 x D0) и read Y (0 x 90). В ответ на каждую команду контроллер посылает 16 бит (число от 0 до 65535), соответствующих координате X или Y точки, которую нажал пользователь. Нажат ли сейчас тачскрин можно определить по состоянию дополнительного пина IRQ. Если он имеет низкое напряжение, значит тачскрин нажат, иначе пользователь его не касается.

Весь код работы с сенсорной панелью выглядит так:

#define READ_X 0xD0
#define READ_Y 0x90

static void ILI9341_TouchSelect ( ) <
HAL_GPIO_WritePin ( ILI9341_TOUCH_CS_GPIO_Port ,
ILI9341_TOUCH_CS_Pin , GPIO_PIN_RESET ) ;
>

void ILI9341_TouchUnselect ( ) <
HAL_GPIO_WritePin ( ILI9341_TOUCH_CS_GPIO_Port ,
ILI9341_TOUCH_CS_Pin , GPIO_PIN_SET ) ;
>

bool ILI9341_TouchPressed ( ) <
return HAL_GPIO_ReadPin ( ILI9341_TOUCH_IRQ_GPIO_Port ,
ILI9341_TOUCH_IRQ_Pin ) == GPIO_PIN_RESET ;
>

bool ILI9341_TouchGetCoordinates ( uint16_t * x , uint16_t * y ) <
static const uint8_t cmd_read_x [ ] = < READ_X >;
static const uint8_t cmd_read_y [ ] = < READ_Y >;
static const uint8_t zeroes_tx [ ] = < 0x00 , 0x00 >;

uint32_t avg_x = 0 ;
uint32_t avg_y = 0 ;
uint8_t nsamples = 0 ;
for ( uint8_t i = 0 ; i 16 ; i ++ ) <
if ( ! ILI9341_TouchPressed ( ) )
break ;

Читайте также:  Как пользоваться программой fraps

avg_x += ( ( ( uint16_t ) x_raw [ 0 ] ) 8 ) | ( ( uint16_t ) x_raw [ 1 ] ) ;
avg_y += ( ( ( uint16_t ) y_raw [ 0 ] ) 8 ) | ( ( uint16_t ) y_raw [ 1 ] ) ;
>

if ( nsamples 16 )
return false ;

uint32_t raw_x = ( avg_x / 16 ) ;
if ( raw_x ILI9341_TOUCH_MIN_RAW_X )
raw_x = ILI9341_TOUCH_MIN_RAW_X ;
if ( raw_x > ILI9341_TOUCH_MAX_RAW_X )
raw_x = ILI9341_TOUCH_MAX_RAW_X ;

uint32_t raw_y = ( avg_y / 16 ) ;
if ( raw_y ILI9341_TOUCH_MIN_RAW_X )
raw_y = ILI9341_TOUCH_MIN_RAW_Y ;
if ( raw_y > ILI9341_TOUCH_MAX_RAW_Y )
raw_y = ILI9341_TOUCH_MAX_RAW_Y ;

// Uncomment this line to calibrate touchscreen:
// UART_Printf("raw_x = %d, raw_y = %d
", x, y);

* x = ( raw_x — ILI9341_TOUCH_MIN_RAW_X ) * ILI9341_TOUCH_SCALE_X /
( ILI9341_TOUCH_MAX_RAW_X — ILI9341_TOUCH_MIN_RAW_X ) ;
* y = ( raw_y — ILI9341_TOUCH_MIN_RAW_Y ) * ILI9341_TOUCH_SCALE_Y /
( ILI9341_TOUCH_MAX_RAW_Y — ILI9341_TOUCH_MIN_RAW_Y ) ;

При использовании тачскрина следует учитывать, что для его надежной работы частота SPI-шины не должна превышать 1 МГц, иначе координаты начинают приходить с существенными искажениями. Но с дисплеем по тому же SPI вы наверняка захотите работать на большей частоте. Одно из решений заключается в том, чтобы использовать отдельные SPI-шины. Если же этого хочется избежать, частоту шины можно менять динамически:

hspi1. Init . BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2 ;

/* . Пропущено: тут проходит тест дисплея,
полностью аналогичный тесту ST7735 . */

hspi1. Init . BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128 ;

int npoints = 0 ;
while ( npoints 10000 ) <
uint16_t x , y ;

Приведенный отрывок кода превращает дисплей в рисовалку:

У меня не нашлось стилуса, поэтому вместо него я использовал ручку, тем концом, где нет стержня. К сожалению, работать с такими сенсорными панелями при помощи пальца крайне затруднительно. Точность определения координат в этом случае ни на что не годится.

Кстати, вы можете заметить, что на приведенном фото я использовал тот же шилд, что был использован для ST7735. Это возможно благодаря тому, что модули на базе ST7735 и ILI9341 имеют совместимое расположение пинов. Понадобилось только добавить на шилд дополнительные гнезда для тачскрина. Правда, из-за нехватки места на плате, гнезда мне пришлось повесить в воздухе, зафиксировав их термоклеем.

Полную версию исходников к этой заметке вы найдете на GitHub. Наиболее же полную информацию об ILI9341 можно найти в его даташите [PDF].

А доводилось ли вам использовать ILI9341 и если да, то в каких проектах? Интересно также, что вам нравится больше — использовать стилус с тачскрином, или же обычные кнопки, потенциометры и роторные энкодеры?

Около года тому назад на сайте появилась статья о том как инициализировать TFT дисплей, под управлением SSD1289, а где-то около месяца назад мне написал один из посетителей сайта. Суть письма была в том, что он заказал дисплей по указанной в статье ссылке, но запустить его не получалось и он предложил этот дисплей и ещё несколько других выслать мне, а я, в свою очередь, должен буду выложить код если получится их запустить.

Как оказалось, дисплей, который ему прислали с али управляется драйвером ILI9341, об этом помогла догадаться надпись на нём.
Верхний дисплей с драйвером SSD1289, нижний с ILI9341.

Поэтому как только ко мне пришёл этот дисплей, сразу начал изучать даташит на ILI9341.

В отличие от SSD1289 у ILI9341 нет регистров, для общения с ним используются команды. Сначала посылаешь команду, а затем набор параметров, то есть после того, как мы послали команду дисплей уже ждёт, что ему передадут параметры. Также надо сказать, что общение с дисплеем осуществляется по одному из двух протоколов: интеловский i8080 и мотороловский M6800, чем они отличаются описывал тут, не стал изменять традициям и выбрал i8080. У этого протокола существует две реализации, для получения более подробной информации можно почитать даташит.

Читайте также:  Совкомбанк elf sovcombank ru

Первым делом необходимо реализовать низкоуровневые функции и тут важно понять, что мы правильно понимаем даташит. Самый простой способ проверить, правильность понимания даташита, это считать какую-то информацию с дисплея, например, его ID.

Тогда функция, для отправки команды, будет выглядеть так.

А функция которая читает данные так.

Из скриншотов понятно, что описанные выше функции заработали, осталось реализовать отправку данных по 8 и 16 бит. Дело в том, что в основном при отправке данных используются только младшие 8 бит шины, но, например, при записи данных в память дисплея используются все 16 бит.

Всё, мы реализовали все необходимые низкоуровневые функции, теперь можно переходить непосредственно к инициализации. Перед тем как перейти к инициализации, надо сказать, что напряжение питания ЖК-ячейки постоянно изменяет свою полярность, сделано это для того, чтобы избежать явлений гидролиза и диссоциации сложных органических соединений, из которых состоит жидкокристаллический материал.

В функции инициализации есть функция TFT_SetOrientation(2), которая определяет порядок отрисовки, MAX_X и MAX_Y объявлены глобально и инициализированы нулями.

А вот как выглядит дисплей после инициализации.

Таким образом, можно сделать одну отладочную плату для обоих дисплеев и ещё важно чтобы напряжение питания МК и дисплея было 3.3 вольта, при питании от 5 вольт дисплей не запустился. Подсветку дисплея тоже запитал от 3.3 вольта через резистор 47Ом.

А на этом всё, в следующей статье мы рассмотрим как выводить символы на дисплей и там же будет проект для Atmega16 в AtmelStudio6.2.

Для желающих быстро проверить работает ли их дисплей, оставляю тут прошивку для Atmega16, которая заливает дисплей разными цветами ili9341_i8080.hex [3,54 Kb] (cкачиваний: 401) .

Наконец добрались руки до дисплея. Купил его еще летом, а вот нормально заняться им вышло только сейчас.
Дисплеев на сегодняшний день огромное количество, мой выбор пал на решение "лоу кост". Такой дисплей у китайских друзей стоит в 5-6$.

В общем то ничего необычного. Разрешение всего 320х240, но зато он цветной и с последовательным интерфейсом, что нещадно бъет по скорости обновления, но сильно экономит ноги контролера (и нервы при соединении на макетке).
У данного дисплея есть контролер для обработки команд и обмена с микроконтроллером — ILI9341.

Из него можно узнать набор команд и последовательность данных.
У дисплея есть ноги для обмена по SPI, нога аппаратного сброса и нога, которая помогает ему понять когда принятые данные — команда, а когда — цвет пикселя.
Для всего этого "дергонога" я решил заготовить макросы:

Этап настройки SPI я пожалуй пропущу. Тут все стандартно, берем настройку из любого примера для вашего камня в режиме 1 (CPOL = 0, CPHA = 0).
Когда это готово, делаем функции посылки команды и данных:

Тут тоже все просто. Для посылки команды нужно чтобы линия DC была в 0, а для данных — в 1. С линией CS поступаем как всегда. Можно в принципе возложить эту ответственность на железо, если ваш контроллер такое умеет.

Инициализация дисплея

Прежде чем рисовать картинки нужно настроить дисплей. Для этого необходимо дернуть ногу аппаратного ресета дисплея, заслать команду сброса и подождать, после чего отправить пачку команд, расписывать их смысла нет, если интересно идем в документ по ссылке выше.

Рисуем

Чтобы что-то нарисовать нужно поставить курсор в нужное место:

Читайте также:  Производство литий ионных батарей

Курсор поставили, можно рисовать:

Ради интереса можно сохранить картинку в GIMP в формат C файла. И подтянуть его в проект. Там вы увидите массив. Его нужно просто вывести в цикле на экран.

Шрифты

Вывести текст на экрна тоже очень просто. Нужно раздобыть массив со шрифтом. Сделать это можно при помощи утилиты BitFontCreator (она крутая, но денег немереных стоит) или же народной приблуды FontsGenerator.exe, которую я нашел на easyelectronics.
В массиве аккуратно уложены битовые маски шрифта. Например имеем 16 пикселей высотой и 8 шириной, тогда нам нужно последовательно выводить по пикселю и при этом следить каким цветом нужно сделать текущий пиксель.

Для описанного примера в массиве будет 16 двухбайтных чисел. В каждом из чисел информация о том, закрашен данный пиксель или нет.
BitFontCreator генерит таблици с шрифтами различной ширины. Т.е. каждая буква имеет различную ширину, это повышает читаемость и делает текст красивее для глаза, но принуждает нас каждый раз смотреть ширину в массиве.

Строку можно выводить обычным методом при помощи указателей:

Получается вот так:

Комментарии

Какая частота полной заливки экрана в секунду у вас получилась?

Не замерял. Но медленно. Порядка двух секунд идет полная перерисовка. Но с частотой не играл.

Я думаю тормоза могут быть вызваны тем, что вы наверное делаете заливку с помощью функции LCD_DrawPixel.
Т.е. каждый пиксел в цикле рисуете с ее помощью. И тут получается Нереально много команд, 10 шт только на установку курсора для каждого пикселя. на самом деле если нужно выводить картинку (или большие цифры в виде картинок) или залитый прямоугольник, то его можно выводить построчно, например слева направо, команды
LCD_SetCursorPosition(x, y, x+(размер картинки по Х), y+1) и LCD_SendCommand(ILI9341_GRAM) даете в начале каждой строки, а потом в цикле рисуете строку двумя командами:
LCD_SendData(color >> 8);
LCD_SendData(color & 0xFF);
у дисплея автоинкремент.
переменную color меняете цветом пикселя из картинки. Скорость увеличивается в разы.
вот например переделанная заливка прямоугольника из этой библиотеки как у вас, процесса заливки не заметно теперь вообще:
void LCD_DrawFilledRectangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color)
< uint16_t i = 0;
uint16_t j = 0;
ILI9341_x = x0;
ILI9341_y = y0;
for(i=0;i > 8);
ILI9341_SendData(color & 0xFF);
>
>
>
Я ещё себе сделал шрифт 64х100, для электронного спидометра на этом экране, переписал все функции вывода картинок и шрифтов таким же образом, и все теперь летает, вывод цифр очень быстрый.) перерисовки не видно. Но у меня STM32F103, работает на 72 Мгц, SPI — 36 Мгц. Это в дебагере показывает так, сколько на самом деле — х.з. Разогнал его до 128 МГц, обновление цифр стало еще быстрее и плавное — хрен заметишь.)
Если интересно могу выслать исходники. zuidwest@bk.ru

Цикл все равно медленный (на мой вкус).
Тут http://blablacode.ru/node/513 настроил DMA. Вот только картинку конвертировать нужно иначе.

Имеет смысл поиграть. Я когда-то брал готовую библиотеку, на частоте SPI 18 МГц экран выдавал 6-7 кадров в секунду. Это уже вполне приемлемо для интерфейса какого-нибудь девайса. В целом, экран вполне годный для изделий, если не гонять на нем видео)

Согласен. Только вот пока задачу под него не подобрал.

Оставьте ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *