Горячее
Лучшее
Свежее
Подписки
Сообщества
Блоги
Эксперты
Войти
Забыли пароль?
или продолжите с
Создать аккаунт
Я хочу получать рассылки с лучшими постами за неделю
или
Восстановление пароля
Восстановление пароля
Получить код в Telegram
Войти с Яндекс ID Войти через VK ID
Создавая аккаунт, я соглашаюсь с правилами Пикабу и даю согласие на обработку персональных данных.
ПромокодыРаботаКурсыРекламаИгрыПополнение Steam
Пикабу Игры +1000 бесплатных онлайн игр Монстрикс — это динамичная стратегия, где ты собираешь, улучшаешь и сражаешься с могучими монстрами.

Монстрикс

Мидкорные, Стратегии, Мультиплеер

Играть

Топ прошлой недели

  • AlexKud AlexKud 38 постов
  • Animalrescueed Animalrescueed 36 постов
  • Oskanov Oskanov 7 постов
Посмотреть весь топ

Лучшие посты недели

Рассылка Пикабу: отправляем самые рейтинговые материалы за 7 дней 🔥

Нажимая кнопку «Подписаться на рассылку», я соглашаюсь с Правилами Пикабу и даю согласие на обработку персональных данных.

Спасибо, что подписались!
Пожалуйста, проверьте почту 😊

Помощь Кодекс Пикабу Команда Пикабу Моб. приложение
Правила соцсети О рекомендациях О компании
Промокоды Биг Гик Промокоды Lamoda Промокоды МВидео Промокоды Яндекс Директ Промокоды Отелло Промокоды Aroma Butik Промокоды Яндекс Путешествия Постила Футбол сегодня
0 просмотренных постов скрыто
Avenir.Sirgun
Avenir.Sirgun
4 месяца назад
Серия Linux

Linux и минимальный комфорт⁠⁠

выяснено экспериментально.

Система 64 бит .
минимальный комфорт :
проц 4ядра по 3ггц, озу 2шт по 4гб двухканал.
1 диск под систему, 4 диска -хранилище.
сетевая карта минимум 1шт 100мбит с поддержкой linux.
видюха под текущий linux kernel .
тишина важный фактор: пк должно быть не слышно вообще с расстояния 1 метр при 100%% нагрузке.
мыш клава моник -- лишь бы работали.
флопик 3,5 желательно.
компактный размер.
без иллюминаций.
Для дома, небольшой сети до 4х компов в такой конфигурации: 3д, программирование, музыка, видео(если видяху помощнее поставить вместо офисной), планирование, сервер почты файлов сообщений ....

Linux и минимальный комфорт Linux, Программирование, IT, Компьютерная помощь, Компьютерное железо, Программа, Python, Сборка компьютера, C++, Julia, Maxima, 3D моделирование, Paint, Arduino, Длиннопост

мате

Linux и минимальный комфорт Linux, Программирование, IT, Компьютерная помощь, Компьютерное железо, Программа, Python, Сборка компьютера, C++, Julia, Maxima, 3D моделирование, Paint, Arduino, Длиннопост

иксвсё4

линукс с хфсе, иногда пользую mate и
конфигурация как я уже написал. оно не реактивное, но ничо не виснет, могу 20 вкладок браузера открыть, рендерить 3д. и +2 очееди копирования .

Linux и минимальный комфорт Linux, Программирование, IT, Компьютерная помощь, Компьютерное железо, Программа, Python, Сборка компьютера, C++, Julia, Maxima, 3D моделирование, Paint, Arduino, Длиннопост

консоль без де. менеджер файлов ncdu


частоту меньше и 2 ядра, 4гб пробовал лично
на ноутбуке Ноутбук HP Compaq cq57 -- просмотр онлайн видео 1080р тормозит. обрабатывать небольшие документы, рисунки годится. NAS будет тормозить когда захочешь несколько очередей синхронизации запустить.

если кому-то интересно могут заглянуть в мой профиль, чтобы пообщаться персонально.

Показать полностью 3
Linux Программирование IT Компьютерная помощь Компьютерное железо Программа Python Сборка компьютера C++ Julia Maxima 3D моделирование Paint Arduino Длиннопост
11
577
Veveds
Veveds
4 месяца назад
Серия Мы очень старались и сделали :)

Мы очень старались и сделали :)⁠⁠

Друзья, супер (!), в первую очередь как педагог и руководитель проекта спешу вам сообщить, что наша команда стала призером в республиканском конкурсе на приз Главы Республики Мордовия. Ура-а-а 🏆 Мы это сделали!

Напомню, что примерно год тому назад, мы поставили перед собой цель: собрать программируемую консоль для школьников, студентов и просто гиков)) и мы её собрали!

1/7

Да, было трудно, создавали код с нуля (кроме ядра ESP32), переписывали, переписывали и снова переписывали код. Мы отказались от RTOS и придумали свой удобный планировщик задач. Отказались от графической библиотеки U8G2 и написали свою 😱 (ох уж эти даташиты на китайском 🙄). Изюминкой нашего проекта стал свой фреймворк exForm для создания графических форм, там есть практически всё, от кнопок до вывода 4 цветных изображений (градации серого), фреймы, линии, чекбоксы, текстовые блоки и многое другое 👍. Даже Desktop есть как на ПК, ну почти 🫣. Осталось совсем чуть-чуть: добавить виртуальную клавиатуру и рабочий эксплорер. Ах-да, совсем забыл, ещё мы добавили, а точнее сами написали и уже потом добавили в проект библиотеку для работы с последовательным портом UART, с расчётом CRC при передаче/приёме данных 💪. Макс из нашей команды разработал и внедрил в проект 3Д модель корпуса, делал всё через Компас, 11 деталей, не единого самореза или болта, все детали собираются практически как в Lego. А сколько пластика потратили на черновые варианты!

Ну а для чего все 🤷 это?

Дети, старшеклассники, будущие студенты таким образом знакомятся с it разработкой. В интересной и увлекательной форме изучают язык программирования C++. Это не сухая теория и не сухие задачки из ОГЭ, ЕГЭ, а реальная разработка it продукта. Руки только не доходили полностью вести DevOps.

В планах: добавить много новых функций, разработать единую плату, изменить дизайн корпуса) да много всего впереди!

Если бы вы только знали, как мы сильно испугались, когда чуть не потеряли важные изменения в репозитории) был какой-то глюк со стороны github. Я наверное, в то время, приобрел кучу седых волос))

Хочу сказать огромное спасибо всем ребятам за проделанную работу 🤝: Сяткина Катя, Орлов Максим, Ксенофонтов Сергей, Самойлов Михаил.

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

Показать полностью 7
[моё] Разработка Программирование C++ Arduino Школа Студенты Образование Учеба Курсы IT
80
229
monobogdan
monobogdan
Посты о ремонте и моддинге ретрогаджетов.
TECHNO BROTHER
1 год назад

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях!⁠⁠

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Пожалуй, немалая часть моих читателей так или иначе интересуется DIY-тематикой. И в различных самодельных девайсах порой есть необходимость вывести какую-либо информацию на дисплей, будь это текст, графики или даже какая-то анимация! Для разных задач существуют самые разные дисплеи и в сегодняшнем материале я хотел бы систематизировать и собрать подробнейший гайд об использовании дисплеев с нерабочих мобильных телефонов: какие бывают протоколы и шины данных, как читать схемы устройств и определять контроллеры дисплеев, какие дисплеи стандартизированы, а какие придётся реверсить самому и как быть с подсветкой. В практической части статьи мы подключим дисплей используя протокол MIPI DBI к RP2040 с использованием DMA. Интересно? Тогда добро пожаловать в статью!

❯ Виды дисплеев и их протоколы


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

Сейчас же всё гораздо проще и каждый любитель DIY-электроники может и сам подключить дисплейчик к своему проекту и использовать в необходимых ему целях. Ведь не зря написаны десятки библиотек по типу AdaFruit LCD, которые упрощают задачу программисту и дают ему возможность оперировать готовыми и простыми операциями по типу «вывести линию» или «отрисовать изображение». Однако, готовые библиотеки — это, конечно, здорово, но они не всегда дают понимание о том, как работают такие дисплеи на программном и аппаратном уровне. И первая часть статьи как раз и будет посвящена этому.

Всего в мире дисплейных матриц существует несколько общепринятых аппаратных протоколов. Некоторые из них можно легко использовать в собственных проектов с микроконтроллерами, с другими придется повозиться:

  • Параллельная шина 8080 — одна из самых простых и понятных шин данных, как в теории, так и на практике. Суть её очень простая: на каждый бит отводится по одной сигнальной линии, плюс две дополнительные линии для сообщения статуса передачи: RD означает запрос чтения, а WR — запрос на запись. Большинство дисплеев использует девятый, неявный бит D/C, который сообщает контроллеру, задаём ли мы номер команды, или уже пишем аргументы для этой команды. Что самое приятное — шина по сути стандартизирована и во многих дисплеях команды на старт записи в видеопамять, а также получение ID-контроллера идентичны. Шина бывает 8-битной и 16-битной (её состояние задаётся битами IM0..IM2 и используется не только для подключения дисплеев, но и микросхем параллельной флэш-памяти, ОЗУ и т. д. Такие шины используются в дисплеях с разрешением до 480x320.

  • SPI — шина, которая наверняка знакома большинству моих читателей. Достаточно простая — у нас есть две сигнальные линии с входным (MISO) и выходным (MOSI) битом, плюс сигнал тактирования, который согласовывает передачу данных. Таким образом, шина получается полнодуплексной. Фактически, каждый байт передаётся по одному биту через одну сигнальную линию, что, по сравнению с 8080, заставляет повышать тактовую частоту контроллера SPI, но при этом занимает гораздо меньше пинов самого МК или процессора. В программном плане, большинство дисплеев представленных в различных интернет-магазинах полностью совместимы с дисплеями 8080, ведь SPI — просто один из режимов работы. Единственный нюанс — из SPI дисплея не всегда можно вычитать ID-контроллера и вообще что-либо читать из регистров дисплея.

  • I2C — относительно редко используемая шина для дисплеев из-за её невысокой производительности, однако, тем не менее, очень подходящая для МК (благодаря использованию только двух сигнальных линий — SDA для данных и SCL для тактирования. Даже чипселект здесь программный благодаря тому, что каждое устройство имеет собственный адрес!), однако её можно найти в дисплеях некоторых телефонов из самого начала 2000-х годов.

  • TTL/параллельный RGB — тут, в общем-то, меня упрекали пару раз из-за того, что я продолжаю называть её TTL, но так сложилось исторически — даже в даташитах эту шину называют именно так. С логической точки зрения она очень простая: у нас есть 16/24 сигнальные линии, где 5 (или 8) бит используются для красного и синего канала и 6 (или опять же 8) бит используются для зеленого цвета (т. е. в 16-битном цвете у нас RGB565, а в 24-битном — RGB888). К ним идут сигналы HSYNC для горизонтальной синхронизации и VSYNC для вертикальной. Вообще, необязательно использовать все сигнальные линии предоставляемые дисплеем — можно использовать, например, RGB332 и использовать всего 8 сигнальных линий. Однако для отображения картинки, необходимо строго соблюдать тайминги синхронизации, иначе дисплей будет просто показывать белый цвет. Помимо цифрового варианта, бывает также аналоговый, очень похожий на телевизионный RGB или VGA. Такие дисплеи обычно используются для матриц до 1024x768 включительно.

  • MIPI DSI — протокол, используемый для дисплеев высокого разрешения — от 480x800 и выше, его можно встретить в большинстве современных смартфонов и планшетов. Кроме того, такие дисплеи используют относительно мало пинов — по два на каждый канал LVDS (обычно в смартфоне около двух-четырех каналов) + две сигнальные линии на тактирование. Звучит всё хорошо? Как-бы не так: протокол дифференциальный и на каждый канал (т. е. логический бит) приходится по две сигнальные линии — одна с положительная, а вторая отрицательная. Затем одна вычитается из другой и получается окончательный сигнал, а сделано это для уменьшения помех от передачи данных по нескольким линиям с очень высокой тактовой частотой без увеличения битности шины.

  • LVDS/eDP — Протоколы, используемые в матрицах ноутбуков, телевизоров и иногда планшетов. На физическом уровне близки к DSI, на программном — если честно, не знаю, но наслышан о некой стандартизации и высоком уровне совместимости. Даже «неродные» ноутбучные матрицы вполне «заводятся», максимум после перепрошивки родной EEPROM, даже если дисплей другого разрешения!


В списке выше, мы рассмотрели несколько популярных аппаратных шин для дисплеев. В данной статье, мы разберемся в программных особенностях таких дисплеев и узнаем, где взять по дисплею одного из следующих типов: SPI, I2C, а также 8080.

❯ Виды дисплеев и их протоколы


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

У рассматриваемых нами дисплеев есть собственная видеопамять, благодаря чему нет необходимости соблюдать тайминги, а также общий набор команд (или аппаратных регистров), которые мы можем записывать и тем самым менять поведение дисплея. Если мы просто подадим питание на дисплей и попытаемся что-то вывести — у нас ничего не выйдет, поскольку при каждом аппаратном RESET'е, состояние большинства регистров, кроме SleepOn и PowerOn не определено и может содержать в себе любой «мусор». Для корректной работы дисплея, нам необходимо послать определенный набор команд, называемый инициализацией, который установит настройки драйвера дисплея, такие как контраст, параметры цветности, направление развертки изображения из VRAM и т. д. Пожалуй, стоит сразу отметить, что некоторые люди называют регистры дисплея командами — это означает одно и тоже!

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Пример инициализации. На самом деле, не все люди делают такую простыню из вывозов функций чтения/записи регистров дисплея, поскольку это кушает драгоценный ROM. На AVR, например, команды инициализации можно хранить в ROM и читать из PROGMEM.

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

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Набор команд для контроллеров дисплеев частично стандартизирован спецификацией MIPI DBI, которая описывает и закрепляет некоторые конкретные адреса регистров, общие для всех контроллеров дисплея. К ним относится, например, установка «окна» для записи (0x2B и 0x2A), sleepout (0x11) и некоторые другие. Проприетарными командами остаются настройки питания, развертки, контраста и самого драйвера дисплея. Ну и всяческие LUT, а также палитровые режимы (если они есть) тоже проприетарные.

Пример одной из таких стандартизированных команд:

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Почти во всех дисплеях есть разделение отправляемых байтов на команду (или выборка номера регистра для чтения/записи) и на данные. Как обработать текущий байт определяет отдельный пин (или бит, в зависимости от конфигурации дисплея), называемый D/C (Data/Command), иногда также можно встретить названиеRS. Обычно, при записи команды, D/C должен быть на низком уровне, при записи данных, соответственно, на высоком. Суть простая: записываем номер команды (или регистра) при низком D/C, а затем дописываем необходимые аргументы (или конфигурацию регистра) при высоком уровне D/C.
Примерно так:

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Касательно сброса, то в дисплеях обычно существуют два вида этого процесса: аппаратный сброс через соответствующий пин и программный с помощью специальной команды. Пин RESET никогда нельзя оставлять в «воздухе» (т. е. не подключенным) в надежде что «да состояние пинов МК после ресета известно, мусора на шине явно не будет». Мусора может и не будет, а вот дисплей упадет в вечный ресет, поскольку ожидает перехода сигнала RESET в высокий уровень. Тоже самое касается и пина CS, отвечающий за выбор устройства на шине. Если вам не нужен CS и у вас висит только одно устройство на шине — просто притяните его к массе. Некоторые контроллеры (например, ILI9325) адекватно реагируют на CS «в воздухе», некоторые — нет. Только после того, как RESET оказался на высоком уровне, дисплей начнёт принимать команды:

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Переходим конкретно в выводу данных. Для начала вывода изображения на дисплей, нам необходимо выполнить команду 0x2C, которая переведет контроллер дисплея в режим записи данных в видеопамять. После этого, нам остаётся лишь установить высокий уровень на пине D/C и просто слать непрерывный поток пикселей. Контроллер дисплея сам инкрементирует координаты на дисплее и после того, как координаты выйдут за границы нужной области, дисплей сам их переведет в изначальные. Таким образом, достаточно лишь один раз проинициализировать дисплей и просто гонять в него данные, например, с помощью DMA.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Всё просто и понятно :)

❯ Дисплеи с шиной 8080


Пожалуй, подобные дисплеи найти проще всего, поскольку они использовались в большинстве кнопочных телефонов из нулевых. Такие экранчики можно встретить во многих моделях Nokia, Samsung, LG, Fly, Sony Ericsson и большинстве китайских телефонов. С поиском распиновки и разводкой таких дисплеев всё относительно просто и одновременно сложно: на некоторые модели телефонов (например, почти на все Nokia) можно свободно найти схему в гугле и узнать распиновку коннектора дисплея… однако этот коннектор сначала надо сдуть и развести на breakout-плате, или под микроскопом вывести перемычки. В некоторых случаях (например, Siemens S-серии), дисплей просто прижимался к контактам на плате, а сами контакты имели более чем паябельный шаг.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Из схемы на Nokia N70. Этот дисплей применялся во многих Symbian-смартфонах Nokia тех лет: N-Gage/N-Gage QD, N70, N72, 6600 и некоторых других.

Но особо удобными можно считать дисплеи с паябельными шлейфами с большим шагом пинов — такие можно встретить в некоторых телефонах Samsung и большинстве китайских телефонов. Пытливый читатель спросит «так это ж китаец, где ты на него схему будешь искать?». И вот тут, китайские производители нас приятно порадуют, поскольку за редким исключением, такие дисплеи имеют стандартизированную распиновку: лично мне известны матрицы 37 Pin, 39 Pin и 44 Pin. Как найти для них распиновку? Пишем на «алике» или «таобао» 37 pin lcd tft и смотрим: в описании продавец частенько прилагает распиновку (правда учтите, что 37 pin не имеет пинов IM для настройки ширины шины, а 16-битный интерфейс может быть слишком прожорилвый по числу пинов):

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост
Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

В случае с китайцами, иногда можно найти и схему (нажимайте на зеленую стрелку) на устройство: например, почти на все модели Fly схемы лежат в свободном доступе, где почти всегда можно найти распиновку дисплея. Иногда производитель даже выводит тестпоинты на все сигнальные линии и дисплей с тачскрином можно использовать, не выпаивая его с платы!

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Распиновка на Fly IQ239. На нижней части изображения, вы можете увидеть, что такие, безусловно, здоровенные дисплеи можно купить за копейки и сейчас :)

Но задумывались ли вы когда-нибудь, откуда на тачскринах в дисплеях с «али» взялись кнопки «домой», «сообщения», «телефон»? Это ведь те самые дисплеи, которые использовались в «ноклах», просто припаянные к удобной плате! :) Кроме того, на китайские дисплеи без проблем можно найти даташит: обычно они используют контроллеры от ST или ILI, в зависимости от разрешения дисплея.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост
Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

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

Концептуально, аппаратная реализация протокола одновременно простая и понятна любому: программа устанавливает состояние каждого бита передаваемого байта на сигнальных линиях D0..D7 (либо D00..D15, если шина у нас 16-битная), а затем просто «дёргает» линию RD (Read или чтение), либо WR (Write или запись) по переходу из низкого уровня в высокий, благодаря чему контроллер дисплея понимает, что байт (или слово в случае 16-битного интерфейса) можно «забирать» с шины. По переходу из высокого уровня в низкий, контроллер снова переходит в режим ожидания следующего байта с шины.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Где взять такие дисплейчики? Да почти везде! Но лучше всего брать дисплеи с китайчиков, которые можно развести на вот таких breakout-платах, которые можно заказать на алике за пару сотен рублей.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Обратите внимание на то, как по свински припаивают подсветку на некоторых дисплеях. И это завод! Лучше сразу прозвоните прежде чем подавать питание. Я, вот, забыл, понадеялся на производителя и по итогу сжёг подсветку :(

Другой вопрос, где искать на них информацию? Помимо схем, можно просто поискать на алике «37 pin lcd tft», «39 pin tft lcd», «24 pin tft lcd» и т. п. Обычно продавцы сами выкладывают распиновку и даже прикладывают ID контроллера дисплея. Поскольку иногда различия в распиновках всё же попадаются, обращайте внимание на то, куда у вас идут дорожки от подсветки и от резистивного тачскрина (если есть), а также вызванивайте все пины с массой — это поможет подобрать правильную распиновку без логического анализатора. Вот, например, дисплейчик из китайской нерабочей реплики Nokia 130 с здоровым 2.4" дисплеем… казалось бы, вообще не понятно что за дисплей, однако воспользовавшись смекалкой, мы находим его распиновку!

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост
Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

❯ SPI-дисплеи


SPI-дисплеи в телефонах встречались относительно редко. В основном, подобные дисплейчики можно было найти в моделях начала 2000х годов: сименсах, моторолах, ранних сонериках T-серии и Nokia на S40. Иногда SPI-дисплеи можно встретить в современных кнопочных телефонах — обычно они имеют шлейф с менее чем 15 пинами, как некоторые модели Fly. Обычно контроллер дисплея поддерживал сразу несколько аппаратных шин, а производитель телефона ещё на этапе установки шлейфа к контроллеру дисплея замыкал необходимые IM-пины выбирая необходимую шину, поэтому программный протокол фактически идентичен дисплеям с шиной 8080.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Несомненным плюсом SPI-дисплеев можно назвать малое число пинов для работы с матрицей: достаточно всего два (плюс сигнал D/C, если дисплей не 9-битный), если повесить RESET на VIO, либо три (четыре), если хотите управлять аппаратным RESET вручную. Но есть и, в некоторой степени, минусы: например, не все микроконтроллеры умеют работать в 9-битном режиме и возможно последний бит придётся досылать «ногодрыгом» (что ломает любую возможность реализации DMA).

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Многие дисплеи с этим интерфейсом задокументированы ещё в начале 2000х годов на известных форумах и сайтах, таких как VRTP, Радиокот и easyelectronics, поэтому проблем с их подключением не возникнет даже у новичка. Даже такой крутой и уважаемый дядька, как @DIHALT, когда-то писал полезный материал об использовании FSMC в STM32.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Достать их новыми можно и сейчас: различные магазины запчастей для телефонов бывают продают их по 20-30-40 рублей… Я недавно себе целую коробочку накупил, в том числе и просто для ремонта смартфонов для будущих статей :)

❯ I2C-дисплеи


Дисплеи с такой шиной — настоящая редкость и обычно попадались в телефонах самого начала нулевых годов с низким разрешением дисплея. Из известных мне — Ericsson'ы и ранние Sony Ericsson T-серии, ODM Motorola (головастики например) и… пожалуй всё.
Казалось бы, разве I2C может быть полезен для работы с дисплеями, где требуется активный вывод графики? Ведь он совсем медленный! Однако, даже он может пригодится для некоторых проектов, а в большинстве МК частенько попадается аппаратный TWI.

Кроме того, I2C дисплейчики удобно отлаживать: благодаря тому, что периферийное устройство должно отрапортовать ACK (состояние успешности получения байта) мастер-устройству, можно сразу определить обрыв линий до дисплея. Но какой-то конкретной информации по ним я не смогу написать — они все совсем разные :( Правда, полезным линком поделюсь, ребята с форума VRTP собрали хорошую таблицу с различными контроллерами дисплеев, где бывают и i2c!

❯ Подсветка


Отдельного радела стоит тема подсветки дисплеев. По первой может показаться, что тут всё просто: современным дисплеями достаточно 5В, а на старых можно замерить напряжение бустера на живом девайсе и смастерить свой DC-DC повышающий преобразователь, или взять, например, уже готовый драйвер, как известный в определенных кругах LTYN. На самом деле и тут есть свои нюансы.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Итак, каким образом реализована подсветка в том или ином устройстве? Обычно её реализация заключается в последовательном соединении двух и более светодиодов, которые формируют небольшую ленту под рассеивающей плёнкой. На современных китайских дисплейчиках, для работы в полную яркость достаточно всего лишь 5В источника питания + токоограничивающего резистора. Но что самое приятное, подсветка в таких дисплеях способна работать и при 3.3В, пусть менее ярко, но всё равно вполне читабельно.

Если вы делаете портативное маломощное устройство, работающее от одного Li-Ion аккумулятора, то достаточно лишь пустить 3.3В с линейного стабилизатора, который формирует напряжение VSYS для микроконтроллера. Таким образом, у вас будет стабильная подсветка среднего уровня яркости. В качестве альтернативного «бомж» варианта, когда нет возможности собрать нормальный драйвер подсветки, можно попробовать подключить светодиоды напрямую к АКБ, но при разряде дисплей будет потихоньку «тухнуть». Ещё один «бомж» вариант — разобрать дисплейный модуль, порезать дорожки на ленте и соединить пару светодиодов параллельно, выведя их через отверстие, откуда выходит шлейф дисплея, однако в таком случае, потребление подсветки заметно увеличится.

Правильным выходом будет взять с того-же телефона бустер подсветки с индуктивностью и иной необходимой обвязкой, и собрать бустер самому. Особой популярностью когда-то пользовались вышеупомянутые LTYN из телефонов Samsung (это маркировка известного драйвера LT1937). Уровнем подсветки на подобных бустерах телефоны управляют с помощью встроенного ШИМ-контроллера, чем можете воспользоваться и вы :)

❯ Запускаем дисплейчик на практике


В первой части статьи, я постарался ввести вас в курс дела и кратко рассказать о том, как работают такие дисплейчики «под капотом». Как видите — с теоретической точки зрения, ничего сложного нет: пересылаем данные на дисплей, да вовремя дёргаем пин D/C. Но какого же это на практике?


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

В качестве МК, мы возьмём мой любимый RP2040, который, по моему мнению, незаслуженно обделен вниманием. Время от времени я делаю всякие прикольные девайсы на базе этого МК, поэтому крайне рекомендую его всем моим читателям :)

Давайте же перейдем к практической части статьи!
Обычно при создании проекта, я просто клонирую с гита RPi сэмплы с уже готовыми файлами CMake, беру hello world, конфигурирую CMakeLists.txt и пишу свою программу. На малинке пока что нет такого удобного способа создания проекта, как idf.py create-project :)
Само собой, для удобства отладки я всегда включаю встроенную в чипсет эмуляцию UART через USB.

if (TARGET tinyusb_device)
add_executable(hello_usb
main.cpp
)

# pull in common dependencies
target_link_libraries(hello_usb pico_stdlib hardware_spi)

# enable usb output, disable uart output
pico_enable_stdio_usb(hello_usb 1)
pico_enable_stdio_uart(hello_usb 0)

# create map/bin/hex/uf2 file etc.
pico_add_extra_outputs(hello_usb)

# add url via pico_set_program_url
example_auto_set_url(hello_usb)
elseif(PICO_ON_DEVICE)
message(WARNING "not building hello_usb because TinyUSB submodule is not initialized in the SDK")
endif()

И инициализирую USB-стек и биндинги stdout к нему:

stdio_init_all();
sleep_ms(1000);

Задержка здесь важна, иначе девайс отказывается определятся в системе. Переходим, собственно, к разводке дисплея. Для работы нам достаточно лишь питания, подсветки, общей массы и четырёх сигнальных линий: MOSI, CLK, DC, RESET. На CS я обычно ставлю перемычку с массой, т. к обычно не вешаю что-то ещё на одну шину с дисплеем.

Переходим к инициализации дисплея. Наш экранчик работает на базе контроллера ST7735R и имеет разрешение 128x160. Сначала, назначаем функции для пинов и дёргаем RESET:

gpio_set_function(LCM_SPI_CLK, GPIO_FUNC_SPI);
gpio_set_function(LCM_SPI_MOSI, GPIO_FUNC_SPI);

// HW reset
gpio_init(LCM_RESET);
gpio_set_dir(LCM_RESET, true);
gpio_put(LCM_RESET, false);
sleep_ms(400);
gpio_put(LCM_RESET, true);

gpio_init(LCM_DC);
gpio_set_dir(LCM_DC, true);

spi_init(spi0, 105535000);

Весьма негусто скажете вы? Ну, с минорными изменениями, здесь заработает дисплейчик любого разрешения, даже 480x320! Переходим к фактической инициализации:

void lcmCommand(unsigned char byte)
{
gpio_put(LCM_DC, 0);
spi_write_blocking(spi0, &byte, sizeof(byte));
}

void lcmData(unsigned char byte)
{
gpio_put(LCM_DC, 1);
spi_write_blocking(spi0, &byte, sizeof(byte));
}

...

lcmCommand(0x11);
sleep_ms(120);
lcmCommand(0xB1);
lcmData(0x01);
lcmData(0x2C);
lcmData(0x2D);
lcmCommand(0xB2);
lcmData(0x01);
lcmData(0x2C);
lcmData(0x2D);
lcmCommand(0xB3);
lcmData(0x01);
lcmData(0x2C);
lcmData(0x2D);
lcmData(0x01);
lcmData(0x2C);
lcmData(0x2D);

lcmCommand(0xB4);
lcmData(0x07);

lcmCommand(0xC0);
lcmData(0xA2);
lcmData(0x02);
lcmData(0x84);
lcmCommand(0xC1);
lcmData(0xC5);
lcmCommand(0xC2);
lcmData(0x0A);
lcmData(0x00);
lcmCommand(0xC3);
lcmData(0x8A);
lcmData(0x2A);
lcmCommand(0xC4);
lcmData(0x8A);
lcmData(0xEE);

lcmCommand(0xC5);//VCOM
lcmData(0x0E);
lcmCommand(0x36);//MX, MY, RGB mode
lcmData(0xC8);

lcmCommand(0xe0);
lcmData(0x02);
lcmData(0x1c);
lcmData(0x07);
lcmData(0x12);
lcmData(0x37);
lcmData(0x32);
lcmData(0x29);
lcmData(0x2d);
lcmData(0x29);
lcmData(0x25);
lcmData(0x2b);
lcmData(0x39);
lcmData(0x00);
lcmData(0x01);
lcmData(0x03);
lcmData(0x10);
lcmCommand(0xe1);
lcmData(0x03);
lcmData(0x1d);
lcmData(0x07);
lcmData(0x06);
lcmData(0x2e);
lcmData(0x2c);
lcmData(0x29);
lcmData(0x2d);
lcmData(0x2e);
lcmData(0x2e);
lcmData(0x37);
lcmData(0x3f);
lcmData(0x00);
lcmData(0x00);
lcmData(0x02);
lcmData(0x10);
lcmCommand(0x2A);
lcmData(0x00);
lcmData(0x02);
lcmData(0x00);
lcmData(0x81);

lcmCommand(0x2B);
lcmData(0x00);
lcmData(0x01);
lcmData(0x00);
lcmData(0xA0);
lcmCommand(0x3A);//65k mode
lcmData(0x05);
lcmCommand(0x29);//Display on

// Set viewport
lcmCommand(0x2A);
lcmData(0 >> 8);
lcmData(0 & 0xFF);
lcmData(128 >> 8);
lcmData(128 & 0xFF);

lcmCommand(0x2B);
lcmData(0 >> 8);
lcmData(0 & 0xFF);
lcmData(160 >> 8);
lcmData(160 & 0xFF);

Прошиваем наш МК и смотрим что получилось. Видим шум на экране? Значит дисплей инициализирован верно!

После инициализации дисплея, мы можем выводить на него данные! Дабы дать возможность процессору заниматься другими делами во время передачи картинки на дисплей, мы настроим один из DMA-каналов. DMA-контроллер занимается пересылкой данных из ОЗУ в другой участок ОЗУ (аппаратный memcpy) или периферию. Как раз для второго случая, т. е. пересылки данных в контроллер SPI, мы и будем использовать DMA!

Аллокейтим фреймбуфер, куда мы будем выводить нашу картинку и настраивает DMA-канал:

int backBufSize = LCM_WIDTH * LCM_HEIGHT * 2 + 1;
backBuffer = (byte*)malloc(backBufSize);

printf("LCM: Setting up DMA channel...\n");
bulkDMAChannel = dma_claim_unused_channel(true);
cfg = dma_channel_get_default_config(bulkDMAChannel);
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
channel_config_set_dreq(&cfg, spi_get_dreq(spi0, true));

Переходим к выводу изображения на дисплей. Для того, чтобы просто установить цвет пикселя в любых координатах экрана, достаточно лишь посчитать смещение от начала указателя на фреймбуфер к определенным координатам экрана. Формула очень простая и понятная: ширина дисплея * Y-координата + x координата и результат предыдущих операций помноженный на число байт в одном пикселе.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

__inline void pixelAt(short x, short y, short color)
{
if(x < 0 || y < 0 || x >= LCM_WIDTH || y >= LCM_HEIGHT)
return;

byte* col = (byte*)&color;
*((short*)&backBuffer[(y * 128 + x) * 2]) = color;
}

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

Теперь для вывода картинки, нам достаточно лишь скопировать изначальное изображение в наш фреймбуфер и попросить DMA-канал вывести изображение на дисплей. Для прозрачных картинок без альфа-канала (т. е. с цветовым ключом), функция будет выглядеть так:

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

А вот как с этим всем работать:

stdio_init_all();

sleep_ms(1000);


printf("LCM test by monobogdan\n");


lcmInitInterface();

lcmAllocBackBuffer();

lcmInit();


grDrawBitmapTransparent((void*)&pikabu, PIKABU_WIDTH, PIKABU_HEIGHT, 0, 0);

lcmFlush();


while(1)

{


}

Запускаем и...

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Можно сделать чуть комплекснее, добавив альфа-блендинг и аффинные трансформации (возможность поворота и скейла картинок), но пока-что такой задачи не стоит. Ну что, всё очень просто и понятно? :) Пример прошивки можно найти на моём GitHub!

Производительность такого способ на RP2040 можно увидеть вот в этом видосе (на Пикабу не смог залить из-за ограничения на число медиа-элементов). Обратите внимание, что подход предложенный выше больше подходит именно для динамического вывода изображения без dirty-регионов. Он подойдет для игровых консолей, камер, анимаций или устройств с выводом динамической информации по типу осциллографов. Если вам нужно обновлять картинку реже, например, если вы делаете умные часы с плеером, то нет необходимости занимать довольно большой объем ОЗУ фреймбуфером, ведь вы можете писать напрямую в видеопамять. Тут уже решать в зависимости от конкретной ситуации именно вам :)

❯ Заключение


Вот мы с вами и систематизировали информацию о том, как использовать дисплеи с мобильных телефонов в своих проектах. Надеюсь, информация была достаточно полезной для вас!
Однако, у меня к вам просьба: пожалуйста, не «дербаньте» рабочие девайсы «на запчасти» :(
Это будет не очень гуманно по отношению к нашему «технобалдежу», где мы наоборот стараемся найти применение стареньким девайсам :)

Был ли для вас материал полезен? Пишите в комментариях.

Полезный материал?
Всего голосов:
Какие дисплейчики подключали?
Всего голосов:

❯ Важное объявление для читателей касательно будущей рубрики

Друзья! Я, как и многие мои читатели, помимо программирования и железа обожаю тачки! Особенно те тачки, где что-то нужно доделывать самому… и речь, конечно-же, о ТАЗах! Я долго думал, но всё же решился: сейчас я коплю на будущий интересный проект, связанный с ультрабюджетным электронным дооснащением автомобиля, который старше меня в полтора раза — скорее всего, речь пойдет о ВАЗ 2108/2109/21099, причём не исключено что карбюраторной! В планах довольно крутой проект, заключающийся в следующем: мы спроектируем очень дешевый бортовой компьютер (т.е панель) для управления автомобилем на базе дешевого Б/У планшета за пару сотен рублей. Планшет будет связан с управляющим МК через UART (о подобной коммуникации через хардварные протоколы я уже писал целых две статьи: сам себе Linux смартфон, превращаем планшет с нерабочим тачскрином в игровую консоль), и с планшета мы сможем не только управлять основными системами машины (стеклоподъемники, центральный замок и соленоид багажника), но и собирать и пытаться примерно посчитать некоторую информацию о расходе, километраже и стабильности работы двигателя на карбюраторной(!) машине без электронных систем с завода!

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

В наши задачи будет входить не только проектирование аппаратной части такого оснащения, но и разработка симпатичного интерфейса для самой панели, дабы было не хуже чем в BMW :D Всеми схемами, исходным кодом и инструкциями я буду делится с вами в каждой статье и, как обычно, расскажу обо всех деталях реализации во всех подробностях! У меня уже есть некоторые идеи и наработки. Собственно, почему-б и не попробовать? Будет новая рубрика в блоге: апгрейд автомобилей глазами электронщика и прожженного программера.

Мобильные экранчики в ваших проектах: большой и понятный о гайд о различных дисплеях! Опрос, Гаджеты, Покупка, Программирование, C++, Raspberry Pi, Arduino, Дисплей, Экран, 2D, Графика, Embedded, Своими руками, Микроконтроллеры, Код, Электроника, Железо, Длиннопост

Фото не моё, из интернета

Если вам нравятся мои статьи, вас интересует развитие такой рубрики и у вас есть желание и возможность — можете помочь проекту копеечкой с помощью формы доната ниже. Пикабу позволяет остаться анонимным и донатить даже без регистрации. Сейчас у меня есть 40 тысяч рублей личных накоплений, на покупку самой машины планирую выделить 70-80 тысяч рублей (я живу в Краснодарском крае, так что здесь ещё есть шансы найти что-то +- живое за такие деньги), так что остаётся собрать около 30-35 тысяч рублей. За каждую копейку я готов отчитаться (по факту покупки машины я сделаю пост с фотографиями авто, ДКП, а также оглашу фронт будущих работ и сразу начну заниматься проектом).

Интересный проект с тазиком?
Всего голосов:
Показать полностью 25 3
[моё] Опрос Гаджеты Покупка Программирование C++ Raspberry Pi Arduino Дисплей Экран 2D Графика Embedded Своими руками Микроконтроллеры Код Электроника Железо Длиннопост
12
7
Kfyauk
1 год назад
Лига программистов C/C++

Почему игнорируется условие IF⁠⁠

Привет всем. Прошу помочь знающих людей. Пробую немного разобраться в программировании микроконтроллеров на С++. Создал программу тестера батареек. Функция Void loop состоит по сути из трех условий:

1) если напряжение на батарейке больше 1,6 в то горит зеленый светодиод (newLed),

2) если от 1,4 до 1,6в, то горит желтый светодиод (okLed),

3) и все остальное - горит красный (oldLed).

Загрузке в ардуину происходит немного другое. Программа начинает бесконечный цикл и , даже без подключения проверяемой батарейки)загорается красный (ну это понятно, т.к. нулевое напряжение соответствует третьему условию), но далее программа возвращается к первому условию и загорается зеленый светодиод. И так бесконечно происходит игнорирование условия IF, по которому зеленый светодиод включается только когда более 1,6 в.

#define newLed 2

#define okLed 4

#define oldLed 6

int analogValue = 0;

float voltage = 0;

int ledDelay = 2000;

void setup()

{

pinMode(newLed,OUTPUT);

pinMode(okLed,OUTPUT);

pinMode(oldLed,OUTPUT);

}

void loop()

{

analogValue = analogRead(0);

voltage = 0.0048*analogValue;

if (voltage >= 1.6)

{ digitalWrite(newLed, HIGH);

delay(ledDelay);

digitalWrite(newLed, LOW);

}

else if(voltage < 1.6 && voltage > 1.4)

{

digitalWrite(okLed, HIGH);

delay(ledDelay);

digitalWrite(okLed, LOW);

}

else

{

digitalWrite(oldLed, HIGH);

delay(ledDelay);

digitalWrite(oldLed, LOW);

}}

_______________________________

кстати такое поведение ардуины заметил не только в этой программе. Там где программы состоят из условий IF постоянно игнорируется первое условие (оно постоянно выполняется). При загрузке скетча в симулятор Arduino в интернете пограммы работают корректно. В чем может быть дело?

Показать полностью
Arduino C++ Текст
8
129
monobogdan
monobogdan
1 год назад
TECHNO BROTHER

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI?⁠⁠

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

Зачастую в процессе разработки собственных устройств или моддинга уже существующих, встаёт задача выполнения стороннего кода: будь то ваши собственные программы с SD-флэшек, или программы, написанные другими пользователями с помощью SDK для вашего устройства. Тема компиляторов и кодогенерации достаточно сложная: чтобы просто загрузить ELF или EXE (PE) программу, вам нужно досконально разбираться в особенностях вашей архитектуры: что такое ABI, релокации, GOT, отличие -fPIE от -fPIC, как писать скрипты для ld и т. п. Недавно я копал SDK для первых версий Symbian и основываясь на решениях из этой ОС понял, каким образом можно сделать крайне «дешевую» загрузку любого нативного кода практически на любом микроконтроллере, совершенно не вникая в особенности кодогенерации под неё! Сегодня мы с вами: узнаем, что происходит в процессе загрузки программы ядром Linux, рассмотрим концепцию, предложенную Symbian Foundation и реализуем её на практике для относительно малоизвестной архитектуры — XTensa (хотя она используется в ESP32, детали её реализации «под капотом» для многих остаются загадкой). Интересно? Тогда добро пожаловать под кат!

❯ Как это работает?


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

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост




Давайте для общего понимания вкратце разберемся, как происходит загрузка программ в Windows/Linux:

1. Система создаёт процесс и загружает в память программы секции из ELF/PE. Обычные программы для своей работы используют 3 секции: .text (код), .data (не-инициализированный сегмент памяти для глобальных переменных), .bss (сегмент памяти для инициализированных переменных). Каждому процессу выделяется собственное адресное пространство, называемое виртуальной памятью, которое не позволяет программе испортить память ядра, а также позволяет не зависеть от разметки физической памяти на выполняющей машине. Концепцию виртуальной памяти реализует специальной модуль в процессоре, называемый MMU.

2. Если бы наши программы не использовали никаких зависимостей в виде динамических библиотек, то на этом процесс загрузки можно было бы закончить: каждая программа имеет свой адрес загрузки, относительно которого линкер строит связи между обращениями к коду/данным программы. Фактически, для самых простых программ линкеру остаётся лишь прибавить адрес загрузки программы (например, 0x100) к каждому абсолютному обращению к памяти.
Однако современные программы используют десятки библиотек и для всех предусмотреть собственный адрес загрузки не получится: кто-то где-то всё равно будет пересекаться и вероятно, портить память. Кроме того, современные стандарты безопасности в Linux рекомендуют использовать позиционно-независимый код, дабы использовать преимущества ASLR (Address Space Layout Randomization, или простыми словами возможность загрузить программу в случайное место в памяти, дабы некоторые уязвимости, завязанные на фиксированном адресе загрузки программы перестали работать).

3. Поэтому для решения этой проблемы придуман т. н. динамический линкер, который уже на этапе загрузки программы или библиотеки патчит программу так, чтобы её можно было загрузить в любой участок памяти. Для этого используются данные, полученные от обычного линкера а этапе компиляции программы: помимо .text, .data и .bss, линкер создаёт секции .rel и .rel-plt, которые называются релокациями. Если объяснять совсем условно, то релокации — это просто запись вида «какой абсолютный адрес в коде программы нужно пропатчить» -> «на какое смещение его пропатчить». Самая простая релокация выглядит вот так:

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

Где по итогу:

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

.rel-plt же служит для резолвинга вызовов к dll/so: изначально программа ссылается на заранее определенные в процессе компиляции символы, которые уже в процессе загрузки патчатся на физические адреса функций из загруженной библиотеки.

И казалось бы — всё очень просто, пока в дело не вступают GOT (Global Offset Table — глобальная таблица смещений) и особенности реализации конкретного ABI. И ладно бы x86 или ARM, там всё разжевано и понятно, однако на других архитектурах начинаются проблемы и не всегда очевидно что и где за что отвечает.

А ведь чаще всего нужно просто загрузить небольшую программу, которой не нужны комплексные загрузчики: немного кода, немного данных и всё. И тут у нас есть три выхода:

  1. Писать полноценный загрузчик ELF-бинарников. ELF может оказаться громоздким для некоторых окружений и его реализация может оказаться тривиальной не для всех.

  2. Зарезервировать определенный сегмент в памяти (пусть с 0xFFF по 0xFFFF) и скомпилировать нашу программу с адресом загрузки 0xFFF с параметром -fno-pic. В таком случае, линкер сгенерирует обращения к памяти по абсолютным адресам — если переменная лежит по адресу 0xFFF, то программа будет обращаться сразу к этому адресу памяти, без необходимости что либо динамически линковать. Именно такой подход использовался во времена ZX Spectrum, Commodore 64 и MS-DOS (однако там роль «виртуальной памяти» выполняла такая особенность 8086, как сегменты). У такого подхода есть и минусы: относительная невозможность загрузки сразу нескольких программ одновременно, зарезервированное пространство линейно отъест небольшой кусок памяти у основной прошивки, нет возможности динамической аллокации секций. Зато такой код теоретически будет работать быстрее, чем PIC.

    Проблемы реализации такого способа: иногда нужно лезть в систему сборки основной прошивки и патчить скрипт линкера так, чтобы он не трогал определенный регион памяти. В случае esp32, например, это требует патча в сам SDK и возможного «откола» от мейнлайн дистрибутива.

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


Недавно мы сидели в чате ELF-сцены (разработка нативных программ под телефоны Siemens, Sony Ericsson, Motorola и LG с помощью хаков) и думали, как же можно реализовать загрузчик сторонних программ на практически неизвестных платформах. Кто-то предлагал взять ELF под основу — однако с его реализацией под некоторые платформы есть трудности, а кто-то предлагал писать «бинлоадер» — самопальный формат бинарников, который получается из, например, тех же эльфов.

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

В это же время я копал SDK для Symbian и хорошо помнил, что в прикладных приложениях для этой ОС нет поддержки глобальных переменных вообще. Да, сегмент .data и .bss полностью отсутствует — переменные предлагается хранить в структурах. Почему так сделано? Всё дело в том, что каждая программа в Symbian — это dll-библиотека, которую загружает EKA и создаёт экземпляр CApaApplication. И дабы была возможность загрузить dll один раз для всех программ (что справедливо для системных библиотек), ребята полностью выкинули возможность использования любых глобальных переменных. А ведь идея интересная!

Однако в таком подходе есть несколько серьезных ограничений:

  • Отсутствие глобальных переменных может стать проблемой при портированиии уже существующего софта, хотя вашим программам ничего не мешает передавать в каждую функцию структуру с глобальным стейтом, который можно при необходимости изменять. Кроме того, нет ограничений на использование C++ (за исключением необходимости ручной реализации new/delete и отсутствием исключений).

  • Отсутствие преинициализированных данных. Вот это уже может стать относительно серьёзной проблемой, у которой, тем не менее, есть свои обходные решения. Например если вы храните команды для инициализации дисплея в таблице, или какие-либо калибровочные данные — вы не сможете их объявить, просто используя инициализаторы в C. Тоже самое касается и строковых литерал. Тут есть два варианта: часть таблиц можно вынести на стек (если эти самые таблицы достаточно маленькие), либо подгружать необходимые данные из бинарника с помощью основной прошивки (например, LoadString и т. п.).


Давайте же на практике посмотрим, имеет ли право на жизнь такой подход!

❯ Практическая реализация


Формат нашего бинарника будет до безобразия прост: небольшой заголовок в начале файла и просто сырой дамп сегмента .text, который можно экспортировать из полученного elf даже без необходимости писать скрипт для линкера. При этом нужно учесть, что ESP32 — это микроконтроллер частично Гарвардской архитектуры, т. е. шина данных и кода у него расположены отдельно. Однако у чипа есть полноценный MMU, который позволяет маппить регионы физической памяти в виртуальную память, чем мы и воспользуемся в итоге!

Заголовок нашего бинарника будет выглядеть вот так:

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

Программа общается с основной прошивкой посредством псевдо-syscall'ов: функции, которая в качестве первого аргумента ожидает номер нужной службы и один 32х-битный указатель для описания структуры с параметрами. Реализация syscall'ов — одна из самых простых и неприхотливых с точки зрения обратной совместимости с будущими прошивками.

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

Концептуально всё очень просто: GetGlobalStateSize сообщает нашему загрузчику размер структуры для хранения глобального стейта, в то время как Start уже фактически заменяет main() в нашей программе. Необходимости в crt0 нет, поскольку весь необходимый инит выполняет бутлоадер ESP32. Впрочем, при желании вы можете выделить отдельный стек для вашей программы — это повысит надежность, если выполняемая программа удумает испортить стек.

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

Собираем нашу программу:

xtensa-esp32-elf-cc.exe test.c -fno-pic -nostdlib -nostartfiles -Wl,--section-start=.text=0x0

xtensa-esp32-elf-objcopy.exe --only-section=.text --output-target binary a.out run.bin

-fno-pic отключает генерацию кода, зависимого от GOT, -nostdlib и -nostartfiles убирает из билда crt0 и stdlib, благодаря чему мы получаем только необходимый код. --section-start задает смещение для загрузки секции .text на 0x0 (в идеале это делать необходимо из скрипта для ld).
objcopy скопирует из полученного ELF только необходимую нам секцию .text.

Как же это работает на практике? Давайте дизассемблируем выходной бинарник и посмотрим, что у нас дает на выхлопе cc:

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

Обратите внимание, что Start вызывает подфункции с помощью инструкции CALLX8, которая в отличии от обычного Immediate-версии CALL8, выполняет переход относительно текущего адреса в PC, благодаря чему переход полностью независим от адреса загрузки программы в памяти. А благодаря тому, что все данные, в том числе и указатель на глобальный стейт передаются через стек, нет необходимости релокейтить сегменты данных.

По итогу всё, что нужно от загрузчика бинарников — это загрузить программу в память для инструкций, выделить память для структуры с стейтом программы и передать управление Start. Всё!
Конкретно в случае ESP32, у нас есть два возможных решения задачи загрузки программы в память:

  1. Загрузить программу в IRAM. Такая возможность теоретически есть, однако на практике загрузчик ESP32 устанавливает права только на чтение и выполнение на данный регион памяти. Попытка что-то скопировать туда закончится исключением SIGSEGV. Кроме того, сегмент IRAM относительно небольшой — всего около 200Кб.

  2. Самопрограммирование. Для этого, в esp32 есть два механизма — Partition API и SPI Flash API. Я выбрал Partition API для простоты реализации.


Для нашей прошивки необходимо будет переразметить флэш-память. Для этого запускаем idf.py menuconfig, идём в Partition Table -> Custom partition table CSV. Создаём в папке проекта partitions.csv, куда пишем:

# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
executable, data, undefined, 0x110000, 0x10000

Для заливки программы можно использовать соответствующее Partition API, либо parttool.py:

parttool.py --port "COM41" write_partition --partition-name=executable --input "run.bin"

Переходим к загрузчику программы:

Выполняем сторонние программы на микроконтроллерах с Гарвардской архитектурой: как загружать программы без знания ABI? Опрос, Гаджеты, Программирование, C++, Avr, Arduino, Esp32, Embedded, Своими руками, Самоделки, Esp8266, Assembler, Железо, Микроконтроллеры, Длиннопост

Прошиваем ESP32:

idf.py build

idf.py flash

idf.py monitor

И смотрим результат:

SysCall 25

SysCall 35

SysCall 15

Всё работает!

❯ Заключение


Как видите, ничего сложного в выполнении сторонних программ при условии соблюдении некоторых ограничений нет. Да, в таком подходе есть как серьезные плюсы, так и минусы, однако он делает своё дело и позволяет реализовать запуск игр на кастомных игровых консолях, или сторонних программ на самодельных компьютерах. Ну и конечно же не стоит забывать про плагины! Авось в вашем решении нужна возможность расширения функционала устройства, однако предоставлять исходный код или даже объектные файлы нет возможности — тогда вам может пригодится и такая методика.

Пожалуй, стоит упомянуть ещё один… очень своеобразный метод, который я иногда встречаю при реализации самодельных компьютеров. Люди пишут… эмуляторы 6502/Z80 :)
И если такой подход ещё +- применим к ESP32, то в AVR просадки производительности будут слишком серьезными. Так зачем, если можно использовать все возможности ядра на максимум?

Полезный материал?
Всего голосов:
Приходилось ли загружать сторонний код в ваших устройствах?
Всего голосов:
Показать полностью 9 2
[моё] Опрос Гаджеты Программирование C++ Avr Arduino Esp32 Embedded Своими руками Самоделки Esp8266 Assembler Железо Микроконтроллеры Длиннопост
12
4
NutrientElement
NutrientElement
1 год назад
Arduino & Pi

Особенности шаблонного программирования на C++⁠⁠

Шаблоны в C++ - это механизм обобщённого программирования, который позволяет создавать функции и классы, работающие с разными типами данных, не зная их заранее.

Особенности шаблонного программирования на C++ Программирование, Программист, IT, Arduino, Linux, Разработка, C++, Информационная безопасность

Шаблоны в C++ отличаются от универсальных типов в других языках, таких как C# и Java, тем, что они поддерживают не только параметры типов, но и параметры значений, также называемые параметрами выражений.

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

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

Пример использования шаблона функции в C++:

// Объявление шаблона функции swap, которая меняет местами два значения

template<typename T>

void swap(T& a, T& b) {

T temp = a; // Создание временной переменной типа T

a = b; // Присваивание значения b переменной a

b = temp; // Присваивание значения temp переменной b

}

// Вызов шаблона функции swap с разными типами аргументов

int x = 10, y = 20;

swap(x, y); // Автоматический вывод типа T как int

std::cout << x << " " << y << std::endl; // Вывод 20 10

std::string s1 = "Hello", s2 = "World";

swap(s1, s2); // Автоматический вывод типа T как std::string

std::cout << s1 << " " << s2 << std::endl; // Вывод World Hello

Интересные факты и фичи языков программирования у нас в канале, заходи :)

Показать полностью
[моё] Программирование Программист IT Arduino Linux Разработка C++ Информационная безопасность
17
NutrientElement
NutrientElement
1 год назад
Arduino & Pi
Серия Программирование

Способы оптимизации кода в C++⁠⁠

Оптимизация кода в C++ - это процесс улучшения эффективности, скорости и качества кода, написанного на языке C++.

Способы оптимизации кода в C++ Arduino, C++, Программирование, Программист, IT

Оптимизация кода в C++ может быть выполнена на разных уровнях, таких как:

  • Оптимизация на уровне алгоритмов и структур данных. Это означает выбор наиболее подходящих и эффективных алгоритмов и структур данных для решения задачи, учитывая сложность, память, время и другие факторы. Например, использование сортировки слиянием вместо сортировки пузырьком, или использование хеш-таблицы вместо списка для поиска элементов.

  • Оптимизация на уровне языка. Это означает использование возможностей и особенностей языка C++, которые могут повысить производительность кода. Например, использование константных ссылок вместо копирования объектов при передаче аргументов функциям, использование шаблонов вместо дублирования кода для разных типов данных, использование лямбда-выражений вместо обычных функций для передачи поведения в качестве параметра.

  • Оптимизация на уровне компилятора. Это означает использование параметров компилятора, которые могут изменить способ генерации исполняемого кода компилятором. Например, использование параметра /O2 для включения оптимизации по скорости выполнения, использование параметра /Ob2 для включения раскрытия функций (inline expansion), использование параметра /GL для включения оптимизации по всему программному модулю.

Оптимизация кода в C++ требует знания того, какие части программы должны выполняться быстро, какой размер и скорость выполнения кода, какие затраты на реализацию новых возможностей, какой минимальный объем работы, необходимый для выполнения задания5 Оптимизация кода в C++ также требует тестирования и профилирования кода для измерения и анализа его характеристик работы, таких как время выполнения, потребление памяти, количество вызовов функций и других. Для этого можно использовать различные инструменты, такие как Visual Studio Debugger, Visual Studio Profiler, Intel VTune Amplifier и другие.

Интересные факты и фичи языков программирования у нас в канале, заходи :)

Показать полностью
[моё] Arduino C++ Программирование Программист IT
1
194
monobogdan
monobogdan
2 года назад
TECHNO BROTHER

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера⁠⁠

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост

К сожалению, в наше время многие старые, но весьма неплохие по характеристикам гаджеты отправляются напрямую в помойку, и их владельцы не подозревают, что им можно найти применение. Сервер, мультимедийная-станция, да даже просто как TV-приставка — люди в упор не замечают сфер, где старенький планшет мог бы быть полезен. Но как быть, если посвящаешь жизнь портативным гаджетам, кодингу и копанию в железе? Правильно: сделать довольно мощную игровую консоль из старого планшета самому! Сегодня вам расскажу, как я сделал свою портативную приставку из планшета с нерабочим тачскрином, Raspberry Pi Pico и 8 кнопок! За рабочим результатом прячется несколько дней работы: поиск UART на плате, разработка контроллера геймпада на базе RPi Pico, написание приложения-сервиса, которое слушает события и отправляет их в подсистему ввода Linux в обход Android. Интересно? Тогда жду вас под катом!

❯ Мотивация


Прошло уже практически 10 лет с того момента, как у меня появилась моя первая портативная консоль. Несмотря на то, что я был заядлым ПК-игроком, я уже успел посмотреть на PS3 и PSP, но денег на их покупку у меня особо не было, да и к тому времени уже был в наличии Android-планшет. Но к моему 13-летию в 2014 году, когда я ходил и выбирал себе будущий девайс на день рождения, отец и мама решили подарить мне мою первую портативную консоль. Изначально, я уговаривал её купить мне целых два девайса, но бюджет был ограничен 4.000 рублей, а я хотел взять смартфон Fly IQ239 и консоль JXD S601 одновременно:

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост


Однако, увидев здоровую 7-дюймовую консоль в магазине TREC (думаю, жители южной части РФ помнят такой), мама уговорила меня взять именно её, мотивируя это «ну и чего ты будешь тыкаться в этот мелкий экран? Возьми большую». После покупки гаджета, я был доволен: играл какие-то игрушки с ретро-платформ, устанавливал игры на Android, сидел в ВК через Kate Mobile. Что еще нужно было школяру? Однако, планшет прожил у меня недолго: с очередного лага я психанул и ударил по нему кулачком, унеся на тот свет и дисплей и тачскрин. Так консолька и пролежала в подвале около 8 лет. Впрочем, мне продолжали импонировать подобные устройства и в прошлом году я купил и написал про несколько подобных девайсов.

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост



Несколько месяцев назад, мой читатель Кирилл Севостьянов с Хабра прислал мне HTC HD2 в качестве донора и планшет Prestigio PMP7170B3G, который был рабочим, но… у него отказал тачскрин. Я всё думал, чего бы с ним сделать и решил реализовать игровую консольку своими руками из подручных средств. Идея крутилась в голове довольно давно, но реализовал я её только сейчас.

❯ Что нам нужно сделать?


Итак, что должно быть у портативной консоли? Чипсет, дисплей, звук, ОС — это всё нам уже предоставляет планшет. Нам остаётся лишь сделать свой геймпад. Давайте подумаем, что нам будет нужно для того, чтобы его сделать и передавать от него события на планшет:

  • Контроллер для геймпада: тут нам подойдет практически любой микроконтроллер, который работает от 3.3в. Выбор большой: Arduino Pro Mini 3.3v, ESP32, RPi Pico. Я остановился на последнем: недавно я взял себе две штучки «пощупать» их — и они мне очень понравились!

  • Физический интерфейс: с планшетом нужно как-то общаться. У нас есть три варианта: USB (не факт, что поддержка преобразователей включена в ядре), UART и SPI/I2C на пятачках тачскрина (потребуют написания драйвера т. к. в android-устройствах нет прямого доступа к SPI/I2C из userland'а). Я остановился на UART: его легко найти на большинстве китайских планшетов, а если не получилось — то на помощь может прийти схема платы.

  • Программная реализация: как это будет работать? Я решил реализовать геймпад в виде сервиса на Android, который слушает состояния кнопок с UART и «инжектит» события напрямую в драйвер ввода. Таким образом, поддержка нашего геймпада появляется даже в самой системе — можно управлять менюшкой или приложениями как с клавиатуры!


    С планом определились, пора начать с программной части: сначала нам обязательно понадобится ROOT-доступ. Его получение на разных девайсах отличается — на prestigio уже был порт CWM и я просто поставил SuperSU. Без ROOT доступа мы не сможем использовать UART!

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост


Теперь нам нужно найти пятачки UART на плате. Разведен он не везде, но в случае устройств на MediaTek — почти всегда, ещё и пятачки подписаны. На моём планшете он нашёлся сразу: был между двух металлических экранов и соответствовал 4-ому каналу UART. Получить к нему доступ можно в /dev/ttyMT3. Я использую ESP32 в качестве UART преобразователя: подпаиваемся к RX/TX, запускаем putty и заходим в adb shell. Определяем бодрейт (скорость) нашего UART порта — на MediaTek он обычно равен 921600, на других чипсетах — 115200. Пытаемся что-то вывести и хоба — мы уже можем «поболтать» с планшетом!

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост

❯ Приложение-сервис


Итак, у нас уже есть доступ к UART и мы можем общаться с планшетом из внешнего мира. Но получить события с кнопок пол дела, нужно их ещё и послать в систему. Для этого есть целых три способа:

  1. InputManager.injectInputEvent — именно этим методом пользуется команда input, которую вы можете использовать через adb. Но увы, он работает только при наличие разрешения INJECT_EVENTS, который доступен только системным приложениям — находятся они в /system/app и подписаны тем же сертификатом, что и остальная прошивка.

  2. Модуль uinput дает возможность создать виртуальное устройство ввода и посылать события из userland'а — т. е. из прикладного приложения. У моего планшета было устройство /dev/uinput, но lsmod показывал, что сам модуль не загружен. Так что отметаем — он есть не везде.

  3. Прямой инжект событий в character устройство — весьма грязный хак, который позволяет инжектить события, не притворяясь системным приложением, но имеет некоторые ограничения. Именно его я и выбрал и о ограничениях ниже.


Сначала нам нужно узнать, какие кнопки поддерживают загруженные устройства ввода в системе. Для этого используем команду getevent -li. Там есть разные устройства ввода, в том числе и тачскрин (если вам нужно симулировать нажатия на экран), мне же подошёл драйвер физических кнопок mtk-kpd. Он занимается обработкой кнопок громкости, включения и т. п. Тут важно обратить внимание на то, что если попытаться послать кнопку, которое устройство не реализует (например пробел), то ничего не произойдет:

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост



Инжект событий я писал на C, т. к. это требовало прямой записи input_event, а в Java прокинул его через Jni. Концепция простая: открываем устройство /dev/input/event2 и посылаем в него события ввода и синхронизации (это обязательно!), которые затем Android читает и обрабатывает:

#include <linux/uinput.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <android/log.h>

#include <jni.h>

int uinput;

extern "C" JNIEXPORT void JNICALL Java_com_monobogdan_inputservicebridge_InputNative_init(JNIEnv *env, jclass clazz) {

uinput = open("/dev/input/event2", O_WRONLY);

__android_log_print(ANDROID_LOG_DEBUG , "Test", uinput >= 0 ? "Open event OK" : "Failed to open event"); }

void emit(int fd, int type, int code, int val) {

struct input_event ie; ie.type = type;

ie.code = code; ie.value = val;

ie.time.tv_sec = 0;

ie.time.tv_usec = 0;

write(fd, &ie, sizeof(ie)); }

extern "C" JNIEXPORT void JNICALL Java_com_monobogdan_inputservicebridge_InputNative_sendKeyEvent(JNIEnv *env, jclass clazz, jint key_code, jboolean pressed) {

__android_log_print(ANDROID_LOG_DEBUG , "Test", "Send");

emit(uinput, EV_KEY, key_code, (bool)pressed ? 1 : 0);

emit(uinput, EV_SYN, SYN_REPORT, 0);

}

Основной обработкой занимается сервис, который я реализовал в отдельном потоке: он слушает события с UART и посылает соответствующие изменения состояния через sendKeyEvent. На вход приходят простые сообщения вида:

U L где U/D — нажато, не нажато, а L — однобайтовый идентификатор кнопки. В случае L — это влево, R — вправо и т. п. Вся доступная раскладка хранится в словаре. Причём само чтение из UART реализовано костылем с чтением «чужого» stdout, т. к. android-приложения не умеют сами по себе работать с root правами. В теории, это могло дать неприятный оверхед, но на практике никакого серьезного инпут лага это не создает. Не забываем сделать устройство event записываемым — ставим ему права 777:

package com.monobogdan.inputservicebridge;

public class InputListener extends Service {

private static final int tty = 3;

private InputManager iManager;

private Map<Character, Integer> keyMap;

private Method injectMethod;

private Process runAsRoot(String cmd)

{

try {

return Runtime.getRuntime().exec(new String[] { "su", "-c", cmd });

}

catch (IOException e)

{

e.printStackTrace();

return null;

}

}

@override

public void onCreate() {

super.onCreate();

// According to linux key map (input-event-codes.h)

keyMap = new HashMap<>();

keyMap.put('U', 103);

keyMap.put('D', 108);

keyMap.put('L', 105);

keyMap.put('R', 106);

keyMap.put('E', 115);

keyMap.put('B', 158);

keyMap.put('A', 232);

keyMap.put('C', 212);

InputNative.init();

try {

runAsRoot("chmod 777 /dev/input/event2").waitFor();

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

Executors.newSingleThreadExecutor().execute(new Runnable() {

@override

public void run() {

Process proc = runAsRoot("cat /dev/ttyMT" + tty);

BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));

while(true)

{

try {

String line = reader.readLine();

if(line != null && line.length() > 0) {

Log.i("Hi", "run: " + line);

boolean pressing = line.charAt(0) == 'D';

int keyCode = keyMap.get(line.charAt(2));

Log.i("TAG", "run: " + keyCode);

InputNative.sendKeyEvent(keyCode, pressing);

}

}

catch(IOException e)

{

e.printStackTrace();

}

/*try {

Thread.sleep(1000 / 30);

} catch (InterruptedException e) {

e.printStackTrace();

}*/

}

}

});

}

@override

public IBinder onBind(Intent intent) {

return null;

}

}

Таким образом, если мы отправляем с ПК «D L» — система считает, что мы зажали стрелку влево, а U L — считает что мы отпустили. Но если mtk-kpd поддерживает стрелки и еще некоторые действия без каких либо проблем, то enter в список обрабатываемых кнопок не входит: придется мудрить! И тут нам приходит на помощь механизм трансляции кодов кнопок в действия: они хранятся в специальных файлах .kl в /system/usr/keylayout/. Я назначил DPAD_CENTER на… кнопку регулировки громкости звука! Ну, а почему бы и нет. :) Таким образом можно переназначить уже имеющиеся кнопки громкости на, например, start/select.

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост

❯ Геймпад


После того, как сервис был готов и отлажен, нужно было реализовать хардварную часть проекта — сам геймпад. В качестве контроллера я, как уже говорил, выбрал Raspberry Pi Pico на базе МК RP2040 — бодреньком контроллере с двумя ARM Cortex-M0 ядрами. Стоит копейки, а в отличии от ESP'шек, его SDK не такое перегруженное и выглядит более приближенным к bare-metal.

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост



На данный момент, я решил развести все кнопки на бредборде — макетной плате без пайки, т. к. макеток для пайки у меня под рукой не было. Сделал примитивный геймпад:

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост



Развел на соответствующие GPIO:

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост



И написал примитивную прошивку, которая отслеживает состояние кнопок. В прошивке точно так же есть словарь, задающий ассоциацию между физическими пинами и «виртуальными» кнопками. При нажатии или отжатии кнопки, программа изменяет стейт и отсылает новое состояние планшету.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "pico/stdlib.h"

#include "pico/time.h"

#include "hardware/uart.h"

struct keyMap

{

int gpio;

char key;

bool pressed;

int lastTick;

};

keyMap keys[] = {

{

15,

'L',

false,

0

},

{

14,

'U',

false,

0

},

{

13,

'D',

false,

0

},

{

12,

'R',

false,

0

},

{

11,

'E',

false,

0

},

{

10,

'B',

false,

0

},

{

20,

'A',

false,

0

},

{

21,

'C',

false,

0

}

};

#define KEY_NUM 8

int main() {

stdio_init_all();

uart_init(uart0, 921600);

gpio_set_function(PICO_DEFAULT_UART_TX_PIN, GPIO_FUNC_UART);

gpio_set_function(PICO_DEFAULT_UART_RX_PIN, GPIO_FUNC_UART);

sleep_ms(1000); // Allow serial monitor to settle

for(int i = 0; i < KEY_NUM; i++)

{

gpio_init(keys[i].gpio);

gpio_set_dir(keys[i].gpio, false);

gpio_pull_up(keys[i].gpio);

}

while(true)

{

int now = time_us_32();

for(int i = 0; i < KEY_NUM; i++)

{

char buf[5];

buf[1] = ' ';

buf[3] = '\n';

buf[4] = 0;

if(!gpio_get(keys[i].gpio) && !keys[i].pressed && now - keys[i].lastTick > 15500)

{

buf[0] = 'D';

buf[2] = keys[i].key;

puts(buf);

keys[i].lastTick = now;

keys[i].pressed = true;

continue;

}

if(gpio_get(keys[i].gpio) && keys[i].pressed && now - keys[i].lastTick > 15500)

{

buf[0] = 'U';

buf[2] = keys[i].key;

puts(buf);

keys[i].pressed = false;

keys[i].lastTick = now;

}

}

}

}

Собираем всё вместе и тестируем. Хоба, всё работает, мы можем перемещаться по менюшке используя наш геймпад!

Сам себе игровая консоль: превращаем планшет с нерабочим тачскрином в игровой девайс из 8 кнопок и микроконтроллера Смартфон, Телефон, Идея, Своими руками, Arduino, Embedded, Встраиваемые системы, Планшет, Игры, Консоли, Самоделки, Моддинг, Android, Linux, Java, C++, Kernel, Покупка, Raspberry pi, Микроконтроллеры, Видео, Длиннопост



А почему бы не попробовать поиграть в какую-нибудь игру? Ну мы же консоль вроде делаем: берём эмулятор NES, биндим кнопки в настройках и наслаждаемся игрой в Марио!

❯ Заключение


Реализация этого проекта заняла у меня не так уж и много времени: всего около 3-х дней работы по вечерам. Вероятно кто-то спросит: «а чего ты просто Bluetooth геймпад не купил?». Так это не прикольно ведь. Гораздо приятнее играть в девайс, к которому ты приложил руку сам. Более того, не у всех старых планшетов есть BT. Обошёлся на данной стадии проект недорого: планшет мне подарили бесплатно (точно также у вас дома может лежать подобный), RPi Pico — 350 рублей, кнопки по 10 рублей/штучка.

В целом, я сам по себе обожаю копаться в различных железках и их софтварной части (вспомнить хотя-бы статью про перекомпиляциюu-boot из вендорских исходников для нонейм консоли), а созидать что-то свое вообще вызывает какие-то нереальные всплески эндорфина — оно и понятно! :)

Однако несмотря на то, что мы уже имеем рабочий «прототип», проект далёк от завершения: я намерен довести его до конца и окончательно перевоплотить старый планшет в автономную игровую консоль (и рассказать об этом во второй части статьи). Для этого мне понадобится распечатать корпус и кнопки на 3D-принтере. К сожалению, у меня в городе ни у кого особо нет 3D-принтеров, поэтому начну копить на Ender 3, а от вас, читателей, с удовольствием почитаю мнение в комментариях и советы касательно выбора принтера!

Статья подготовлена при поддержке TimeWeb Cloud. Подписывайтесь на меня и @Timeweb.Cloud, чтобы не пропускать еженедельные статьи про моддинг различных гаджетов!

Показать полностью 10 1
[моё] Смартфон Телефон Идея Своими руками Arduino Embedded Встраиваемые системы Планшет Игры Консоли Самоделки Моддинг Android Linux Java C++ Kernel Покупка Raspberry pi Микроконтроллеры Видео Длиннопост
33
Посты не найдены
О нас
О Пикабу Контакты Реклама Сообщить об ошибке Сообщить о нарушении законодательства Отзывы и предложения Новости Пикабу Мобильное приложение RSS
Информация
Помощь Кодекс Пикабу Команда Пикабу Конфиденциальность Правила соцсети О рекомендациях О компании
Наши проекты
Блоги Работа Промокоды Игры Курсы
Партнёры
Промокоды Биг Гик Промокоды Lamoda Промокоды Мвидео Промокоды Яндекс Директ Промокоды Отелло Промокоды Aroma Butik Промокоды Яндекс Путешествия Постила Футбол сегодня
На информационном ресурсе Pikabu.ru применяются рекомендательные технологии