// ** This file was downloaded from: // ** http://www.afox-consulting.com//product/SID-Shield.html // ** File: SID_6581_I2C.ino // ** Author: A Fox Consulting & Design // ** Date: 03/19/2014 // // ** DISCLAIMER: This source code was written to interface with a // ** specific in-house prototype product developed by A Fox Consulting // ** And Design. The code here is experimental and is meant to provide // ** proof-of-concept of the device only and is not intended for // ** commercial use. // // Arduino SID-Shield Interface Demo // // Wiring set-up: // Digital 9 --> PHI2 // Digital 8 --> ~CS #include "Wire.h" #define pSID_PHI2 9 #define pSID_CS 8 #define MCP_ADDR 0x20 // The MCP23017 can have an address from 0x20 - 0x27 #define NOISE_WAVEFORM 0b1000 #define SQUARE_WAVEFORM 0b0100 #define SAWTOOTH_WAVEFORM 0b0010 #define TRIANGLE_WAVEFORM 0b0001 /* * Notetable: these values represents notes on a C64 * SID chip. Pick a value from each vector for correct * frequency parameters, note_hi[x] = $d400, note_lo[x] = $d401 * The numbers in the C64 hardware reference manual are simply * WRONG. Index 0 = C-0, index 36 = C-3 (flat C), * index 57 = A-4 (flat A), index 95 = A-7 (last B in octave 8 * is not possible to replay with c64) * * Public Domain - Linus Walleij 2001 */ word notes[95] = { 0x0116,0x0127,0x0138,0x014B,0x015E, 0x0173,0x0189,0x01A1,0x01BA,0x01D4,0x01F0, 0x020D,0x022C,0x024E,0x0271,0x0296,0x02BD, 0x02E7,0x0313,0x0342,0x0374,0x03A8,0x03E0, 0x041B,0x0459,0x049C,0x04E2,0x052C,0x057B, 0x05CE,0x0627,0x0684,0x06E8,0x0751,0x07C0, 0x0836,0x08B3,0x0938,0x09C4,0x0A59,0x0AF6, 0x0B9D,0x0C4E,0x0D09,0x0DD0,0x0EA2,0x0F81, 0x106D,0x1167,0x1270,0x1388,0x14B2,0x15ED, 0x173A,0x189C,0x1A13,0x1BA0,0x1D44,0x1F02, 0x20DA,0x22CE,0x24E0,0x2711,0x2964,0x2BDA, 0x2E75,0x3138,0x3426,0x3740,0x3A89,0x3E04, 0x41B4,0x459C,0x49C0,0x4E22,0x52C8,0x57B4, 0x5CEB,0x6271,0x684C,0x6E80,0x7512,0x7C08, 0x8368,0x8B38,0x9380,0x9C45,0xA590,0xAF68, 0xB9D6,0xC4E3,0xD098,0xDD00,0xEA24,0xF810 }; // -------------------------------------------------------------------------------- // Low-level utility routines to communicate with the MCP23017 // and thus the SID chip. // -------------------------------------------------------------------------------- // The most basic function, this writes a value "value" to register "reg" of the SID chip. void write_SID (byte reg, byte value) { Wire.beginTransmission(MCP_ADDR); Wire.write(0x12); // Start writing at register GPIOA Wire.write(value); // Write "value" to port A Wire.write(reg); // Write "teg" to port B Wire.endTransmission(); // Do the write digitalWrite(pSID_CS, 0); delayMicroseconds(2000); digitalWrite(pSID_CS, 1); } // And now some basic utility routines to call to set various registers of the SID chip void setADSRenvelope(byte voice, byte _attack, byte _decay, byte _sustain, byte _release) { byte value; voice -= 1; value = ((_attack & 0x0F) << 4) | (_sustain & 0x0F); write_SID((voice * 7) + 0x05, value); value = ((_sustain & 0x0F) << 4) | (_release & 0x0F); write_SID((voice * 7) + 0x06, value); } void setWaveform(byte voice, byte _waveform, byte _enable) { byte value; voice -= 1; value = ((_waveform & 0x0F) << 4) | (_enable & 0x01); write_SID((voice * 7) + 0x04, value); } void setPulseWidth(byte voice, word _PW) { voice -= 1; write_SID((voice * 7) + 0x03, ((_PW & 0x0F00) >> 8)); write_SID((voice * 7) + 0x02, (_PW & 0x00FF)); } void setFrequency(byte voice, word _Frequency) { voice -=1 ; write_SID((voice * 7) + 0x01, ((_Frequency & 0xFF00) >> 8)); write_SID((voice * 7) + 0x00, (_Frequency & 0x00FF)); } // -------------------------------------------------------------------------------- void setup() { //OSCCAL -= 7; // Calibrate the internal oscillator // // You should read 1MHz on pin 15 pinMode(pSID_PHI2, OUTPUT); pinMode(pSID_CS, OUTPUT); digitalWrite(pSID_CS, 1); // Set SID Chip Select HIGH // Initialize Timer1 for a 1MHz output TCNT0 = 0; // Reset timer, just in case TCCR1A = _BV(COM1A0); // Set PORTB1 to toggle on OC1B compare match // | _BV(COM1B0); // | _BV(WGM10) // | _BV(WGM11); TCCR1B = _BV(WGM12); // CTC mode, OCR1A as TOP // | _BV(WGM13); // Fast PWM mode, OCR1A as TOP OCR1B = 0b0000000; OCR1A = 0b0000111; // Set counter TOP to 8-1 = 7. For a 16MHz. clock this = 1 Mhz. TCCR1B |= _BV(CS10); // set prescale to div 1 and start the timer Wire.begin(); Wire.beginTransmission(MCP_ADDR); // Available addresses are 0x20 - 0x27 Wire.write((byte)0x00); // Start at IODIRA register Wire.write((byte)0x00); // Set all of port A to OUTPUT Wire.write((byte)0x00); // Set all of port B to OUTPUT Wire.endTransmission(); } // -------------------------------------------------------------------------------- // High-Level code begins here // -------------------------------------------------------------------------------- void loop() { byte i = 0; byte j = 0; delay(250); write_SID(0x18, 0b00001111); // VOLUME (15) for(i=1;i<4;i++) { setADSRenvelope(i, 0x00, 0x00, 0x0F, 0x08); setPulseWidth(i, 0x0800); // PW = ~50% setWaveform(i, TRIANGLE_WAVEFORM, 0); } while(true) { PlayPrettyMusic(); delay(1000); ToneSweep(); delay(1000); ToneOscillator(); delay(1000); PlayWithFilters(); delay(1000); MakeSomeNoise(); delay(1000); MakeLowRumble(); delay(1000); NoiseSweep(); delay(1000); PoppingSounds(); delay(2000); } } // -------------------------------------------------------------------------------- void PlayPrettyMusic() { byte i; // Play a chord setFrequency(1, notes[48]); setWaveform(1, TRIANGLE_WAVEFORM, 1); delay(1000); setFrequency(2, notes[52]); setWaveform(2, TRIANGLE_WAVEFORM, 1); delay(1000); setFrequency(3, notes[55]); setWaveform(3, TRIANGLE_WAVEFORM, 1); delay(2000); // Change waveforms for(i=1;i<4;i++) { setWaveform(i, SAWTOOTH_WAVEFORM, 1); } delay(2000); for(i=1;i<4;i++) { setWaveform(i, SQUARE_WAVEFORM, 1); } delay(2000); for(i=1;i<4;i++) { setWaveform(i, TRIANGLE_WAVEFORM, 1); } delay(2000); // Pause for(i=1;i<4;i++) { setWaveform(i, TRIANGLE_WAVEFORM, 0); } delay(1000); // More pretty music setADSRenvelope(3, 0x00, 0x00, 0x0F, 0x00); setFrequency(3, notes[55]); setWaveform(3, TRIANGLE_WAVEFORM, 1); delay(150); setWaveform(3, TRIANGLE_WAVEFORM, 0); delay(10); setFrequency(3, notes[53]); setWaveform(3, TRIANGLE_WAVEFORM, 1); delay(150); setWaveform(3, TRIANGLE_WAVEFORM, 0); delay(10); setFrequency(3, notes[52]); setWaveform(3, TRIANGLE_WAVEFORM, 1); delay(150); setWaveform(3, TRIANGLE_WAVEFORM, 0); delay(10); setFrequency(3, notes[50]); setWaveform(3, TRIANGLE_WAVEFORM, 1); delay(150); setWaveform(3, TRIANGLE_WAVEFORM, 0); delay(10); setADSRenvelope(i, 0x00, 0x00, 0x0F, 0x08); setFrequency(3, notes[43]); setWaveform(3, TRIANGLE_WAVEFORM, 1); setWaveform(2, TRIANGLE_WAVEFORM, 1); setWaveform(1, TRIANGLE_WAVEFORM, 1); delay(2000); setWaveform(3, TRIANGLE_WAVEFORM, 0); setWaveform(2, TRIANGLE_WAVEFORM, 0); setWaveform(1, TRIANGLE_WAVEFORM, 0); } void ToneSweep() { byte i; setWaveform(1, TRIANGLE_WAVEFORM, 1); for(i=0;i<255;i++) { setFrequency(1, (i << 8)); delay(8); } delay(500); setWaveform(1, TRIANGLE_WAVEFORM, 0); setFrequency(1, 0x0000); } void ToneOscillator() { byte i,j; setWaveform(1, TRIANGLE_WAVEFORM, 1); for(j=0;j<20;j++) { for(i=0;i<=100;i+=10) { setFrequency(1, (i << 8)); delay(4); } for(i=90;i>0;i-=10) { setFrequency(1, (i << 8)); delay(4); } } setWaveform(1, TRIANGLE_WAVEFORM, 0); } void PlayWithFilters() { byte i, j; setWaveform(1, TRIANGLE_WAVEFORM, 1); write_SID(0x16, 0xF0); write_SID(0x17, 0x01); for(j=0;j<8;j++) { write_SID(0x18, (j << 4) | 0x0F); // Cycle through none, LP, BP and HP filtering for(i=0;i<250;i+=25) { setFrequency(1, (i << 8)); delay(100); } } write_SID(0x18, 0x0F); write_SID(0x17, 0x00); setFrequency(1, 0); } void MakeSomeNoise() { byte i; setWaveform(1, NOISE_WAVEFORM, 1); for(i=0;i<10;i++) { setFrequency(1, (random(0, 100) << 8)); delay(500); } setWaveform(1, NOISE_WAVEFORM, 0); setFrequency(1, 0x0000); } void MakeLowRumble() { setADSRenvelope(1, 0x0D, 0x00, 0x0F, 0x0C); // Set Attack and Release to 3 seconds setFrequency(1, 0x0400); setWaveform(1, NOISE_WAVEFORM, 1); delay(5000); setWaveform(1, NOISE_WAVEFORM, 0); delay(5000); setADSRenvelope(1, 0x00, 0x00, 0x0F, 0x08); // Back to normal } void NoiseSweep() { byte i,j; setWaveform(1, NOISE_WAVEFORM, 1); for(j=0;j<10;j++) { for(i=0;i<100;i+=1) { setFrequency(1, (i << 8)); delay(1); } } setWaveform(1, NOISE_WAVEFORM, 0); } void PoppingSounds() { byte i; setFrequency(1, 0x0900); setADSRenvelope(1, 0x00, 0x00, 0x0F, 0x07); // Set ADSR to fast attack, moderate release for(i=0;i<10;i++) { setWaveform(1, NOISE_WAVEFORM, 1); delay(5); setWaveform(1, NOISE_WAVEFORM, 0); delay(300); } setADSRenvelope(1, 0x00, 0x00, 0x0F, 0x08); // Back to normal setFrequency(1, 0x0000); }