Подключение дисплея от планшета к stm32
Завалялся у меня 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 (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В.
Фотография участка модуля 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. За работу «тача» отвечает отдельный чип, чаще всего это что-то вроде 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.
Всем спасибо
Читайте также: