有一次,看完足够多的“每个人都在玩”时,我也想玩我的Raspberry pi。是的,不仅仅是玩游戏,而是使用真实设备玩游戏。为什么在地铁上花了150卢布从丹迪买了一个操纵杆(好吧,不是从丹迪买来的,而是从Simbas Junior买来的)。感兴趣的人可以单击下面的按钮。在文章末尾,将有一个到证明的链接。我们的中国朋友以座右铭收集它-没有品质,但您坚持。立即将本机电缆焊接到横截面为2平方微米的导体,并替换为某个工业接口转换器的电缆,该电缆在下一次调试后得到,只有5根线。在更改代码之前,您必须弄清楚游戏手柄本身的工作方式。游戏手柄具有移位寄存器。游戏手柄有5条线-2条-电源,3条信息-闩锁(频闪),时钟(脉冲)和数据。将逻辑单元提供给锁存器时,将保存移位寄存器的输入状态,并在输出-数据处立即提供“ A”按钮的状态,并且当输出-时钟线上的逻辑电平-数据发生变化时,会出现与其他七个按钮的状态相对应的电压电平以顺序形式。被按下的按钮对应于-0,而不是被按下-1。此外,对于游戏来说,所有事情都是完全相反的,因此有必要进行反转。下图显示了游戏手柄的示意图。
其次是选择仿真器。该选择落在旧的fceu版本0.98.12上,因为它具有出色的模块化并非常准确地模拟了控制台体系结构,并且是用C编写的。其次是选择了一个用于GPIO的库,我从Mike McCauley 那里选择了bcm2835,它也是用C编写的,并且具有良好的性能。由于我是编程新手,因此我不得不求助于同一位“每个人都在玩”的名人,并要求对代码部分发表评论。然后将您的鼻子刺入那些负责将按钮状态传输到游戏的功能。他们用通俗易懂的语言向我解释了什么以及如何进行。因此,input.c文件负责模拟输入,并且此处将进行主要更改。有几个函数负责模拟游戏手柄-FCEU_UpdateInput,ReadGP和DECLFW(4016),实际上,还有更多功能是主要的。除了input.c,我还必须对file.c和fceu.c进行更改。在第一种情况下,file.c文件中有错误,但是这个问题是google,这个文件有一个补丁,在fceu.c文件中,我在int函数FCEUI_Initialize(void)中添加了bcm2835库的初始化:bcm2835_init();
预添加其头文件#include <bcm2835.h>
现在是input.c,我还添加了bcm2835库头文件(类似于fceu.c)和<unistd.h>库头文件以使用usleep。接下来,我宣布了将涉及的GPIO端口: #define LATCH RPI_V2_GPIO_P1_11
#define CLK RPI_V2_GPIO_P1_13
#define DATA RPI_V2_GPIO_P1_15
在void InitializeInput(void)函数中,我添加了一个代码,其中注册了每个GPIO端口的操作模式,并立即将负责锁存(选通)的端口和时钟重置为0。 bcm2835_gpio_fsel(LATCH, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(CLK, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(DATA, BCM2835_GPIO_FSEL_INPT);
bcm2835_gpio_set_pud(DATA, BCM2835_GPIO_PUD_UP);
bcm2835_gpio_write(CLK, LOW);
bcm2835_gpio_write(LATCH, LOW);
现在进入功能:DECLFW(4016)-负责模拟锁存(选通)信号。如前所述,要读取按钮的状态,您需要一段时间应用到Latch-1。有一个Laststrobe变量,其中写入了写入该寄存器的最后一个值。如果Laststrobe为0,则分别写入逻辑1,并且还将将被称为Latch的GPIO引脚馈入1,并在1μs后将其复位为0。并且如果Laststrobe等于1,则将忽略此代码段。static DECLFW(B4016)
{
if (FCExp)
if (FCExp->Write)
FCExp->Write(V & 7);
if (JPorts[0]->Write)
JPorts[0]->Write(V & 1);
if(JPorts[1]->Write)
JPorts[1]->Write(V&1);
if((LastStrobe&1) && (!(V&1)))
{
if(JPorts[0]->Strobe)
JPorts[0]->Strobe(0);
if(JPorts[1]->Strobe)
JPorts[1]->Strobe(1);
if(FCExp)
if(FCExp->Strobe)
FCExp->Strobe();
}
if (LastStrobe==0)
{
bcm2835_gpio_write(LATCH, HIGH);
usleep(1);
bcm2835_gpio_write(LATCH, LOW);
}
LastStrobe=V&0x1;
}
好吧,现在操纵杆本身的轮询为void FCEU_UpdateInput(void)-在此功能中,将从配置模拟器或通过输入某些键(例如游戏手柄,三脚垫,光枪等)启动时选择的输入驱动程序读取数据。 。,所有这些都可以连接到控制台。它可以生成游戏手柄按钮[joy [0] ... joy [3]]状态的字节,数量为2到4,因为您可以启用Pribluda的仿真来连接另外2个游戏手柄。这是发生主要更改的地方。由于我不需要使用4个游戏手柄并从其他驱动程序接收数据的功能,因此我抛出了所有代码并输入了我的代码: joy[0] = 0;
joy[1] = 0;
for (i = 0; i <= 7; i++)
{
joy[0] ^= bcm2835_gpio_lev(DATA) << i;
joy[0] ^= (1 << i);
joy[1] ^= bcm2835_gpio_lev(DATA) << i;
joy[1] ^= (1 << i);
bcm2835_gpio_write(CLK, HIGH);
usleep(1);
bcm2835_gpio_write(CLK, LOW);
usleep(1);
}
此外,我立即分别形成了第一和第二操纵杆的两个字节。由于许多游戏同时从两个端口读取按钮的状态,因此对于它们来说,没有优先端口的概念。但是有些游戏存在这种概念-例如,所有Mario,Kirby,Terminator 2等。也就是说,它们仅从第一个端口(在Mario中,第一个玩家,从第二个中,仅从第二个玩家),即从寄存器4016读取按钮的状态。在调用此函数时将值分配为零也很重要,否则先前的值将被保存在其中。新的将已经叠加在它们上面。原则上,可以将第二个操纵杆的字节保留为零,但是我可以一起玩Mario。ReadGP-已经从字节joy [0] ... joy [3]中提取位,并且ret变量将当前按钮的状态返回给游戏,按钮号由变量joy_readbit [w]设置,其中w是操纵杆的端口号,第一或第二。但是在此功能中,我没有做任何更改。保持原样。为了成功进行编译,请在src目录中的Makefile(执行Configure命令后形成)中,将-lbcm2835 -lm -lrt添加到写入库依赖项的位置。行:LIBS =
通常,一切正常。如果我突然决定购买第二个游戏杆,在同一辆战车上一起玩,我就离开了基础。“ 链接到证明
” 我们使用的数据来自该网站
“特别感谢这里的男人谁帮助理解代码模拟器