Introduction: Scales on the Arduino With an SPI 12 Bit Dac

You will need:

  1. An arduino DAC shield http://tinyurl.com/AudioShield
  2. A powered speaker with a 3.5mm plug
  3. an arduino uno compatible
  4. a host computer for compiling and uploading code

You will get:

  1. The ability to play scales
  2. The ability to play any song you would like to encode
  3. endless hours of great fun and learning

Step 1: Scales on the Arduino Audio Shield

First download and compile the code

/*  freqout(freq, t)
// freq in hz, t in ms a simple tone generation function generates square waves of arbitrary frequency and duration program also includes a top-octave lookup table & transposition function */
#include 
#define LDAC        8
#define ChipSel     9
//using macros for pin numbers is better than using a variable -BM
///Danger Will Robinson!
/****************************
*****************************
  This code will work ONLY with
  Arduino boards using ATMega
  328 or 168 processors!
  Ex: Uno, Nano, Pro Mini. - BM
*****************************
****************************/
void DACWrite( uint16_t );
int ptime;
int   k,  x, dur, freq, t;
int i, j;
float ps;
// variable for pow pitchShift routine
float noteval;
// note values for two octave scale
// divide them by powers of two to generate other octaves
float A     = 14080;
float AS    = 14917.2;
float B     = 15804.3;
float C     = 16744;
float CS    = 17739.7;
float D     = 18794.5;
float DS    = 19912.1;
float E     = 21096.2;
float F     = 22350.6;
float FS    = 23679.6;
float G     = 25087.7;
float GS    = 26579.5;
float AA2    = 28160;
float A2S   = 29834.5;
float B2    = 31608.5;
float C2    = 33488.1;
float C2S   = 35479.4;
float D2    = 37589.1;
float D2S   = 39824.3;
float E2    = 42192.3;
float F2    = 44701.2;
float F2S   = 47359.3;
float G2    = 50175.4;
float G2S   = 53159;
float AA3    = 56320;
//octaves  - corresponds to piano octaves
float oct8 = 4;
float oct7 = 8;
float oct6 = 16;
float oct5 = 32;
float oct4 = 64;
float oct3 = 128;
float oct2 = 256;
float oct1 = 512;
float oct0 = 1024;
//rhythm values
int wh = 1024;
int h  = 512;
int dq = 448;
int q = 256;
int qt = 170;
int de = 192;
int e = 128;
int et = 85;
int dsx = 96;
int sx = 64;
int thx = 32;
// major scale just for demo, hack this
float majScale[] = { A, B, CS, D, E, FS, GS, AA2,
                     B2, C2S, D2, E2, F2S, G2S, AA3
                   };
void setup() {
  pinMode( ChipSel, OUTPUT );     // sets the digital pin as output
  pinMode( LDAC, OUTPUT );
  SPI.begin();
  PORTB |= 0b00000011;
  //set chip select and LDAC to default high state - BM
  Serial.begin(115200);
}
void loop() {
  for (i = 0; i <= 11; i++)  {
    ps = (float)i / 12;
    // choose new transpose interval every loop
    for (x = 0; x <= 15; x++) {
      noteval = (majScale[x] / oct4) * pow(2, ps);
      // transpose scale up 12 tones
      // pow function generates transposition
      // eliminate " * pow(2,ps) " to cut out transpose routine
      dur = 100;
      freqout((int)noteval, dur);
      delay(10);
    }
  }
}
void DACWrite( uint16_t outbuf ) {
  outbuf |= 0b0111000000000000;   //change some bits at the beginning to configure DAC
  char* w_bytes;
  w_bytes = (char*)&outbuf;       //split the two bytes in outbuf into an array
  PORTB &= 0b11111101;            //pull chip select low
  SPI.transfer( w_bytes [1] );    //send them one at a time
  SPI.transfer( w_bytes [0] );
  PORTB |= 0b00000010;            //pull chip select high
  PORTB &= 0b11111110;            //pull LDAC low
  asm volatile("nop");            //wait one clock cycle
  PORTB |= 0b00000001;            //pull LDAC high
}
// freq in hz, t in ms
void freqout(int freq, int t) {
  int hperiod;
  //calculate 1/2 period in us
  long cycles, i;
  hperiod = (500000 / freq) - 7;
  // subtract 7 us to make up for digitalWrite overhead
  cycles = ((long)freq * (long)t) / 1000;
  // calculate cycles
  // Serial.print(freq);
  // Serial.print((char)9);
  // ascii 9 is tab - you have to coerce it to a char to work
  // Serial.print(hperiod);
  // Serial.print((char)9);
  // Serial.println(cycles);
  // play note for t ms
  for (i = 0; i <= cycles; i++) {
    DACWrite( 4095 );
    //send max value to DAC -- equivalent to logic HIGH - BM
    delayMicroseconds(hperiod);
    DACWrite( 0 );
    //send 0 to DAC -- equivalent to logic LOW - BM
    delayMicroseconds(hperiod);
    // - 1 to make up for digitaWrite overhead
    //removed - PORT is much faster - BM
  }
}

Step 2: Upload

Then upload the code sample to the arudino.

If you have the shield (available at: http://tinyurl.com/AudioShield)

You should be able to hear scales on the speaker.

You will need an amplified speaker with a 3.5mm plug to plug into the shield.

The shield has no amplifier and cannot drive a speaker directly.