跳转至

LVGL NES 模拟器

lv_nes 是一个使用触摸屏游玩 NES 游戏的演示包。采用了最简单最基础的 NES 模拟器实现方式,运行较为卡顿,不过也更容易理解 lvgl 系统的相关操作实现。

获取&配置软件包

软件包可以在【全志在线开发者社区 - 资源下载】找到,下载 lv_nes扩展软件包 即可。    

同样的,下载后把软件包放置到 Tina SDK 的根目录里。并重命名为lv_nes.tar.gz

然后打开命令行,解压 lv_nes 软件包

tar xvf lv_nes.tar.gz

接下来就可以在 make menuconfig 中的 Gui->littlevgl 目录中找到  lv_nes 选项。勾选这个选项。

make && pack 编译打包并烧录开发板,就可以使用 lv_nes 目录运行 模拟器了。

可以使用触摸屏与按键操作游戏的运行。

源码结构

lv_nes 的源码包括三个部分,第一个部分是显示输出的对接,第二个部分是 UI 的绘制与输入处理,第三个部分是 NES 模拟器。

显示输出的对接

显示输出对接位于 main.c 这里我们使用 sunxifb 作为显示接口。引入 sunxifb 的头文件。

#include "lv_drivers/display/sunxifb.h"

接下来便是初始化显示驱动与显示驱动的注册

lv_disp_drv_t disp_drv;
lv_disp_draw_buf_t disp_buf;

/*LittlevGL 初始化*/
lv_init();

/*初始化 sunxifb*/
uint32_t rotated = LV_DISP_ROT_NONE;
sunxifb_init(rotated);

/*获取屏幕的大小*/
static uint32_t width, height;
sunxifb_get_sizes(&width, &height);

static lv_color_t *buf;

/*申请缓冲区内存*/
buf = (lv_color_t *)sunxifb_alloc(width * height * sizeof(lv_color_t), "lv_nes");
if (buf == NULL)
{
    sunxifb_exit();
    printf("malloc draw buffer fail\n");
    return 0;
}

/*初始化输出缓冲区*/
lv_disp_draw_buf_init(&disp_buf, buf, NULL, width * height);

/*初始化 LVGL 侧显示驱动*/
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = sunxifb_flush;
disp_drv.hor_res = width;
disp_drv.ver_res = height;
disp_drv.rotated = rotated;
disp_drv.screen_transp = 0;
lv_disp_drv_register(&disp_drv);

同时,除了显示输出,还有触摸的输入需要对接,这里我们使用的是 evdev 驱动作为触摸输入。

lv_indev_drv_t indev_drv;

evdev_init();
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = evdev_read;
/*注册触摸*/
lv_indev_t *evdev_indev = lv_indev_drv_register(&indev_drv);

UI 的绘制与输入处理

UI 的绘制位于 lv_nes.c 中,绘制了屏幕所显示的按钮,模拟器图像输出的位置。由于源码较多这里只给出函数名称,具体实现可以参阅代码。

static void init_ctl_btn(lv_obj_t *obj)  // 绘制屏幕按钮
static void lv_nes_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj) // 模拟器图像输出

按键处理同样位于相同源文件内。

static void ctl_btn_event(lv_event_t *e) // 按键事件监听
static void *nes(void *arg) // 按键处理

另外,由于 NES 的输出为 256 * 240 分辨率,对于开发板屏幕来说太小了,所以使用缩放方法将输出扩大一倍

lv_img_set_zoom(nes->img, 512);

NES 模拟器的实现

要实现 NES 模拟器的功能,就需要实现一个最基础的 NES 系统。包括了 ppu.ccart.ccpu.c 三个文件,实现了最基础的 NES 模拟环境。

其中最重要的是 ppu.c ,它实现了 NES 输出图像回调 lvgl 并显示的功能。

obj->dsc->data = (uint8_t *)pixels;
lv_img_set_src(obj->img, obj->dsc);

由于 NES 模拟器并没有进行任何优化,使用这里直接输出图像会导致系统缩放时产生大量 CPU 占用,导致更加卡顿的情况出现,所以这里采用了跳输出的方法。每四帧回调 lv_img_set_src 方法输出一次图像。

obj->dsc->data = (uint8_t *)pixels;
if(idx == 4)
{
    lv_img_set_src(obj->img, obj->dsc);
    idx = 0;
} else {
    idx++;
}