3
\$\begingroup\$

I'm building a project which uses old FM synthesis chips - the Yamaha YM2612, which was the sound-generation chip used in the Sega Genesis, and also the TI SN76489, which was also in the Sega.

YM2612 has 8 data pins (D0 to D7) which are used to write to the internal registers of the chip. In order to latch a write instruction (a byte, as it were), then values are assigned to D0 through D7, and then one of the pins is flashed low, and then high on the YM2612 IC. Data can be written to specify the sound type, attack, decay, release, sustain, timbre, frequency, note duration, etc., but it's all transferred this way. The SN76489 is very similar here and also uses 8 input pins which work the same way.

I want to build a project which uses the Arduino, PIC, or another microcontroller to control one of these chips and to use it as a synthesizer, so I can preprogram sound patches and play notes through them. This, of course requires writing data to the registers as described above. The problem I'm having is this:

The Sega chip is controlled with a clock frequency of 8 MHz. If I were to use an Arduino, for example, which has a 16Mhz clock, how can I possibly provide the 8 MHz clock for the sound chip while also synchronizing my Arduino's write instructions to happen at the right times?

Optimally, I would load an 8 bit shift register like the 74HC595 with the eight data bits, and then pull the /WR pin low on the chip, and then put it back high again, so 8 bits have been written. But wouldn't this require the Arduino to operate at the same frequency as the sound chip? Also, if the Arduino executes 16 million instructions per second (nominally, or for this example) then some of the write commands will actually take several cycles, because I will have to write 8 bits in a shift register, which might require 16 instructions or something like that, PER BYTE written to the shift register. This seems to mess up all the considerations of frequency and I have no idea how to go about solving it.

\$\endgroup\$
  • 9
    \$\begingroup\$ Communication with the sound chip can happen asynchronously, as such you can run your arduino/pic/z80/... at whatever frequency you desire. You don't have to write new data to the sound chip every cycle (and probably shouldn't) \$\endgroup\$ – Colin Oct 20 '16 at 6:42
  • \$\begingroup\$ Have you considered using MMIO? It will require a bigger chip than the '328P found in the Arduino as well as a bit of glue logic, but it will let you write out the byte in two cycles instead of dozens. \$\endgroup\$ – Ignacio Vazquez-Abrams Oct 20 '16 at 23:14
14
\$\begingroup\$

The YM2612 data port will work asynchronously to the sound generation clock. The write timing is such that the data is written only on the rising edge of *WR.

The sequence is this.

a) The YM2612 is busy doing some sounds generation, according to the control bits in its internal registers. Hundreds or millions of clock cycles pass.

b) A key on your keyboard is pressed. The player's measurement of latency starts now. The Arduino sees it, via interrupt or polling, and then figures out what to send to the sound chip. If you're programming this in C, this could take thousands of Arduino cycles.

c) The Arduino takes 10s or hundreds of its clock cycles loading a 595, using its SPI mechanism would be faster and easier than bit-banging. If you are bit banging in C via the digitalWrite() calls, they can take 10s of clocks each to execute, very slow.

d) Eventually, the Arduino pulls the *WR pin low, and must now wait a minimum time with it low. See the 2612 data sheet for how long it must stay low. It's probably one or two cycles of its clock.

e) After that minimum delay, (writing it low and immediately high again by digitalWrite() in C will mean that it's low for 10s of Arduino clocks) it pulls it high again, and the 8 bit data finally gets to the YM2612 internal registers. After any internal delay in the 2612, the sound finally changes. The user hears the sound change.

f) Rinse and repeat

You could drive the Arduino from 8MHz if that's more convenient, though don't forget to set what clock frequency the Arduino uses in the loader program.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ "If you're programming this in C, this could take thousands of Arduino cycles." ...and that's why Real Men use Assembly Language XD. \$\endgroup\$ – Ian Bland Oct 20 '16 at 23:09
4
\$\begingroup\$

In addition to Neil_UK answer, if you want to be sure that the sound chips are really synchronized with your Arduino clock, you might configure one of its output to generate an 8MHz square wave via PWM and connecting it to the sound IC's clock input.

This is similar to how it's done in the MIDIbox SID project: there, a Microchip PIC16 microcontroller at 16MHz provides a 1MHz clock for a SID 6581 chip.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Alright so it all makes a lot more sense now. The clock signal just gives the YM2612 it's base frequency of operation, but the write instructions (and therefore loading data to the registers) can happen at any time, because all I have to do is pull /WR low, while the new bit values are present at D0 to D7, and all that has to happen is the clock pulse has to go from low to high only ONE time while the /WR pin is low, executing a write command. \$\endgroup\$ – user108391 Oct 20 '16 at 22:44
  • \$\begingroup\$ Oops, I meant to add a bit more, sorry I am kind of new to stackexchange. So my last question is, if my Arduino write asynchronously to the clock frequency, it would make sense that I might execute the write command more than once for one intended write. \$\endgroup\$ – user108391 Oct 20 '16 at 22:45
0
\$\begingroup\$

Wow apparently I can't hit enter because that ends my comment...

Shift-enter works apparently. So here is how I understand it happening, please let me know if there are any glaring errors here (although I just bought the can-oscillators to make this circuit buildable and tinkerable, and I am pretty sure I should be able to figure it out from there.) So, for example

1) The clock pin on the YM2612 sees an 8MHz square wave... all good here

2) At some point, I will pull /WR low, after loading the bit values to pins D0 through D7, depending on what data I am sending to the chip.

3) Now... here's where I am still a bit hazy. So once the /WR pin is pulled LOW, then on the NEXT positive edge of the clock, the data from pins D0 to D7 will be latched into the YM2612. This may happen (and probably will happen) for more than one clock cycle since the clock is giving 8 million rising edges/cycles per second, and the Arduino may work slower than that, so I might execute the write instruction say, 100 times instead of just once. Would this affect the response of the YM2612 in any noticeable way?

I know that with the SN76489, you send 14 bits at a time through the 8-bit bus, with the first four bits being the attenuation of the channel and the remaining 10 being half the period of the output tone. I think that before sending a data write, you have to send a preliminary address write in order to latch the chip to the right address/register you are writing to. I believe it's the same in the YM2612 so it would just rewrite the information which was already there.

Sorry for all the questions, I am just very interested in this stuff and don't know as much as I would like to yet. Both your responses so far have been very very very helpful, and I appreciate it more than you know. I hope you guys have a great weekend!

Also here is a very short YM2612 datasheet for reference

http://pdf-datasheet.datasheet.netdna-cdn.com/pdfhtml/0870/865751/page1.png

You use the A0 and A1 pins to designate which addresses you are writing to, and what data is being written to them. Now that we cleared up the part about the clock, I ordered the can oscillators and will have that sucker on the workbench as quick as I possibly can. I have been waiting to do this project for a long time. Thanks again guys

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You need to get a grasp of data ports from the hardware level. When you want to write to the external chip, your code is going to issue some kind of OUT instruction (I'm an old Z80 coder so that's what I know). At that point, it will pull a WR pin low and throw the data onto the data bus. It's expecting an 8 bit latch to know how to handle that. That latch needs to have an address decoder from the address bus so it knows it's being talked to. That'll be some logic hardware. I'd suggest some reading up on microprocessor interfacing to see the general principles before diving in. \$\endgroup\$ – Ian Bland Oct 20 '16 at 23:25
  • \$\begingroup\$ Thanks Ian, I got it working perfectly, thanks to everybody's help. It was intimidating at first but now it seems simple and straightforward. I am going to expand it piece by piece until it becomes the monster project that I know it can be. Thanks again everyone \$\endgroup\$ – user108391 Nov 14 '16 at 21:19
  • \$\begingroup\$ Great to hear it! \$\endgroup\$ – Ian Bland Nov 15 '16 at 9:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.