当前位置: 首页 > news >正文

基于Arduino的JVS街机I/O板USB HID改造方案

1. 项目概述与核心挑战

手头这台服役多年的世嘉Crazy Taxi街机,其核心的NAOMI主板最终还是没能扛过时间的考验,彻底罢工了。对于像我这样习惯了折腾老式街机改造的人来说,这既是挑战,也是乐趣的开始。我之前处理过不少90年代前的JAMMA标准机台,也做过一些接口板,但这次面对的是更现代的JVS标准机柜,情况有些不同。

JVS,全称Jamma Video Standard,是90年代末期许多街机制造商采用的新一代标准。它摒弃了老式JAMMA将所有信号(视频、音频、控制)挤在一个56针连接器里的做法,转而使用独立的VGA接口、RCA音频接口,以及一个基于RS485串行通信的控制协议。机柜的控制面板输入输出,就靠一块专门的I/O板来管理。这块板子负责采集玩家的摇杆、按钮等所有操作,通过RS485总线发送给主板,同时接收主板指令,控制机柜里的灯、计数器等输出设备。

问题就出在这里。虽然原装的世嘉JVS I/O板上确实有一个USB接口,但你直接把它插到电脑上,电脑是认不出来的。这就像两个人语言不通,一个说RS485“方言”,一个只懂USB“普通话”,根本无法交流。这就是将一台JVS时代的街机连接到现代PC进行模拟器游戏时,需要解决的核心技术障碍:协议转换。

市面上常见的解决方案,是在原装I/O板和PC之间加一个RS485转RS232的硬件转换器,再配合一个在PC端将串口数据模拟成游戏手柄信号的软件。这条路我研究过,但发现很多方案对模拟摇杆、方向盘这类模拟量输入的支持很弱,甚至没有。而我的Crazy Taxi恰恰有三个模拟输入(方向盘、油门、刹车),这让我不得不另寻他法。

我的思路更直接:既然原装板子与PC沟通不畅,干脆把它换掉。用一块我们自己设计的、能直接说“USB普通话”的板子来替代。基于Arduino平台,尤其是内置USB功能的ATmega32U4芯片(比如Arduino Leonardo或Pro Micro),我们可以轻松实现一个标准的USB HID(人机接口设备),让电脑把它识别为一个即插即用的游戏手柄或摇杆。这样,控制路径就简化为“控制面板 -> 我们的HID板 -> PC模拟器”,省去了中间复杂的协议转换环节,也原生支持了模拟输入。

2. 硬件设计:从原理到实现

2.1 核心芯片选型与架构设计

选择Arduino Pro Micro(ATmega32U4)作为核心,是基于几个现实的考量。首先,它原生支持USB HID,无需额外的USB转串口芯片,减少了复杂度和潜在延迟。其次,Arduino生态成熟,有大量现成的库(比如Matthew Heironimus的Joystick库)可以调用,开发效率极高。最后,它的GPIO数量对于这个项目来说,既是优势也是挑战。

原装世嘉JVS I/O板的功能相当丰富。以最常见的Type 1版本(型号837-13551-92)为例,它需要处理每玩家最多14个数字输入(开始、投币、4向摇杆、7个动作键、服务键),外加2个辅助开关(测试、倾斜)和8路模拟输入。输出方面,则要驱动多达28个指示灯或继电器。ATmega32U4的GPIO显然不够用。

我的解决方案是使用数字逻辑芯片进行扩展。对于大量的数字输入,我采用了4片74HC165这样的8位并行输入转串行输出移位寄存器。控制面板上所有的按钮、开关信号以并行方式接入这些芯片,然后通过SPI总线,由Arduino以串行方式快速读取。这就像把一条宽阔的多车道公路(并行信号)收窄成一个收费站通道(串行SPI),虽然一次只能过一辆车(一位数据),但通过快速轮询,可以高效地获取所有车道的信息。

对于8路模拟输入,则简单直接得多。Arduino Pro Micro本身提供了多路ADC(模数转换器)引脚,我将它们直接引接到对应的接口上,无需额外芯片。

输出部分,我使用了一片74HC595串行输入转并行输出移位寄存器,配合一片ULN2003达林顿晶体管阵列来驱动负载。ULN2003的每个通道能提供最高500mA的驱动电流,足以点亮街机里常见的那些1W白炽灯泡(工作电流约200mA)。这里有一个非常重要的注意事项:ULN2003的总功耗是有限的,所有通道的电流总和不能超过1A。千万不要试图用它直接驱动大功率负载,比如力反馈电机,否则芯片会瞬间过热损坏。对于大电流负载,必须设计外置的驱动板,我们的板子只提供控制信号。

2.2 接口兼容性与电源设计

世嘉的JVS I/O板有过三个主要版本,接口定义和数量有所不同。为了让我们的替换板有更好的通用性,我在设计时尽可能兼容了这三种类型。

  • CN3 (60针): 这是Type 1和Type 3板的核心连接器,包含了主要的数字输入和输出信号。
  • CN4 (26针): 这是所有版本共用的模拟输入接口。
  • CN2 (40针) 和 CN1 (34针): 分别是Type 2板的数字输入和独立输出接口。为了兼容,我的板子上也预留了它们的焊盘位置。

这些接口的信号在板内是互连的。例如,玩家1的开始按钮信号,无论在60针还是40针接口上定义,在板内都连接到了同一个输入移位寄存器的引脚上。这样做有个额外的好处:如果机柜本身有一些闲置的输入引脚,用户可以利用这些并联的接口,在不破坏原装线束的前提下,额外接出几个按钮,用于前端菜单导航等扩展功能。当然,我强烈不建议为此在珍贵的原装控制面板上钻孔。

电源设计上,我采用了分离供电策略。板上的逻辑芯片和单片机由USB接口的5V供电。而驱动灯泡的输出电路部分,则需要更强的电流,因此设计了独立的供电接口CN5/CN6,要求接入外部的+5V电源(至少1.5A),这通常可以直接使用街机柜内原有的电源。

这里有一个必须极度警惕的安全警告:CN5/CN6接口没有设计防反接保护电路。如果你不小心将外部电源的正负极接反了,板上所有的芯片,包括Arduino,都会在瞬间烧毁。如果此时板子还通过USB连着电脑,电脑的USB端口也可能遭殃。在接线时,务必、务必、务必确认极性。

2.3 PCB设计与SMT贴片心得

为了追求更小的体积和更好的稳定性,这次我全部采用了表面贴装器件。我将设计好的PCB文件交给了JLCPCB进行制造和SMT贴片组装。这不是我第一次使用他们的服务,整体的体验和成品质量都令人满意。

对于想尝试SMT贴片的新手,这里有一些我踩过坑后总结的实操心得

  1. 设计检查是重中之重:在提交Gerber文件前,一定要用EDA软件运行设计规则检查(DRC)和电气规则检查(ERC)。板子做出来再飞线修补就太难看了。
  2. 物料清单(BOM)和坐标文件(CPL)的准确性:JLCPCB需要你上传BOM和CPL文件来自动匹配元件。确保你的元件库中的参数(尤其是封装和数值)是正确的。我曾遇到过电阻排被软件误识别为普通电阻的情况,如果在提交前不仔细核对元件预览图,贴出来肯定是错的。
  3. 善用元件搜索:JLCPCB的元件库可能没有你原理图中指定的某个具体型号。别慌,尝试用更通用的关键词搜索。比如,把“CD74HC14M”换成“74HC14”,通常能找到更多可用的替代选项。
  4. 信任但也要核查:上传文件后,网站会生成一个板子的可视化预览,显示元件摆放位置。这时你可能会发现某些IC的方向看起来是错的。先别急,JLCPCB的工程师会在生产前进行审核和校正。如果真有严重问题,他们会联系你确认。这个预览界面主要是让你核对“有没有元件缺失”,而不是“元件方向对不对”。

3. 固件开发:让硬件“活”起来

3.1 开源固件与核心逻辑

硬件的骨架搭好了,接下来就需要固件(Firmware)来赋予它灵魂。我选择完全开源固件代码,因为不同的街机柜(双人格斗台、赛车座舱、光枪射击台)其控制面板配置千差万别,一个“万能但臃肿”的固件远不如多个“量身定制”的固件来得高效和灵活。

