Recent site activity

My Stuff‎ > ‎

PIC music

Here is the code for a tiny PIC12F629 microcontroller to play a stripped down interpretation of the "Captain Pugwash" theme. I used the free SourceBoost C compiler and PICkit2 USB programmer. Apart from the PIC the only extra component needed is a piezo sounder. I salvaged one from a musical christmas decoration. The (+) tab of the sounder (inner ring) is connected to pin 2 of the PIC. The (-) tab (outer ring) goes to ground... sorted
 
 
// SOURCEBOOST C
#include <system.h>

// config word; internal oscillator, watchdog and master clear are off
#pragma DATA _CONFIG, _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF

//Set clock frequency
#pragma CLOCK_FREQ 4000000

// This is the tune data... high nybble of each byte is the relative duration (1-16) and the
// low nybble is the note (1-16) or a break (0). Note numbers are mapped to frequencies in
// code. A melody can use only 15 different notes in total and the total number of bytes that
// can be stored in EEPROM on a PIC12F629 is 128. There is a null terminator at the end of the
// melody data
#pragma DATA _EEPROM,
0x44, 0x20, 0x44, 0x10, 0x18, 0x16, 0x10, 0x14, 0x16, 0x10, 0x18, 0x1b, 0x10, 0x18, 0x16, 0x10,
0x14, 0x41, 0x20, 0x41, 0x10, 0x15, 0x13, 0x10, 0x11, 0x13, 0x10, 0x15, 0x17, 0x10, 0x15, 0x13,
0x10, 0x11, 0x44, 0x20, 0x44, 0x10, 0x18, 0x16, 0x10, 0x14, 0x16, 0x10, 0x18, 0x1b, 0x10, 0x18,
0x16, 0x10, 0x24, 0x10, 0x1b, 0x1a, 0x10, 0x19, 0x18, 0x10, 0x17, 0x16, 0x10, 0x15, 0x14, 0x20,
0x16, 0x20, 0x14, 0x10, 0x14, 0x16, 0x10, 0x17, 0x48, 0x20, 0x48, 0x10, 0x18, 0x19, 0x10, 0x1a,
0x1b, 0x10, 0x1a, 0x19, 0x10, 0x18, 0x17, 0x10, 0x16, 0x15, 0x10, 0x16, 0x17, 0x10, 0x16, 0x15,
0x10, 0x11, 0x12, 0x10, 0x13, 0x44, 0x20, 0x44, 0x20, 0x44, 0x20, 0x44, 0x20, 0x24, 0x1b, 0x1a,
0x10, 0x19, 0x18, 0x10, 0x17, 0x16, 0x10, 0x15, 0x34, 0x36, 0x44, 0x40, 0x00

typedef unsigned char byte;

// info used by the interrupt handler
byte next_tmr1h = 0;
byte next_tmr1l = 0;
byte wave = 0;

// interrupt handler called when the timer1 overflows
void interrupt( void )
{
 // check if this is timer1 overflow event
 if( pir1 & (1<<TMR1IF) )
 {
  // set up the timer 1 counters so they will overflow
  // again after the appropriate time delay
  tmr1h = next_tmr1h;
  tmr1l = next_tmr1l;
 
  // toggle pin state GPIO5, which drives the piezo sounder
  wave=!wave;
  gpio = wave? 0b100000 : 0b000000;  
  
  //clear timer 1 interrupt bit
  clear_bit( pir1, TMR1IF );
 }
}

void main( void )
{
 // configure timer1 for interrupts / no prescaler
 t1con=0; 
 intcon.6=1;
 intcon.7=1;
 pie1.0=1;
 clear_bit( pir1, TMR1IF ); //clear timer 1 interrupt bit
 
 // set up IO pins
 trisio = 0;
 gpio=0;

 // loop forever
 for(;;)
 {
  byte addr = 0;
  byte data = 0;
  
  // loop through the tune
  for(;;)
  {
   // read byte from EEPROM
   eeadr = addr;
   eecon1.0 = 1;
   data = eedata;
   ++addr;

   // a zero byte indicates end of the tune
   if(!data || addr>0x7f)
    break;
    
   // extract the note number from the low nybble
   byte note = data & 0x0f;
   
   // play a note?
   if(note)
   {
    // lookup corresponding frequency
    long freq = 0;
    switch(note)
    {
    case 1: freq=196; break; // G
    case 2: freq=220; break; // A
    case 3: freq=247; break; // B
    case 4: freq=262; break; // C
    case 5: freq=294; break; // D
    case 6: freq=330; break; // E
    case 7: freq=349; break; // F
    case 8: freq=392; break; // G
    case 9: freq=440; break; // A
    case 10: freq=494; break; // B
    case 11: freq=524; break; // C
    }
    
    // convert this into the correct timer count values
    // Internal clock is 4MHz and Timer1 counts at 1/4
    // of this frequency (1MHz). We need to call the
    // interrupt handler at double the pitch frequency so
    // that we can generate the 2 phases of the square wave
    // pulse. What we calculate here is the initial 16 bit
    // Timer1 value that will overflow (at 0xffff) after the
    // appropriate period of time.
    long l = (0xffff - (500000L/freq));
    next_tmr1h = l>>8;
    next_tmr1l = l&0xff;
    tmr1h = next_tmr1h;
    tmr1l = next_tmr1l;
    wave = 0; 
    
    // enable the timer (start sound)  
    t1con.0=1; 
   }
   else
   {
    // disable the timer (stop sound)
    t1con.0=0; 
   }
     
   // duration is in top 4 bits. We'll just
   // us an empty "for" loop to provide a delay
   int dur = 70*(data>>4);
   for(int p=0;p<dur;++p)
   {
    // empty for loop for delay
    for(byte q=1;q;++q);
   }
  
  }
 }
}