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

Герои Войны

Стратегии, Мидкорные, Экшены

Играть

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

  • Oskanov Oskanov 9 постов
  • Animalrescueed Animalrescueed 46 постов
  • AlexKud AlexKud 33 поста
Посмотреть весь топ

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

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

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

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

Помощь Кодекс Пикабу Команда Пикабу Моб. приложение
Правила соцсети О рекомендациях О компании
Промокоды Биг Гик Промокоды Lamoda Промокоды МВидео Промокоды Яндекс Маркет Промокоды Отелло Промокоды Aroma Butik Промокоды Яндекс Путешествия Постила Футбол сегодня
0 просмотренных постов скрыто
92
monobogdan
monobogdan
1 день назад
TECHNO BROTHER

Продолжение поста «Я купил игровую консоль и написал для неё BIOS»⁠⁠1

Меня часто спрашивают, почему я не наносек.

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

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

Опрос Покупка Гаджеты Нужно ваше мнение Консоли Игры Своими руками Arduino Инженерия Программирование C++ Arm Raspberry Pi Гифка Длиннопост Ответ на пост Текст
25
942
monobogdan
monobogdan
Посты о ремонте и моддинге ретрогаджетов.
TECHNO BROTHER
2 дня назад

Я купил игровую консоль и написал для неё BIOS⁠⁠1

Осторожно: Статья написана максимально простым языком. Так что если вы гик, но не умеете программировать - вам всё равно будет интересно!

Недавно я наткнулся на DIY-игровую консоль за 1.500 рублей - Waveshare GamePi13. Когда гаджет приехал ко мне, я запустил примеры игр от производителя... и оторопел от 5 FPS в Pong - это ж как плохо нужно код писать!

Не желая мириться с этим, я открыл схему устройства, даташит на RP2040 и принялся писать свой собственный BIOS. Если вам интересно узнать, как работают DIY-консоли «изнутри», можно ли запускать внешние программы на микроконтроллерах из RAM, как реализованы различные подсистемы BIOS, а в конце даже написать «Змейку» - добро пожаловать под кат!

❯ Предисловие

Иногда китайские производители выпускают на рынок дешевые гаджеты с ориентиром исключительно на гиков. Чего-уж говорить, с какой-нибудь R36s чего только не сделали: и кастомные прошивки, и порты игр с ПК, и даже достаточно сложные аппаратные модификации. Однако в тусовке DIY'щиков обычно всё куда хардкорнее...

«Андерграундные» консоли выходят чуть ли не каждый день, но лишь единицы из них становятся хоть сколь либо популярными и попадают на массовый конвейер. От «больших» консолей их отличает простая схемотехника, использование распространенных и дешевых микроконтроллеров общего назначения и полная свобода творчества — что хочешь, то и твори! По характеристикам они чаще всего близки к оригинальному GameBoy или GameBoy Advance, а покупают их инженеры, демосценеры и ретро-энтузиасты, которые не только играют во что-то готовое, но и пишут небольшие игрушки сами!

Самые известные консоли такого формата — это нашумевший Playdate и чуть менее известный Arduboy. Обе консоли сильно ограничены в характеристиках и это подстегивает интерес гиков к постоянной оптимизации кода и попыткам впихнуть «невпихуемое». Выделился даже российский «Микрон», представив свою DIY-консоль «для хардкорных ардуинщиков» — некий MikBoy на базе своего же МИК32 «Амур»!

Я уверен что Микроновцы будут читать эту статью... Если вдруг всё получится и MikBoy пойдёт в серию — то напишите мне пожалуйста сообщение :)

Я уверен что Микроновцы будут читать эту статью... Если вдруг всё получится и MikBoy пойдёт в серию — то напишите мне пожалуйста сообщение :)

Подобным «ардуинщиком» являюсь и я. Ещё со школьных лет меня нереально тянет к микроконтроллерам и Embedded-электронике в целом. О консоли собственной разработки я мечтаю с 14 лет, при этом мне не просто хочется собрать прототип и «забить», но и запустить мелкосерийное ручное производство и продавать устройства подписчикам! К своим 24-годам я сделал два прототипа и развел три платы, но все эти проекты так или иначе откладывались в долгий ящик...

Один из ранних-ранних прототипов, предназначенный для обкатки драйвера дисплея.

Один из ранних-ранних прототипов, предназначенный для обкатки драйвера дисплея.

И вот, 25 сентября мне стукнуло 24 годика. Уже взрослый мальчик получил в качестве подарка донат от постоянного читателя и пошёл изучать маркетплейсы в поисках интересного железа. По ключевым словам «tft lcd diy» был найден «ESP32 Bitcoin Miner V2» (выгодный девкит с 2.8" и ESP32-S2), девкит ESP32 с 4.3" дисплеем и емкостным тачскрином, а также некий Waveshare GamePi13, о котором мы сегодня с вами и поговорим!

Отдельное спасибо хотелось бы сказать тем самым подписчикам. Без вашей поддержки этой статьи бы не было!

Waveshare — знаменитый в кругах энтузиастов SBC производитель. В основном компания занимается дисплеями, модулями расширения и одноплатными компьютерами.

Waveshare — знаменитый в кругах энтузиастов SBC производитель. В основном компания занимается дисплеями, модулями расширения и одноплатными компьютерами.

В тот же день я заказал устройство, и уже через 3 недели трепетного ожидания, GamePi13 оказался у меня на столе. На первый взгляд консоль показалась очень маленькой: её 1.3" дисплей был даже меньше, чем у Nokia 6230i, а кнопки оказались расположены непривычно близко друг к другу. Ко всему прочему, у консоли не было предусмотрено вообще никакого корпуса: ни «болванки» от производителя, ни STL-файлов для печати. Что-ж, это только придаёт брутальности нашему устройству!

Оба устройства помещаются в одну ладошку... А ведь когда-то 6230i казался реально большим!

Оба устройства помещаются в одну ладошку... А ведь когда-то 6230i казался реально большим!

Как вы уже могли заметить, консоль состоит из двух независимых модулей: платы разработки Waveshare RP2040-PiZero и «бутербродного» геймпада с дисплеем, который подключается к гребёнке основной платы. В этом и кроется главный секрет устройства: геймпад изначально рассчитан именно для «одноплатников» Raspberry Pi, но поскольку Waveshare также выпускает плату RP2040 с Pi-совместимой гребёнкой, они решили заодно адаптировать его и для PiZero.

❯ Что внутри?

Хоть PiZero и похожа на референсную плату в лице Raspberry Pi Pico, у неё есть несколько серьёзных отличий:

  • Во первых, на плате установлена SPI-флэшка объёмом аж в 16МБ. Это максимальный объём, который поддерживает XIP-контроллер в RP2040. В RPi Pico же используется флэш-память объёмом всего в 2МБ.

  • Далее внимание привлекает использование менее эффективного ULDO RT9193 вместо полноценного DC-DC преобразователя в оригинальном Pico. Сам микроконтроллер сможет работать при разрядке аккумулятора ниже 3.6В, а вот периферия — под вопросом. Иными словами, мы не сможем использовать «все соки» из аккумулятора и нам придётся реализовывать отсечку по напряжению.

  • На плате распаяна микросхема-чарджер литий-ионных аккумуляторов ETA6096 с током зарядки аж в 1А. Если захотите использовать аккумулятор меньшей емкости — стоит подобрать резистор ISET большего номинала, иначе есть риск перегрева.

  • Из разъёмов распаян HDMI (да, я тоже в шоке), слот для MicroSD (под него отдали весь SPI0) и два Type-C: один для аппаратного USB-контроллера в RP2040, второй для USB через PIO. В общем, пытались угодить всем.

Плата с геймпадом не менее интересная. С фронтальной стороны у нас расположилось 10 кнопок и 1.3" IPS-дисплей с разрешением 240x240, использующий контроллер ST7789. Вообще, для такой диагонали разрешение дисплея крайне избыточно: оно не только съедает драгоценные килобайты оперативной памяти для фреймбуфера, но и значительно грузит DMA-контроллер и всю шину SPI. Я бы на месте инженеров установил бы сюда «золотой стандарт» — недорогой 1.8" 128x160. Все кнопки подключены к отдельным пинам без сдвигового регистра и занимают значительную часть доступных GPIO.

Я бы сделал лучше!

Я бы сделал лучше!

С обратной стороны расположился небольшой динамик, усилитель, построенный на базе NS8002, 3.5мм джек для подключения наушников, а также токоограничивающий резистор подсветки и обвязка для дисплея. Подсветка подключена напрямую к VSYS и рассчитана на питание от 3.3В, так что никакой регулировки яркости и продвинутых режимов сна!

Производитель платы — компания SpotPear.

Производитель платы — компания SpotPear.

Ну что-ж, собираем наш бутерброд обратно, подключаем Type-C и смотрим на одну из представленных демо-игр — Тетрис!

Нет, это не пережатая гифка, игра действительно идёт буквально в 1 FPS и с мерцанием — и это на микроконтроллере с ядром Cortex-M0+ на частоте аж в 150МГц! Я напомню, что N-Gage с процессором TI OMAP на более старом ядре ARM926EJ-S с частотой 104МГц умудрялся тянуть первый Tomb Raider с полностью программным рендерингом в 25 FPS!!!

Далее я решил открыть официальный вики Waveshare и изучить информацию о консоли, где нашел несколько примеров игр для неё, одной из которых был Pong. Какое же было моё разочарование, когда я узнал, что обе игры написаны полностью на Python: игровая логика, маршалинг данных, работа с «железом» — всё это было на интерпретируемом языке и более того, написано плохо и крайне неэффективно!

class hardware():
def init():
spi0=SPI(1,baudrate=900000000, phase=0, polarity=0,
sck=Pin(game_kit.lcd_sck, Pin.OUT),
mosi=Pin(game_kit.lcd_sda, Pin.OUT))
display = st7789.ST7789(spi0, 240, 240,
reset=Pin(game_kit.lcd_rst, Pin.OUT),
dc=Pin(game_kit.lcd_dc, Pin.OUT),cs=Pin(game_kit.lcd_cs, Pin.OUT),
xstart=0, ystart=0, rotation=0)
# 初始界面,提示游戏开始
display.fill(st7789.BLACK)
display.text(font2, "Pong!", 90, 90)
display.text(font2, "Let's go!", 60, 140)
time.sleep(1)

hardware.display = display


class pong():
def __init__(self):
# bgm
self.bgm = p_music(p_music.song2, tempo=1, duty=500, pins=[
Pin(game_kit.buzzer, Pin.OUT)])
# 控制音乐暂停和播放的键start
self.key_start = button(game_kit.key_start, self.key_start_callback)

# led
self.led = Pin(game_kit.led_sta, Pin.OUT)

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

Драйвер дисплея даже не пытается использовать DMA, из-за чего даже Понг, состоящий из трёх прямоугольников умудряется тормозить.

def blit_buffer(self, buffer, x, y, width, height):
"""
Copy buffer to display at the given location.

Args:
buffer (bytes): Data to copy to display
x (int): Top left corner x coordinate
Y (int): Top left corner y coordinate
width (int): Width
height (int): Height
"""
self.set_window(x, y, x + width - 1, y + height - 1)
self.write(None, buffer)

Звуковая подсистема, состоящая из одноканальной тональной пищалки на аппаратном ШИМ-контроллере, тоже была со своими «приколами». Например «тишина» — это 0, то есть магнит всегда прижат к нижней части, хотя должно быть PWM_MAX / 2.

Под впечатлением от такого кода, я решил попробовать написать SDK для этой консоли сам. Однако моё видение идеальной DIY-консоли сильно отличалось от того-же Arduboy или Playdate!

❯ Архитектура

При проработке архитектуры будущего «BIOS», я сразу же поставил для себя несколько чётких задач:

  • Во первых, BIOS должен быть достаточно абстрактным для того, чтобы скрывать от игры детали реализации конкретного «железа». Иными словами, игра оперирует не DMA-контроллерами, FPU-сопроцессором и SPI, а набором простых и понятных подсистем: графика, ввод, звук, хранилище. Кроме того, это позволяет легко портировать игры для такого BIOS'а на другие платформы: можно без проблем реализовать симулятор (не эмулятор!) консоли на ПК или портировать её на ESP32 с минимальными изменениями.

  • Во вторых, мы ставим производительность в основной приоритет при разработке устройства. В конце-концов это же позорище, что простейшая игра тормозит и мерцает на мощном микроконтроллере, но при этом тетрисы с трёхмерной графикой вполне шустро работали на телефонах Sony Ericsson 2005 года. Именно поэтому для написания игр используются не скриптовые языки по типу Lua или JS, а самый обычный «C с классами».

  • В третьих, сам BIOS должен быть легко портируем между разными платами (у SpotPear есть вторая похожая плата — уже с 1.5" и стиком) и даже аппаратными платформами. Этот проект может стать основной прошивкой для консоли уже моей разработки и иметь вот такую «кроссплатформу» было бы отнюдь не лишним!

Руководствуясь критериями выше, я решил писать BIOS на C++ (на деле C с классами) с активным использованием интерфейсов и VMT. Это позволяет не только удобно структурировать модули и повышает читаемость кода игры, но и избавляет от необходимости вручную составлять таблицу системных вызовов к API. Тем не менее, в таком подходе есть один серьёзный нюанс: когда у подсистем появляются новые методы или добавляются перегрузки к прошлым, их необходимо по порядку добавлять в конец интерфейса, иначе VMT ломается.

vtable for CTest:
.word 0
.word typeinfo for CTest
.word CTest::Test()
.word CTest::Abc()
vtable for ITest:
.word 0
.word typeinfo for ITest
.word __cxa_pure_virtual
.word __cxa_pure_virtual

В своё время Microsoft решила эту проблему в COM с помощью QueryInterface и миллиона вариаций этих самых интерфейсов: IDirectSound8, IDirectDraw7 и т.д, но мы можем не изобретать велосипед, а просто предоставлять «старым» играм такие же «старые» версии VMT.

Основным объектом в BIOS'е является CSystem, который содержит в себе ссылки на другие подсистемы консоли, а также на информацию о текущей аппаратной платформе:

/// @brief Primary system service, supplied to both games and system modules.
class ISystem
{
public:
virtual CSystemInfo* GetSystemInfo() = 0;

virtual void* Alloc(uint32_t size) = 0;
virtual void Free(void* ptr) = 0;

virtual IGraphicsService* GetGraphicsService() = 0;
virtual IInputService* GetInputService() = 0;
virtual IDebugService* GetDebugService() = 0;
};

Несмотря на кажущуюся «динамическую» натуру системы, никаких IID я переизобретать не стал. BIOS должен реализовывать ровно тот минимальный функционал системы, который нужен. Экземпляр CSystem создаётся так называемым «портом» на конкретную плату, который должен заполнить структуру с указателями на реализации подсистем — прямо как machine-файлы в Linux! И RAII не нарушили, и полный контроль без костылей сохранили — ляпота!

void InitializePlatform()
{
CommManager = new CCommunicationManager();
CDebugService* dbgSvc = new CDebugService();

/* Print some userful debug information */
CJEDECFlashID* flashId = FlashManager.GetFlashID();

dbgSvc->Print("Initializing platform");
dbgSvc->Print("Flash memory manufacturer: 0x%x, capacity: %dKb", flashId->Manufacturer, flashId->Capacity / 1024);
dbgSvc->Print("CID: %d", FlashManager.GetCID());
dbgSvc->Print("First available: %d", FlashManager.GetFirstUserSector());

/* Service initialization */
InputService = new CInputService(dbgSvc);
GraphicsService = new CGraphicsService(dbgSvc);

/* Platform description info */
PlatformInfo.DebugService = dbgSvc;
PlatformInfo.GraphicsService = GraphicsService;
PlatformInfo.InputService = InputService;

System = new CSystem(&PlatformInfo);
}

int main() {
InitializePlatform();

while (true) {
/* Tick all platform-depend services here */
CommManager->Tick();
PowerStateManager.Tick();
InputService->Tick();

System->Tick();
}
}

В целом, базовая архитектура примитивная и понятная. Перейдем же к деталям реализации конкретных модулей.

❯ Графика

Первая подсистема, которую я реализовал — была графической. Концептуально она разделена на два отдельных модуля: драйвер дисплея, который позволяет получить его параметры и в будущем управлять его состоянием, а также модуль для рисования на поверхностях. Прямо как в DirectDraw:

struct CFrameBufferInfo
{
uint16_t Width;
uint16_t Height;
CColor* Pointer;
uint32_t Size;
};

class IDrawingSurface : public ISystemService
{
public:
virtual void Clear(CColor color) = 0;
virtual void DrawBitmap(CBitmap* bitmap, int x, int y) = 0;
virtual void DrawBitmapEx(CBitmap* bitmap, int x, int y, CSpriteInfo* spriteInfo) = 0;
virtual void DrawRect(CColor color, int x, int y, int width, int height) = 0;
virtual void FillRect(CColor color, int x, int y, int width, int height) = 0;
virtual void DrawLine(CColor color, int x1, int y1, int x2, int y2) = 0;
virtual void DrawString(CColor color, int x, int y, CAnsiChar* str) = 0;
};

class IGraphicsService : public ISystemService
{
public:
virtual void SetPowerState(bool isPowerEnabled) = 0;
virtual void SetBacklightState(bool isBacklightEnabled) = 0;
/* Maybe some controller-related functions in future? Like BIAS and HW rotation? */

virtual CFrameBufferInfo* GetFrameBufferInfo() = 0;
virtual IDrawingSurface* GetDrawingSurface() = 0;
virtual void Flush() = 0;
};

Сам драйвер дисплея классический: в его задачи входит инициализация контроллера, выделение памяти под фреймбуфер и регулярное обновление изображения на матрице. Поскольку в таких устройствах используются стандартные MIPI DBI экраны с набором команд DCS, часть кода инициализации и работы с дисплеем стало возможным унифицировать:

/* Perform hardware reset */
gpio_put(PIN_LCD_RST, 0);
sleep_ms(DISPLAY_INIT_SLEEP_TIME);
gpio_put(PIN_LCD_RST, 1);
sleep_ms(DISPLAY_INIT_SLEEP_TIME); /* Wait for display controller to complete initialization */

Reset(); /* Perform software reset to maintain default register state */
SendCommand(cmdSLPOUT, 0, 0); /* Disable sleep mode */
SendCommand(cmdCOLMOD, 0x05); /* Set color format and decoding*/
SendCommand(cmdINVON, 0, 0); /* Disable inversion */
SendCommand(cmdNORON, 0, 0); /* Enable normal mode */
SendCommand(cmdMADCTL, cmdMADCTL_RGB); /* Set pixel size */

uint8_t windowSize[] = { 0 >> 8, 0, DISPLAY_WIDTH >> 8, DISPLAY_WIDTH }; /* Set display window (note this is not safe for displays with sides not equal in size) */
SendCommand(cmdCASET, windowSize, 4);
SendCommand(cmdRASET, windowSize, 4);

SetPowerState(true); /* Enable display */

Вероятно читатель может спросить: «зачем выделять целых 115КБ под фреймбуфер, если можно использовать команды CASET/RASET и рисовать отдельные спрайты прямо в память дисплея?». Дело в том, что в таком случае скорость отрисовки будет падать обратно пропорционально размеру и числу рисуемых изображений. Если мы попытаемся нарисовать параллакс-фон, состоящий из трёх картинок с размерами 240x240, то нашим узким местом станет не только цена обращения к XIP-кэшу, но и производительность SPI-контроллера (который напрямую тактируется от системного PLL) и мы получим те самые 1-2 FPS. Кроме того мы потеряем возможность использования DMA и нам придётся ждать каждой транзакции на экран: это проблема многих «самодельных» консолей, которую, впрочем, можно решить обратившись к опыту предков — а именно PPU.

В своём проекте я решил активно задействовать DMA-контроллер для отправки фреймбуфера на дисплей. Концепция простая: мы указываем ему переслать фреймбуфер, начинаем подготавливать следующий кадр и если транзакция ещё не завершена - то дожидаемся её окончания, дабы картинка оставалась целостной. Однако если обновление логики следующего кадра завершается быстрее, чем DMA-контроллер успевает отправить сканлайны - мы можем получить эффект тиринга.

/* Setup DMA for SPI */
dmaChannel = dma_claim_unused_channel(true);

dmaConfig = dma_channel_get_default_config(dmaChannel);
channel_config_set_transfer_data_size(&dmaConfig, DMA_SIZE_8);
channel_config_set_dreq(&dmaConfig, spi_get_dreq(spi1, true));
channel_config_set_read_increment(&dmaConfig, true);
channel_config_set_write_increment(&dmaConfig, false);

...

if(!dma_channel_is_busy(dmaChannel))
{
uint8_t cmdByte = cmdRAMWR;
gpio_put(PIN_LCD_CS, 0);
gpio_put(PIN_LCD_DC, 0);
spi_write_blocking(spi1, &cmdByte, 1);

gpio_put(PIN_LCD_DC, 1);
dma_channel_configure(dmaChannel, &dmaConfig, &spi_get_hw(spi1)->dr, frameBufferInfo.Pointer, frameBufferInfo.Size, true);
}

Далее переходим к фактической отрисовке изображений. На данный момент поддерживается только один формат пикселей — RGB565, поскольку нет особого смысла использовать 8-битную палитру для изображений 32x32 (но есть смысл использовать 4х-битную, как на NES). Процесс рисования называется блиттингом и поскольку реализация полноценного альфа-блендинга слишком дорогая для реалтайм графики на микроконтроллерах, для описания прозрачности используется техника колоркеев.

Взято с <a href="https://pikabu.ru/story/ya_kupil_igrovuyu_konsol_i_napisal_dlya_neyo_bios_13327291?u=https%3A%2F%2Fru.pinterest.com%2Fpin%2F663506957573347585%2F&t=pinterest&h=b409d0ad71297189d8f711597c010a1f143b8fba" title="https://ru.pinterest.com/pin/663506957573347585/" target="_blank" rel="nofollow noopener">pinterest</a>

Взято с pinterest

ColorKey — это как ChromaKey, но для описания прозрачного цвета используется только базовый цвет, а не цвет + порог допустимых цветов. Помните как в играх 90-х были картинки с розовым фоном цвета Magenta? Вот это оно самое :)

for(int i = 0; i < min(y + bitmap->Height, frameBufferInfo->Height) - y; i++)
{
CColor* bitmapScanline = &bitmap->Pointer[i * bitmap->Width];
CColor* scanline = &frameBufferInfo->Pointer[(y + i) * frameBufferInfo->Width + x];

for(int j = 0; j < min(x + bitmap->Width, frameBufferInfo->Width) - x; j++)
{
uint16_t sample = *bitmapScanline;

if(sample != bitmap->ColorKey)
*scanline = sample;

scanline++;
bitmapScanline++;
}
}

Рисование текста реализовано знакомым для Embedded-инженеров способом: шрифты описываются в формате 8x8, где 8 битов каждого байта обозначают наличие или отсутствие пикселя в текущей позиции. Такие шрифты не только занимают очень мало места, но их также очень легко и быстро рисовать, а также масштабировать под различные разрешения экранов. На данный момент я задумываюсь — стоит ли добавлять в консоль поддержку полноценного UTF-16, если учесть что основной таргет на русскоязычную аудиторию, где и CP866 хватает с головой?

Какой же дисплей чёткий...

Какой же дисплей чёткий...

❯ Ввод

Далее мы плавно переходим к реализации драйвера ввода. Как я уже говорил выше, все кнопки подключены к своим отдельным GPIO без использования сдвигового регистра или I/O Expander'а, что с одной стороны и хорошо (некоторые китайские производители реализовывают консоли с кнопками, основанными на матричном (!!!) принципе), а с другой — отъедает большинство GPIO у RP2040. Свободными пинами мы могли бы выполнять множество полезной работы: получать уровень заряда аккумулятора у Fuel Gauge, управлять уровнем подсветки с помощью ШИМ-контроллера и ключа, или, в конце-концов, сделать порт для подключения периферии... но нет так нет.

Сам по себе драйвер ввода до жути примитивный: он позволяет получить состояние отдельных кнопок, осей (как Input.GetAxis в Unity) и проверить, нажата ли хоть какая-то кнопка:

class IInputService : public ISystemService
{
public:
virtual EKeyState GetKeyState(EKeyCode keyCode) = 0;
virtual int GetAxis(EInputAxis axis) = 0;
virtual bool IsAnyKeyPressed() = 0;
};

Для удобства и портабельности BIOS'а между платами, кнопки геймпада маппятся к соответствующим GPIO в отдельной таблице трансляции, которая также содержит состояния этих самых кнопок:

// Should be layouted in order of EKeyCode enum
CButtonState ButtonMapping[] = {
{
PIN_KEY_LEFT
},
{
PIN_KEY_RIGHT
},
{
PIN_KEY_UP
},
{
PIN_KEY_DOWN
},
{
PIN_KEY_A
},
{
PIN_KEY_B
},
{
PIN_KEY_X
},
{
PIN_KEY_Y
},
{
PIN_KEY_LEFT_TRIGGER
},
{
PIN_KEY_RIGHT_TRIGGER
}
};

Дело в том, что в нашем проекте недостаточно иметь лишь одно булево: нажата-ли кнопка или нет, для компенсации дребезга кнопок у нас также реализуется задержка перед следующей проверкой и дополнительное состояние для удобства реализации меню — «только что отпущена».

void CInputService::Tick()
{
timeStamp = get_absolute_time();

for(int i = 0; i < ButtonMappingCount; i++)
{
CButtonState* buttonState = &ButtonMapping[i];
bool gpioState = !gpio_get(buttonState->GPIO); // Buttons are pull-up to high when not pressed

// Check if there was elapsed enough time
if(timeStamp > buttonState->LastStateChange)
{
if(buttonState->State == EKeyState::ksReleased)
buttonState->State = EKeyState::ksIdle;

if(buttonState->State == EKeyState::ksIdle && gpioState)
buttonState->State = EKeyState::ksPressed;

if(buttonState->State == EKeyState::ksPressed && !gpioState)
buttonState->State = EKeyState::ksReleased;

buttonState->LastStateChange = timeStamp + KEY_DEBOUNCE_THRESHOLD;
}
}
}

Таким образом, мы получаем куда более удобную подсистему ввода, чем условная битовая маска с обозначением каждой кнопки и ручной обработкой её состояний в игре...

EKeyState CInputService::GetKeyState(EKeyCode keyCode)
{
uint32_t code = (uint32_t)keyCode;

if(keyCode >= ButtonMappingCount)
return EKeyState::ksIdle; /* Maybe we should throw an exception? */

return ButtonMapping[code].State;
}

int CInputService::GetAxis(EInputAxis axis)
{
EKeyCode a = EKeyCode::keyLeft;
EKeyCode b = EKeyCode::keyRight;

if(axis == EInputAxis::inputAxisVertical)
{
a = EKeyCode::keyUp;
b = EKeyCode::keyDown;
}

return GetKeyState(a) == EKeyState::ksPressed ? -1 : (GetKeyState(b) == EKeyState::ksPressed ? 1 : 0);
}

А вот и результат:

❯ Запуск программ

Вот мы и подошли к, возможно, самой интересной подсистеме в нашем BIOS'е. Думаю многие читатели так или иначе интересовались тем, как же компилятор и линкер превращают исходный код и объектный файлы в пригодные для выполнения программы и библиотеки. Вопрос запуска нативных программ на микроконтроллерах интересовал и меня — я даже написал целых три статьи об этом: в первой мы поговорили о ESP32 и Xtensa, а во второй реализовали BinLoader путём реверс-инжиниринга и хакинга кнопочного телефона, а в третьей сделали полу-универсальный ElfLoader для нескольких моделей телефонов на разных платформах.

Но начнём мы с простого. Каждая программа делится на три основных секции:

  • .text — содержит в себе машинный код функций и так называемые Literal pools. Может быть как в ROM, так и в RAM. На системах, где есть возможность выполнять код и в ROM, и в RAM, есть отдельная секция - .iram.

  • .data — содержит инициализированные переменные, которые обычно попадают в оперативную память. Для статических констант есть отдельная секция, называемая .rodata.

  • .bss — содержит в себе не-инициализированные переменные, обычно это нули. В исполняемый файл секция .bss напрямую не записывается, остаётся лишь информация о том, каков её размер, а саму секцию затем выделит динамический линкер.

Куда попадут части программы определяет специальная утилита — линкер, которая на основе специального скрипта «раскладывает» данные по нужным секциям. Благодаря этому скрипту, мы можем, например, перенести часть функций в оперативную память для более быстрого исполнения или добавить в начало программы заголовок с описанием приложения.

В моём случае, я решил загружать игры в SRAM и дабы не реализовывать нормальный динамический линкер и релокации, решил выделить под игру фиксированный кусочек оперативной памяти объёмом в 128КБ. Для этого я отредактировал скрипт линкера Pico C SDK так, чтобы сразу после вектора прерываний шла наша программа:

. = ALIGN(4);

.ram_vector_table (NOLOAD): {
*(.ram_vector_table)
} > RAM

iram_program_reserve_size = 128K;

.iram_program (NOLOAD) : {
. = ALIGN(4);
PROVIDE(iram_program_origin = .);
. += iram_program_reserve_size;
} > RAM

.uninitialized_data (NOLOAD): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM

Для компиляции программы также используется кастомный скрипт для линкера и особый Makefile, где после сборки программы мы копируем все её секции в выходной файл в «сыром» виде. Поскольку программа собирается под выполнение из конкретного адреса — пока речь идёт о переносимости только между одной аппаратной платформой. На RP2040, RP2350 и возможно STM32 такое «прокатит», но вот на других ARM-процессорах — большой вопрос!

OUTPUT_FORMAT("elf32-littlearm")


SECTIONS
{
. = 0x200000c0;

.program_info : {
*(.program_info*)
}

.text : {
*(.vtable*)
*(.text*)
*(.rodata*)
*(.data*)
*(.bss*)
}

/DISCARD/ : {
*(.ARM.*)
*(.comment*)

}
}

Каждое приложение, как и базовая система, предполагает использование ООП и поэтому представляет из себя реализацию класса IApplication. Для этого нам нужна некоторая runtime-поддержка: аллокатор, функция для создания экземпляра приложения, а также указатель на ISystem. Именно поэтому каждая программа должна экспортировать специальный заголовок, где содержится указатель на функцию-инициализатор:

#define PROGRAM_HEADER 0x1337
#define PROGRAM_INFO(name, clazz) int test; CAllocator* __Allocator; IApplication* __createInstance(CAllocator* allocator, ISystem* systemPtr) { __Allocator = allocator; return new clazz(systemPtr); } \
CProgramInfo __program_info __attribute__ ((section(".program_info"))) = { PROGRAM_HEADER, BIOS_VERSION, name, &__createInstance };

struct CProgramInfo
{
uint32_t Header;
uint32_t BIOSVersion;
CAnsiChar Name[32];

CreateApplicationInstanceFunction CreateApplicationInstance;
};

...

PROGRAM_INFO("Blink", CBlinkApplication)

Таким образом, для выполнения нашей программы и вызова её обработчиков событий нам достаточно лишь загрузить файл по адресу 0x200000c0 и создать экземпляр IApplication. Всё очень просто и понятно!

CAllocator allocator;
allocator.Alloc = malloc;
allocator.Free = free;

IApplication* app = ((CProgramInfo*)ptr)->CreateApplicationInstance(&allocator, System);

Но "моргалка" ведь слишком просто, согласитесь? Поэтому мы с вами напишем ремейк классической игры Змейка, которая работает в настоящие 60 FPS!

❯ Заключение

Вот таким нехитрым образом я понемногу реализовываю свою мечту детства: «андерграунд" консоль собственной разработки. Конечно здесь ещё много чего нужно доделывать перед тем, как начинать разводить свою плату, но начало ведь положено! В контексте GamePi13, я считаю что моя реализация SDK для консоли всё таки немного лучше, чем то, что предлагает производитель «из коробки».

Я понимаю что мой не совсем трушный эмбеддерский подход может вызвать разные ощущения у читателей: так что приглашаю всех заинтересованных в комментарии, обсудим с вами «сломанный Branch-prediction из-за виртуалов», «UB из-за того, что порядок указателей на реализации в VMT может отличаться» и «какого фига игры у тебя оказались в SRAM, а высокопроизводительный код на Flash, если у XIP кэш всего в 16КБ!».

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

Если вам понравилась статья...

И у вас появилось желание что-то мне задонатить (например прикольный гаджет) - пишите мне в телегу или в комментариях :) Без вашей помощи статьи бы не выходили! А ещё у меня есть Boosty.

Что думаете о таком формате статей?
Всего голосов:
Если бы я собрался с духом и произвел 20-50 штучек консолей-самоделок с полностью готовым SDK, примерами и туториалами, купили бы себе такую
Всего голосов:
Что думаете о таком проекте?
Всего голосов:

Подготовлено при поддержке @Timeweb.Cloud

Показать полностью 18 3
[моё] Опрос Покупка Гаджеты Нужно ваше мнение Консоли Игры Своими руками Arduino Инженерия Программирование C++ Arm Raspberry Pi Гифка Длиннопост
72
2
shagolnet
shagolnet
15 дней назад

Ардуино или Распберри?⁠⁠

Поделитесь пожалуйста мнением, кто уже использует какую-либо технологию. Хочу умный дом (датчики протечек, вентили водопровода с приводами, шторы, кондиционер и всё прочее, примерно стандартный набор). Начитался уже кучу материалов, и вообще запутался. Сначала казалось, что Ардуино нет равных в простоте, потом модные блогеры настояли, что Распберри куда лучше, хотя мне с точки зрения логики и программирования показалось, что Ардуино лучше, и на него всевозможных датчиков намного больше. В общем видимо замылилось, нужна пауза и мнение от кого-то, кто реально в быту уже использует что-то подобное.

Arduino Raspberry pi Умный дом Текст
24
36
Аноним
Аноним
19 дней назад
Лига Радиолюбителей

DigiPi: универсальный цифровой модем радиолюбителя⁠⁠

источник Хабр

TNC MFJ-1278 <a href="https://pikabu.ru/story/digipi_universalnyiy_tsifrovoy_modem_radiolyubitelya_13263814?u=https%3A%2F%2Fwww.rigpix.com%2Ftextandimaging%2Fmfj_1278.htm&t=%D0%B8%D1%81%D1%82%D0%BE%D1%87%D0%BD%D0%B8%D0%BA&h=b4df779a4fe0a3efdde76a228a2f90b2bd29f8e8" title="https://www.rigpix.com/textandimaging/mfj_1278.htm" target="_blank" rel="nofollow noopener">источник</a>

TNC MFJ-1278 источник

Каждый радиолюбитель проходит стадию, когда вместо хаоса проводов, антенн и адаптеров хочется сконцентрировать все в аккуратную и удобную систему. Но это весьма непростая задача: разные вендоры, протоколы и скорости передачи данных. Один трансивер требует пачки драйверов, другой не умеет нажимать PTT через CAT, третьему вообще нужен COM-порт, и желательно нативный. В итоге радиолюбительский «шэк» (от английского shack — «хижина») превращается во Франкенштейна, где приходится учитывать особенности каждого отдельного девайса.

DigiPi был создан как попытка навести порядок, объединив функции звукового интерфейса, контроллера трансиверов и небольшого сервера, позволяющего управлять всем непосредственно из браузера с любого устройства — от лэптопа до планшета. Сегодня я покажу, как DigiPi на Raspberry Pi 4 превращается в удобный центр управления радиооборудованием. Затем шаг за шагом соберем собственную систему с его встроенными модулями.

