Подключение дисплея от планшета к stm32

Обновлено: 04.07.2024

Завалялся у меня TFT LCD дисплей 2.4 дюйма с сенсорным экраном. Никакой информации о нем я не знал. На модуле есть слот для sd карты, стабилизатор напряжения и пара LVC245A.

На дисплее есть резистивный сенсор. Управление по 4 проводам. Выводы подключены к выводам передачи данных. X+ на LCD_D1, X- на LCD_CS, Y+ на LCD_RS и Y- на LCD_D0. Я так понимаю этот модуль рассчитан на Arduino Uno. Это совершено не удобно, нужно использовать ногу где есть АЦП. А еще это большая потеря скорости отдачи данных на дисплей.

Я отпаял шлейф сенсора и подключил отдельно, но об этом в отдельной статье.

На МК STM32F103C8T6 нет FSMC. Я советую использовать подряд идущие пины одного порта, например A0-A7 для передачи данных на экран. Можно и не с нулевого пина, просто при записи делать побитовый сдвиг.

В моем случае я сделал плату для соединения экрана и модуля с МК STM32F103C8T6 по этому подключение будет таким.

То есть B8 - B15 я подключил к портам LCD_D0 - LCD_D7. Выводы RST, CS, RS, WR я подключил к A9-A12. Вывод RD я не использую, так как читать с экрана данные мне не нужно, по этому я подключил его к питанию 3.3В.

Команды, которые нам пригодятся для теста дисплея

Вспомогательные функции для задержки

Обмен данными с дисплеем на базе ILI9341 и инициализация

Таблица настроек вывода пикселя

Функции вывода пикселей на экран

Команда CASET (Column Address Set) - принимает два байта, номер первого пикселя в строке, по оси X и два байта номер последнего.

Команда PASET (Page Address Set) аналогично принимает принимает по Y.

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

Команда RAMWR это запись наших пикселей. Если взять ширину 5 пикселей а высоту 2, то всего нужно будет передать 2 * 5 = 10 пикселей по 2 байта.

Некоторые цвета для теста

Или можно воспользоваться макросом для вычисления цвета из RGB

Остается настроить тактовую частоту и порты на вывод с подтяжкой к питанию

И можно что то выводить на дисплей

ILI9341 вывод с STM32

Комментарии к статье: Подключение сенсорного дисплея на ILI9341 (R61520, SPFD5408) к STM32

Здравствуйте! Интересно, но жалко не описана работа с резистивной панелью. Планируется ли в дальнейшем?

Я пользовался двумя статьями с easyelectronics.ru. Первая описывает как опрашивать экран, называется "Работа с резистивным сенсорным экраном". Вторая как откалибровать "Освоение STM32F103VE + TFT LCD + touch screen (часть 4)".

Сайт не дает ссылки запостить, по этому пишу названия. В гуле по названиям и имени сайта они легко находятся.

Достался мне нерабочий телевизор PE-1180. Это ни чем не примечательный китайский переносной телевизор с разрешением экрана 800x480 пикселей и диагональю 11.5 дюйма. Телевизор не показывал аналоговое видео (из эфира и с композитного входа) и у него было что-то с питанием — при подаче напряжения на него с лабораторного блока питания, срабатывала защита по току — 3А, и телевизор не включался. При питании от штатного блока питания телевизор работал нормально — и блок питания даже особо и не грелся, видимо, такой бросок тока давали какие-то переходные процессы. Что это было — особенности китайской схемотехники или проблема телевизора — непонятно. К телевизору можно было подключить источник VGA сигнала, правда необычное разрешение не очень-то хорошо сказывалось на картинке.

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

Как подключить модуль LCD с большим разрешением и без видеопамяти к контроллеру — под катом.


Ниже приведена временная диаграмма сигналов для LZ9JG17: clk — тактовый, HSY — горизонтальной синхронизации, DATA — данных.

