项目介绍
44VBA模拟器:
44vba是一个基于VBA-M(VisualBoyAdvance-M)项目的GBA模拟器。与VBA-M不同的是,44vba使用了新的是在vba-next核心,并支持多种平台,如ESP32-S3、嵌入式linux和wasm等。
44vba是一个开源项目,可以在GitHub上找到该项目的代码和相关文档。
需求:
目标:实现简易GBA模拟游戏机功能,熟悉BL808的工程编译,屏幕驱动
具体要求:
- 完成屏幕的基础驱动
- 完成GBA模拟器软件 44VBA 的基础移植
- 通过串口进行游戏操作
设计思路
了解和熟悉BL808工程编译和屏幕驱动是实现GBA模拟器的基础。所以首先需要了解BL808的基础知识,包括它的编译工具链、开发环境和支持库等。
其次,需要了解GBA模拟器软件的基础知识,包括它的架构、运行原理和操作系统要求等。可以先熟悉它的基础移植流程,并根据需要进行修改和优化。
在实现GBA模拟器的过程中,由于M1S的物理按键只有两个,所以就采用串口进行游戏操作。因为Demo部分已经实现了CLI功能,所以串口控制这部分也是基于此的。
环境搭建
软件的开发是需要在Linux平台进行,在M1s_BL808_example: M1s_BL808_example 的仓库上有详细的说明文档以及上手指南M1s DOCK 上手 - Sipeed Wiki,一些常见的问题在上面都能找到答案。
可以正常编译说明环境没有问题
屏幕驱动
M1S使用的是ST7789V芯片。ST7789V是一款TFT-LCD显示驱动芯片,由STMicroelectronics公司推出,主要用于智能手表、便携式电子设备、家庭娱乐和汽车电子等领域。
它支持18位色深、320x480像素的分辨率,可以实现清晰、生动的图像显示。ST7789V采用SPI接口标准。
ST7789V的特点:
- 低功耗:ST7789V采用了低功耗设计,可以在长时间使用时减少能耗,延长电池寿命。
- 高对比度:ST7789V具有高达1000:1的对比度,可以在不同的光照条件下显示更清晰、更生动的图像。
- 支持多种显示模式:ST7789V支持全屏显示和局部显示两种模式,可以根据应用需求进行选择。
- 兼容性强:ST7789V具有广泛的兼容性,可以与多种硬件和软件平台配合使用。
- 显示速度快:ST7789V的显示速度快,可以在不同的应用场景下满足快速响应的要求。
在BL808的SDK中已经实现ST7789V显示驱动,所以这里就直接调用即可。
添加UART控制命令解析
采用用串口通讯的方式来进行控制,按键对应关系如下表:
| PC按键 | 游戏按键 | 控制命令符 | | :------: | :------: | :--------: | | “A” | "a" | "ctrl -b" | | “S" | "b" | "ctrl -c" | | "Tab" | "select" | "ctrl -d" | | "Space" | "start" | "ctrl -e" | | "Right": | "right" | "ctrl -f" | | "Left" | "left" | "ctrl -g" | | "Up" | "up" | "ctrl -h" | | "Down" | "down" | "ctrl -i" |
static void cmd_c906_gba_ctrl(char *buf, int len, int argc, char **argv)
{
// static TaskHandle_t task_handle = NULL;
// bool to_be_exit = false;
// char *gba_path = NULL;
gba_ctrl_value = 0;
int opt;
getopt_env_t getopt_env;
utils_getopt_init(&getopt_env, 0);
// put ':' in the starting of the string so that program can distinguish
// between '?' and ':'
while ((opt = utils_getopt(&getopt_env, argc, argv, ":abcdefghijklm:")) != -1) {
switch (opt) {
case 'a':
printf("ctrl test\r\n");
break;
case 'b':
gba_ctrl_value |= 1 << 0;
break;
case 'c':
gba_ctrl_value |= 1 << 1;
break;
case 'd':
gba_ctrl_value |= 1 << 2;
break;
case 'e':
gba_ctrl_value |= 1 << 3;
break;
case 'f':
gba_ctrl_value |= 1 << 4;
break;
case 'g':
gba_ctrl_value |= 1 << 5;
break;
case 'h':
gba_ctrl_value |= 1 << 6;
break;
case 'i':
gba_ctrl_value |= 1 << 7;
break;
case 'j':
gba_ctrl_value |= 1 << 8;
break;
case 'k':
gba_ctrl_value |= 1 << 9;
break;
case 'l':
gba_ctrl_value |= 1 << 10;
break;
case 'm':
gba_ctrl_value |= 1 << 11;
break;
case ':':
// printf("%s: %c requires an argument\r\n", *argv, getopt_env.optopt);
break;
case '?':
// printf("unknow option: %c\r\n", getopt_env.optopt);
break;
}
}
// optind is for the extra arguments which are not parsed
// for (; getopt_env.optind < argc; getopt_env.optind++) {
// printf("extra arguments: %s\r\n", argv[getopt_env.optind]);
// }
printf("gba_ctrl_value:%d \r\n", gba_ctrl_value);
}
const static struct cli_command cmds_user[] STATIC_CLI_CMD_ATTRIBUTE = {
{"gba", "c906 gba command", cmd_c906_gba},
{"ctrl", "c906 gba ctrl command", cmd_c906_gba_ctrl},
};
模拟器是通过void UpdateJoypad(void)函数去更新获取控制信息,这里通过串口的CLi传入控制信息,然后在模拟器主函数中进行更新。
static void emuMain(void *arg)
{
printf("Emuinit \r\n");
CPUSetupBuffers();
CPUInit(NULL, false);
CPUReset();
while (1) {
if(joy == 0){
joy = gba_ctrl_value;
}
UpdateJoypad();
emuRunFrame();
}
}
GBA游戏控制Python脚本
gba_ctrl.py脚本是由Python编写,通过keyboard来监听和发送键盘事件、serial库来实现串口的数据通信,进而实现的GBA游戏启动和控制功能。
import keyboard
import serial
import time
serialPort="COM58" #串口号
baudRate=2000000 #波特率
ser=serial.Serial(serialPort,baudRate,timeout=0.5)
print("参数设置:串口=%s ,波特率=%d"%(serialPort,baudRate))#输出串口号和波特率
#启动游戏
# "a", "b", "select", "start", "right", "left", "up", "down", "r", "l",
ctrl_str = ""
def callback(x):
print(x)
print()
if x.name == "a":
ctrl_str = "ctrl -b"
elif x.name == "s":
ctrl_str = "ctrl -c"
elif x.name == "tab":
ctrl_str = "ctrl -d"
elif x.name == "space":
ctrl_str = "ctrl -e"
elif x.name == "right":
ctrl_str = "ctrl -f"
elif x.name == "left":
ctrl_str = "ctrl -g"
elif x.name == "up":
ctrl_str = "ctrl -h"
elif x.name == "down":
ctrl_str = "ctrl -i"
else:
ctrl_str = ""
print("ctrl-cmd:", ctrl_str)
ser.write(("ctrl -a"+'\n').encode())
ser.write((ctrl_str+'\n').encode())
#启动游戏
time.sleep(1)
ser.write(("gba -l /flash/MarioBreak.gba"+"\n").encode())
keyboard.on_press(callback)
# 按下任何按键时(包括长按),都会调用callback,其中一定会传一个值,就是键盘事件
keyboard.wait()
游戏添加启动
1 GBA游戏文件下载
Gameboy Advance Development (gbadev.org)
选择要下载的游戏下载完成后将GBA文件拷贝进M1S在电脑映射的U盘里面。
2 启动脚本
可以在gba_ctrl.py脚本启动游戏处更改对应启动游戏的名称。
#启动游戏
time.sleep(1)
ser.write(("gba -l /flash/MarioBreak.gba"+"\n").encode())
然后运行
python .\gba_ctrl.py
后续计划
玩游戏没有声音总觉得缺点什么,后面计划增加一个基于IIS的音频输出功能。
参考
2 Gameboy Advance Development (gbadev.org)