我喜欢旧电脑游戏。 我爱旧铁,但还不足以在家中收藏。 另一件事是选择一些旧的芯片,然后尝试自己复制一些东西,将新旧结合起来。 在本文中,这个故事是关于我如何将AVR微控制器连接到YM3812的,YM3812在诸如Adlib,Sound Blaster和Pro AudioSpectrum之类的声卡中使用。 我没有创造出根本上崭新的东西,我只是结合了不同的想法。 也许有人会对我的实施感兴趣。 也许我的经验会促使某人创建自己的复古项目。

有一天,我在Internet上逛逛,遇到了一个有趣的针对Arduino和Raspberry Pi的OPL2音频板项目。 简而言之:将开发板连接到Arduino或Raspberry Pi,分别加载草图或软件,然后聆听。 选择OPL2芯片,听其声音并尝试自己做某事的诱人想法并没有离开我,我下令组装并开始弄清楚它是如何工作的。
关于YM3812芯片管理的几句话
要播放音乐,必须设置寄存器。 有些负责调音乐器,有些负责弹奏音符,等等。 寄存器地址为8位。 寄存器的值为8位。 规范中给出了寄存器列表。
要传输寄存器,必须正确设置控制输入CS,RD,WR和A0以及数据总线D0..D7上的读数。
在安装数据总线时,需要CS输入来阻止它。 设置CS = 1(关闭输入),设置D0..D7,设置CS = 0(打开)。
RD输入必须是逻辑单元
要写入寄存器的地址,请设置WR = 0,A0 = 0
要写入寄存器的值,请设置WR = 0,A0 = 1
用于Arduino和Raspberry Pi的OPL2音频板

注册转移程序:
- 在初始化期间,设置PB2 = 1以阻止
YM3812
的输入 - 我们通过注册地址
2.1 PB1 = 0(A0 = 0)
2.2我们通过SPI接口发送寄存器地址字节。 数据存储在移位寄存器74595
2.3 PB2 = 0(WR = 0,CS = 0)。 芯片7404反转信号并将1供给ST_CP 74595
的输入,ST_CP 74595
的输出Q0..Q7进行切换。 YM3812
写寄存器地址
2.4 PB2 = 1(WR = 1,CS = 1) - 我们传递寄存器的值
3.1 PB1 = 1(A0 = 1)
3.2我们通过SPI接口传输数据字节,类似于p.2.2
3.3 PB2 = 0(WR = 0,CS = 0)。 YM3812
写入数据
3.4 PB2 = 1(WR = 1,CS = 1)
逆变器7404
和石英XTAL1
实现具有3.579545MHz的频率的矩形脉冲发生器,这对于YM3812
操作是必需的。
YM3014B
将数字信号转换为模拟信号,并由LM358
运算放大器放大。
需要LM386
音频放大器,以便无源扬声器或耳机可以连接到设备,例如 LM358
功率不足。
现在,让我们尝试从所有这些中提取声音。 我(也许不仅是我)想到的第一件事是如何使其在DosBox中都能正常工作。 不幸的是,无法立即使用Adlib硬件进行播放,因为 DosBox对我们的设备一无所知,也不知道如何在任何地方传输OPL2命令(到目前为止还不知道)。
该项目的作者提供了Teensy的草图,用作MIDI设备。 自然,声音将由预编译的乐器组成,并且声音将有所不同,我们将在OPL2芯片上模拟MIDI设备。 我没有Teensy,也无法尝试此选项。
串口操作
有一个草图SerialPassthrough 。 有了它,我们可以通过串行端口传输命令。 它仅用于在DoxBox中实现支持。 我使用了SVN中的版本: svn://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk
在src/hardware/adlib.cpp
我们更改OPL2的实现:
#include "serialport/libserial.h" namespace OPL2 { #include "opl.cpp" struct Handler : public Adlib::Handler { virtual void WriteReg( Bit32u reg, Bit8u val ) {
组装前,将COM端口号替换为当前端口号。
如果您删除//adlib_write(reg,val);
行中的注释//adlib_write(reg,val);
,然后声音将通过模拟器和设备同时播放。
在DosBox设置中,您将需要指定OPL2的用法:
[sblaster] oplemu=compat oplmode=opl2
这是我的方法:
看起来很笨重。 即使您使用Arduino而不是面包板,也需要连接电线。 系统上的端口号可能会更改,您将必须重新构建DosBox。 我真的很想使所有内容看起来简洁,删除不必要的零件并将所有内容组装在一块板上。
OPL2-USB
提出了一个主意,为什么不制造一个独立的设备,使它在连接时具有最少的组件和最少的麻烦。 首先,您可以卸下74595
并使用atmega端口。 此处仅用于减少导线数量。 其次,您可以使用现成的晶体振荡器来摆脱7404
芯片。 如果将设备连接到扬声器,则也不需要音频放大器。 最后,如果将atmega直接连接到USB,例如使用V-USB库: https ://www.obdev.at/products/vusb/index.html,则可以摆脱USB-UART。 为了不打扰编写驱动程序并安装它们,可以将微控制器设置为自定义HID设备。

端口B和C部分忙于连接ISP编程器和石英。 端口D保持完全空闲,我们将其用于数据传输。 我在PCB设计过程中分配了其余端口。
完整的方案可以在这里进行研究: https : //easyeda.com/marchukov.ivan/opl2usb
带电阻的LED1
是可选的,在组装过程中我没有安装它们。 需要U4保险丝,以免意外烧毁USB端口。 也不能设置,而是用跳线代替。
为了使设备紧凑,我决定尝试将其组装在SMD组件上。
左侧为数字部分,右侧为模拟部分。
对我来说,这是设计和组装成品设备的第一次经验,没有门框就不可能做到。 例如,对于机架,板角上的孔的直径应为3毫米,但事实证明它们为1.5毫米。
可以在github上查看固件。 在早期版本中,一个命令以一个USB数据包发送。 事实证明,由于USB 1.0的开销大和速度慢,在动态轨道上DosBox开始变慢,DosBox挂起发送数据包和接收响应的过程。 我必须制作一个异步队列并分批发送命令。 这增加了一些延迟,但并不明显。
V-USB设定
如果我们早先已经确定要发送数据到YM3812,那么USB将不得不修补。
将usbconfig-prototype.h
重命名为usbconfig.h
并将其添加(以下仅是编辑内容):
在main.c
文件中,我们定义了宗地数据结构
声明HID的句柄
PROGMEM const char usbHidReportDescriptor[] = {
事件处理程序:
我推荐这些有关V-USB的俄语文章:
http://microsin.net/programming/avr-working-with-usb/avr-v-usb-tutorial.html
http://we.easyelectronics.ru/electro-and-pc/usb-dlya-avr-chast-2-hid-class-na-v-usb.html
DosBox支持
可以在同一存储库中查看DosBox的代码。
为了在PC端使用该设备,我使用了hidlibrary.h
库(不幸的是,我没有找到原始库的链接),该库需要进行一些修改。
我决定不接触OPL仿真器,而是实现自己的单独类。 现在切换到配置中的USB看起来像这样:
[sblaster] oplemu=usb
在adlib.cpp的Adlib模块的构造函数中adlib.cpp
添加条件:
else if (oplemu == "usb") { handler = new OPL2USB::Handler(); } else {
在dosbox.cpp
新的配置选项:
const char* oplemus[]={ "default", "compat", "fast", "mame", "usb", 0};
可以在这里获取编译后的exe: https : //github.com/deadman2000/usb_opl2/releases/tag/0.1
录影带
准备就绪的设备连接方式:
通过声卡记录的声音:
结果和计划
我对结果感到满意。 连接设备很容易,没有问题。 当然,我对DosBox所做的修改将永远不会进入正式版本和流行分支,因为 这是一个非常具体的解决方案。
接下来的选择是OPL3。 在OPL芯片上构建跟踪器仍然是一个主意
类似项目
VGM播放器
ISA总线上的声卡OPL2