1 项目介绍
本项目基于Sipeed M1s Dock 移植44VBA代码,借助配套的LCD显示屏,通过外扩物理按键,实现GBA模拟器及游戏控制。游戏文档通过板子虚拟的U盘存入板子flash中,程序开机可自动运行GBA游戏。
2 设计思路
回想儿时小霸王游戏机,需要四个基本结构:游戏机主体、游戏卡、游戏手柄、电视机。即对应控制器、存储器、按键、显示屏(音、视频)。Sipeed M1s Dock套件已有控制器本身及板载存储,以及配套LCD显示器,在暂可不考虑音频的情况下,只需要外接物理按键即可实现GBA模拟器方案。
3 硬件介绍
3.1 简介
Sipeed M1s Dock 是基于 Sipeed M1s 模组来设计的一款核心板,引出了 MIPI CSI、SPI LCD 等 FPC 接口,免去接线难的烦恼。使用最精简的设计,用于客户对模组进行模组评估,或者爱好者直接上手游玩等用途。
3.2 板卡特点
- 主芯片 BL808 RISC-V 480Mhz + NPU BLAI-100
- 板载 USB 转 UART 调试器(可实现一键点击烧录,无需按实体按键)
- 1.69 寸 240x280 电容触摸屏
- 200W 像素摄像头
- 支持 2.4G WIFI / BT / BLE
- 板载 1 个模拟麦克风、1 个 LED、1 个 TF 卡座
- 引出一路 USB-OTG 到 USB Type-C 接口
3.3 外扩物理按键
本项目外扩物理按键,使用如下按键模块:包含五向导航按键+两个独立按键。正好与游戏手柄类似,成本低廉且非常适合本项目使用。五向按键可以控制游戏基本的上下左右,两个独立按键可以实现A/B键功能,控制射击、跳跃等操作。
4 项目44VBA介绍
44VBA是一个可以在不同平台运行的GBA模拟器,包括 ESP32-S3, embedded linux, wasm等。github项目地址:https://github.com/44670/44vba。
项目目前在多个平台已经做了测试:
Name | Tested hardware | Performance | Notes |
---|---|---|---|
ESP32-S3 | ESP32-S3-WROOM-1-N8R8 | 20 fps | frameskip: 1 |
SDL2 | AMD 3800X | 1800 fps | |
SDL2 | Switch | 314 fps | |
SDL2 | Apple M1 | 2300 fps | |
SDL2 | Vita | 131 fps | frameskip: 1, overclocked |
SDL1 | New 3DS | 111 fps | frameskip: 1, overclocked |
watchOS | Apple Watch Series 5 | 451 fps | Not public yet |
WebAssembly | - | - | https://gba.44670.org |
在其代码结构中,libretro-common/libretro/src文件夹为模拟器基本库源码,port-xxxx文件夹存放的是在不同平台下的移植代码。需要移植内容主要是:LCD屏幕刷新显示、物理按键检测及响应、音频输出等。
5 软件环境
5.1 开发环境
根据sipeed官方文档,当前M1s需要在Linux环境下进行编译,本项目在windows环境下使用VMware安装ubuntu 64 16.04.7虚拟环境进行开发。
5.2 软件库
开发之前,还需要将两个仓库clone到本地,做好配置及准备工作:
(1)SDK仓库
git clone https://gitee.com/sipeed/M1s_BL808_SDK.git
(2)例程仓库
git clone https://gitee.com/Sipeed/M1s_BL808_example.git
6 开发步骤
6.1 建立样例
在M1s_BL808_example下建立play_with_gba文件夹用于存放GBA样例及移植信息,文件夹结构如下:
├─44vba
│ ├─libretro
│ ├─libretro-common
│ └─src
└─port-bl808
6.2 显示实现
44VBA需实现的显示函数为:
void systemDrawScreen (void)
该函数是将GBA的像素点阵列uint16_t pix[2 * 256 * 160] 显示到LCD屏幕上。由于Sipeed M1s Dock已提供LCD的画图接口,直接调用既可。
void st7789v_spi_draw_picture_blocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t *picture);
实现代码如下:
void systemDrawScreen(void)
{
static uint16_t FB[240 * 256];
frameDrawn = 1;
frameCount++;
uint16_t *src = pix; // Stride is 256
uint16_t *dst = FB; // Stride is 240
for (int y = 0; y < 240; y++) {
for (int x = 0; x < 256; x++) {
*dst++ = __builtin_bswap16(*src++);
}
}
const int x = 25;
const int y = 30;
st7789v_spi_draw_picture_blocking(x, y, x + 256 - 1, y + 210 - 1, FB);
}
6.3 音频实现
44BVA需实现的音频播放函数为:
void systemOnWriteDataToSoundBuffer(int16_t * finalWave, int length);
由于Sipeed M1s Dock未板载播放接口,且不影响GBA正常显示、控制,因此本项目暂不进行实现。
6.4 控制实现
44VBA的键值通过变量(uint64_t joy = 0)进行传递,所以需要实现读取按键值并存入该变量中。
void bl808_key_init()
{
GLB_GPIO_Cfg_Type cfg;
cfg.drive = 0;
cfg.smtCtrl = 1;
cfg.gpioFun = GPIO_FUN_GPIO;
cfg.gpioMode = GPIO_MODE_INPUT;
cfg.pullType = GPIO_PULL_UP;
for (int i = 0; i < 12; i++) {
cfg.gpioPin = osKeyMap[i];
if (cfg.gpioPin != 0xff) {
GLB_GPIO_Init(&cfg);
}
}
}
uint8_t osKeyMap[12] = {KEY_A_PIN, KEY_B_PIN, KEY_SELECT_PIN, KEY_START_PIN, KEY_LEFT_PIN, KEY_RIGHT_PIN,
KEY_UP_PIN, KEY_DOWN_PIN, KEY_R_PIN, KEY_L_PIN, KEY_TRUBO_PIN, KEY_MENU_PIN};
uint32_t bl808_key_read()
{
uint32_t ret = 0;
for (int i = 0; i < 12; i++) {
if (osKeyMap[i] != 0xff) {
int res = GLB_GPIO_Read(osKeyMap[i]);
if (res == 0) {
ret |= 1 << i;
}
}
}
return ret;
}
按键对应IO分配如下:
#define KEY_A_PIN (28)
#define KEY_B_PIN (31)
#define KEY_SELECT_PIN (22)
#define KEY_START_PIN (23)
#define KEY_UP_PIN (6)
#define KEY_DOWN_PIN (7)
#define KEY_LEFT_PIN (29)
#define KEY_RIGHT_PIN (40)
在按键分配IO的时候需要特别小心,因为有些IO可能存在与板载其它外设进行复用的情况,所以尽量选取未使用到的IO来进行按键分配。
6.5 GBA文件读取
int fd = -1;
if (0 > (fd = aos_open(gba_path, 0))) {
printf("[failed] open file %s\r\n", gba_path);
return;
}
int gba_bin_len = 0;
gba_bin_len = aos_lseek(fd, 0, SEEK_END);
if (0 >= gba_bin_len) {
aos_close(fd);
printf("[failed] the length of gba file is less then zero\r\n");
return;
}
aos_lseek(fd, 0, SEEK_SET);
if (gba_bin_len != aos_read(fd, rom, gba_bin_len)) {
aos_close(fd);
printf("[failed] err occur while reading model\r\n");
return;
}
aos_close(fd);
puts("[OS] Starting emu task...\r\n");
xTaskCreate(emuMain, (char *)"emuMainLoop", 8192, NULL, 14, NULL);
6.6 GBA线程实现
static void emuMain(void *arg)
{
CPUSetupBuffers();
CPUInit(NULL, false);
CPUReset();
while (1) {
extern uint32_t bl808_key_read();
joy = bl808_key_read();
UpdateJoypad();
emuRunFrame();
}
}
6.7 GBA开机启动
CPU运行完文件系统、LCD控制初始化后,直接启动GBA模拟器运行命令。
static char* cmd_argv[]={
"gba",
"-l",
"/flash/GBA_HunDouLuo.GBA"
};
int main()
{
fatfs_register();
st7789v_spi_init();
st7789v_spi_set_dir(1, 0);
st7789v_spi_clear(0x0);
bl808_key_init();
extern int cmd_c906_gba_cli_init(void);
cmd_c906_gba_cli_init();
extern void cmd_c906_gba(char *buf, int len, int argc, char **argv);
cmd_c906_gba(NULL,0,3,&cmd_argv);
return 0;
}
6 方案效果
7 项目小结
Sipeed M1s Dock是一个功能强大的套件平台,板载资源丰富,可以轻松实现很多应用。通过本项目熟悉了44VBA的移植、M1s Dock的LCD驱动及GPIO控制等。遗憾的是目前的开发只能在Linux下进行,较为不便。如果能够在MDK或者Windows下开发,我想会更加方便、可玩性更高一些。
再次感谢电子森林提供的本次活动。