Last Updated or created 2025-09-24
UPDATE: FM using the DAC is also working
At the hackercamp I mentioned a few posts ago, I met a guy who was into synths and we started talking.
We discovered we have much in common.
I showed him my AY-3-8910 player, but he had some other sound chips he wanted to play with.
After we found out which ones, I decided to buy some.
And last week I started building a player for it.
Ultimate goal is to make this midi enabled.
The chip in question is a YM2203 from Yamaha. It has three channels and can do FM sounds.
Today the first sounds! (2 channels)
- Arduino UNO (clone)
- YM2203
- YM3014 DAC
- LM324 Amplifier
- 1Mhz crystal (that’s what I could find)
Below is the schematic, code will follow. (Needs a lot of cleanup)
I think I’m going to make some kind of library/class.
For easy programming.
FM via DAC
CODE FOR PSG (Programmable Sound Generator)
(FM code will follow)
// --- Pin definitions for Arduino Uno --- #define YM_D0 2 // D0–D7 on Arduino D2–D9 #define YM_A0 10 // Address select #define YM_WR 11 // Write #define YM_RD 12 // Read #define YM_CS 13 // Chip Select #define YM_RST A1 // Reset // CLK is 1Mhz #define CLK_FREQ 1000000UL struct Note { uint16_t freq; // Hz uint16_t dur; // ms uint16_t vol; // ms }; // --- Simple melody table (frequencies in Hz) --- Note melody[] = { {440, 400, 15}, {494, 400, 15}, {523, 400, 15}, {587, 400, 15}, // A4, B4, C5, D5 {659, 400, 15}, {698, 400, 15}, {784, 800, 15}, // E5, F5, G5 {0, 400, 0} // rest }; const int melodyLength = sizeof(melody) / sizeof(Note); // --- Setup I/O pins for YM2203 --- void setupPins() { pinMode(YM_CS, OUTPUT); pinMode(YM_WR, OUTPUT); pinMode(YM_RD, OUTPUT); pinMode(YM_A0, OUTPUT); pinMode(YM_RST, OUTPUT); for (int i = 0; i < 8; i++) { pinMode(YM_D0 + i, OUTPUT); } digitalWrite(YM_CS, HIGH); digitalWrite(YM_WR, HIGH); digitalWrite(YM_RD, HIGH); digitalWrite(YM_RST, HIGH); } // --- Put 8-bit value on D0–D7 bus --- void setDataBus(uint8_t val) { for (int i = 0; i < 8; i++) { digitalWrite(YM_D0 + i, (val >> i) & 1); } } // --- Write register/data to YM2203 --- void ymWrite(uint8_t reg, uint8_t val) { digitalWrite(YM_CS, LOW); // Send register address digitalWrite(YM_A0, LOW); setDataBus(reg); digitalWrite(YM_WR, LOW); delayMicroseconds(1); digitalWrite(YM_WR, HIGH); // Send data digitalWrite(YM_A0, HIGH); setDataBus(val); digitalWrite(YM_WR, LOW); delayMicroseconds(1); digitalWrite(YM_WR, HIGH); digitalWrite(YM_CS, HIGH); } // --- Set PSG Channel A frequency --- void setChannelAFreq(uint16_t freq) { if (freq == 0) { // rest ymWrite(0x08, 0x00); // volume 0 (mute) return; } // YM2203 PSG frequency formula: Fout = clock / (16 * N) // N = 12-bit value uint16_t N = CLK_FREQ / (16UL * freq); ymWrite(0x00, N & 0xFF); // Freq LSB ymWrite(0x01, (N >> 8) & 0x0F); // Freq MSB } // --- Set PSG Channel B frequency --- void setChannelBFreq(uint16_t freq) { if (freq == 0) { // rest ymWrite(0x09, 0x00); // volume 0 (mute) return; } // YM2203 PSG frequency formula: Fout = clock / (16 * N) // N = 12-bit value uint16_t N = CLK_FREQ / (16UL * freq); ymWrite(0x02, N & 0xFF); // Freq LSB ymWrite(0x03, (N >> 8) & 0x0F); // Freq MSB } // --- Set PSG Channel C frequency --- void setChannelAFreq(uint16_t freq) { if (freq == 0) { // rest ymWrite(0x0A, 0x00); // volume 0 (mute) return; } // YM2203 PSG frequency formula: Fout = clock / (16 * N) // N = 12-bit value uint16_t N = CLK_FREQ / (16UL * freq); ymWrite(0x04, N & 0xFF); // Freq LSB ymWrite(0x05, (N >> 8) & 0x0F); // Freq MSB } // --- Set PSG Channel A volume --- void setChannelAVol(uint8_t vol) { ymWrite(0x08, vol); } // --- Set PSG Channel B volume --- void setChannelAVol(uint8_t vol) { ymWrite(0x09, vol); } // --- Set PSG Channel C volume --- void setChannelAVol(uint8_t vol) { ymWrite(0x0A, vol); } // --- Arduino setup --- void setup() { setupPins(); // Reset YM2203 digitalWrite(YM_RST, LOW); delay(100); digitalWrite(YM_RST, HIGH); delay(100); // Enable PSG Channel A tone, disable noise ymWrite(0x07, 0b11111110); // bit0=0 enables tone A // ymWrite(0x07, 0b11111101); // bit1=0 enables tone B // ymWrite(0x07, 0b11111011); // bit2=0 enables tone C } // --- Play melody in loop --- void loop() { for (int i = 0; i < melodyLength; i++) { setChannelAFreq(melody[i].freq); setChannelAVol(melody[i].vol); delay(melody[i].dur); } }