Что умеет DigiPi

APRS TNC

На Хабре есть прекрасная публикация про этот протокол радиолюбительской связи, поэтому лишь вкратце напомню, что это такое. Он отлично работает для передачи коротких текстовых сообщений, данных телеметрии и координат маяков GPS. В мире существует глобальная сеть APRS, которая функционирует поверх другого протокола канального уровня AX.25. Последний позволяет работать напрямую или через ретрансляторы (digipeaters).

Те представляют собой классическую радиорелейную станцию, которая расширяет дальность действия связи, принимая и ретранслируя фреймы AX.25 в эфир. Основой станции служит TNC (Terminal Node Controller), который по своей сути очень схож с обычным Dial-up-модемом. Они оба преобразуют аналоговый сигнал в цифровой вид и обратно. Различие в том, что TNC в качестве среды передачи данных использует радиоэфир вместо телефонной линии и «завязан» на протоколы радиосвязи, такие как вышеупомянутый AX.25.

Push-To-Talk

Проблемой остается еще и то, что надо как-то эмулировать нажатие тангенты (PTT). Чаще всего для этого используется отдельный кабель COM или USB2COM, который передает соответствующий сигнал по CAT-протоколу. Однако если трансивер не поддерживает переход в режим передачи с помощью CAT, приходится задействовать отдельные управляющие линии порта RS-232. Для этой цели в основном применяются DTR (Data Terminal Ready) и RTS (Request To Send).

Если на выходе USB2COM-адаптера выставлять эти линии в HIGH/LOW, то на пинах появится +5V или 0V соответственно. А это значит, что, подключив такие линии к гнезду тангенты, можно добиться ее «аппаратного» нажатия. Забавно, но на практике я столкнулся с весьма интересным глюком при использовании такого способа.

Когда я впервые настраивал работу своего Icom IC-706MKIIG для пакетных видов связи, то как раз использовал аудиоинтерфейс MFJ-1279M. В приложении WSJTX выставил активацию PTT через DTR/RTS. При выходе на передачу столкнулся с тем, что на каких-то диапазонах это виртуальное нажатие на PTT как бы «залипало» и трансивер продолжал передавать даже за пределами выделенного временного «окна» FT8.

Ответ был прост — наводки. Для решения пришлось навесить ферритовые бочонки на USB2COM-кабели и сделать воздушный высокочастотный дроссель (RF-choke) для снижения синфазных токов на внешней оплетке фидерного кабеля. После этого «залипания» PTT ушли.

APRS iGate

APRS чаще всего применяется на VHF-диапазоне, а это значит, что в качестве передатчика подходят любые портативные станции, вроде народного Quansheng UV-K6. Проблема в том, что роль TNC должен исполнять какой-либо внешний компьютер. Многие радиолюбители решили этот вопрос с помощью смартфона и специального кабеля APRS-K1. Но что делать, если хочется поднять свой диджипитер? Вот тут как раз пригодится DigiPi.

DigiPi способен не только работать в качестве простого TNC для VHF- и HF-радиостанций. Он умеет функционировать в роли iGate во всемирной радиолюбительской сети APRS-IS, которая объединяет подобные репитеры и позволяет им передавать данные в глобальном масштабе.

Так, на МКС есть любительская радиостанция Kenwood TM-D710GA, оборудованная встроенным AX.25 TNC, которая работает в режиме диджипитера с позывным RS0ISS-4. Если в момент пролета МКС над головой отправить APRS-пакет, в поле пути которого указано что-то вроде:

RS0ISS-4,WIDE2-1

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

APRS WebChat

Еще одна интересная фича DigiPi относительно работы с APRS, — имплементация веб-сервиса aprs.chat. Он представляет собой своеобразный центр всех APRS-IS сообщений для конкретного позывного. Дело в том, что у APRS-пакетов нет механизма подтверждения доставки на уровне протокола. Если путь закончился и ни один адрес не сработал, пакет просто исчезает.

APRS Chat играет роль почтового ящика, компенсируя тем самым отсутствие постоянного канала связи. Стоит помнить, что работает это лишь в пределах APRS-IS. Вместо того чтобы заходить в веб-интерфейс сервиса, роль клиента может играть скрипт, встроенный в DigiPi:

DigiPi APRS WebChat

DigiPi APRS WebChat

Так вы никогда не пропустите сообщение, которое проходило через APRS-IS и может служить альтернативным способом связи, когда привычные мессенджеры недоступны.

AX.25 Node Network

Эта опция в DigiPi не что иное, как возможность построения «радиоинтернета» из 90-х, но без TCP/IP. Это расширение протокола, позволяющее строить многошаговые маршруты (сетевой уровень). AX.25 сам по себе канальный, то есть он может сделать ряд штатных действий вроде соединения, обмена фреймами данных и отключения. Проблема возникает, когда получателя нет в прямой видимости, а следовательно, нет понимания, куда маршрутизировать пакет дальше.

При включенной функции AX.25 Node Network одноплатник с DigiPi превращается не просто в цифровой модем, а в узел для пакетной радиосети. Каждый такой узел будет хранить таблицу «соседей» и может направлять трафик через них, формируя сеть, подобную ARPANET. Это делает возможным выстраивать цепочки соединений для достижения конечного узла.

Это очень любопытная технология, которая напоминает эпоху BBS (Bulletin Board System). Пользователи могут не только связаться с конкретным узлом, но и взаимодействовать с каким-либо сервисом вроде доски объявлений. Подобный «альтернативный интернет» может быть очень полезен, поскольку полностью независим от традиционных сетей связи.

Winlink Email

Электронная почта для многих давно уже кажется пережитком прошлого. Зачем она нужна в эпоху мессенджеров и глобального проникновения интернета? Но ни одна современная система связи не застрахована от отказов оборудования. Мобильная связь может быть прервана землетрясением, а спутниковая группировка Starlink хотя и редко подвержена сбоям, но все же такое случается.

Система Winlink была изначально разработана радиолюбителями, чтобы передавать обычную электронную почту по радиоканалу на HF- и VHF-диапазонах. При этом предусматривалась интеграция с традиционными службами. Winlink Email можно условно поделить на серверную и клиентскую часть.