По какой-то причине в реальности 216 тактовых импульсов отсчитывались от 2 фронта импульса HSY. Для правильной работы индикатора нужно выводить данные о всех 800 видимых пикселях — иначе нормального изображения не добиться.

Для хранения одного кадка потребуется (800*480)/8 = 48000 байт. К сожалению, у контроллера STM32-DISCOVERY всего 8 килобайт ОЗУ. Поэтому на DISCOVERY возможно формировать только текст. При размере знакоместа 8x16 на экране можно расположить 100x30 знакомест — то потребует 3000 байт ОЗУ.

По поводу подключения в модулю. Модуль LCD соединялся с основной платой телевизора через 40-пиновй шлейф, к выводам которого не подпаятся. Однако рядом с разъемом шлейфа на плате модуля имеются круглые тестовые площадки достаточно большого размера, чтобы к ним можно было припаять провод. Поскольку даташит на контроллер LCD у меня был, то прозвонив мультиметром выводы LZ9JG17 и тестовые площадки, я определил назначение тестовых площадок модуля. С цепями питания еще проще — при подключенной плате телевизора измерил напряжения на наиболее толстых дорожках рядом с разъемом, и определил, что где. Модуль требует 5В 0.1А для питания основных цепей и 12В 0.8А для инвертора. Напряжение сигналов, подаваемых на модуль — 3.3В, так как LZ9JG17 питается именно от 3.3 В. Источник 3.3 вольт есть на самом модуле. Сигнал управления на инвертор подается с еще одного вывода разъема, для того включить подсветку, нужно подать на него 3.3В.

image

Фотография участка модуля LCD с распиновкой.

Подписи соответствуют подписям выводов LZ9JG17 в даташите. Площадки HENAB, HRVC, HRVC остаются не подключенными.
Что интересно, диод на плате установили китайцы — когда я в первый раз разбирал телевизор, отверстия под винты были закрыты заводской наклейкой.
Вывод VSY модуля у меня соединяется с выводом PC5 платы DISCOVERY, HSY — с PC4, clk — с PA5(SPI_SCK), запараллеленные входы данных — с PA7(SPI_MOSI).
Опыт показал, что вывод clk нужно соединить с землей через конденсатор 18пФ, иначе возникают артефакты изображения.


Фотография конструкции в сборе:


Фотография готового устройства:

Работает LCD модуль стабильно, мерцание экрана не заметно. В принципе, подобным образом к DISCOVERY можно подключить любой экран с RGB интерфейсом, лишь бы был даташит на контроллер LCD и индикатор мог работать на соответствующей частоте развертки(у меня 17 Гц).

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

Дисплей выглядит следующим образом

Подключим к нашей отладочной плате только нижние следующим образом


Вот соответствие ножек на плате и на дисплее

Питание мы подаём 5 вольт, так как на плате модуля есть стабилизатор, а подсветку питаем 3-мя вольтами. С шиной SPI мы работаем постоянно, причём на контроллере STM32F1 мы её прошли вдоль и поперёк, применяя различные библиотеки. Думаю, F4 также не станет исключением и мы с ним также будем работать не только с применением библиотеки HAL, но так как с данной библиотекой мы уже работали с шиной SPI на контроллере STM32F4, то, думаю, нам следует пока её и использовать, чтобы не наделать ошибок.

Запустим проектогенератор Cube MX и создадим новый проект, выбрав соответствующий контроллер


Включим кварцевый резонатор



Настроим тактирование в Closk Configuration


Включим SPI1 и настроим пока небольшую скорость, чтобы не думать, что ошибки из-за слишком высокой скорости, потом прибавим


У нас включились 3 основные ножки нашей шины. Включим на выход остальные ножки, которые присутствуют в нашей таблице


Добавим нашим ножкам скорости


Включим RNG для лучшего формирования случайных чисел


Немного перестроим частоты в Clock Configuration, чтобы получить вот тут 48, только так, чтобы основная частота осталась максимальной


Присвоим имя проекту, выберем среду разработки и удвоим размер стека и кучи


Сгенерируем проект и откроем его в Keil.


По SPI данные передаются байтами бит за битом, это обычный способ передачи данных, такой же как у USART'а или I2C. То есть для передачи одного бита нужен один условный такт, соответственно чтоб передать байт нужно сделать восемь тактов. В отличии от SPI, параллельная шина может «закидавать» в дисплей все восемь бит за один условный такт, где каждый бит «летит» по своему проводу. Такой способ подразумевает более высокую скорость передачи данных, но требует значительно большее количество пинов микроконтроллера.

Обычно эти дисплеи оснащаются ещё и тач-панелью, которая тоже работает по SPI. За работу «тача» отвечает отдельный чип, чаще всего это что-то вроде TSC2046 (буквы могут быть другие). И вот тут есть интересный момент.

Суть заключается в том, что в дисплей нам нужно передавать данные как можно быстрее (чтоб картинка быстрее отрисовывалась) , поэтому мы указываем максимально возможную скорость SPI (для BluePill это 18Мбит/с, какую максимальную скорость поддерживает сам дисплей я не знаю, вроде как до 50Мбит/с) . Тач-панель мы можем подключить к этому же SPI, а пинами CS (Chip Select) выбирать с чем мы сейчас работаем, с дисплеем или «тачем». Однако драйвер «тача» корректно работает только на скорости 1-2Мбит/с, если указать больше, то начинаются проблемы. Соответственно, если мы подключаем дисплей и «тач» к одному SPI, то нам нужно будет переключать скорость SPI прямо «на лету».

Другой вариант, это подключить дисплей и «тач» на разные SPI. С одной стороны кажется что так проще, но с другой, вам может понадобится флешка (подразумевается отдельный чип флеш-памяти) или SD карта для хранения изображений, и обе эти штуки тоже работают по SPI, а у BLuePill всего два таких интерфейса.

Чтоб было понятно, картинка в 16-ти битном цвете, размером во весь экран (320х240) весит 153600 байта. То есть ни о каком хранении её внутри простенького микроконтроллера и речи быть не может.

Следуя из выше сказанного, лучше всего подключить дисплей и «тач» на один SPI, так как они прекрасно уживаются вместе и не мешают друг другу при правильной организации программы, а второй SPI оставить для хранилища (флешка или SD карта), или ещё для чего-то.


Теперь к вопросу почему не стоит вешать на один SPI дисплей и хранилище. Дело в том, что процесс вычитывания с флешки/карты и вывод на дисплей занимают определённое время. Считывать данные из хранилища нужно по кусочкам, создав промежуточный буфер, например 10Кб (при условии что у BluePill всего 20Кб RAM, и какая-то часть из них нужна самой программе) , и сначала вычитывать в этот буфер данные, а потом отправлять его в дисплей. То есть со всеми накладными расходами, картинка на весь экран будет выводиться около 200мс, а это как вы понимаете 5 FPS

Исходя из выше сказанного, чтоб вывести данные хранящиеся в SPI-флешке на экран как можно быстрее, нужно во-первых, подключать хранилище и дисплей к разным интерфейсам, и во-вторых использовать DMA таким образом чтоб данные одновременно читались из флешки и отправлялись в дисплей. То есть заполняем половину буфера и начинаем её отправлять, пока первая половина отправляется, вторая заполняется, и т.д. В результате мы сможем получить максимально возможную скорость — картинка на весь экран будет выводится за

70 мс, что равно 14 FPS. Это относится к SPI-флешке, работа с SD-картой будет происходить дольше так как там используется FATFS, которая требует дополнительного времени.

Расчёты для работы с SPI-флешкой: при скорости SPI 18Мбит/с, за 1 мс можно передать 2250 байт, соответственно 153600 байт передастся за 68 мс (153600 / 2250 = 68), плюс 2 мс на накладные расходы. Ну и 1000мс / 70мс = 14 FPS.

