Как стать автором
Обновить

Сам себе игровая консоль: как я сделал свой «тетрис» с нуля. Что происходит, когда программист встречается с железом?

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров11K
Всего голосов 36: ↑34 и ↓2+44
Комментарии84

Комментарии 84

Друзья! Вчера на DTF я проводил опрос среди читателей и несколько человек успели "зарезервировать" девайс, чтобы я потом собрал такой и для них. Если Вам тоже было бы интересно - отпишите под этот коммент.

а исходник игрушки доступен? было бы интересно и ее портировать )

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

Есть смысл в этом проекте?

Конечно, есть. В основном, на мой взгляд, в том, что автор с интересом и удовольствием провёл время.

Из практических соображений, я бы брал таки ESP32, там и RAM/Flash больше, и SPI быстрее и если делать игровую платформу, то вайфай уже есть -- можно многопользовательские игры делать. Сам давно думаю про игровую платформу, но не могу придумать "идеал". Хочется дисплей побольше, где-то бы 3.5", но чтобы разрешение там было не таким большим, как у тех, что доступны. Самый "разумный" у меня есть ILI9341, 320x240 2.8" -- это самый близкий к желаемому. Но расход памяти под буфера/спрайты/виртуальные экраны довольно высок при таком разрешении. Вот и решил пока выждать, авось вернутся RPi Zero по 5 долларов...

заказали бы его по себестоимости?

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

Из практических соображений, я бы брал таки ESP32, там и RAM/Flash больше, и SPI быстрее

У еспшки из серьезных преимуществ лишь наличие внешней SPI-шины для SRAM и встроенного DAC. Малинка по производительности уделает есп, т.к гонится аж до 400(!)мгц, правда, с соответствующим потреблением и тепловыделением :)

В ОЗУ пока не упираемся, а ПЗУ хватает для игрушек уровня Java-игр с кнопочных мобилок (что уже неплохо для DIY!).

Хочется дисплей побольше, где-то бы 3.5", но чтобы разрешение там было не таким большим, как у тех, что доступны

Эт да. Вот бы хотя-бы 2.4" матрицу 128х160. Впрочем, если есть осцилл - можете купить китайский закос под Galaxy S2/S3/S4 с авито на Java (100-200 рублей стоят). Дисплеи у них с паябельными шлейфами с очень приятным шагом (1мм), 5в подсветкой, интерфейсом 8080 и достаточно низкого разрешения (240х320 на 5" - норма), распиновки на них нет, но с осциллом можно будет найти сигнальные линии прям на рабочей мобилке :) Бонусом - резистивный тачскрин.

Вот и решил пока выждать, авось вернутся RPi Zero по 5 долларов...

Не вернутся. Но для вас тоже есть вариант за норм деньги) Lctech Pi Zero на базе AllWinner F1C200s - ARMv5 ядро без FPU, 64мб DDR ОЗУ прямо на чипе, Linux/Melis OS в качестве системы. 1.5к рублей на алике. Что самое классное там - так это коннектор под 40pin дисплеи от навигаторов прямо. Подключаешь дисплей и он работает из коробки. Но тач (резистивный) работать не будет, по крайней мере, без отключения UART-консоли :c

Модуль Lctech Pi F1C200S Linux с открытым исходным кодом | Запасные части | AliExpress

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

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

но на DTF уже несколько человек зарезервировали под себя и попросили собрать :)

Прошу прощения за необразованность, но это где?

Я "из спортивного интереса" сделал себе "часики": https://www.youtube.com/watch?v=7H-2-X1M7PA -- заинтересовавшихся было чуть больше нуля.

dtf.ru


У меня ж техноблог, люди интересуются

часики прекрасны! а исходник доступен? хочу сконвертировать для своего мульти-девайса )

Внимательный зритель мог бы нажать на описании под видео "показать больше":

Обратите внимание на последнюю строчку
Обратите внимание на последнюю строчку

Но, вдруг чего не видно, оставлю ссылку ещё и тут: https://github.com/jef-sure/ili9341_dgx

спасибо!

Кстати, если вдруг захочется рукописные шрифты самостоятельно делать, у меня для этого отдельный проект появился. Я его не закончил ещё, но получить нужные наборы кривых позволяет. В часах он не использовался, там я координаты "на глаз" подогнал. Изза незаконченности проекта, описания/документации нет, но, достаточно клонировать и загрузить в браузере index.html: https://github.com/jef-sure/hw-fonts

Кстати, если вдруг захочется рукописные шрифты самостоятельно делать, у меня для этого отдельный проект появился. Я его не закончил ещё, но получить нужные наборы кривых позволяет. В часах он не использовался, там я координаты "на глаз" подогнал. Изза незаконченности проекта, описания/документации нет, но, достаточно клонировать и загрузить в браузере index.html: https://github.com/jef-sure/hw-fonts

на ESP32-S3 предусмотрено несколько интерфейсов для дисплеев, включая RGB и есть готовые платы с дисплеями, да еще и с картридерами MicroSD. готовый набор по сути, что вполне может привлечь программистов не паяльщиков. Более того - odroid go на ней уже сделали

 F1C лучше все таки в baremetal попробовать было бы. Но это уже для тонких ценителей.

Bare-metal интересно, да. А вот исходники RTOS Melis не дают, ток под другой чипсет(

На еспшке 8080 интерфейс софтовый вродь

прекрасная статья, отличный проект! идея собственных консолей продолжает расширяться ) я свой тоже продолжаю ковырять )) https://www.espboy.com
https://habr.com/ru/articles/558954/

и даже уже есть для самостоятельной сборки упрощенный комплект https://oshwlab.com/espboy/espboy-easy-v1_copy

Пасиб большое! Интересная тема, однако я беру чип помощнее как раз таки для того, чтобы получить крутой графон уровня сонериков. Аффинные трансформации, альфа-блендинг, возможно софтовый 3D-растеризатор (без FPU, угу) :)

отличные планы! но esp8266 тоже кое что может )) почти все из перечисленного опробовано. можете брать мои исходники и адаптировать. а может захочется собрать ESPboy и опробовать фирменные AppStore/WebAppStore и фирменные IDE -- LittleGameEngine/MicoJS? ))

https://youtu.be/Us3U-DFOUJQ

https://youtu.be/fPyr4FFCSZI

https://youtu.be/qqwesfUvMiA

https://youtu.be/bzgAMhvfRJU

https://youtu.be/dR8sXnjZ5G4

Смотрю статью и вижу, что Вы там пишете про разрешение 128*128 экрана EspBoy, но на фотографии вижу девайс, в котором запущен ZX Spectrum с игрой Dizzy. Там же разрешение 256*192, картинка масштабируется, что-ли?

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

картинка интерполируется до 128x128

а в эмуляторе GameBoy есть выбор, обрезать края или тоже интерполировать

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

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

Хороший проект. Но мне просто интересно: как в 1993 году игра Doom работала на одноядерном процессоре с частотой 33 МГц, с экраном 320х240, и производительности хватало, а Вы пишете, что выбрали 2-ядерный процессор с частотой 133 МГц для экрана с меньшим разрешением потому, что иначе производительности не хватит? И это при том, что в Doom 2.5-мерная графика, а здесь всего лишь 2-3 десятка спрайтов. Такое и на ZX Spectrum работало, пусть даже с куда более бедной цветовой палитрой. Я это к чему: может, для такой игры и Arduino хватило бы, если к нему памяти добавить для экранного буфера? Или выше про ESP написали.

2 ядра мне изначально не были нужны, я взял RPi Pico поскольку про них мало информации и особо никто ничего серьезного не делал. Хотелось испытать его в деле. В думе, фактически выводом графики и звука занимаются отдельные чипы - на МК же эта работа ложится на проц (при наличии DMA, как я и показал в статье, это можно переложить на него).

Основная суть в том, чтобы иметь задел на будущее: возможность применять аффинные трансформации к спрайтам, альфа-блендинг, выводить партиклы и.т.п. Про разрешение упор не столько в проц, сколько в ОЗУ - всего 264кб, 240x320 сожрет половину памяти :(

Касательно частоты - консоль сейчас жрет слишком много (аж 0.2А), клок буду тормозить до 70мгц. Если что - астероиды в посте не единственная игра под нее :)

Что эта консоль, что EspBoy - out of stock :(

tindie.com запретил российским разработчикам публиковаться и paypal тоже всех залочил. на моем офф сайте www.espboy.com западным ребятам можно купить и они покупают. но я советую купить на Aliexpress копеечные детальки и на perfboard запаять согласно схемы с сайта. Или если что-то поприличнее, то взять Gerber DIY версию c easyeda.com и заказать у китайцев удобные готовые платки

Половина памяти - а так ли это много? Сколько сейчас памяти код программы занимает? Если 10%, то и ладно. Нагрузку на процессор пробовали измерять? Например, если рендеринг производится с частотой 50 кадров/сек, сколько миллисекунд занимает подготовка данных для одного кадра? Если почти 20 мс, тогда это впритык. А если, например, всего 2 или 5 мс, то получится, что процессор почти не нагружен.

Там же гарвардская архитектура - шины кода и данных отдельно. Обрабатывать 240х320 фреймбуфер, заливая его небольшими спрайтами - не проблема. Проблемы с филлрейтом могут начатся на полупрозрачных параллаксовых фонах только.

если рендерить по точкам, то это адски медленно

можно в моих исходниках ESPboy на Github поглядеть, как я рендерю быстро. в последней реализации рендера GB эмулятора, например, так вообще используется рендер, который исключает простои шины SPI.

https://github.com/xsrf/nbSPI

Но так приходится извращаться потому, что DMA нету. А если есть DMA, все гораздо проще, но тогда приходится кучу памяти тратить на буфер экрана. Но если рендерить вручную, то буфер экрана можно делать под любую удобную грубину цвета, экономя память, но на лету преобразуя построчно вывод на дисплей... еще есть методика слайсами рендерить и игру и ее потом на дисплей, имея размер буфера скажем 1/4 экрана или 1/8. вообще разные методики есть которые "не в лоб"

Пишут, что есть RP2040-zero - может, она меньше потребляет? Или это от размеров девайса не зависит?

Дисплей -- основной потребитель в консоли. Микроконтроллер кушает мало. Вайфай и блютус могут тоже кушать, ещё какое-то оборудование, но тут речь больше про дисплей.

RP2040 и есть RPi Pico :)

Не совсем. RP2040 -- MCU, а RPi Pico -- плата разработчика на этом микроконтроллере.

основные проекты по этой теме в мире это:

  1. www.arduboy.com

  2. www.pokitto.com

  3. www.gamebuino.com

ну и мой

4. www.espboy.com,

есть еще куча, например PlayDate, но еще более специфические

мой ESPboy может практически любой софт воспринимать от вышеперечисленных после перекомпиляции и небольших правок кода (а понаписано за последние годы для этих девайсов тонны всякого и комьюнити довольно активные, пришлось переделать основные их библиотеки под ESPboy), а для удобства загрузки приложений есть AppStore и WebAppStore, чего больше ни у одного проекта пока нет

а вообще еще больший перечень консолей на MCU тут:

https://github.com/oshaboy/awesome-indie-handhelds

а более крутых тут:

https://en.wikipedia.org/wiki/List_of_handheld_game_consoles

Есть загрузчик бинарников, или прошивка статически линкуется?

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

еще можно через веб загружать https://espboy.m1cr0lab.com/demo/appstore/ через Arduino IDE и через tool от Espressif

Так и не понял, там просто OTA-раздел используется, или именно самопальный формат?

ота

Понял. Скрипт линкера вообще каша у есп канеш. Хотел протянуть стдлиб от свой прошивки в кастомные бинарники, но у тенсилики непонятный закрытый ABI и забил

но у меня при компиляции внутри каждого основного кода добавляется еще код AppStore, который запускается, если стартануть консоль с зажатой кнопкой А или Б и таким образом всегда по OTA можно прошивку обновить, выбрав нужное из списка, если конечно доступен WiFi. потому, что каталог и приложения конечно же тоже из сети подгружаются и если я туда чего-то добавляю, у всех пользователей оно становится тоже доступно

Ну вот это и называется "как бы" статической линковкой, когда по сути сервисная часть прошивки линкуется с игрой)) В идеале конечно загружать сторонний код, но при этом прокидывать std и аллокатор от основной прошивки дабы не дублироваться.

сделал такое один отчаянный парень из Франции. но через такие дикие извращения, что я не стал идею развивать ) не люблю я магию, да и квалификация низковата для такого. стараюсь все делать на уровне школьного курса С/С++ и официальной документации, без реверсинжениринга и извращений )

да ничего отчаянного нет, можно конечно грузить эльфы и сразу патчить релокации, но это не оч круто для эмбеда такого уровня (хотя есп32 и сам эльфы грузит).

Ну для меня такое дюже сложно )

Спасибо за ссылки, поизучаю. Интересно, какие игры реализованы на Arduboy, где всего 2.5 килобайта памяти? OLED-дисплей SSD1306 в графическом режиме под буфер прилично памяти требует, что там еще запихать удаётся?

2.5 килобайта ОЗУ. ПЗУ там 32кб, чего хватает под простую монохромную графику, или процедурно-генерируемую.

Процедурно-гененируемую во флеш-память? Этак поиграешь какое-то время, и флешка сдохнет.

Не, зачем во флэшу? Имелось ввиду, что изображение будет строиться из примитивов и сразу гнаться на контроллер дисплея, а-ля lineto или fillrect.

Lineto и FillRect - это же медленно.

а разве в контроллер st7735 встроено аппаратное ускорение рисования примитивов? я так глубоко конечно не копался, но используя самые продвинутые графические библиотеки из существующих (не от Adafruit, она как раз самая тормозная), тестировал, и это все получалось не особенно быстро...

Я в этом не разбираюсь, потому и спрашиваю. С Adafruit совсем немного поигрался и на глаз заметил, что она тормозная донельзя. В паре своих проектов на Arduino использовал библиотеку, которая на oled-дисплее ssd1306 рисует только в текстовом режиме, для минимизации используемой памяти, но и она скоростью не отличается - 50 раз в секунду разве что одну строку текста можно обновлять. Вот я и удивляюсь, какими средствами на таком дисплее, да еще и при минимальной памяти, можно реализовать аркадную игру.

Эт с чего такие выводы? Самые обычные геометрические операции, у lineto тем более есть две реализации как минимум.

У меня пока что мнение такое, что самое быстрое обновление экрана - это засылать строки данных в контроллер на отображение попорядку из буфера, причем методом nbSPI. Все отрисовки при этом делать в буфере экрана в ОЗУ, причем не обязательно буфер иметь в формате RGB565, главное перед отправкой строки в контроллер, сконвертировать строку из буфера в RGB565. Рисование примитивов прям в контроллер будет сильно медленнее даже на разогнанной SPI (из-за относительной тормознутости SPI). Но я могу и ошибаться ) И конечно нужно использовать хадварный SPI, а не софтварный битбангом... Этот битбанг SPI раз в 10 медленнее, чем хадварный

Если что - либа Adafruit для дисплеев это лютый говнокод. Мало того что под эмбедом юзают ООП (пусть и полустатический), так еще и рисование в ней неоптимальное, а где-то вообще SPI битбангом реализовывает

Можете подсказать быструю графическую библиотеку для SSD1306?

Не могу, я сам всегда пилю их)) А в чем именно у вас проблема, реализации драйвера для общения с дисплеем (инициализация/поставить точку в таком-то месте или реализации функций для вывода графики (те же линии/прямоугольники/картинки)?

Пока проблема с пониманием, какие реально скорости отрисовки выжать из такого дисплея при подключении по i2c. Хочу приделать такой дисплей к MIDI-синтезатору, чтобы на нём рисовать что-то вроде фортепианной клавиатуры, на которой показывать, какие ноты в данный момент играются. Вот и думаю, насколько реально такое перерисовывать, наример, 50 раз в секунду.

Они не очень шустрые, но I2C в ардуине хватит)

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

для 128x64 I2C OLED на ESP8266 вот эту использовал и она довольно быстрая https://github.com/ThingPulse/esp8266-oled-ssd1306

если только текст выводить, то маленькую u8g2 https://github.com/olikraus/u8g2

что-то на эту тему Гайвер написал и жутко хвалил, но я не тестировал.

А для ST7735 и похожего три есть известные и довольно быстрые

Я Гайверовскую пробовал с 1306, мне очень понравилась. Но я не профессионал, балуюсь для себя.

Спасибо, попробую её при случае.

128x64 == 1KB данных. Максимальная скорость I2C для этого дисплея, если я правильно помню, 400 килобит/сек, получается 50 килобайт/сек. Если весь экран пересылать постоянно, то максмально 50 кадров/сек и получится.

Если посмотреть на даташит от SSD1306, то минимальный период для I2C SCL - 2,5 us. Что именно 400kHz.

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

Я вообще не в курсе частоты I2C "по умолчанию", просто ставлю сколько надо и всё. Если всё работает, то можно дальше ничего не подбирать :)

Спасибо. Я пока с Arduino Pro Micro развлекаюсь, до ESP и STM не знаю, когда доберусь.

Я пока с Arduino Pro Micro развлекаюсь

А смысл? Я себе сейчас взял для "микро" RP2040 Zero, вот уж реально "микро" и при этом нормально так всё по мощности. А вот эти вот ардуиновские "2 KB RAM" при работе с какой-либо графикой вообще не прикольны.

Один из смыслов - при подключении к PC он выглядит в системе, как MIDI-устройство. Я на нем сделал синтезатор, развиваю его потихоньку:

https://habr.com/ru/articles/731036/

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

2.5 кБ памяти - да, не прикольно, но это как раз некий challenge: что удастся из этого выжать. Есть библиотеки для ssd1306, которые памяти по минимуму требуют. Буду разбираться, как руки дойдут.

либа Adafruit для дисплеев это лютый говнокод

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

под эмбедом юзают ООП

Это, как раз, не страшно, а часто удобно. Если там не увлекаться виртуализацией, то код сравнимый.

так еще и рисование в ней неоптимальное

К чести TFT_eSPI, автор творчески подошёл к переписыванию адафрутовской библиотеки.

Мне написали, что Adafruit отрисовку каждого пиксела даже для текста делает через вызов функции writePixel, которая ещё одну функцию вызывает. Понятно, что это для универсальности сделано, но это очень медленно.

Adafruit зло )

writePixel

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

Я исходники не смотрел. Но, как мне сказали, эта writePixel вызывает ещё одну функцию. А если это ещё и функция, отнаследованная от виртуальной функции базового класса, это ещё добавляет накладные расходы. И никакие инлайны в таком случае не сработают.

Там всё не так прямо плохо. Проблема вообще в принципе оптимизации пересылок. Допустим, надо отрисовать точку, для этого типичному SPI дисплею требуется 5 операций:

  1. послать команду установки горизонтальной области х,х;

  2. послать координаты х,х;

  3. послать команду установки вертикальной области у,у;

  4. послать координаты у,у;

  5. послать данные цвета точки.

Если же надо нарисовать прямоугольник, то там совершенно те же 5 операций, только коородинаты несколько большую облать задают и за одну 5ю операцию заливается всё. Именно разбивка на операции тормозит всё рисование. Нарисовать одну точку или 200 по времени отличается не так сильно. Поэтому, стремятся применять оптимизации рисования примитивов, например, рисовать наклонную линию из отрезков горизонтальных/вертикальных линий, а не по точкам. Экономить пересылку координат, если одна совпадает, то не пересылать её повторно. Ну и так далее.

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

На счёт текста -- многие библиотеки делают отрисовку символов попиксельно, обеспечивая таким образом "прозрачность" для незаполненных участков.

Я для себя решил, что рисовать надо в виртуальных экранах, а затем просто пересылать на экран результат. Это получается часто эффективнее и быстрее, чем рисовать попиксельно на настоящем экране.

Мне сказали, что Adafruit текст как раз попиксельно и рисует.

Вот да. Я тоже так делал, когда нужна скорость. "Я для себя решил, что рисовать надо в виртуальных экранах, а затем просто пересылать на экран результат. Это получается часто эффективнее и быстрее, чем рисовать попиксельно на настоящем экране. ". Полагаю, что "виртуальный экран" - это буфер в ОЗУ, как правило строка в высоту равную высоте символов.

Я же давал ссылку на гитхаб с часиками, там это всё реализовано. Если коротко, то рисовать "в ОЗУ" намного быстрее, чем на физическом экране. Вплоть до того, что алгоритмы примитивов должны быть разными.
Я сделал грубый замер бенчмарков для наклонной линии:

  1. Если рисовать наклонную линию отрезками горизонтальных/вертикальных линий на физическом дисплее, то в среднем это в два раза быстрее, чем по точкам;

  2. Если рисовать наклонную линию горизонтальными/вертикальными отрезками в ОЗУ, то в среднем это в два раза медленнее, чем точками. Этот эксперимент, правда, был очень грубым и на другой архитектуре.

Рисование в ОЗУ быстрее во много раз. Если сделать несколько виртуальных экранов (буферов в ОЗУ) то можно разные комбинации с ними делать. Шрифты, рисующиеся отдельными точками, уже не так уж медленно работают. Вот второй пример (ссылка на гитхаб в описании): https://www.youtube.com/watch?v=9nOHqma-i0U

на Arduboy порядка 300 игр старой школы https://arduboy.ried.cl

штук 150 я портировал уже на ESPboy и потихоньку остальные добиваю

но последняя версия ArduboyFX имеет еще мегабайт флеша на борту и стали возможны игры совсем другого уровня, например https://community.arduboy.com/t/prince-of-arabia-fx

еще более свежее достижение - парни научились на ЧБ OLED рисовать градации серого и сделали под это библиотеку, https://community.arduboy.com/t/arduboyg-grayscale-library теперь новые игры делаются еще более крутые с учетом 4 полутонов и FX памяти

https://community.arduboy.com/t/untitled-grayscale-rpg-dev-log

https://community.arduboy.com/t/dark-under-ii

Зарегистрируйтесь на Хабре, чтобы оставить комментарий