2023寒假一起练平台(3)- 基于Sipeed M1s Dock的GBA模拟器
基于Sipeed M1s Dock完成44VBA移植,通过外接物理按键的方式,实现GBA模拟器功能。
标签
2023寒假在家练
GBA模拟器
44VBA
launcher
更新2023-03-28
909

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 接口

FmQLoWR5EMTXYI_jWIh7cEW1UQW1

3.3 外扩物理按键

本项目外扩物理按键,使用如下按键模块:包含五向导航按键+两个独立按键。正好与游戏手柄类似,成本低廉且非常适合本项目使用。五向按键可以控制游戏基本的上下左右,两个独立按键可以实现A/B键功能,控制射击、跳跃等操作。

Fv5LUYGsSQOScoRpnT9PniHoHKwa

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屏幕刷新显示、物理按键检测及响应、音频输出等。

Fu5v5_aVm0XInUaAkC07ICo4qx7P

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 方案效果

FtB6QhJg7il4ClkFP43PauMH23VM

7 项目小结

Sipeed M1s Dock是一个功能强大的套件平台,板载资源丰富,可以轻松实现很多应用。通过本项目熟悉了44VBA的移植、M1s Dock的LCD驱动及GPIO控制等。遗憾的是目前的开发只能在Linux下进行,较为不便。如果能够在MDK或者Windows下开发,我想会更加方便、可玩性更高一些。

再次感谢电子森林提供的本次活动。

附件下载
play_with_gba.zip
团队介绍
www.etrd.org
团队成员
launcher
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号