Появление игры Pong ознаменовало начало новой эры. И хотя это была не первая видеоигра, именно она породила ажиотаж в этой сфере развлечений. Впервые для потребителей это чудо игростроя стало доступно с момента релиза Magnavox Odyssey в 1972 году. Odyssey поставлялся с 12 играми, но настольный теннис (Pong) стал на тот момент самой популярной из них. Позднее в компании Atari доработали принцип виртуального настольного тенниса, выпустив тот самый Pong, который мы все помним и любим. В течение нескольких лет я даже был в некоторой степени одержим этой игрой. У меня была идея воссоздать её с использованием матрицы светодиодов. Эта идея показалась мне весьма занятной в плане реализации, и я решил создать такую вариацию, которая бы и отдавала дань корням, и несла в себе мои авторские штрихи.
Материалы
▍ Детали
- резистор 10КОм – 16 шт.;
- Светодиоды 3В 5мм – 128 шт.;
- угловой энкодер – 2 шт.;
- сдвиговые регистры SN74Hc595 – 3 шт.;
- Arduino Nano – 1 шт.;
- Arduino Uno (для прототипирования) – 1 шт.;
- блок питания 5В – 1 шт.;
- разъём питания «мама» — 1 шт.;
- винт 4-40 х 1" – 4 шт.;
- винт 4-40 х 5/8 – 2 шт.;
- гайка 4-40 – 10 шт.;
- саморез #6 x 1/4" – 2 шт.;
- шлейф (взял от старого ПК);
- многожильный провод (использовал старый ethernet-кабель);
- гребёнки – 26 пинов;
- печатная плата – 1 шт.;
- филамент для 3D-печати (я использовал чёрный PLA+ фирмы Esun и серый от Hatchbox, но производитель, по сути, не важен);
▍ Инструменты
Ниже я просто привожу те инструменты, которые использовал сам. По факту же бренд значения не имеет.
- паяльник (я использовал более старую модификацию приведённой по ссылке модели, но на работе у меня точно такой, и после нескольких сотен часов использования он по-прежнему в прекрасной форме);
- отвёрточный набор;
- 3D-принтеры (PRUSA I3 MK3S, Creality Ender 3);
Шаг 1: предварительное тестирование
Я знал, что это будет не самая лёгкая задача, поэтому начал неспеша. Сперва я хотел просто убедиться, что моих навыков программирования будет достаточно, чтобы заставить кучу светодиодов мигать по определённому паттерну, поэтому начал с одного ряда из 16 штук. Я взял два сдвиговых регистра, соединил их в каскад и подключил к Arduino Uno. Моей задачей было сделать так, чтобы светодиоды поочерёдно загорались слева направо.
Это был не первый мой опыт работы со сдвиговыми регистрами, поэтому особых сложностей не возникло. Используя макетную плату, я подключил защёлку (latch), тактовый генератор (clock) и линию данных (data) от Arduino к первому сдвиговому регистру. Затем ко второму подключил уже только защёлку и тактовый генератор. Данные передаются с пина 9 первого сдвигового регистра на пин 14 второго. Передача происходит в виде бит, поэтому при отправке на второй регистр 1, или (00000001), его пин 15 будет подтянут на высокий уровень, а пины с 1 по 7 (11111110) останутся на низком. Отправка 2, или (00000010), подтягивает вверх пин 1, все остальные остаются подтянуты к земле. Если отправить 128, или (10000000), пин 7 окажется подтянут вверх, а все остальные останутся на низком уровне. При отправке 13, или (00001101), пины 15, 2, 3 подтягиваются вверх, а все остальные остаются в нуле.
Итак, поскольку для этого теста я использовал два сдвиговых регистра, подключённых последовательно, то для отправки данных на второй регистр потребуется две команды. Первая будет передана на первый регистр, после чего нужно будет отправить вторую, чтобы передать первую на второй регистр. Сложности здесь никакой нет, достаточно один раз разобраться. Я подключил 16 светодиодов, чтобы убедиться в корректной работе регистров, после чего перешёл к следующему шагу.
Шаг 2: сборка матрицы светодиодов
Я не люблю собирать корпус до того, как налажу работу всей электроники. Меня не радует перспектива потратить уйму времени на проектирование во Fusion 360, чтобы потом напечатать корпус и осознать, что из-за необходимости добавления дополнительных компонентов на плату, она теперь в него не входит.
Однако для этого проекта сразу собрать корпус под матрицу из 128 светодиодов было проще, чем подключать их все для тестирования к макетной плате. Корпус для светодиодов состоит из 2 напечатанных на 3D-принтере деталей. В первой детали предусмотрены 128 отверстий такого размера, чтобы светодиоды входили в них плотно. Устанавливал я их поочерёдно, дополнительно фиксируя с обратной стороны капелькой суперклея. Затем я последовательно спаял все плюсовые выводы в каждом из 16 столбцов и подключил к этим 16 столбцам провода шлейфа.
После этого сзади я установил вторую деталь корпуса, в которой предусмотрено 16 прорезей. Все минусовые выводы светодиодов должны пройти через эти прорези. Шлейф прокладывается в нижней части – здесь важно, чтобы его провода не пережались.
После соединения две детали корпуса стягиваются четырьмя винтами с гайками 4-40. Затем я спаял все минусовые выводы по горизонтали. Прорези в пластике гарантируют, что выводы светодиодов никогда не замкнут между собой. После — я подключил к 8 минусовым линиям провода ещё одного шлейфа.
Шаг 3: написание кода
После сборки матрицы можно было переходить к написанию кода. Первым делом я припаял 24 штыря гребёнок к каждому проводу двух шлейфов и установил их на макетной плате. Далее к 16 плюсовым штырям гребёнок я подключил резисторы, к которым присоединил два сдвиговых регистра. В завершение я подключил к 8 минусовым штырям гребёнки третий сдвиговый регистр.
Мы уже разобрали принцип действия двух соединённых последовательно сдвиговых регистров, но третий работает наоборот – он используется для подтягивания светодиодов к земле. То есть если мы захотим их выключить, то нужно будет отправить 255, или (11111111), а если включить – 0, или (00000000). Так что, если вы решите поочерёдно подтянуть каждый ряд к земле, то нужно будет отправить (11111110), затем (11111101), затем (11110111) и так далее.
Управление я реализовал в коде с помощью двухмерного массива.
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
Нуль представляет отключённый светодиод, а 1 единица включённый. В Pong присутствует три движущихся объекта – игрок 1, игрок 2 и мяч. При каждом движении любого из них массив обновляется, отражая текущее положение. Данные из массива отправляются на сдвиговый регистр по одной строке за раз. Для вышеприведённого массива отправлять на второй сдвиговый регистр ничего не потребуется, поскольку он управляет последними 8 столбцами, которые в первом ряду все представлены нулями. Далее отправляется 1, или (00000001), другому сдвиговому регистру, потому что зажёгся первый светодиод. После этого третий регистр получает 254, или (01111111), в результате чего к земле подключается только первый ряд.
Данный процесс повторяется для каждого ряда.
▍ Движение
В игре участвует двое игроков, поэтому потребуется способ управлять ими обоими. Для этого я использовал 2 угловых энкодера. Сами игроки представлены в игре 3 светодиодами по двум сторонам экрана. Поворот энкодера по часовой стрелке перемещает 3 огонька светодиодов вниз, а поворот против часовой стрелки – вверх.
Мяч обновляет своё положение каждый 40-й цикл. Мне эта скорость показалась достаточной для тестирования, но если игру захочется усложнить, можно будет сократить разрывы между обновлениями, тем самым ускорив движение мяча.
Когда мяч ударяется о ракетку игрока, он изменяет направление в зависимости от того, в какую из её секций попадает. При попадании в нижний светодиод ракетки мяч отскакивает по траектории вверх. Если он попадает в центральный светодиод, то вертикальное движение в его рикошет не закладывается. Попадая же в верхний сегмент ракетки, мяч отскакивает вниз.
Скачать:
Шаг 4: пайка компонентов
Для проверки ПО я использовал Arduino Uno, поскольку для быстрого тестирования он более удобен. Так как у Uno есть гребёнка, подключать к нему новые компоненты можно легко и быстро. Однако мне показалось, что для моего случая эта модель излишне велика, поэтому я переключился на Arduino Nano. Эта плата имеет полностью аналогичную распиновку, так что код менять не потребовалось.
Сначала я припаял к Nano сдвиговые регистры, затем гребёнку, 16 резисторов, а потом и паутину проводов. Если бы я затратил больше времени, то наверняка получилось бы посимпатичнее, но когда имеешь дело со столь небольшой платой и таким количеством компонентов, лёгкий беспорядок практически неизбежен. Последними я припаял два угловых энкодера. Затем я подключил питание, чтобы проверить сборку – к моему удивлению, все компоненты заработали с первого раза.
Шаг 5: проектирование и печать
Теперь, когда вся электроника работала должным образом, можно было спроектировать во Fusion 360 все остальные компоненты устройства.
Рекомендация: многие детали задуманы для установки с натягом. Когда я делаю отверстие или паз для подобной плотной установки, то добавляю 0.01 дюйма в тело детали.
Поскольку матрица светодиодов уже была готова, первым делом я озадачился её установкой в основной корпус. Для этого требовалось лишь спроектировать под неё заднюю крышку-кронштейн. По факту эта крышка не только служит в качестве крепления к основному корпусу, но также закрывает провода и выводы светодиодов. Крепится она к матрице четырьмя гайками, которые вставляются в специальные пазы и притягиваются винтами.
Следующей деталью был основной корпус. В нём располагается печатная плата, разъём питания и два угловых энкодера. Для крепления блока с матрицей в нём сбоку предусмотрены пазы под две гайки 4-40. После установки блока матрицы в неё вставляются винты, которые в эти гайки вкручиваются.
Помимо этого, я изготовил рукоятки для двух энкодеров. Их я напечатал на Ender 3, используя филамент Hatchbox. Рукоятки одеваются на энкодеры плотно, но для верности я всё же капнул изнутри чуточку клея.
Последней печатной деталью была крышка для основного корпуса, которая служит для скрытия электронных компонентов. При её установке я не использовал ни клея, ни винтов – фиксируется она крепко. Когда я её проектировал, то подумал, что фиксация вдавливанием не сработает, то есть окажется либо слишком плотной, либо слишком слабой, и крышка будет постоянно выпадать. Но в итоге получилось идеально. Даже если бросить собранное изделие в стену, крышка не отпадёт. А если мне нужно его разобрать, то я просто поддеваю её правильным образом, и она легко снимается.
Скачать:
Шаг 6: итоговая сборка
Этот этап был очень прост. Сначала я вставил гайки в подготовленные под них пазы на задней крышке матрицы и в основном корпусе, всего получилось 6 штук. Затем прикрутил печатную плату к корпусу. Выбранная мной компоновка элементов на плате позволяла использовать для её крепления лишь два винта. Этого вполне достаточно, но если бы я паял плату заново, то предусмотрел бы дополнительные места для её прикручивания. Затем я установил угловые энкодеры и одел на них рукоятки, после чего закрыл отсек электроники крышкой. В завершение я вставил блок с матрицей в основной корпус и прикрутил. Вот и вся сборка.
Шаг 7: геймплей и выводы
Теперь осталось лишь подключить питание – и можно приступать к игре. Вам наверняка интересно, каков получился геймплей. Честно говоря, не очень. Функционирует устройство прекрасно. Энкодеры работают отлично, и игроки могут с лёгкостью прогнозировать угол отскока мяча. Единственная проблема в том, что игра получилась излишне простой. В высоту матрица имеет размер всего восемь светодиодов притом, что три из них заняты ракеткой игрока, которая блокирует 40% всей его стороны. А ведь для зарабатывания очка нужно послать мяч в не занятое ракеткой поле противника. В общем, пропустить мяч становится очень трудно.
Хотя в целом я своим проектом доволен. Реализовать его было очень интересно, да и выглядит он круто.
Автор: Дмитрий Брайт