/*****************************************************************************/ /* */ /* RTTTL Ring Tone Player for Microchip PIC16F87x Microcontrollers */ /* Copyright Craig.Peacock@beyondlogic.org */ /* Version 1.0 17th August 2003 */ /* Modified and implemented on avr atmega168. Jim Remington 4/2008 */ /* sjames_remington at yahoo dot com */ /*****************************************************************************/ #define F_CPU 8000000UL #include #include #include #include #include /* global variables for ring tone player */ volatile unsigned int note_duration; //note duration in units of 8.192 milliseconds, decremented by TIMER0 // prototypes void init_tunes(void); void play_note(unsigned char note, unsigned char octave, unsigned int duration); int main(void) { unsigned int pointer = 0; unsigned char octave = 0; unsigned int duration = 0, tempo=0; unsigned char note = 0; unsigned char defaultoctave = 0, beat_speed=0; unsigned int defaultduration = 0; char buf[10],buf2[10]; // AxelF const unsigned char static Melody[] = "32p,8g,8p,16a#.,8p,16g,16p,16g,8c6,8g,8f,8g,8p,16d.6,8p,16g,16p," "16g,8d#6,8d6,8a#,8g,8d6,8g6,16g,16f,16p,16f,8d,8a#,2g,4p,16f6,8d6," "8c6,8a#,4g,8a#.,16g,16p,16g,8c6,8g,8f,4g,8d.6,16g,16p,16g,8d#6,8d6," "8a#,8g,8d6,8g6,16g,16f,16p,16f,8d,8a#,2g"; defaultoctave = 5; defaultduration = 4; beat_speed = 80; /* //HappyBirthday const unsigned char static Melody[] = "8g.,16g,a,g,c6,2b,8g.,16g,a,g,d6,2c6,8g.,16g,g6,e6,c6,b,a,8f6.,16f6," "e6,c6,d6,2c6,8g.,16g,a,g,c6,2b,8g.,16g,a,g,d6,2c6,8g.,16g,g6,e6,c6,b," "a,8f6.,16f6,e6,c6,d6,2c6"; defaultoctave = 5; defaultduration = 4; beat_speed = 100; */ tempo = 812 / (int) beat_speed; //consistent with picaxe "tempo", value 1-15, which is a note duration multiplier if ( (tempo<1) || (tempo>15) ) tempo=6; //reasonable default init_tunes(); sei(); do { octave = defaultoctave; /* Set Default Octave */ if ((Melody[pointer] == '3') && (Melody[pointer+1] == '2')) { duration = 1; // 1/32 note pointer += 2; } else if ((Melody[pointer] == '1') && (Melody[pointer+1] == '6')) { duration = 2; // 1/16 pointer += 2; } else if (Melody[pointer] == '8') { duration = 4; pointer++; } else if (Melody[pointer] == '4') { duration = 8; pointer++; } else if (Melody[pointer] == '2') { duration = 16; pointer++; } else if (Melody[pointer] == '1') { duration = 32; pointer++; } else duration = defaultduration; if (Melody[pointer + 1] == '#') { /* Process Sharps */ switch (Melody[pointer]) { case 'a' : note = 1; break; case 'c' : note = 4; break; case 'd' : note = 6; break; case 'f' : note = 9; break; case 'g' : note = 11; break; } pointer +=2; } else { switch (Melody[pointer]) { case 'a' : note = 0; break; case 'b' : note = 2; break; case 'c' : note = 3; break; case 'd' : note = 5; break; case 'e' : note = 7; break; case 'f' : note = 8; break; case 'g' : note = 10; break; case 'p' : note = 12; break; } pointer++; } if (Melody[pointer] == '.') { /* Duration 1.5x */ duration = duration + (duration>>1); pointer++; } if (Melody[pointer] == '4') { octave = 4; pointer++; } else if (Melody[pointer] == '5') { octave = 5; pointer++; } else if (Melody[pointer] == '6') { octave = 6; pointer++; } else if (Melody[pointer] == '7') { octave = 7; pointer++; } if (Melody[pointer] == '.') { /* Duration 1.5x */ duration = duration + (duration>>1); pointer++; } duration *= tempo; //increase duration by "tempo" ala picaxe play_note(note, octave, duration); } while (Melody[pointer++] == ','); /* done, so just hang */ while(1) {}; } /* Define timer constants for the low octave of notes x=sharp (#) This table is defined according to the International Equal-Tempered Scale, with A4 = 440.00 Hz frequency standard The formula for the timer constant = 8E6/(2*frequency in Hz) */ #define a4 9091 #define ax4 8584 #define b4 8097 #define c4 7648 #define cx4 7220 #define d4 6814 #define dx4 6431 #define e4 6070 #define f4 5731 #define fx4 5405 #define g4 5102 #define gx4 4819 // Subroutine to play a note for "duration" milliseconds (in units of 8 ms) // note = 0-11 for A,A#,B,C,C#,D,D#,E,F,F#,G,G# // note > 11 = silence for "duration" milliseconds // octave = 4,5,6,7 defined according to the international scale void play_note(unsigned char note, unsigned char octave, unsigned int duration) { static unsigned int timer1_constant[]={a4,ax4,b4,c4,cx4,d4,dx4,e4,f4,fx4,g4,gx4,0,0,0,0}; static unsigned int timer1_count; // set note duration (global), units are 8.192 ms note_duration = duration; timer1_count=timer1_constant[(note&0xF)]; if (timer1_count==0) {DDRB &= ~(1); timer1_count=0xFFFF;} //silence else { // handle octave switch (octave) { case 4 : { break;} //octave 4, do nothing case 5 : { timer1_count >>= 1; break;} //divide by 2 case 6 : { timer1_count >>= 2; break;} //divide by 4 case 7 : { timer1_count >>= 3; break;} //well, 8 default : { }; } DDRB |= 1; //set data direction reg bit 0 to output; } //end if // set note frequency OCR1A = timer1_count; // Wait until note has finished playing while(note_duration) { }; // note is playing DDRB &= ~(1); // turn off buzzer note_duration = (duration>>3); // pause 1/8 of total note duration while (note_duration) { }; // and wait } /* TIMER 1 OCR1A interrupt service routine This routine is called when TIMER1 count (TCNT1) = OCR1A, which is set on the fly for each note. Operation: toggle the buzzer line, producing frequency = 1/(2*timer period) */ ISR(TIMER1_COMPA_vect) { PORTB ^= 1; //toggle buzzer output line, if sound flag is on } /* TIMER 0 overflow interrupt service routine This routine is called every 8.192 milliseconds Operation: decrement global note timer */ ISR(TIMER0_OVF_vect) { if (note_duration) note_duration--; } /* Set up buzzer to play tones. Timer/Counter 2 is used in CTC mode, clear timer on compare/match */ void init_tunes(void) { DDRB |= 1; //set data direction reg bit 0 to output PORTB &=~(1); //set buzzer output low note_duration=0; //initialize globals //initialize timer 0 TCCR0A = 0; TCCR0B = _BV(CS02); //normal mode with CPU clock/256 = 122 Hz TCNT0=0; //clear counter TIMSK0 = (1<