前言:
此时大家对“开发板arduino 手柄”可能比较珍视,各位老铁们都需要学习一些“开发板arduino 手柄”的相关资讯。那么小编在网摘上网罗了一些有关“开发板arduino 手柄””的相关内容,希望各位老铁们能喜欢,同学们快快来学习一下吧!前言
自制了一个ESP32游戏机,可以随手放兜里,想玩就玩!
最实用的地方是,它还可以作为手柄使用。连接电视、电脑、手机都可以。
本文就记录一下它的软硬件设计思路。
看看它是如何从0到1实现一个成品的!有些什么技巧?
看过的网友都表示——“学习过游戏机的源代码后,写程序都简单了!”
电路设计
由于游戏机比较小,所以电路也尽量设计得比较简单,重点都在软件。
另外元件都是0603,如图所示,铁板烧就能焊!
编译
RachelSDK 为 PIO 工程, 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()
获取数据库实例,是一个简单的 RAM 上 KV 数据库,可以用于 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 手柄