龙空技术网

做了个ESP32游戏机,也是个手柄!DIY一个很轻松的

嘉立创EDA 1903

前言:

此时大家对“开发板arduino 手柄”可能比较珍视,各位老铁们都需要学习一些“开发板arduino 手柄”的相关资讯。那么小编在网摘上网罗了一些有关“开发板arduino 手柄””的相关内容,希望各位老铁们能喜欢,同学们快快来学习一下吧!

前言

自制了一个ESP32游戏机,可以随手放兜里,想玩就玩!

最实用的地方是,它还可以作为手柄使用。连接电视、电脑、手机都可以。

本文就记录一下它的软硬件设计思路。

看看它是如何从0到1实现一个成品的!有些什么技巧?

看过的网友都表示——“学习过游戏机的源代码后,写程序都简单了!”

电路设计

由于游戏机比较小,所以电路也尽量设计得比较简单,重点都在软件。

原理图

PCB图

另外元件都是0603,如图所示,铁板烧就能焊!

PCB实物图

编译

RachelSDKPIO 工程, VS Code 下载 PlatformIO 插件,用 VS Code 打开文件夹即可。

SDK 目录树

.├── apps│   ├── app_ble_gamepad               BLE 手柄│   ├── app_music                     音乐播放器│   ├── app_nofrendo                  NES 模拟器│   ├── app_raylib_games              Raylib 游戏│   ├── app_screencast                WiFi 投屏│   ├── app_settings                  设置│   ├── app_genshin                   __,__!│   ├── app_template                  App 模板│   ├── launcher                      启动器│   ├── utils                         通用组件库│   ├── assets                        公共资源│   ├── tools                         App 相关工具(脚本)│   └── apps.h                        App 安装回调├── hal│   ├── hal.cpp                       HAL 基类│   ├── hal.h                         HAL 基类│   ├── hal_rachel                    HAL Rachel 派生类│   ├── hal_simulator                 HAL PC 模拟器派生类│   └── lgfx_fx                       lgfx 派生类(拓展图形API)├── rachel.cpp└── rachel.h                          RachelSDK 入口

SD卡目录树

NES 模拟器、音乐播放器等会尝试加载SD卡里指定目录的资源文件。

.├── buzz_music                        蜂鸣器音乐│   ├── harrypotter.json│   ├── nokia.json│   ...├── fonts                             字体│   └── font_text_24.vlw└── nes_roms                          NES ROM 文件    ├── Kirby's Adventure (E).nes    ├── Snow Bros (U).nes	...

font_text_24.vlw 这个字体我用的是Zpix很嗨好看,可以替换任何自己喜欢的。

NES ROM 直接丢进去就行,不是很大的应该都能玩。

SDK 结构

创建 App

自动创建

写了个 python 脚本用来简化 App 创建:

python3 ./src/rachel/apps/tools/app_generator.py

$ Rachel app generator > <

$ app name:

hello_world

$ file names:

$ - ../app_hello_world/app_hello_world.cpp

$ - ../app_hello_world/app_hello_world.h

$ app class name: AppHello_world

$ install app hello_world

$ done

App 创建好了, 重新编译上传:

新创建的 App 基本模板如下,详细的生命周期和API可以参考 Mooncake 项目。

// Like setup()...void AppTemplate::onResume(){    spdlog::info("{} 启动", getAppName());}// Like loop()...void AppTemplate::onRunning(){        spdlog::info("咩啊");    HAL::Delay(1000);    _data.count++;    if (_data.count > 5)        destroyApp();}

Mooncake 框架内部集成了 spdlog 日志库,当然你也可以继续用 cout, printf, Serial...

手动创建

常用的 App API

destroyApp()

关闭 App,调用后会告诉框架你不玩了,框架会把你的 App 销毁释放,所以在 onRunning() 被阻塞的情况下是无效的。

// 有效void AppTemplate::onRunning(){    destroyApp();}// 无效void AppTemplate::onRunning(){        destroyApp();    HAL::Delay(66666666666);}

getAppName()

获取 App 名字,会返回你设置的 App 名字。

// 你的 App 头文件里:class AppHello_world_Packer : public APP_PACKER_BASE{    // 这里修改你的 App 名字:    std::string getAppName() override { return "文明讲礼外乡人"; }    ...}

getAppIcon()

获取 App 图标,启动器在渲染画面时会调用。

// 你的 App 头文件里:class AppHello_world_Packer : public APP_PACKER_BASE{    ...    // 这里修改你的 App 图标(有默认图标)    void* getAppIcon() override { return (void*)image_data_icon_app_default; }    ...}

mcAppGetDatabase()

获取数据库实例,是一个简单的 RAMKV 数据库,可以用于 App 退出数据保存、多 App 间的数据共享(当然断电没)。

