YM2203 Player

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 used A1 for reset signal, and have not used the complete amplifying part

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);
  }
}

Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *