在100ask stm32mp157板子上运行超级玛丽
一、下载arm-NES-linux
git clone https://gitee.com/ant1423/arm-NES-linux.git
二、修改源码支持标准输入控制
这一步主要修改源码根目录下的linux/joypad_input.cpp
文件,要生成linux可用的程序,只需要在linux
目录下面执行make
命令即可,但是由于编译的是cpp文件,所以需要交叉编译工具链支持c++,文末直接给出代码
添加了一个新的函数以支持从标准输入获取输入事件,这里的映射关系是参考的上面手柄来设置的
设置一个T_JoypadInput结构体
最后在InitJoypadInput
函数里注册就行了
三、 编译运行
编译只需要在arm-NES-linux/linux
目录下执行make命令就行了,编译生成InfoNes
文件,把它和游戏ROM拷贝到板子上,添加执行权限就可以运行
注意,如果myir的QT程序正在运行,需要关闭myir的QT程序
systemctp stop myir
关闭成功后即可执行InfoNes
,如运行超级玛丽
./InfoNES Super_Mario.nes
效果如下图所示
按在串口输入H开始游戏,J是跳跃,K是攻击,AD是移动
四、问题
1、颜色显示的不正确,这里可能需要修改RGB格式
1、在按下按键的时候,延时设置的是500毫秒,在按键按下之后也没有上报松开事件,整体的体验不是很好
2、在通过第一关的时候,内核会报错,这里没有仔细调。无法进入下一关,并且无法重新启动游戏
joypad_input.cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <termios.h>
#include<sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#define JOYPAD_DEV "/dev/joypad"
#define USB_JS_DEV "/dev/input/js0"
typedef struct JoypadInput{
int (*DevInit)(void);
int (*DevExit)(void);
int (*GetJoypad)(void);
struct JoypadInput *ptNext;
pthread_t tTreadID; /* 子线程ID */
}T_JoypadInput, *PT_JoypadInput;
struct js_event {
unsigned int time; /* event timestamp in milliseconds */
unsigned short value; /* value */
unsigned char type; /* event type */
unsigned char number; /* axis/button number */
};
//全局变量通过互斥体访问
static unsigned char g_InputEvent;
static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_tConVar = PTHREAD_COND_INITIALIZER;
static int joypad_fd;
static int USBjoypad_fd;
static PT_JoypadInput g_ptJoypadInputHead;
struct termios g_tStoredSettings;
struct termios g_tNewSettings;
static void *InputEventTreadFunction(void *pVoid)
{
/* 定义函数指针 */
int (*GetJoypad)(void);
GetJoypad = (int (*)(void))pVoid;
while (1)
{
//因为有阻塞所以没有输入时是休眠
g_InputEvent = GetJoypad();
//有数据时唤醒
pthread_mutex_lock(&g_tMutex);
/* 唤醒主线程 */
pthread_cond_signal(&g_tConVar);
pthread_mutex_unlock(&g_tMutex);
}
}
static int RegisterJoypadInput(PT_JoypadInput ptJoypadInput)
{
PT_JoypadInput tmp;
if(ptJoypadInput->DevInit())
{
return -1;
}
//初始化成功创建子线程 将子项的GetInputEvent 传进来
pthread_create(&ptJoypadInput->tTreadID, NULL, InputEventTreadFunction, (void*)ptJoypadInput->GetJoypad);
if(! g_ptJoypadInputHead)
{
g_ptJoypadInputHead = ptJoypadInput;
}
else
{
tmp = g_ptJoypadInputHead;
while(tmp->ptNext)
{
tmp = tmp->ptNext;
}
tmp->ptNext = ptJoypadInput;
}
ptJoypadInput->ptNext = NULL;
return 0;
}
static int joypadGet(void)
{
return read(joypad_fd, 0, 0);
}
static int joypadDevInit(void)
{
joypad_fd = open(JOYPAD_DEV, O_RDONLY);
if(-1 == joypad_fd)
{
printf("%s dev not found \r\n", JOYPAD_DEV);
return -1;
}
return 0;
}
static int joypadDevExit(void)
{
close(joypad_fd);
return 0;
}
static T_JoypadInput joypadInput = {
joypadDevInit,
joypadDevExit,
joypadGet,
};
static int USBjoypadGet(void)
{
/**
* FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A 连B
* 0 1 2 3 4 5 6 7
* A B Select Start Up Down Left Right
*/
//因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值
static unsigned char joypad = 0;
struct js_event e;
if(0 < read (USBjoypad_fd, &e, sizeof(e)))
{
if(0x2 == e.type)
{
/*
上:
value:0x8001 type:0x2 number:0x5
value:0x0 type:0x2 number:0x5
*/
if(0x8001 == e.value && 0x5 == e.number)
{
joypad |= 1<<4;
}
/*下:
value:0x7fff type:0x2 number:0x5
value:0x0 type:0x2 number:0x5
*/
if(0x7fff == e.value && 0x5 == e.number)
{
joypad |= 1<<5;
}
//松开
if(0x0 == e.value && 0x5 == e.number)
{
joypad &= ~(1<<4 | 1<<5);
}
/*左:
value:0x8001 type:0x2 number:0x4
value:0x0 type:0x2 number:0x4
*/
if(0x8001 == e.value && 0x4 == e.number)
{
joypad |= 1<<6;
}
/*右:
value:0x7fff type:0x2 number:0x4
value:0x0 type:0x2 number:0x4
*/
if(0x7fff == e.value && 0x4 == e.number)
{
joypad |= 1<<7;
}
//松开
if(0x0 == e.value && 0x4 == e.number)
{
joypad &= ~(1<<6 | 1<<7);
}
}
if(0x1 == e.type)
{
/*选择:
value:0x1 type:0x1 number:0xa
value:0x0 type:0x1 number:0xa
*/
if(0x1 == e.value && 0xa == e.number)
{
joypad |= 1<<2;
}
if(0x0 == e.value && 0xa == e.number)
{
joypad &= ~(1<<2);
}
/*开始:
value:0x1 type:0x1 number:0xb
value:0x0 type:0x1 number:0xb
*/
if(0x1 == e.value && 0xb == e.number)
{
joypad |= 1<<3;
}
if(0x0 == e.value && 0xb == e.number)
{
joypad &= ~(1<<3);
}
/*A
value:0x1 type:0x1 number:0x0
value:0x0 type:0x1 number:0x0
*/
if(0x1 == e.value && 0x0 == e.number)
{
joypad |= 1<<0;
}
if(0x0 == e.value && 0x0 == e.number)
{
joypad &= ~(1<<0);
}
/*B
value:0x1 type:0x1 number:0x1
value:0x0 type:0x1 number:0x1
*/
if(0x1 == e.value && 0x1 == e.number)
{
joypad |= 1<<1;
}
if(0x0 == e.value && 0x1 == e.number)
{
joypad &= ~(1<<1);
}
/*X
value:0x1 type:0x1 number:0x3
value:0x0 type:0x1 number:0x3
*/
if(0x1 == e.value && 0x3 == e.number)
{
joypad |= 1<<0;
}
if(0x0 == e.value && 0x3 == e.number)
{
joypad &= ~(1<<0);
}
/*Y
value:0x1 type:0x1 number:0x4
value:0x0 type:0x1 number:0x4
*/
if(0x1 == e.value && 0x4 == e.number)
{
joypad |= 1<<1;
}
if(0x0 == e.value && 0x4 == e.number)
{
joypad &= ~(1<<1);
}
}
return joypad;
}
return -1;
}
static int USBjoypadDevInit(void)
{
USBjoypad_fd = open(USB_JS_DEV, O_RDONLY);
if(-1 == USBjoypad_fd)
{
printf("%s dev not found \r\n", USB_JS_DEV);
return -1;
}
return 0;
}
static int USBjoypadDevExit(void)
{
close(USBjoypad_fd);
return 0;
}
static T_JoypadInput usbJoypadInput = {
USBjoypadDevInit,
USBjoypadDevExit,
USBjoypadGet,
};
/* 从标准输入获取输入 */
static int CmdJoypadGet(void)
{
/**
* FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A 连B
* 0 1 2 3 4 5 6 7
* A B Select Start Up Down Left Right
*/
//因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值
/*
A --> Left
S --> Down
W --> Up
D --> Right
G --> Select
H --> Start
J --> A
K --> B
*/
unsigned char joypad = 0;
static unsigned char LastJoypad = 0;
char c = 0;
static char LastC = 0;
int ret = 0;
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(STDIN_FILENO, &read_fds);
struct timeval tTimeOut;
tTimeOut.tv_usec = 500000; //500ms
ret = select(1, &read_fds, NULL, NULL, &tTimeOut);
if(ret <= 0)
{
printf("select timeout or error, ret = %d, errno:%d -> %s\r\n", ret, errno, strerror(errno));
return 0;
}
if(FD_ISSET(0, &read_fds))
{
read(0, &c, 1);
}
else
{
printf("error ! invaild fd\n");
return 0;
}
printf("Get Input From Cmd: %c\r\n", c);
switch (c)
{
case 'A':
case 'a':
/* Left */
joypad |= 1<<6;
break;
case 'S':
case 's':
/* Down */
joypad |= 1<<5;
break;
case 'D':
case 'd':
/* Right */
joypad |= 1<<7;
break;
case 'W':
case 'w':
/* Up */
joypad |= 1<<4;
break;
case 'J':
case 'j':
/* A */
joypad |= 1<<0;
break;
case 'K':
case 'k':
/* B */
joypad |= 1<<1;
case 'G':
case 'g':
/* Select */
joypad |= 1<<2;
break;
case 'H':
case 'h':
/* Start */
joypad |= 1<<3;
break;
default:
break;
}
printf("Get Input Data Return %u\r\n", joypad);
LastJoypad = joypad;
LastC = c;
return joypad;
}
static int CmdJoypadDevInit(void)
{
tcgetattr (0, &g_tStoredSettings);
g_tNewSettings = g_tStoredSettings;
g_tNewSettings.c_lflag &= (~ICANON);
g_tNewSettings.c_cc[VTIME] = 0;
g_tNewSettings.c_cc[VMIN] = 1;
tcsetattr (0, TCSANOW, &g_tNewSettings);
return 0;
}
static int CmdJoypadDevExit(void)
{
tcsetattr (0, TCSANOW, &g_tStoredSettings); // 恢复终端参数
return 0;
}
static T_JoypadInput CmdJoypadInput = {
CmdJoypadDevInit,
CmdJoypadDevExit,
CmdJoypadGet,
};
int InitJoypadInput(void)
{
int iErr = 0;
// iErr = RegisterJoypadInput(&joypadInput);
// iErr = RegisterJoypadInput(&usbJoypadInput);
iErr = RegisterJoypadInput(&CmdJoypadInput);
return iErr;
}
int GetJoypadInput(void)
{
printf("get input sleep...\r\n");
/* 休眠 */
pthread_mutex_lock(&g_tMutex);
pthread_cond_wait(&g_tConVar, &g_tMutex);
printf("wake up get data...\r\n");
/* 被唤醒后,返回数据 */
pthread_mutex_unlock(&g_tMutex);
return g_InputEvent;
}