void AppTemplate::onResume(){    // 看看数据库里有没有这个 key    if (mcAppGetDatabase()->Exist("开了?"))    {        // 数据库里拿出来, 看看开了几次        int how_many = mcAppGetDatabase()->Get("开了?")->value();        spdlog::info("开了 {} 次", how_many);		        // 加上这一次, 写进数据库        how_many++;        mcAppGetDatabase()->Put("开了?", how_many);    }    // 没有就创建一个    else        mcAppGetDatabase()->Add("开了?", 1);}

mcAppGetFramework()

获取 Mooncake 框架实例,一般用来写启动器。

// 看看安装了几个 Appauto installed_app_num = mcAppGetFramework()->getAppRegister().getInstalledAppNum();spdlog::info("安装了 {} 个 App", installed_app_num);// 看看他们都叫什么for (const auto& app_packer : mcAppGetFramework()->getAppRegister().getInstalledAppList()){    spdlog::info("{}", app_packer->getAppName());}

HAL 硬件抽象层

HAL为单例模式,SDK初始化时会注入一个HAL实例。

对于 HAL Rachel ,按住 按键A 开机,会暂停在初始化界面,可以查看详细的HAL初始化log。如果有不同底层硬件需求,只需派生新的HAL对象,重写 API 方法 (override) 并在初始化时注入即可。

Include

#include "{path to}/hal/hal.h"

显示 API

显示驱动使用 LovyanGFX。

// 获取屏幕驱动实例HAL::GetDisplay();// 获取全屏Buffer实例HAL::GetCanvas();// 推送全屏buffer到显示屏HAL::CanvasUpdate();// 渲染FPS面板HA::RenderFpsPanel();

系统 API

HAL Rachel 在初始化时会以RTC时间调整系统时间,所以时间相关的POSIX标准API都可以正常使用。

// 延时(毫秒)HAL::Delay(unsigned long milliseconds);// 获取系统运行毫秒数HAL::Millis();// 关机HAL::PowerOff();// 重启HAL::Reboot();// 设置RTC时间HAL::SetSystemTime(tm dateTime);// 获取当前时间HAL::GetLocalTime();// 优雅地抛个蓝屏HAL::PopFatalError(std::string msg);

外设 API

// 刷新IMU数据HAL::UpdateImuData();// 获取IMU数据HAL::GetImuData();// 蜂鸣器开始哔哔HAL::Beep(float frequency, uint32_t duration);// 蜂鸣器别叫了HAL::BeepStop();// 检查SD卡是否可用HAL::CheckSdCard();// 获取按键状态HAL::GetButton(GAMEPAD::GamePadButton_t button);// 获取任意按键状态HAL::GetAnyButton();

系统配置 API

// 从内部FS导入系统配置HAL::LoadSystemConfig();// 保存系统配置到内部FSHAL::SaveSystemConfig();// 获取系统配置HAL::GetSystemConfig();// 设置系统配置HAL::SetSystemConfig(CONFIG::SystemConfig_t cfg);// 以系统配置刷新设备HAL::UpdateSystemFromConfig();

通用组件库

一些比较有用的通用封装库放在了这里 rachel/apps/utils/system

选择菜单

创建一个选择菜单。

Include

#include "{path to}/utils/system/ui/ui.h"

Example

using namespace SYSTEM::UI;// 创建选择菜单auto select_menu = SelectMenu();// 创建选项列表std::vector items = {    "[WHAT 7 TO PLAY]",    "Jenshin Import",    "Light Soul",    "Grand Cop Manual",    "Super Maliao",    "Quit"};// 等待选择auto selected_index = select_menu.waitResult(items);spdlog::info("selected: {}", items[selected_index]);

进度条窗口

创建一个带有进度条的窗口(u1s1, 现在应该算是页面)。

Include

#include "{path to}/utils/system/ui/ui.h"

Example

using namespace SYSTEM::UI;for (int i = 0; i < 100; i++){    ProgressWindow("正在检测智商..", i);    HAL::CanvasUpdate();    HAL::Delay(20);}

蜂鸣器音乐播放器

参考 arduino-songs 的 json 格式蜂鸣器音乐播放器。

Include

#include "{path to}/utils/system/audio/audio.h"

Example

using namespace SYSTEM::AUDIO;// 播放SD路径上的json音乐文件BuzzMusicPlayer::playFromSdCard("/buzz_music/nokia.json");

Include

#include "{path to}/utils/system/inputs/inputs.h"

Example

using namespace SYSTEM::INPUTS;auto button_a = Button(GAMEPAD::BTN_A);while (1){    if (button_a.pressed())        spdlog::info("button a was pressed");    if (button_a.released())        spdlog::info("button a was released");    if (button_a.toggled())        spdlog::info("button a was toggled");    HAL::Delay(20);}

深入

需要源码,或更深入的了解具体框架实现方式,可以自行去文末参考资料处查看文献~都开源了的。

也欢迎收藏本文,保姆级教学,不愁学不到真技术!

参考资料:

[1]

— 完 —

嘉立创EDA·头条号

关注我,看一手优质开源项目

标签: #开发板arduino 手柄