Первая носит имя Winlink Email Server, однако многие ее называют RMS (Radio Message Server). Задачей узла с настроенным RMS является пересылка писем, отправленных по радиоканалу на обычные почтовые сервисы и наоборот. Все сообщения хранятся на распределенных серверах Winlink CMS (Common Message Servers), которые установлены по всему миру. Интересно, что каждый сервер CMS является зеркалом других CMS, это обеспечивает отказоустойчивость. Пока есть хотя бы один живой такой сервер, вся корреспонденция будет в целости и сохранности.

RMS же доступа ко всей почте не имеет, однако способен кешировать сообщения, помещая их в собственный буфер. Как только письмо передано CMS, оно удаляется из кеша RMS. В итоге каждый клиент может подключиться к любому RMS-серверу и получить свою почту. Ну а CMS играет роль «мозга» этой системы и с конечными пользователями не взаимодействует.

У сети Winlink есть официальный и альтернативный клиенты. Первый называется Winlink Express, работает только под Windows и имеет забавную особенность. Формально софт бесплатный и не требует оплаты. Однако приложение будет каждый раз вам напоминать о возможности внести добровольное пожертвование, которое пойдет на поддержание работы сети, оплату серверов Winlink CMS и так далее.

Если задонатить проекту $24, назойливые сообщения пропадают, а Winlink Express начинает нормально сохранять сделанные настройки. Получается такой добровольно-принудительный взнос. «А если не будут брать — отключим газ…»

Альтернативный клиент называется Pat Winlink Email Client, и именно он включен в состав DigiPi. Он легковесный, кросс-платформенный, написан на Go и имеет современный минималистичный веб-интерфейс:

Создание письма через Pat Winlink Client

Создание письма через Pat Winlink Client

Так что если вдруг остались без интернета, но у вас есть «малинка» с образом DigiPi и КВ-трансивер, вы точно сможете создать и отправить Email через Winlink. Правда, не без помощи следующего софта.

ARDOP Modem

Чтобы письмо успешно долетело до адресата в Winlink, вам, как ни странно, помимо клиентской части нужен отдельный TNC вроде SCS PACTOR Modem. Это довольно дорогая железка, работающая на проприетарном семействе протоколов PACTOR. Современный SCS P4dragon DR-7800 стоит примерно 2000 долларов и позволяет устанавливать соединение со скоростью 10 500 бод. Да-да, вы правильно поняли — 10,5 Кбит/с.

SCS P4dragon DR-7800

SCS P4dragon DR-7800

Альтернативой PACTOR может служить протокол VARA HF, о котором мы не так давно рассказывали на Хабре. Платная версия софт-модема этого протокола способна выжать 7–8 Кбит/с, ну а доступная для всех радиолюбителей бесплатная версия ограничена смешной скоростью в 175 бит/с. За $69 вы получаете почти то же самое, что и с железкой за $2000, — идеально для моряков, если под рукой нет терминала Starlink.

Но есть еще более простая и дешевая альтернатива — ARDOP (Amateur Radio Digital Open Protocol) Modem. Это тоже программный модем, но полностью открытый и документированный. Абсолютно все исходники выложены на GitHub, и это позволило интегрировать его во множество проектов, в том числе и в Pat Winlink Email Client.

Потолок скорости у ARDOP всего лишь 2 Кбит/с, но это с лихвой окупается бесплатностью и доступностью. Работает быстрее, чем VARA HF Modem для радиолюбителей, и при этом не зависит от закрытых протоколов. В рамках DigiPi включение режима ARDOP Modem означает поднятие локального демона на порту 8515, к которому можно подключить как Pat Winlink, так и любое другое приложение с поддержкой этого транспорта.

WSJTX FT8

Протокол FT8 прочно вошел в жизнь современных радиолюбителей, позволяя проводить связи с уровнем сигнала до -24 дБ относительно шумового фона в полосе 2,5 кГц. На практике это означает, что там, где корреспондента не слышно вообще, FT8 успешно декодирует символы из эфирного шума. Этот вид цифровой связи максимально заточен на работу в самых плохих условиях и поэтому стал очень популярен.

Разработкой FT8 и первого приложения WSJT занимался Джозеф Хотон Тейлор-младший (Joseph Hooton Taylor Jr., K1JT), американский астрофизик и лауреат Нобелевской премии по физике. Вместе со Стивом Франке (Steve J. Franke, K9AN), почетным профессором кафедры электротехники и вычислительной техники Университета Иллинойса, им удалось создать протокол на грани чистой математики и практики радиолюбительской связи.

Приложение WSJT-X в DigiPi

Приложение WSJT-X в DigiPi

Разработчик DigiPi сделал очень удобную, на мой взгляд, штуку. Он завернул приложение в сессию noVNC, что позволяет без проблем получить интерфейс программы прямо в браузере. Кстати, именно библиотека noVNC давно используется в Proxmox для получения доступа к консоли виртуальных машин и прекрасно себя зарекомендовала.

Так что если вы хотели удаленно управлять своим трансивером и проводить связи на FT8 — это готовое решение. Оно, может быть, не столь удобно, как JTDX, но вполне справляется со своими обязанностями.

SSTV

Довольно часто радиолюбители принимают и декодируют SSTV (Slow-Scan TeleVision) изображения, передаваемые с МКС или спутников вроде УМКА-1. Но этот протокол успешно применяется и на коротких волнах. Если вы захотите не только принять, но и сами отправить какую-либо картинку в эфир, в DigiPi встроено приложение QSSTV:

QSSTV на DigiPi

QSSTV на DigiPi

Доступ здесь реализован точно так же, прямо из браузера. Единственный момент — изображения вначале придется передать на «малинку», например, через SFTP.

FLDigi

Еще один софт-модем, поддерживающий огромное количество протоколов, таких как CW (Азбука Морзе), PSK31, MFSK, RTTY, Olivia и более десятка других. Это одно из наиболее универсальных приложений, которое заменяет собой множество аппаратных модемов из разных эпох. Написано оно на C/C++ и использует графический интерфейс FLTK:

Запущенный FLDigi в DigiPi

Запущенный FLDigi в DigiPi

Многим оно может показаться устаревшим, но поверьте на слово — это вовсе не так. Несмотря на архаичный вид интерфейса, FLDigi один из лучших софт-модемов на этой планете, который позволяет работать с десятками видов связей, включая разную экзотику вроде Hell 80 (конкурент RTTY из 1970 года).

JS8Call

Представьте себе FT8 «на стероидах». Если последний был заточен на автоматический обмен данными в строго отведенные временные «окна», то JS8Call больше рассчитан на свободное общение между корреспондентами. В нем есть все те же «плюшки» в виде способности принимать сигналы даже на уровне -24 дБ, но при этом можно отвечать когда угодно, а не строго через 15 секунд.

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

JS8Call в DigiPi

JS8Call в DigiPi

Работает это как тот же самый FT8, поверх которого строится структура сообщений. Это позволяет не только посылать короткие фразы или команды, но и выстраивать цепочки ретрансляции. Прекрасное поле для экспериментов и DX.

Как скачать DigiPi

Если честно, одной публикации даже близко не хватит описать все возможности DigiPi — штука интересная и определенно заслуживающая внимания. Но есть одна «ложка дегтя», которая может вам помешать. DigiPi нельзя скачать без активного спонсорства на Patreon. Здесь автор гуляет на очень тонкой грани между open source и коммерческим приложением.

Все используемые скрипты и конфигурационные файлы в DigiPi открыты и доступны в репозитории проекта на GitHub. Вы без проблем найдете там все для крафта собственного образа DigiPi. Но «рецепта» сборки автор не дает, оставляя это на исследование пользователя. Позволю отметить, что ChatGPT создает вполне рабочий скрипт для имплементации всего этого добра в стандартный Raspbian, однако все равно потребуется доработка напильником.

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

Я оформил такую подписку на оригинальный образ DigiPi за $1 в месяц только потому, что активно пользуюсь этой штукой и хочу получать обновления. В целом ничего мне не мешает отменить подписку в любой момент и остаться на той версии образа, которая у меня есть. Более того, образ я спокойно могу распространять среди своих друзей по радиоклубу или скормить ChatGPT для составления собственного скрипта сборки.

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

источник Хабр, автор Антон k0mar0v

Показать полностью 8
Радиолюбители Радиостанция Радиосвязь Короткие волны Модем Радиоэлектроника Электроника Raspberry pi Habr Длиннопост
20
5
alfaprojet.ru
alfaprojet.ru
27 дней назад
Серия Электроника и гаджеты

Raspberry Pi представила клавиатуру со встроенным компьютером — Raspberry Pi 500+⁠⁠

Устройство объединяет полноценный мини-ПК и механическую клавиатуру в одном корпусе. Модель оснащена съёмными колпачками клавиш и индивидуальной RGB-подсветкой для каждой клавиши. Внутри установлен процессор Raspberry Pi 5, что позволяет использовать клавиатуру как самостоятельное вычислительное устройство.

Для работы достаточно подключить к ней внешний монитор — отдельный системный блок не требуется. Стоимость новинки составит 200 долларов США (около 16 600 рублей).

Источник видео: leepspvideo

Читайте нас в Telegram! Там тоже очень много интересных новостей

Показать полностью
Гаджеты Электроника Технологии Компьютер Клавиатура Raspberry pi Вертикальное видео Короткие видео Видео Telegram (ссылка)
6
19
razvlekalovo
razvlekalovo
1 месяц назад
Лига электриков

ARM-кластер Raspberry Pi с погружным охлаждением на силиконовой жидкости⁠⁠

3 Raspberry Pi 5 (16 ГБ) + HAT + твердотельный накопитель на 256 ГБ; 1 коммутатор; Cloudflare (шлюз, туннель/прокси и брандмауэр); K3s; 1 л силиконовой жидкости плотностью 50 сСт; аквариум для бойцовой рыбки

Вертикальное видео Электроника Видео Без звука Короткие видео Raspberry pi
8
19
razvlekalovo
razvlekalovo
1 месяц назад
Лига электриков

Raspberry pi 5 с двумя экранами и аккумулятором в старой коробке из-под сигар⁠⁠

1/6
Показать полностью 6
Сборка Электроника Raspberry pi
8
80
AnatoliyB
AnatoliyB
1 месяц назад
Arduino & Pi

Pixel Table: от идеи до реализации интерактивного пиксельного стола⁠⁠

Первый рабочий прототип

Первый рабочий прототип

Давайте знакомиться!

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

Этап 0: Как всё начиналось

Однажды мне попался на глаза проект пиксельного стола, и я подумал, что было бы круто сделать стол не просто с красивыми визуальными эффектами, а ещё и с играми и звуковым сопровождением, т.е интерактивный. И я начал изучать тему…

Стол из интернета

Стол из интернета

Этап 1: Исследование существующих проектов

В интернете есть сотни проектов НЕинтерактивных (не реагирующих на касания) столов и матриц на адресных светодиодах, они предназначены исключительно для вывода красивых картинок. Вспомним тот же рюкзак с дисплеем от @AlexGyver:

Схемотехника у всех подобных проектов весьма простая: один контроллер + несколько сотен адресных светодиодов. Но для добавления игровых функций нужно как-то считывать нажатия.

Проектов же интерактивных столов в интернете встречается сильно меньше ввиду сложности электроники и огромных трудозатрат на сборку в домашних условиях.

  • Часть разработок не имеет обратной связи на нажатия, а выступают просто красивым светодиодным декором, а нам нужны интерактивные функции для игр и других эффектов. Это в разы усложняет разработку;

  • Все существующие проекты собраны на коленке и имеют огромные недочёты в плане трудозатрат на сборку: несколько сотен проводных соединений и точек пайки, десятки человекочасов работы. Такое нам не подходит, я слишком ленив, чтобы руками всё это паять, а значит нужны печатные платы без проводных соединений, разъём-в-разъём, плата-к-плате, side-by-side;

  • Конструкция корпуса зачастую тоже достаточно сложная, с вырезами/выпилами, с клеем или герметиком. Такое тоже не нравится, нужно что-то максимально простое из обычного мебельного ЛДСП, чтобы можно было заказать раскрой на ближайшем производстве;

Этап 2: Проектирование печатной платы

Изначально я решил сделать стол размера 25х15 пикселей, эдакий правильный прямоугольник со сторонами, кратными 5, т.е весь стол можно собрать из 15 плат размера 5х5, я называю их сегментами.

В качестве канала связи рассматривал RS485 и CAN:

  • RS485 подразумевает топологию master-slave, т.е нужно явно опрашивать все ведомые устройства, чтобы избежать коллизий, что будет сложно реализовать для такого количества пикселей;

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

В качестве микроконтроллера был выбран самый популярный и дешёвый микроконтроллер с CAN шиной на борту – STM32F103, имеющий к тому же десятки китайских клонов на случай необходимости дальнейшей оптимизации по цене/наличию. Наверняка у каждого здесь есть завалявшаяся дома платка Blue Pill с подобным контроллером. Вот и у меня была.

В качестве светодиодов были выбраны обычные RGB адресные WS2812B. Тут всё очевидно – работают независимо при последовательном подключении:

В качестве датчика нажатия был выбран оптический сенсор VCNL36821S, комбинирующий в себе светодиод и фотодиод, работающие в ИК диапазоне длин волн 800..1000нм, а значит он не будет реагировать на RGB спектр светодиодов 400..700нм. С другой стороны, это не самое дешёвое решение, но точно одно из самых простых при автоматизированной сборке печатных плат. Один smd элемент и готово, никаких тебе емкостных сенсоров в виде улиток из проволоки (вспоминаем про желание избежать трудоемкости при сборке).

VCNL36821S

VCNL36821S

Сколько датчиков и светодиодов можно подключить к одному контроллеру STM32F103 в корпусе LQFP-48 без использования всякого рода расширителей портов? Все звёзды сошлись на размере сегмента 5х5 = 25 пикселей. В прототипе задействованы абсолютно все ноги контроллера, даже пришлось позаимствовать одну ногу SWD из разъёма для программирования.

Одна из сложностей при проектировании печатной платы заключалась в том, что у сенсора VCNL36821S отсутствует возможность задавать адрес I2C, а у микроконтроллера STM32F103 только две шины I2C. Как же быть? Ответ был найден на просторах Stackoverflow и заключался в коммутации линии SDA через диоды Шоттки. Гениально, как мне кажется:

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

Версия 1.0

Версия 1.0

Управлять столом будет обычная Raspberry Pi 4B+ вот с такой платой расширения для CAN шины:

Сами платы были заказаны в Китае на PcbWay с автоматизированной сборкой, т.е сам я ничего не паял.

Ну и затратная часть на платы:

  • Текстолит * 15 плат = 11 800 ₽

  • Компоненты * 15 плат = 29 600 ₽

  • Авто монтаж * 15 плат = 30 600 ₽

  • Доставка с Китая = 13 560 ₽

  • Итого: 85 560 ₽

Этап 3: Проектирование корпуса

Имея размер одной печатной платы, можно собрать всё это в деревянный корпус. Я выбрал формат обычного журнального столика.

В прототипе я решил сделать у стола равномерные отступы, чтобы расположить там малинку, блок питания и динамики, но позже понял, что это было плохой идеей:

  • Во-первых, такой отступ становится весьма неочевидным в играх, например пинг-понг, когда мячик отражается от невидимой стены;

  • Во-вторых, эстетически это смотрится весьма странно, когда поле светится не всё.

А для дополнительной электроники можно просто сделать двойное дно и доступ для обслуживания.

Оргстекло 3 мм нашел у местных рекламщиков и попросил вырезать прямоугольник нужного размера.

В качестве материала корпуса была выбрана фанера, т.к она намного крепче и долговечнее обычной мебельной ЛДСП.

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

Сетку вырезал на лазерном ЧПУ из фанеры 3 мм, размер каждой ячейки 4х4х4 см.

Затраты на корпусные детали:

  • Раскрой сетки из фанеры 3мм = 5 000 ₽

  • Фанера для корпуса стола + раскрой = 4 600 ₽

  • Акрил + резка = 4 200 ₽

  • Краска + валик = 1 750 ₽

  • Метизы, втулки, клей = 830 ₽

  • Покраска своими руками = Бесценно

  • Итого: 16 380 ₽

Этап 4: Написание прошивки и отладка

Для написания кода использовал программы STM32CubeMX и STM32CubeIDE, для прошивки и отладки китайские клоны программатора ST-LINK V2 и логического анализатора Saleae Logic. Вообще обожаю эту связку, никогда ещё программирование и отладка микроконтроллеров не были настолько простыми и доступными.

Из интересного, что можно было бы рассказать про написание прошивки:

Т.к контроллер весьма небольшой, и чтобы не тащить в проект тяжеловесную RTOS, я часто использую самописную систему событий, привязанную к 1 мс таймеру. Занимает буквально сотню строчек кода: установить событие через N мс, проверить готовность события к исполнению, очистить событие. Это покрывает практически все мои нужды по организации логики программы под микроконтроллер и занимает памяти чуть более, чем ничего.

Отдельно хочу рассказать про логику организации мной адресного пространства CAN. Я разбил адресное пространство 2048 адресов на 4 группы:

  • широковещательная команда;

  • целевые команды конкретным сегментам;

  • посылки от сегмента;

  • и в конце простые пакеты с цветами.

В CAN, чем ниже адрес пакета, тем у него выше приоритет. Это означает, что посылки от устройств (с кликом или ответом на запрос) имеют приоритет выше, чем пакеты с цветами. Таким образом, клик всегда будет получен почти мгновенно, даже когда идет активная “отрисовка” картинки и шина занята.

В коде я реализовал поддержку трех разных палитр:

  • RGB6 – 6 бит на цвет, где 3 бита цвет RGB, а 3 бита яркость 0-7;

  • RGB12 – 12 бит на цвет, RRRRGGGGBBBB;

  • RGB24 – 24 бита на цвет.

Т.к в стандартном CAN есть ограничение на 8 байт данных, то приходится делить адресные пространства ещё и на адреса конкретных пикселей внутри сегмента. А т.к на каждые 8 байт данных полезной нагрузки CAN имеет ещё оверхед 47 бит в виде адреса и других заголовков пакета, то с расширением палитры цветов, FPS падает непропорционально. Примерно вот таких значений мне удалось добиться при скорости CAN 500 kbit/s:

  • 75 FPS для RGB6;

  • 38 FPS для RGB12;

  • 13 FPS для RGB24;

Я остановился на палитре RGB12: достаточная цветопередача при сохранении адекватной частоты кадров. Стоит отметить, что это максимальная частота кадров изображения, а с наличием большого числа нажатий, частота кадров будет проседать, т.к вспоминаем про приоритет кликов над пакетами цветов.

Для любознательных, ссылки на исходники платы и прошивки будут в конце статьи.

Расходы: бесплатно по ночам.

Этап 5: Финальная сборка и ошибки

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

  • Как вы могли заметить, на платах я сделал разъёмы папа-мама на нижней стороне текстолита. Такая конструкция очень удобна при сборке и стоит три копейки, но делает стол абсолютно непригодным для ремонта, т.к нет возможности достать и заменить одну плату, приходится откручивать всех соседей;

  • Оргстекло, которое я нашел на местном производстве, как оказалось, плохо подходит для оптического сенсора, т.к имеет слабую светопропускаемость. Это приводит к очень маленькому полезному сигналу с датчиков и необходимости искать компромисс между ложными срабатываниями и “силой” (читай – площадью) нажатия;

  • Также я допустил небрежность и запитал всю матрицу плат двумя тонкими проводами… Как вы можете догадаться, долго такая сборка не проработала, больше 20А в пике как-никак. Решение простое – подкинуть питание ещё в несколько точек по периметру.

Сопутствующие расходы:

  • Блок питания 5V 150W = 4 560 ₽

  • Raspberry Pi 4B+ = 12 000 ₽

  • Плата расширения CAN = 2 940 ₽

  • USB Type C = 250 ₽

  • Плата аудиоусилителя = 296 ₽

  • Динамики + накладки = 407 ₽

  • Кабель AUX = 274 ₽

  • Разъём питания = 281 ₽

  • Кабель питания = Бесплатно от старого монитора

  • Сборка своими руками = Бесценно

  • Итого: 18 068 ₽

Этап 6: Управляющее ПО на Малинке

Моя любимая часть, моя гордость…

Годом ранее, работая над своим основным проектом Pixel Quest, я к нашему бэкенду на Go подключил Lua интерпритатор. С тех пор все игры нашей сети локаций разрабатываются на Lua.

Для Pixel Quest мы сделали открытую систему разработки игр. Что это значит:

  • во-первых, у нас есть визуальный конструктор игр "Пол–это лава", где можно попрактиковаться в покадровой отрисовке игр;

  • а во-вторых, у нас есть собственная онлайн IDE для разработки игровых сценариев на простом скриптовом языке Lua, который может освоить любой толковый школьник за несколько вечеров, при этом исходный код наших игр публично открыт в репозитории на GitHub, что делает обучение ещё проще.

В свой онлайн редактор для удобного тестирования игр мы добавили вот такую 3D визуализацию:

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

Почему нас сравнивают с Roblox?

Роблокс даёт возможность игрокам самим разрабатывать игры и даже зарабатывать на них, и мы в перспективе хотим сделать что-то похожее. А ещё на ютубе нашу игру “Безопасный цвет” постоянно сравнивают с играми “Color blocks” или “Block party” из Roblox, хотя когда я её разрабатывал, я ещё ничего не знал о Роблоксе...

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

Визуальный покадровый конструктор игр Пол – это лава

Визуальный покадровый конструктор игр Пол – это лава

Этап 7: Версия 2.0

Для будущей версии я переразвёл печатную плату с учетом предыдущего опыта, а именно:

  • сделал разъёмы не горизонтальными, а вертикальными, таким образом можно будет вынимать и устанавливать отдельные платы. Платы между собой будут соединяться П-образными штырьками;

  • удвоил количество светодиодов, чтобы получить более яркую и сочную картинку, а также резервирование на случай выхода светодиода из строя;

  • сделал плату размером 4х5 пикселей, чтобы избавиться от “рамки” по краям и получить поле нужного размера 24х15 для совместимости с игровыми комнатами, а также расширить диапазон возможных CAN адресов с 16 до 32, что даст возможность строить столы большего размера;

Нашёл в Китае и протестировал новое более тонкое и более прозрачное оргстекло, которое даёт более чёткий рисунок граней пикселей и увеличивает полезный сигнал с датчиков в три раза, что делает игру более комфортной, а ложные срабатывания сводит к нулю.

Заключение

Небольшое видео, суммирующее вышесказанное:

Сам прототип с видео сейчас стоит и радует детей на одной из наших локаций, а именно в городе Смоленске.

Ссылки на исходники печатных плат, прошивки под микроконтроллер и другие полезные материалы по проекту можно найти вот в этом телеграм посте (будет обновляться): t.me/pixel_quest/360. Там же в канале будет выкладываться и новая информация по проекту.

Исходники управляющего ПО в открытом виде дать не готов, т.к они представляют коммерческую ценность, но в случае, если стол вызовет достаточный интерес у комьюнити, мы сможем предоставить специальную версию для запуска игр и удобной разработки собственных Lua сценариев.

Далее хотим попробовать наладить серийное производство. Мы считаем, что потенциал у проекта огромный, хотелось бы его развивать.

Какие сценарии развития продукта мы видим:

  • Установка в школы программирования или робототехники для обучения детей. Я сам разработчик и сам когда-то посещал подобный кружок программирования, думаю такой яркий стол сможет вызвать дополнительный интерес у ребят к разработке. А тем более возможность прийти поиграть в свою игру на большом пиксельном полу!

  • Установка в виде вендинговых игровых автоматов в ТЦ и для привлечения внимания к нашему основному бизнесу;

  • Размещение на локациях Pixel Quest в качестве дополнительного развлечения;

  • Использование в качестве отладочного стенда для тестирования ПО, игр и различных эффектов в миниатюре.

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

Как вы считаете, в каком направлении больше перспектив?
Всего голосов:
Показать полностью 21 4 1
[моё] Опрос Электроника Сборка Raspberry pi Видео Длиннопост Своими руками Программирование Игры Разработка Alexgyver Вертикальное видео Короткие видео Без звука
22
Посты не найдены
О нас
О Пикабу Контакты Реклама Сообщить об ошибке Сообщить о нарушении законодательства Отзывы и предложения Новости Пикабу Мобильное приложение RSS
Информация
Помощь Кодекс Пикабу Команда Пикабу Конфиденциальность Правила соцсети О рекомендациях О компании
Наши проекты
Блоги Работа Промокоды Игры Курсы
Партнёры
Промокоды Биг Гик Промокоды Lamoda Промокоды Мвидео Промокоды Яндекс Маркет Промокоды Отелло Промокоды Aroma Butik Промокоды Яндекс Путешествия Постила Футбол сегодня
На информационном ресурсе Pikabu.ru применяются рекомендательные технологии