rp2040 + ST7789 240x320 display module
- Для получения бинарного файла универсальной прошивки одного устройства необходимо подготовить среду сборки и произвести компиляцию исходного кода, находящегося в этом репозитории. Подготовку среды достаточно выполнить один раз на одном персональном компьютере, процесс описан в этом разделе
- Общий процесс компиляции исходного кода проекта описан в этом разделе. Переходить к этим действиям следует только после выполнения подготовки среды
- Если в исходный код были внесены изменения, при этом среда для компиляции на используемом ПК была подготовлена ранее, а компиляция проекта уже производилась, то полный порядок действий для выполнения повторной компиляции указан в этом разделе
- Загрузить zip-архив с сайта. Для загрузки выбрать последнюю версию с UCRT runtime и без поддержки LLVM
- Загруженный архив распаковать в
C:\, таким образом должна появиться директорияC:\mingw\, в которой будет находиться папкаbin - Добавить
C:\mingw64\binвPATH(WIN + R->sysdm.cpl->Enter->Дополнительно/Advanced->Переменные среды/Environment Variables)
- С сайта (VPN required) загрузить
Arm GNU Toolchain, в названии файла должно содержатьсяarm-none-eabi - Следовать инструкциям инсталлятора, не меняя параметров установки
- Добавить
C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\ВЕРСИЯ\bin\вPATH, указав установленную версию - Убедиться в правильности установки можно путем ввода команды
arm-none-eabi-gcc --versionвcmd
- С сайта загрузить установщик
- Следовать инструкциям инсталлятора, не меняя параметров установки
- Переменная
PATHдолжна обновиться автоматически - Убедиться в правильности установки можно путем ввода
git –-versionвcmd
- С сайта загрузить zip-архив для win32
- Извлечь его в
C:\, таким образом должна появиться директорияC:\xpack-openocd-<...>\bin\ - Добавить вышеуказанную директорию в
PATH
Для проверки выполнить в командной строке:
g++ --versionmingw32-make --versionarm-none-eabi-g++ --versiongit --versioncmake --versionopenocd --version
- Клонировать репозиторий
git clone https://git.fh-aachen.de/Terstegge/YAHAL.git - Создать переменную среды
YAHAL_DIR, указывающую на папку репозитория - Перейти в директорию с примером
cd YAHAL/examples/rp2040-launchpad/blink_simple - Создать папку
buildи перейти в неёmkdir build && cd build - Выполнить
cmake -G "MinGW Makefiles" .. - Скомпилировать пример, выполнив
mingw32-make - При правильно выполненной установке пример будет успешно скомпилирован, процесс завершится выводом компоновщика информации об использованных регионах памяти, а также строчкой
[100%] Built target blink_simple, в папкеbuildпоявится файлblink_simple.bin - При возникновении ошибки
unrecognized option --no-warn-rwx-segmentв файлеYAHAL/cmake/toolchains/arm-gcc.cmakeзаменить--no-warn-rwx-segmentна--no-warn-rwx-segments, очистить папкуbuildи повторить попытку - При повторном возникновении ошибки, удалить в том же файле флаг
-Wl,--no-warn-rwx-segmentsили-Wl,--no-warn-rwx-segmentизARM_GCC_LINK_FLAGS
- Загрузить Zadig с сайта
- Подключить rp2040 (USB)
- Запустить загруженный
.exe-файл и нажатьInstall Driver - Дождаться сообщения об успешной установке
- Подключить к ПК, удерживая в момент подключения нажатой кнопку
BOOT - Открыть проводник, найти
.uf-файл прошивки - Убедиться, что среди устройств и дисков появился USB-накопитель
RPI-RP2 - Скопировать
.uf-файл на USB-накопительRPI-RP2 - После завершения процесса rp2040 автоматически отключится и перезагрузится
- Клонировать репозиторий с прошивкой
- Перейти в папку репозитория
- Создать папку
buildи перейти в нее - Выполнить
cmake -G "MinGW Makefiles" ..и дождаться завершения работы cmake - Выполнить
mingw32-make - Убедиться в отсутствии ошибок
- В папке
buildпоявятся файлы.bin,.hex,.elf,.uf2
- Если необходимо обновить исходный код в локальном репозитории до последней версии с GitHub, выполнить
git pull - Создать папку
buildили очистить её содержимое в случае, если она была создана ранее и содержит в себе файлы - Выполнить
cmake -G "MinGW Makefiles" ..и дождаться завершения работы cmake - Выполнить
mingw32-make - Убедиться в отсутствии ошибок
- В папке
buildпоявятся файлы.bin,.hex,.elf,.uf2
- Убедиться, что установлены
gcc,g++,makeиcmake - Установить GNU ARM Cross-compiler
- Загрузить
debugprobe_on_pico.uf2с сайта - Прошить rp2040 через USB, как описано в этом разделе
- Проделать действия, описанные в этом разделе
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program flappy_back.elf verify reset exit"
| rp2040-zero pin | target |
|---|---|
| GND | GND |
| 2 | SWCLK |
| 3 | SWDIO |
Проект представляет из себя исходный код игры (Flappy Bird), предназначенный для запуска на плате ieye. Код написан на C++ (стандарт C++20) с использованием библиотеки YAHAL
В основе аппаратного решения:
- микроконтроллер
rp2040с двумя ядрами Arm Cortex M0+ с тактовой частотой до 133 МГц, 264 КБ SRAM-памяти, поддержкой до 16 МБ внешней флеш-памяти - модуль экрана
GY-TFT028F2762.8'' 240(RGB)x320 SPI ST7789V3
| # | pin | Назначение |
|---|---|---|
| 3 | GPIO1 | BUT_L |
| 6 | GPIO4 | BUT_R |
| 7 | GPIO5 | LCD_RST |
| 8 | GPIO6 | LCD_SCK |
| 9 | GPIO7 | LCD_MOSI |
| 11 | GPIO8 | LCD_DC |
| 12 | GPIO9 | LCD_CS |
| 15 | GPIO12 | LEDS |
| 24 | SWCLK | SWD |
| 25 | SWDIO | SWD |
| 29 | GPIO18 | LCD_BL |
CMakeLists.txt- скрипт сборки проектаCharacterDrawer.cpp,CharacterDrawer.h- класс отрисовки символов (текста), шрифтDifficulty.cpp,Difficulty.h- класс уровней сложностиGetColors.cpp,GetColors.h- класс, содержащий фон в виде массива байтов, а также методы, связанные с его отрисовкойScoreEntry.cpp,ScoreEntry.h- лидербордflappy_back.cpp- содержит функцию mainYAHAL_import.cmake- скрипт, задача которого - найти путь к установленному YAHALGame.cpp,Game.h- логика игры, движения, коллизии, визуализация и прочее
Вся логика игры описана в файлах Game.cpp и Game.h. Класс Game содержит 2 публичных метода:
Game()- конструктор, здесь определяется pin mapping и задается скорость SPI SCKrun()- главный метод программы, содержащий бесконечный цикл, вызывается вmain()для запуска игры
Класс Game имеет следующие приватные свойства, значения по умолчанию определены в конструкторе класса:
_lcd_cs- объектgpio_rp2040_pin, пин CS экрана_spi- объектspi_rp2040, аппаратный SPI rp2040, используемый для подключения экрана_lcd_rst- объектgpio_rp2040_pin, пин RESET экрана_lcd_dc- объектgpio_rp2040_pin, пин выбора Data/Command экрана_lcd- объектst7735s_drv, дескриптор экрана_s1- объектgpio_rp2040_pin, пин первой кнопки_s2- объектgpio_rp2040_pin, пин второй кнопки_timer- объектtimer_rp2040, таймер игры_accel_x- объектadc_rp2040_channel, TrueRNG на основе АЦП для генерации уровня_getColors- объектGetColors, для отрисовки фона_name- массив байтов, имя игрока, максимум 3 символа_diff- объектDifficulty, уровень сложности_sb- объектScoreboard, таблица лидеров_mode-uint32_t, текущий режим (главное меню / игра / таблица лидеров / меню настроек / изменение имени / выбор уровня сложности)_cd- объектCharacterDrawer, для отрисовки текста
Класс Game имеет следующие приватные методы:
read_area_and_get_color- принимает координаты прямоугольника (mins и maxs), обновляет фон в этом прямоугольникеdrawBird- отрисовывает птицу (по пикселям) в её текущей позицииcheck_collision- проверяет столкновение со столбом и инкрементирует набранное количество очков при успешном пролете препятствияdeath_screen- выводит экран смерти (постепенно заполняет экран красным цветом)play_mode- выполняется в бесконечном цикле в режиме игрыmenu_mode- выполняется в бесконечном цикле в режиме отображения главного менюscore_mode- выполняется в бесконечном цикле в режиме отображения таблицы лидеровdifficulty_mode- выполняется в бесконечном цикле в режиме выбора уровня сложностиsetting_mode- выполняется в бесконечном цикле в режиме отображения меню настроекname_mode- выполняется в бесконечном цикле в режиме изменения имени игрокаcolor- вызывается для определения цвета текста при отображении таблицы лидеров (топ 3 - разными цветами)
В функции main создается объект класса Game, его свойства принимают значения по умолчанию, которые заданны в Game.cpp.
В конструкторе инициализируется SPI, пины, экран, таймер, после чего вызывается метод run.
Изначальное значение _mode - 0, что соответствует режиму отображения главного меню, поэтому далее вызывается menu_mode. Здесь основной таймер настраивается таким образом, чтобы в лямбда-функции обратного вызова каждые 25ms (hardcoded) проверять нажатие клавиш, исключая дребезг контактов (для регистрации нажатия необходимо, чтобы низкий уровень пина был зарегистрирован 2 раза подряд). На экране один раз отрисовывается текст, позиция текста hardcoded по пикселям. S1 используется для подтверждения выбора, S2 - для переключения между пунктами меню. Один раз в десятикратный период таймера (дважды hardcoded) происходит изменение цвета текста текущего выбранного пункта меню (здесь позиции текста также жутко hardcoded по пикселям). Если был выбран пункт START, происходит смена режима на 1, т.е. теперь значение _mode соответствует вызову play_mode из главного бесконечного цикла программы.
При входе в метод play_mode выполняется отрисовка фона (целиком), обнуляется счет, позиция птицы по высоте (hardcode по пикселям), таймер настраивается на периодическое срабатываение через каждые 5мс * на множитель сложности (hardcoded, frametime), в лямбда-функции обратного вызова проверяется нажание клавиши S1, вне зависимости от состояния клавиши каждый новый callback увеличивает скорость игры. Если клавиша нажата, изменяется вертикальная скорость птицы (дребезг не учитывается). Каждый фрейм:
- происходит вычисление новой позиции птицы (
_bird_xиспользуется как вертикальная скорость в пикселях в один фрейм) - случайным образом генерируется новая позиция для препятствия (hardcoded пиксели, судя по всему постоянно в кадре 3 препятствия)
- отрисовываются препятствия и фон (частично), hardcode пикселей по отрисовке препятствий
- отрисовывается птица в новой позиции (если позиция изменилась)
- проверка коллизии с препятствием (если есть, то стоп таймера, обновление лидерборда, 2 секунды задержки (hardcoded), экран смерти и выход в главное меню), здесь же увеличивается количество очков (переменная) при пролете препятствия
- отрисовка нового количества очков, позиция символов вычисляется по пикселям (hardcoded)
- позиция препятствий (ноль их системы координат в системе координат экрана) смещается влево на 1 пиксель (hardcoded)
- скорость птицы (вертикальная) увеличивается на 1 пиксель во фрейм (имитация ускорения свободного падения)