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