我编写了两个主要的演示固件,放在了GitHub项目页面上。

  • JVSCab2PC_2P: 适用于双玩家、最多每玩家7个按钮的格斗或动作类机台。这个固件将板子模拟成一个拥有30个数字按钮的USB游戏手柄。它轮询所有数字输入(通过移位寄存器),当检测到按钮状态变化时,通过USB向PC发送对应的手柄按键按下或释放信号。
  • JVSCab2PC_DRIVING: 适用于单玩家赛车座舱,比如我的Crazy Taxi。它模拟成一个拥有3个模拟轴(方向盘、油门、刹车)和12个数字按钮的USB摇杆。这是支持模拟输入的关键。

两个固件都基于一个优秀的开源库:Matthew Heironimus的Arduino Joystick Library。这个库让HID设备模拟变得非常简单,并且与键盘、鼠标库兼容,为未来实现多模式HID设备留下了可能。

输入处理的核心逻辑在于高效读取移位寄存器。我借鉴了Nick Gammon的一篇关于74HC165的文章中的代码。在Arduino的loop()主循环中,程序会不断地通过SPI读取4片输入移位寄存器的状态(总共32位,对应32个数字输入),并与上一次循环的状态进行比较。一旦发现任何一位发生了变化(从0到1或从1到0),就立即通过Joystick.setButton()函数向PC报告对应的按钮事件。为了消除机械开关的抖动,代码里还实现了简单的软件消抖。

3.2 模拟量处理与“组合键”技巧

对于赛车固件,模拟量的处理是关键。Arduino的analogRead()函数会读取ADC引脚上的电压值(0-5V对应0-1023)。但直接把这个值发送给PC是不行的,因为USB摇杆的模拟轴范围通常是-32767到+32767。我们需要做一个映射:

int rawSteer = analogRead(STEER_PIN); // 读取原始值,0-1023 int mappedSteer = map(rawSteer, 0, 1023, -32767, 32767); // 映射到摇杆范围 Joystick.setXAxis(mappedSteer); // 设置X轴(方向盘)

油门和刹车也同理。这里有个细节:很多赛车踏板在松开时是满电压(5V),踩下时电压降低。你可能需要在映射前用1023 - rawValue进行反转。

另一个实用的技巧是针对输入引脚有限的机台(比如只有几个按钮的赛车)。我在JVSCab2PC_DRIVING固件中实现了一个“组合键”功能:当“开始”按钮被按住时,再按其他按钮,会触发另一套按键映射。这样,有限的物理按钮就能实现菜单导航、视角切换等更多功能,而无需改造控制面板。

3.3 输出功能与性能考量

目前的演示固件主要处理输入,输出功能暂未启用。但硬件上已经预留了通过74HC595和ULN2003驱动输出的能力。如果你想测试输出,可以在loop()函数末尾添加一个简单的控制函数,让输出通道以一定频率闪烁。

关于性能,大家最关心的是输入延迟。我进行了一个简单的测试:测量当同时按下6个按钮(模拟最极端情况)时,主循环loop()的执行时间增加了多少。测试结果如下:

  • JVSCab2PC_2P(纯数字):循环时间从空闲时的<0.4ms增加到3.8-5.0ms。
  • JVSCab2PC_DRIVING(含模拟量):循环时间从<3.2ms增加到8.6-9.8ms。

可以看到,处理模拟量读取和计算确实会增加一些开销。但在实际游戏中,几乎不可能出现精确到同一毫秒内按下6个按钮的情况。在更现实的“同时”按下3个按钮的场景下,延迟分别降低到1.0-1.6ms和4.8-6.0ms。如果是单按钮操作,延迟就与空闲循环时间无异。这个延迟水平,对于街机游戏来说是完全可接受的,尤其是赛车游戏对帧级精确输入的要求不如格斗游戏那么苛刻。

4. 安装、配置与问题排查

4.1 硬件安装步骤

以我的SEGA NAOMI通用机柜为例,安装过程其实很直接:

  1. 安全第一:断开街机柜的所有电源。
  2. 拆卸旧板:打开控制台面板,你会看到一堆线缆连接着原装的JVS I/O板。拧下固定它的4颗螺丝,小心拔掉上面的所有连接器。主要是那个最大的60针排线(数字I/O)和那个较小的26针排线(模拟输入)。
  3. 安装新板:将我们的JVSCab2PC板子固定在原位置。最关键的一步来了:将60针和26针的连接器插回新板。务必注意接口方向!两个连接器上都有防呆缺口,这个缺口必须对准板子上插座标出的缺口方向。如果强行反插,后果就是烧板子。
  4. 连接电源:如果你需要用到输出功能(驱动灯泡),找到机柜电源提供的+5V线,连接到板子的CN5或CN6接口。再次警告:请用万用表确认极性,红线接+5V,黑线接GND。
  5. 连接PC:最后,用一根USB A to B方口线(就是常见的打印机线),将板子连接到你的电脑。

首次连接时,Windows可能会提示安装驱动程序。这是因为Arduino Pro Micro默认使用的是ATmega32U4的USB转串口驱动。你可以根据系统提示自动搜索,或者去Arduino官网下载对应的驱动。安装一次后,这台电脑就会把它识别为一个标准的游戏控制器。

4.2 软件配置与测试

硬件连接好后,你可以在Windows的“设置 -> 游戏控制器”里看到一个新设备。打开“属性”,就可以测试每个按钮和模拟轴是否响应正常。

接下来就是在你喜欢的模拟器里进行映射了。无论是MAME、Supermodel(世嘉Model 3模拟器),还是TeknoParrot(现代街机模拟平台),配置过程都和设置一个普通的USB手柄一模一样。以MAME为例,进入游戏的输入设置菜单,按下你想映射的键位(如“Player 1 Start”),然后按下街机控制台上对应的开始按钮,MAME就会自动识别并绑定。

对于赛车游戏,你需要将方向盘、油门、刹车分别映射到模拟摇杆的X轴、Y轴、Z轴或油门刹车轴上。记得在模拟器的控制器设置里,校准一下这些模拟轴的范围,确保从最左到最右、从松开到踩死都能被正确识别。

4.3 常见问题与排查实录

在实际安装和调试过程中,你可能会遇到以下问题。这里是我的排查记录:

问题现象可能原因排查步骤与解决方案
电脑完全无法识别设备1. USB线损坏或接触不良。
2. 板子未供电或短路。
3. 驱动程序问题。
1. 换一根已知良好的USB线试试。
2. 检查板子上的电源指示灯(如果有的话)是否亮起。用万用表测量Arduino的VCC和GND之间是否有5V电压。
3. 打开设备管理器,查看“端口(COM和LPT)”或“通用串行总线控制器”下是否有带黄色叹号的未知设备。尝试手动更新驱动,指定到Arduino IDE安装目录下的drivers文件夹。
电脑能识别,但按钮无反应1. 固件未正确上传。
2. 输入连接器插反或接触不良。
3. 移位寄存器相关电路故障。
1. 确认Arduino Pro Micro已通过JVSCab2PC_2PJVSCab2PC_DRIVING固件正确编程。可以用一个简单的“Blink”例程测试单片机是否工作。
2.重点检查60针和26针连接器是否完全插紧,且方向正确。可以拔下后用万用表通断档,测量按钮开关到板子输入引脚的通路。
3. 检查74HC165的电源、地线、SPI时钟和数据线是否焊接良好。
部分按钮失灵,其他正常1. 特定按钮的微动开关损坏。
2. 对应线路在排线或板子上断路。
3. 该按钮对应的移位寄存器引脚虚焊。
1. 直接短接控制台上怀疑失灵的按钮的两个焊点,看PC端是否有反应。如果有,则是开关问题。
2. 沿着失灵按钮的线路,用万用表分段检查通断。
3. 检查对应74HC165芯片的输入引脚焊接。
模拟轴读数跳动或不准确1. 模拟电位器磨损或接触不良。
2. 电源电压不稳,导致ADC参考电压波动。
3. 接线过长引入干扰。
1. 这是老街机的通病。可以尝试向电位器内部喷入精密电器清洁剂并反复旋转。
2. 确保给板子供电的+5V稳定。可以在Arduino代码中读取analogRead(A0)的值并输出到串口监视器,观察其稳定性。
3. 尽量使用屏蔽线连接模拟传感器,并将屏蔽层单点接地。
输出通道不工作(灯不亮)1. 外部+5V电源未连接或接反。
2. ULN2003驱动芯片损坏。
3. 固件未启用输出功能。
1.首先确认CN5/CN6外部电源极性正确且电压为5V!这是烧板高发区。
2. 测量ULN2003的输入引脚(来自74HC595)在固件控制下是否有电平变化。如果有,但输出没变化,则芯片可能已损坏。
3. 默认演示固件未激活输出。你需要参考文中代码,在loop()里调用输出控制函数并重新上传固件。
在游戏中感觉操作有延迟1. PC性能或模拟器设置问题。
2. USB端口供电不足或干扰。
3. 固件循环时间过长。
1. 这是最常见的原因。尝试关闭垂直同步,降低游戏分辨率,或检查模拟器本身的输入延迟设置。
2. 将板子连接到主板后置的USB 2.0端口,避免使用机箱前置端口或USB Hub。
3. 可以简化固件代码,移除不必要的调试输出或计算,优化loop()效率。

最后一点个人体会是,改造老设备,尤其是街机这种充满时代情怀的东西,平衡“功能实现”与“原样保留”很重要。这套方案的核心价值在于“无痛替换”——它直接利用了原装的线束和接口,没有对机柜进行任何不可逆的修改。原装的JVS I/O板可以完好地保存起来。当某天你想让机器恢复原状,或者它的价值更多在于收藏时,你可以轻松地换回原装板子,这台街机依然是一台完整的、原始的SEGA NAOMI街机。这种可逆的现代化,才是对经典最好的致敬。

http://www.jsqmd.com/news/934545/

相关文章:

  • SpringBoot课程管理系统毕业设计包:含可运行源码、MySQL建表脚本与全套毕设文档
  • 论文AI率过高难通过?亲测有效降AI工具指南 - 老米_专讲AIGC率
  • 从旋变芯片到伺服控制:AD2S1210在电机位置反馈中的实战配置指南
  • 高效研究周报撰写指南:从个人探索到团队知识管理
  • 手机号码定位系统:3分钟掌握地理信息查询的核心技术
  • 从CAD小白到建模高手:用OpenCASCADE 7.8.0一步步教你打造一个带螺纹的3D瓶子模型
  • 从零打造桌面电子时钟:Atmega328P硬件设计与Arduino固件开发全流程
  • PyTorch中flatten()的三种返回值,你真的搞清楚了吗?(附view()对比)
  • AI时代蓝领转型:从操作工到技术协作者的实战路径
  • 别再只用JSP了!SpringBoot3整合Thymeleaf,5分钟搞定一个动态用户列表页
  • 别再让EC11编码器误触了!一个Arduino避坑程序帮你搞定旋转方向与按键
  • AI时代不可替代性:五大核心能力与人机协同策略
  • YOLOv9+OpenCV车辆跟踪实战:如何用Python把普通摄像头变成智能交通监控?
  • 实测20款去AI味工具怎么选?降AIGC率实用避坑指南 - agihub
  • 别再只用RC滤波了!用GP8101 PAC芯片实现PWM转高精度模拟电压(0-5V/10V)
  • 6 月 3 日起谷歌 Workspace 开放新功能:可分享 Gemini 对话快照且不影响原对话
  • 如何快速掌握哔哩下载姬:新手的高效8K视频下载指南
  • 避坑指南:QT+VTK开发机械臂可视化时,关于模型旋转、装配体联动和实时渲染的5个常见问题
  • 基于Arduino的智能密码锁:从硬件搭建到状态机编程全解析
  • 解决Qt自定义多选ComboBox的滚动条Bug:一个hidePopup()重写带来的启示
  • Simulink里调用Adams整车模型?一个视频讲清信号接口与联合仿真原理
  • 2026实测10款论文降AI工具:免费+付费全指南,AI率60%直降至5% - 仙仙学姐测评
  • 从URDF到MJCF:用MuJoCo仿真UR5机械臂,我的模型转换与可视化踩坑实录
  • 用STM32CubeMX和HAL库快速搭建RS485 Modbus从站(附源码解析)
  • 纯C实现的校园新闻系统,带管理员/用户/访客三级权限与文件存储
  • FlipIt翻页时钟:Windows桌面终极复古时钟屏保解决方案
  • 告别黑盒:深入解析西部数据UFS芯片的44个SMART健康参数(附高通XBL读取源码)
  • G-Helper终极指南:5分钟掌握ASUS笔记本轻量化性能控制
  • 运维老鸟的openEuler桌面化实战:用UKUI/DDE打造图形化运维工作站,效率翻倍
  • 告别繁琐点击!在Atmel Studio 7.0里一键烧录AVR芯片(USBasp/串口双模式保姆级教程)