Если картинка не на весь экран, то естественно она будет считываться и выводится быстрее, а значит мы получим больше FPS. Вот пример…


Зацикленный вывод тринадцати картинок размером 200х214. Каждая картинка выводится за 41 мс, то есть 24 FPS. Размер экрана 2.4".


Работа с флешкой или SD-картой будет описана не в этой статье, а в следующей. Всё выше сказанное сделано для того, чтоб объяснить как и почему нужно использовать интерфейсы SPI.

Подключение

У всех дисплеи разные поэтому перечислю те контакты дисплея, к которым будем подключаться. SPI (MOSI, MISO, CLK). CS (Chip Select) подача низкого уровня на этот контакт говорит о том, что мы собираемся работать с дисплеем (к шине SPI можно подключать множество устройств, и у каждого из них есть свой CS с помощью которого мы выбираем с кем собираемся общаться) . DC подача низкого уровня на этот контакт говорит о том, что мы собираемся отправлять в дисплей команду, а подача высокого уровня означает что мы будем слать данные. RST (RESET) сброс дисплея (всё это библиотека делает сама).

Помимо этого нужно конечно же подать питание, и включить подсветку. Подсветку можно сделать постоянную, а можно организовать регулируемую, через транзистор с помощью таймера в режиме ШИМ. Питание и подсветку я подавал от отдельного источника питания, то есть не от самой платы.


Тачскрин будем подключать к тому же SPI что и дисплей. У него есть свой CS, и контакт (называется что-то типа IRQ) на котором появляется низкий уровень во время нажатия. Этот контакт мы будем опрашивать в бесконечном цикле.

Конфигурация



Устанавливаем максимальную скорость SPI (18.0 MBits/s). Это для системной частоты 72Мгц. Если ваш камень допускает большую частоту, то можно попробовать увеличить скорость SPI, но сначала протестируйте на этой.


Если у вас нет тачскрина, тогда TOUCH_CS и IRQ не нужны. Пин IRQ настраиваем как GPIO_Input , остальные (кроме SPI) как GPIO_Output . Эти контакты можете назначить на удобные вам ножки, а названия нужно дать такие как нарисовано, тогда в библиотеке ничего не надо будет менять.

Названия вводятся в разделе GPIO…



User Label


И настраиваем USART для вывода инфы.

Подключаем всё, и если подсветка работает то переходим к кодингу.

Программа

Скачиваем библиотеку и добавляем в проект файлы:

ILI9341_GFX.c
xpt2046_touch.c
fonts.c

ILI9341_GFX.h
xpt2046_touch.h
fonts.h
img.h

Если тача нет, то xpt2046_touch.c и xpt2046_touch.h не нужны.

Номер SPI и пины задефайнены в файлах ILI9341_GFX.h и xpt2046_touch.h .


В main.c инклюдим…


Далее перед бесконечным циклом добавляем следующий код…


Всё это есть в примере, но тем не менее продублирую.

Здесь перечислены и прокомментированы все пользовательские функции, которые нужны для вывода текста, различных фигур, и картинок из внутренней памяти микроконтроллера.


Условимся что сейчас будем пользоваться экраном в горизонтальном положении. Если будете использовать экран в вертикальном положении, тогда нужно поменять местами значения ширины и высоты в файле ILI9341_GFX.h …

В этом же файле задефайнены другие цвета, и указан ресурс для создания своих.


В самом начале программы мы задали ориентацию зкрана…


Аргумент этой функции указывает от какого угла ведётся отсчёт начальных координат. В примере, в самом конце, это демонстрируется — начальные координаты у картинки и текста везде одинаковые, а угол от которого они начинаются меняется.



Собственно можно прошивать и смотреть что получилось. Если дошло до моего логотипа значит всё

Теперь по поводу функции вывода картинок из массива…

