![](https://habrastorage.org/webt/kb/ze/cu/kbzecuwdv23cybc3z0yfyhysxmq.jpeg)
В команде М.Видео-Эльдорадо очень много увлеченных людей. Поэтому достижения заморских Левшей всегда вызывают неподдельный интерес. Вот и сегодня предлагаем вашему вниманию крутую историю от первого лица.
Мне понадобилось много времени, но я наконец смог создать собственный картридж для Game Boy. С функцией Wi-Fi! Пока он может демонстрировать простой обмен данными в стиле telnet и получать доступ к статьям Википедии, но я уверен, что в будущем я смогу показать больше. В этой статье я расскажу, как он устроен и работает.
Если вы хотите просто посмотреть на картридж в работе или узнать все подробности, то я крайне рекомендую сначала просмотреть видео, потому что в нём описываются трудности и их решения. Статья будет менее наглядной в объяснениях и в ней в основном представлено подробное описание устройства, поэтому если вы незнакомы с параллельным интерфейсом памяти Game Boy, то стоит начать с его описания в видео.
На что способен этот картридж
Прежде чем вы потратите своё время на эту статью, мне нужно рассказать, на что способен этот картридж и, что более важно, на что он не способен. Это простой картридж Game Boy на 32 КБ с микроконтроллером ESP8266, добавляющим функции Wi-Fi.
Благодаря нему вы можете получать доступ к Интернету или локальной сети через Game Boy или отправлять с него данные. Так как ESP8266 может выполнять большой объём предварительной обработки для Game Boy, вполне можно реализовать клиент Twitter или браузер Reddit. Это не стандартный браузер, но можно относительно просто реализовать сервисы на ESP8266 и написать простой код, который исполняется на Game Boy, используемом в качестве интерфейса.
На момент написания статьи я реализовал всего два демо: простое коммуникационное демо, позволяющее подключиться к картриджу по telnet, а также отправлять и получать текстовые сообщения. И клиент Википедии, позволяющий вводить название статьи, а затем получать «выдержку» из этой статьи и отображать её. [На случай, если вам понравилась экранная клавиатура: это не моя работа, а стандартная клавиатура из Game Boy Development Kit gbdk-2020. Это часть реализации stdio проекта, которую я использую в своём демо.]
![](https://habrastorage.org/webt/di/a6/sm/dia6smvnvdknzc8xyqon39pkkzs.jpeg)
Слева направо: экран приветствия демо Википедии, в том числе текущий IP-адрес картриджа Game Boy, экранная клавиатура для ввода названия статьи, начало статьи, отображаемой на экране Game Boy (прокрутка выполняется кнопкой A).
Этот картридж не позволяет играть в старые игры онлайн или получать доступ к библиотеке ROM-файлов по сети. Чтобы играть в старые игры онлайн, нужно было бы подвергнуть их сильной переработке, потому что они не знали, что такое сеть, как обращаться с задержками и как вообще работает доступ к сети в архитектуре моего картриджа.
Доступ к библиотеке ROM кажется более реалистичной задачей, но поскольку в моём картридже ПО Game Boy исполняется с обычной EEPROM, которую нельзя программировать микроконтроллером, для этого потребовалось бы изменить конструкцию, или позволив ESP8266 программировать двоичными данными из сети, или полностью заменив память, что сделано в других проектах; однако этот подход имеет свои недостатки, о которых рассказывается ниже.
Интерфейс картриджа
Так как я решил добавить WiFi в картридж, а не к линк-кабелю (для этого уже есть два отличных проекта (проект Энди Веста, демонстрирующий WiFi по линк-кабелю со специальной игрой, игра в Tetris по линк-кабелю WiFi с несколькими людьми), нам сначала нужно поговорить о том, как работает картридж Game Boy и как мы можем реализовать связь.
В отличие от линк-кабеля, обменивающегося данными с низкой частотой в 8 кГц, картриджи Game Boy являются не интерфейсом для реализации произвольной коммуникации, а серьёзной параллельной шиной памяти, работающей на частоте 1 МГц. Вместо расслабленной передачи данных для синхронизации многопользовательских игр она работает в качестве основы всей памяти Game Boy с очень конкретным протоколом и безжалостной тактовой синхронизацией.
Если для вас это звучит слишком драматично, скажу, что ту же шину Game Boy использует для доступа к внутренней ОЗУ и VRAM. На самом деле, картридж видит эти запросы на своих контактах, поэтому этот интерфейс очень важен. Если вы хотите знать все подробности, то порекомендую вам руководство по процессору Game Boy, ну а вкратце ситуация такова:
![](https://habrastorage.org/webt/nv/q5/4z/nvq54zl8xno00jwzvhjwokqkgag.jpeg)
Схема контактов картриджа Game Boy
Разъём картриджа содержит 16 адресных контактов и 8 контактов данных. На адресных контактах Game Boy задает адреса памяти, которые он хочет считать или записать, а затем на контактах данных считывает или записывает один байт данных. Этим доступом управляют тактовый контакт (CLK), контакт чтения (RD), контакт записи (WR) и контакт выбора чипа (CS). RD и WR используются для указания того, считываются или записываются данные, а CLK обеспечивает опорный сигнал синхронизации на 1 МГц для тайминга чтения/записи. CLK особенно важен, потому что RD и WR не возвращают своё состояние между последовательными операциями чтения и записи, поэтому без сигнала синхронизации было бы сложно разобрать последовательность считываний.
Контакт CS для нашего картриджа не особо важен. Обычно контакт CS используется для включения или отключения конкретных чипов (поэтому его также называют «контактом включения чипа» или «контактом включения вывода»), чтобы переключаться между разными чипами на одной шине.
Однако в простом картридже для Game Boy ROM использует только нижнюю половину памяти. [Существуют более объёмные картриджи Game Boy, в которых используется так называемый Memory Bank Controller (MBC), позволяющий сопоставлять с этим участком 32 КиБ разные области большой памяти.] 16 контактов могут адресовать 64 КиБ памяти, но на ROM есть только 32 КиБ, а оставшиеся 32 КиБ используются для адресации внутренней памяти (ОЗУ, VRAM и т. п.) и регистров (подробности см. в руководстве по ЦП).
Следовательно, с точки зрения простого картриджа, самый старший адресный контакт A15 действует как инвертированный вход CS, так как он имеет значение HIGH только для верхних 32 КиБ.
Также в разъёме картриджа есть питание через контакты +5V и заземления (GND), которые мы будем использовать для WiFi-картриджа, и сброс (RST), а также контакт звука, который нам не понадобится.
Почему это сложно?
Кажется, что интерфейс прост, не так ли? Разумеется, первым делом я захотел использовать современный подход: подключить ко всем этим контактам современный микроконтроллер и обеспечивать связь программно. Возможно, нам понадобилось бы несколько схем сдвига уровня или приемопередатчиков шины, а скромное количество контактов ограничивает выбор возможных микроконтроллеров, но в целом современный микропроцессор на 100 МГц без проблем бы мог общаться с ЦП Game Boy на 4 МГц по шине данных 1 МГц, правда? Да, на самом деле это возможно, как показывают, например, STM32-картридж (168 МГц) Emeryth и RP2040-картридж u/LyneByLine (0xen). Последний даже работает с Game Boy Color, имеющим вдвое большую тактовую частоту. Однако RP2040 нужно разогнать до 200 МГц, поэтому по сравнению тактовых частот кажется, что эти проекты находятся на пределах возможного такого подхода.
Проблема в том, что тактовый сигнал 1 МГц для картриджа означает такты памяти. Один такт равен одному записанному или считанному байту. Такт ЦП может быть более универсальным, но ЦП не способен проверять адресные контакты, интерпретировать их значения, получать или даже вычислять данные, которые должны быть ответом, и задавать выходные контакты за один такт.
Как показывают перечисленные выше проекты, он на это способен, но это означает, что резервов на что-то другое практически не остаётся. ЦП или может следить за контактами и больше почти ничего не делать, или попробовать поработать с прерываниями. Но у вас есть только 500 нс, чтобы отреагировать на эти прерывания, а поскольку мы постоянно получаем события чтения и записи, даже для внутреннего доступа к памяти, прерывания срабатывают постоянно.
Для реакции на простые прерывания Arduino требуется несколько микросекунд, так что их всё равно нельзя использовать. Я не знаю точно, насколько быстрее всё будет работать, если писать код на языке ассемблера, но это всё равно освободит не так много времени для ЦП.
А лишнее время обработки нам нужно. ЦП должен разобраться с WiFi-подключением, сделать запросы к Game Boy (например, http-запросы к Википедии) и преобразовать данные для Game Boy, потому что мы точно не хотим парсить JSON-ответ Википедии на самом Game Boy. Возможно, здесь могут помочь новые многоядерные микропроцессоры, и возможно, однажды davedarko заставит работать свой проект WiFi-картриджа на основе ESP32, но пока все эти способы являются только доказательством реализуемости концепции. Поэтому я выбрал более изящное решение (по крайней мере, мне так кажется) и обошелся простым старым ESP8266 (80 МГц), оставшимся от другого проекта. [Точнее, я использую модуль ESP-12F.]
Как это работает
Идея заключается в том, чтобы уменьшить количество запросов к памяти, которые должен обрабатывать микроконтроллер, а также количество контактов, которыми ему нужно управлять. То есть первым делом, вместо того, чтобы пытаться заменить весь картридж микропроцессором, воспользуемся компонентом, идеально подходящим для обработки обыденных запросов к памяти: старой доброй EEPROM на 32 КиБ. Давайте начнём с простой схемы картриджа на 32 КиБ, потому что большинство запросов к памяти статичны, например, получение изображений или байтовый код, выполняемый в Game Boy. Получение данных из сети, а значит, и от ESP, происходит реже.
![](https://habrastorage.org/webt/v4/tw/0x/v4tw0xudqd2tx5vc0cfcam8yqnk.jpeg)
Печатная плата WiFi-картриджа.
Чтобы получать данные и от ESP, я решил отобразить два байта пространства памяти Game Boy на ESP8266: 0x7ffe и 0x7fff. Если Game Boy захочет считать с ESP, он получает доступ к 0x7ffe, чтобы один за другим получать байты, а если он захочет записать на ESP последовательность байтов, то использует 0x7fff. [Также это можно определить при помощи контакта RD или WR, но когда я проектировал свое решение, хотел избегать контакта WR, потому что он задается достаточно поздно в такте (см. руководство по ЦП).
Однако теперь, когда всё работает, я думаю, что один контакт RD можно было использовать в сочетании с только одним адресом ESP. Но никого не волнует трата одного байта, поэтому я не буду заново проектировать всю плату только для того, чтобы она стала эффективнее на один байт.] Эти два адреса имеют два преимущества. Они находятся на самом конце обычного пространства ROM на 32 КиБ и влияют на наш код программы, только если бы мы использовали 32 КиБ и они бы имели двоичный вид, который легко можно было бы распознать при помощи нескольких логических вентилей. Так как 0x7ffe = 0b0111111111111110 и 0x7fff = 0b0111111111111111, нам достаточно инвертировать старший бит A15 и пропустить его через вентиль AND (в моей конструкции NAND) вместе со всеми другими контактами до A1.
Результат даёт понять, был ли адресован ESP (ESPADDR), а последний бит A0 можно использовать, чтобы определить, выполняется чтение или запись. Если скомбинировать этот последний контакт с ESPADDR, то мы получим ESPRD, который имеет высокий сигнал, только если Game Boy пытается выполнить чтение из ESP, и это единственный случай, когда ESP должен включать вывод на этих контакта, которые подключены к линии данных с D0 по D7.
![](https://habrastorage.org/webt/f1/ce/8u/f1ce8uwecgurosfmnp8fckfbu80.png)
Логические вентили комбинируют адресные контакты, обеспечивая более абстрактные логические состояния, определяющие, какой компонент адресуется.
Но самое важное то, что комбинирование ESPADDR с тактовым сигналом Game Boy через вентиль AND (на самом деле я вместо него использовал логику NOR) приводит к ESPCLK, который имеет высокое значение, только если Game Boy хочет обменяться данными с ESP, что включает в себя задний фронт исходного сигнала CLK, являющийся важным показателем момента, когда Game Boy начинает управлять контактами данных. Это значит, что теперь ESP нужно беспокоиться только об единственном входе, который изменяется только когда ESP должен отреагировать и нам нужен только ESPRD на втором контакте, чтобы решить, как реагировать на ESPCLK.
В оставшееся время ESP может делать что-то другое и заниматься собственными делами. Единственный недостаток здесь заключается в том, что поскольку Game Boy меняет адрес слегка позже следующего нарастания тактового сигнала, мы получаем небольшой дополнительный пик в конце каждого ESPCLK, который нам не нужен, но его легко можно отфильтровать программно. [Я даже не совсем уверен, что этот пик приводит к срабатыванию прерывания, потому что он слишком медленный. Да, если прерывание уже выполняется, оно может запуститься снова, но на этом этапе мы даже ещё не вошли в функцию прерывания предыдущего триггера.]
![](https://habrastorage.org/webt/wm/mf/hi/wmmfhid6jzi4u-7atphk_ftreg4.png)
Скриншот осциллографа, демонстрирующий тактовый сигнал Game Boy, один из адресных контактов и ESPCLK, а также ESPRD. На последнем возникает высокий сигнал только если адресные контакты адресуют ESP. (Стоит заметить, что из-за ограничений на метки в этом диапазоне они помечены как eCLK и eRD).
Эти логические вентили также используются для управления выводами других компонентов на шине. ESP со своей логикой на 3,3 В подключён к линиям данных Game Boy на 5 В через двунаправленный приёмопередатчик шины, а его контакт включения вывода напрямую управляется ESPADDR, чтобы отключать его, если только в контуре не находится ESP.
Аналогично, ещё несколько логических вентилей генерируют ROMADDR, который имеет высокий сигнал, только если адрес находится в диапазоне от 0x0000 до 0x7ffd, чтобы отключать вывод EEPROM во всех остальных случаях. За исключением нескольких возможных временных наложений на несколько наносекунд, это должно устранить самые опасные конфликты шины, за исключением одного: ESP должен программно отключать свои выводы с D0 по D7, если только ESPRD не имеет высокого сигнала.
Если он когда-нибудь включает вывод при низком сигнале ESPRD, а ESPADDR при этом имеет высокий сигнал, то он будет работать в обратном направлении на приемопередатчике шины, по сути вызвав короткое замыкание. Пока в моём случае это привело только к провалу напряжения на Game Boy и ESP; разумеется, в процессе разработки это происходило довольно часто, но я не могу гарантировать, что рано или поздно это не приведёт к повреждениям.
И так мы переходим к подаче напряжения на картридж. У нас есть смешанная система с ESP, работающим на логике 3,3 В, и EEPROM с логическими вентилями, использующими те же 5 В, что и Game Boy. Поэтому 3,3 В преобразуются из 5 В Game Boy при помощи NCP1117, обеспечивающего достаточный запас. На самом деле, тот же регулятор напряжения используется в NodeMCU DevKit v1.0; как и в NodeMCU, я добавил конденсатор на 100 мкФ, чтобы стабилизировать вывод, наряду со стандартными парами на 100 нФ и 10 мкФ.
Тем не менее, мой первый прототип имел серьёзные проблемы с провалами напряжения, поэтому хоть мне и не удалось полностью избавиться от конфликтов шины из-за других погрешностей проектирования первого прототипа, я добавил поверх 680 мкФ. С ним картридж достаточно стабилен, по крайней мере, когда батарейки свежие. Если они хотя бы немного разрядились, то начинают наблюдаться повреждения памяти и даже при полном заряде батарей можно слышать статические сигналы из динамика Game Boy, когда ESP8266 использует свой модуль WiFi.
Подозреваю, что старый линейный регулятор напряжения Game Boy не может справляться с резкими скачками тока модуля WiFi. Весь Game Boy использует чуть больше 100 мА при 5 В. Если мы наивно допустим идеальную эффективность регулятора мощности, то это приводит примерно к 150 мА при 3,3 В. Стоит также заметить, что такое потребление должно быть относительно стабильным, потому что старое оборудование наподобие Game Boy не переключается быстро между разными режимами питания и не меняет тактовые скорости, как современное оборудование.
В отличие от него, ESP8266 может быстро менять своё энергопотребление, получая ток в интервале от нескольких 10 мкА до целых 200 мА. При обычном паттерне использования мы можем допустить, что он получает около 80 мА (что будет вполне нормально для Game Boy) с пиками до 150 мА при использовании модуля WiFi, что является достаточно сильным ударом для регулятора напряжения Game Boy. Не знаю, позволит ли добавление конденсаторов большей ёмкости решить проблему, хотя в этой ситуации хорошо то, что эти пики WiFi кажутся короткими, и Game Boy вполне справляется с повышенным энергопотреблением, если у него есть время адаптироваться.
Полные схемы, структуру печатной платы и файлы gerber можно найти в репозитории проекта на github.
Паттерны связи
Разобравшись с оборудованием, мы можем приступить к написанию кода. Нам нужно написать две программы: код, который будет храниться в EEPROM и выполняться в Game Boy, и ПО для ESP8266.
Код для Game Boy написан при помощи Game Boy Development Kit gbdk-2020, который в случае этих текстовых примеров сильно упрощает работу. Так как gbdk-2020 реализует stdio, мы можем просто добавить несколько putc, print и getc в текстовый редактор, чтобы получить нужный нам код. Пока я не буду пробовать создавать более визуальные демо, основная работа происходит в коде ESP.
Из-за привычности и удобства я писал код ESP в Arduino IDE. Нет ничего плохого в том, чтобы использовать упрощенные команды Arduino, если вы знаете, как избежать их для оптимизации. По сути, код подключается к точке доступа WiFi с заранее прописанными данными входа и мониторит два массива символов как входной и выходной буфер для Game Boy. Демо будет реагировать на команды во входном буфере и записывать результаты в выходной буфер, запрашивать веб-страницы или преобразовывать данные в текст для Game Boy.
Сложность заключается в том, как передавать или получать отдельные данные через выделенные адреса памяти. С точки зрения ESP, он должен реагировать на мониторящий прерывания ESPCLK и на прерывание на ESPRD. [Так как прерывание слишком медленное, чтобы сразу проверить ESPRD после того, как его вызвал возрастающий сигнал ESPCLK, мы должны различать их через отдельные прерывания.] Но поскольку требуется несколько микросекунд ещё до того, как он начнёт исполнять свой код, у нас нет возможности мгновенно реагировать на то, что нам передаёт Game Boy.
Вместо этого мы выполняем срабатывание на спадающем фронте (на нём тайминг, наверно, не так важен) и используем прерывания, чтобы узнать, хотел ли Game Boy только что выполнить считывание или запись, чтобы подготовиться к следующему событию.
Так как мы выполняем свой код на Game Boy, то мы можем научить его терпению и создать различные паттерны связи для считывания и записи байтов из ESP.
Если Game Boy хочет считать из ESP, он делает первую попытку считывания, возвращающую мусорные данные, потому что у ESP ещё не было достаточно времени для включения выхода GPIO. Однако прерывание заставляет ESP вовремя включить вывод для следующей попытки чтения и чтобы подготовить свои выводы для передачи первого байта. Поэтому Game Boy просто не обращает внимания на мусор из первой попытки чтения и (если не повторяет попытку слишком быстро, чем можем управлять мы) может получить первый байт при следующей попытке.
В свою очередь, второе считывание снова приводит к срабатыванию прерывания в ESP, который теперь может подготовить выводы ко второму байту. Следовательно, после отказа от самой первой попытки считывания Game Boy просто может последовательно считывать новые байты, не тратя время на попытки считывания. Они просто должны быть достаточно медленными. Чтобы остановиться, когда данных для чтения больше не осталось, ESP отправляет в качестве последнего байта 0x00, что идеально подходит для передачи null terminated strings. После срабатывания прерывания, когда 0x00 уже подготовлен на выводе, ESP отключает свой вывод, а Game Boy прекращает считывание после обнаружения 0x00.
![](https://habrastorage.org/webt/gn/wf/8a/gnwf8aos5bwtotjnqsy6oghxzus.png)
Когда Game Boy выполняет считывание из ESP8266, ему нужно одно дополнительное считывание, которое игнорируется. Последующие операции считывания должны быть достаточно медленными, чтобы они заставляли ESP подготавливать каждое последующее считывание.
С отправкой данных из Game Boy в ESP8266 всё немного сложнее. Проблема в том, что Game Boy управляет шиной данных только в течение 500 нс и мы не можем ничего изменить. При считывании из ESP, он может просто включить свои выводы на неопределённое время и ждать, пока Game Boy подхватит данные, но когда Game Boy записывает в ESP, он получает окно в 500 нс, чтобы поймать этот байт.
Однако мы можем отправлять одно и то же значение несколько раз. Для этого всё равно требуется хороший тайминг со стороны ESP, потому что это лишь значит, что спустя какое-то время возникнет еще одно окно в 500 нс, но это позволяет нам снова использовать первую запись в качестве триггера. То есть чтобы записать байт в ESP, Game Boy задаёт адресу 0x7fff to его значение, что приводит к слишком позднему срабатыванию прерывания на ESP.
На этот раз Game Boy остаётся в функции прерывания в течение нескольких микросекунд и просто начинает считывать ESPCLK в цикле, дожидаясь нарастания, означающего ещё одну запись. Затем Game Boy просто снова записывает то же значение в 0x7fff, но на этот раз Game Boy не нужно ждать прерывания. Он замечает нарастание ESPCLK в течение нескольких его тактов, ждёт ещё, пока он не спадёт (что происходит, когда Game Boy начинает управлять шиной данных в течение 500 нс, см. руководство по ЦП), а затем без проблем считывает байт за окно в 500 нс. Единственный недостаток заключается в том, что если только мы не хотим, чтобы ЦП постоянно следил за ESPCLK, он должен снова сначала запуститься по прерыванию, и следовательно, Game Boy должен отправлять каждый байт дважды с короткой паузой между операциями записи.
![](https://habrastorage.org/webt/gb/ey/08/gbey08jxe-apbkymovobgtauula.png)
Когда Game Boy выполняет запись в ESP8266, ему нужно повторять каждую попытку записи, потому что первая только подготавливает ESP8266 к перехвату данных при второй попытке.
Однако из самого Game Boy нужно передавать не так много данных; по крайней мере, я не могу придумать ничего, кроме как передача пользовательского ввода и запросов, инициированных действиями пользователя. Поэтому нас вполне устраивает, что запись в ESP выполняется чуть медленнее, чем получение из него данных.
О, и есть ещё одна сбивающая с толку деталь, которую лучше всего изложить в этом разделе: по какой-то причине мне пришлось включить подтягивающие резисторы для контактов GPIOв ESP8266, которые подключены к приемопередатчику шины с D0 по D7. Если этого не сделать, то некоторые из этих контактов (которые всё равно не имеют внешних подтягивающих резисторов) не меняют сигнал на низкий при передаче данных устройством ESP.
Предполагаю, что это как-то связано с тем, как я включаю вывод через регистр GPES в ESP. Возможно, я оказываюсь в какой-то конфигурации Open Drain, которая, разумеется, требует внутренних подтягивающих резисторов? Если кто-то знает, в чём причина, мне бы хотелось её узнать.
Создание собственного картриджа
Как понятно из названия раздела, я поделюсь всеми схемами оборудования и исходным кодом, чтобы вы могли создать собственную версию устройства. Однако прежде чем вы схватитесь за паяльник, стоит изучить предупреждения, прочитать о требуемых инструментах и навыках.
Предупреждение
- Прочитайте о конфликтах шины и ограниченной подаче питания в разделе «Как это работает», чтобы понять, что это хак и он ни в коем случае не является «электрически надежным». Это хак. Game Boy на это не рассчитан, а моя печатная плата — сырое электронное устройство. Результаты могут быть ненадежными и вы можете повредить Game Boy, микроконтроллер или другое оборудование.
- Я не физик и не инженер по электронике. Поэтому для меня достаточно того, что устройство работает и стоит ожидать, что в его конструкции есть электрические изъяны. (Да, иными словами, устройство нестабильно и может повредить Game Boy.)
- Как говорилось выше, ПО может вызвать конфликт шины. Если вы намереваетесь изменять код, вам нужно понимать, как работает обмен данными.
- На печатной плате нет диода, препятствующего обратному тока к источнику питания. Не запитывайте одновременно плату от Game Boy и от того, что используете для программирования ESP!
Необходимые инструменты
- Паяльник и материал, подходящий для пайки SMD.
- Преобразователь USB-serial, пригодный для программирования модуля ESP-12F (обычно для этого можно использовать плату разработки наподобие NodeMCU или похожую плату ESP32), и кабели.
- Программатор EEPROM наподобие TL866II с разъёмом/адаптером PLCC32.
- 3D-принтер для корпуса картриджа, хотя, вероятно, можно вставить в Game Boy и голую плату. Просто помните, что она может быть слегка толстой для стандартных картриджей Game Boy.
- Крайне рекомендуются: вольтметр и осциллограф для отладки на случай, если что-то сразу не заработает.
Рекомендуемые навыки
- Заказ или изготовление печатных плат.
- Подбор электронных компонентов, если вы не будете заказывать именно то, чем пользовался я.
- Пайка SMD (это был мой первый проект с SMD, так что могу сказать, что это не очень сложно).
- Базовые знания об использовании ESP8266, если он не является частью платы разработки (например, переход в режим программирования и прошивка).
- Если вы хотите вносить изменения в ПО, то вам нужны знания C, побитовых операций и null-terminated strings.
Список покупок
- Вам понадобится печатная плата. Свою я заказал на aisler.net, и в прошлом я просто поделился точным проектом, который заказал через этот сайт, так что вы с лёгкостью сможете заказать такой же. Но в этом случае рендер KiCAD отличался от архитектуры, показанной в KiCAD, и от того, что KiCAD экспортировал как файлы gerber, из-за чего некоторые линии 3,3 В отсутствовали на изготовленной плате. Я создал эти линии как часть заполненной области вместо явно указанных линий, но KiCAD отлично понял, что это сетевые соединения, добавил их в экспорт gerber и они даже отобразились в автоматизированном тестировании Aisler, так что мне кажется, в принципе это должно работать.
Я мог бы легко устранить эту проблему при пайке печатной платы, но поскольку Aisler не ответил на мой вопрос о том, как устранить её для других пользователей, я не могу рекомендовать заказывать у них, потому что, честно говоря, не знаю, как предотвратить эту проблему. [Так как это относится только к заказу на Aisler, я сделал большинство фотографий до устранения проблемы. Но если приглядеться к снимкам работающей платы, например, в видео, где я вставляю картридж в Game Boy, можно увидеть серебряный проводник в левом верхнем углу рядом со светодиодом; я использовал его, чтобы создать соединения 3,3 В, а также почти невидимый на конденсаторе 100 мкФ.] Вместо этого вам просто нужно воспользоваться файлами gerber из репозитория github или проектом KiCAD и сделать заказ у производителя плат, которому вы доверяете. - Необходимые компоненты на текущий момент стоят примерно 50 €. Пока самый дорогостоящий компонент — это EEPROM на 32 КиБ, так что логично будет поискать альтернативные источники для его покупки. Кроме того, разумеется, даже спустя лишь несколько недель после того, как я спроектировал, заказал и собрал этот проект, некоторые компоненты уже были недоступны, поэтому, возможно, вам придется искать альтернативы, если вы знаете, что именно искать. Ниже представлен список деталей, а также он есть по этой ссылке на корзину сайта mouser.
Количество | Артикул | Производитель | Описание |
---|---|---|---|
1 | 2491 | Adafruit | 802.11 ESP8266 SMT Module — ESP-12F |
1 | 69802-432LF | FCI / Amphenol | Component Sockets 32P PLCC SOCKET |
1 | AT28C256-15JU | Atmel | EEPROM 256K 32K x 8 150 ns 4.5V-5.5V |
2 | 74AHCT30D-Q100J | Nexperia | Logic Gates 8-input NAND gate |
2 | SN74AHCT02D | Texas Instruments | Logic Gates Quad 2-Input |
1 | SN74LVC4245ADWRG4 | Texas Instruments | Octal Bus Transceiver 3.3V To 5V Shifter |
2 | SN74LV1T34DBVRG4 | Texas Instruments | Voltage Levels Single Power Supply BUFFER Logic Level Shifter (no enable) 5-SOT-23 -40 to 125 |
6 | RC1206FR-0712KL | YAGEO | SMD Thick Film Resistors — SMD 12 kOhms 250 mW 1206 1% |
1 | CRGCQ1206J330R | TE Connectivity | SMD Thick Film Resistors — SMD CRGCQ 1206 330R 5% SMD Resistor |
1 | C1206C106K8PAC | KEMET | SMD/SMT Multilayer Ceramic Capacitors MLCC — SMD/SMT 10V 10uF X5R 1206 10% |
4 | C1206C104M5RACTU | KEMET | SMD/SMT Multilayer Ceramic Capacitors MLCC — SMD/SMT 50V 0.1uF X7R 1206 20% |
1 | TAJE687M010RNJV | Kyocera AVX | Tantalum Capacitors — Solid SMD 10V 680uF 20% 2917 ESR= 400 mOhm |
1 | TAJB107M010RNJ | Kyocera AVX | Tantalum Capacitors — Solid SMD 10V 100uF 1210 ESR=1.4Ohms 20% |
1 | 156120BS75000 | Wurth Elektronik | SMD Standard LEDs — SMD WL-SMRW RvsMnt Mono Rect 1206 Blue |
1 | NCP1117IST33T3G | onsemi | LDO Voltage Regulators ANA 1.0A 3.3V LDO REG |
7 | 5015 | Keystone Electronics | PCB MINI SMT TEST POINT |
После пайки платы нужно заняться сборкой ПО. Весь необходимый код можно найти на github, в папках «esp8266» и «gb» лежит код для ESP8266 и Game Boy. Стоит заметить, что в каждой папке есть подпапки с названиями, соответствующими демо. Всегда нужно использовать соответствующий код для ESP и Game Boy. Не перепутайте демо serial с демо wiki. Надеюсь, в будущем мне удастся добавить ещё несколько демо.
![](https://habrastorage.org/webt/xe/ez/ow/xeezowtrbo7gcinn4q21kjezkpy.jpeg)
Плата в сборе. (Да, мне стоило очистить её от остатков паяльной пасты.)
Чтобы собрать ПО для Game Boy, вам нужно скачать релиз gbdk-2020 и распаковать его так, чтобы получившаяся папка gbdk находилась рядом с папкой gb. После этого вам достаточно будет зайти в папку gb и ввести «make», чтобы собрать все демо (если у вас возникли проблемы в операционной системе без командной строки (например, в Windows), загляните в документацию gbdk-2020 и поищите инструкции о том, как собирать проекты в ней). В результате в каждой папке с демо должен появиться файл .gb, который нужно записать в EEPROM при помощи программатора. Вот и всё. Теперь достаточно вставить EEPROM в разъём PLCC32 на картридже, и Game Boy будет готов к работе.
![](https://habrastorage.org/webt/nc/b8/kz/ncb8kzeup74a9mibnbs-dt0cgpw.jpeg)
Так как плата не имеет возможности программирования EEPROM, он помещается в разъем PLCC32, чтобы можно было его извлекать для программирования.
В папке «esp8266» вы найдёте проекты Arduino, которые можно просто открыть в Arduino IDE. Отредактируйте файл «secrets.h», добавив в него свои данные WiFi (… или разработайте интерфейс Game Boy для выбора сети WiFi). Достаточно просто скомпилировать и загрузить код из Arduino IDE, как и с любым другим ESP8266 (при этом нужно выбрать подходящую плату), но сначала надо будет разобраться с правильным подключением. В вебе много туториалов и примеров, которые можно использовать для программирования ESP-12F и для подключения адаптера USB-serial. Если вы использовали точки тестирования Keystone из представленного выше списка, то можно воспользоваться соединителями из таблицы выше, чтобы закрепить провода при программировании. В большинстве схем достаточно подключить только 3V3, GND, RXD и TXD.
Взглянув на схемы, вы увидите, что на плате есть все резисторы для запуска ESP8266 в режиме исполнения по умолчанию, поэтому нужно подключить GPIO0 к GND при сбросе ESP, ненадолго соединив nRST с GND (это эквивалент удерживания BOOT одновременно с нажатием EN на плате разработки), чтобы перейти в режим программирования. При программировании ESP убедитесь, что Game Boy отключен, потому что в схеме нет диода, препятствующему движению тока обратно в какой-то из источников напряжения.
![](https://habrastorage.org/webt/13/hw/pf/13hwpfghw2zxko75ivnp_geuhrg.jpeg)
Крайне рекомендую использовать зажимы, чтобы подключать ESP для программирования.
На этом этапе вы уже, вероятно, сможете вставить плату непосредственно в Game Boy. Однако я рекомендую распечатать хотя бы нижнюю часть корпуса картриджа, потому что это упростит работу с ним. Просто возьмите файлы STL с github или с thingiverse, создайте слайсы с максимальным разрешением принтера (всё равно деталь не очень большая) без опор и распечатайте их. Стоит заметить, что моя плата имеет толщину 1,6 мм и я модифицировал картридж так, чтобы он имел довольно тонкие стенки с учётом довольно толстой платы, а также с учётом высоты разъёма под EEPROM. Сомневаюсь, что эта плата поместится в любой обычный картридж Game Boy.
Заключение
Хотя получившийся картридж и далёк от того, чтобы запускать его в производство, я очень доволен результатом. В конечном итоге, я просто хотел доказать себе, что разбираюсь в старом оборудовании достаточно, чтобы расширять его возможности при помощи современных инструментов. С точки зрения полезности, я не вижу каких-то функций, которые нельзя было бы реализовать при помощи более качественных и новых инструментов, чем Game Boy. Да, эта консоль очень распространена и достаточно дёшева, но несмотря на её надёжность и прочность, экран настолько плох, что работа с Game Boy очень раздражает.
Сегодня существуют гораздо более дешёвые и качественные портативные альтернативы, поэтому здесь я вижу только ностальгическую ценность. Возможно, кто-нибудь захочет открыть ресторан в ретро-тематике, где гости смогут делать заказы через Game Boy с функцией WiFi?
![](https://habrastorage.org/webt/jz/iv/og/jzivogo3vvjrxgmzmai7jddtfsa.png)
Комментарии (5)
tormozedison
01.03.2022 14:03Можно по телнету гнать в реальном времени MIDI-данные, а силами консоли их тут же озвучивать.
MaximKharin
01.03.2022 17:26+1Интересно, а для NES такой картридж возможен?
Game Boy у меня раньше был, потом я его другу подарил, а он потерял. Сейчас опять заинтересовался темой, у меня был картридж с чиптюн-трекером, может, опять попробую достать.ADiaikin
02.03.2022 12:19+2Кажется нас всем предстоит расчехлить в скором времени свои ZX Spectrum :).
ZekaVasch
Очень трудно читать сей перевод.
mvideo Автор
Текст в оригинале доступен по ссылке.