Она используется для вывода картинок хранящихся во внутренней памяти МК в виде массива. В частности мой логотип лежит в файле img.h и «весит» 12800 байт. Если вам не нужно много больших картинок, то можно создать их столько, сколько позволит объём флеш-памяти микроконтроллера, и обойтись без внешнего хранилища.

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


Конвертер

Чтобы из ваших картинок создавать массивы правильного формата, можно воспользоваться специальным онлайн-ресурсом…




Выберите файл, укажите Code format и Used for как на иллюстрации, и нажмите кнопку Get C string . Полученный массив вставьте в файл img.h вместо моего, впишите в функцию ширину и высоту вашего рисунка и пробуйте. Ширина и высота изображения будет написана там же на ресурсе, внизу.

Чтоб прикинуть размер будущей картинки, умножьте ширину на высоту, и умножьте это на 2 (картинка 16-ти битная, то есть на каждый пиксель два байта). На примере моего логотипа это выглядит так: 80 * 80 * 2 = 12800 байт.

Другие изображения можно так же добавлять в этот файл, только имена массивов меняйте.


Там же внизу можно скачать файл в бинарном виде — Download data will save the converted data as binary file. Этот файл можно использовать при работе с SD картой.


На тот случай, если ресурс окажется не рабочим, я создал свой онлайн-конвертер. Работает очень просто — выбираете файл, загружаете, и если всё хорошо, через несколько секунд (нужно время на обработку) появится кнопка «скачать». В скаченом архиве будут лежать бинарник, и заголовочный файл с массивом.

Так же в библиотеке есть папка Converter, в которой лежит PHP-скрипт для использования на своём компе (понадобиться установленный php7+). Пример использования…


После выполнения появятся описанные выше файлы.


Тачскрин

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


А в цикл добавляем вот такой код…

Когда условие «если нажат тачскрин» сработает (пин IRQ выдаст низкий уровень), SPI переключится на меньшую скорость, функция ILI9341_TouchGetCoordinates(&x, &y) прочитает данные полученные от тачскрина, после чего SPI вернётся к изначальной скорости.

Важно! Если у вас какой-то другой камень, то посмотрите в Кубе какие нужно прописать делители — SPI_BAUDRATEPRESCALER_х .

Теперь можно прошить и потыкать в экран.


В USART и на экран будут выведены координаты нажатия. Это отладочная инфа, в дальнейшем её можно закомментировать. Там же приведён пример как пользоваться этими координатами. То есть, допустим вы рисуете какую-то кнопку на экране, тыкаете по углам этой кнопки, получаете координаты области, и прописываете их в условии — «если больше и меньше по Х, и больше и меньше по Y, тогда что-то делаем».

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

Ну и «удержание кнопки» говорит само за себя, время удержания можно установить какое нужно. При этом ничего в цикле не тормозится. Короткое нажатие на эту кнопку ничего не делает. То есть это сделано не для того чтоб было два варианта действий на одной кнопке, а для защиты от случайного нажатия, вдруг у вас эта кнопка что-то типа "АЗ-5".


Сам по себе тачскрин в связке с чипом XPT2046 является банальным АЦП, и при нажатии выдаёт два 16-ти битных значения (по горизонтали и по вертикали), которые преобразовываются в функции ILI9341_TouchGetCoordinates() в координаты с помощью математических вычислений. В этой функции есть закомментированные строки, которые показывают эти значения. Получая их можно более точно определить края экрана (тачскрины то у всех разные) потыкав и подкорректировав цифры в файле xpt2046_touch.c …


Опытным путём разберётесь какое значение к какому краю относится. У себя я их округлил. Можно было бы сделать калибровку с крестиками по углам, но мне это делать не охота, да и необходимости особой нет.


На этом наверно пока всё, как говорилось выше, в следующей части речь пойдёт о хранении больших картинок на внешнем носителе и выводе их с помощью DMA.



Всем спасибо

Читайте также: