![]() |
|
Using only a speaker and decoupling capacitor, it is possible to generate tunes or melodies from your Microchip PIC16F87x processor. A timer can be used to generate each of the eleven musical notes and another timer can be used to time the note duration. You can even choose to support several octaves if you want a challenge. The code can form the foundation for a range of applications such as christmas toys to customised doorbells or chimes. Add a DIP switch to support multiple melodies. However the hard parts comes from making and coding your own melodies to play. Wouldn’t it make sense to use some of the tens of thousands of Mobile Phone Ring Tones floating around the place. This is what we have done here. One of the more popular standards is the RTTTL (Ringing Tones Text Transfer Language) specification which is used by Nokia mobile phones. These tunes can be save and transported using the .RTX ringtone specification. This specification is no more than a ASCII text file which includes the ringtone name, a control section specifying default attributes and a comma delimited string of notes that can be optionally encoded with the octave and duration. Understanding RTTTL (Ringing Tones Text Transfer Language)A simple RTTTL ring tone is the itchy and scratchy theme song which is displayed below : itchy:d=8,o=6,b=160:c,a5,4p,c,a,4p,c,a5,c,a5,c,a,4p,p,c,d,e,p,e,f,g,4p,d,c,4d,f,4a#,4a,2c7 This ring tone can be split into three sections :
The parameters which can be specified in the control section are :
If any of the parameters is missing from the control section, the following defaults are assumed : 4=duration, 6=scale, 63=beats-per-minute. The circuitAs you can see the circuit required to generate tones is very simple. The 20MHz crystal controls the timing and can not be substituted with another value without recalculating the divisors for each tone. ![]() Calculations for 20MHz The following spreedsheet shows the desired and actual frequencies for each note @ 20MHz. The code supports 4 octaves. ![]() The source code The code has been written in C and compiled with the Hi-Tech PICC Compiler. HiTech Software have a demo version of the PICC for download which works for 30 days. A pre-compiled .HEX file has be included in the archive which has been compiled for use with (or without) the ICD. To add new tones is simply a matter of cut and paste. You may choose to search Overtonez.co.uk or any number of internet sites for new ring tones. Once you have one, simply cut the note commands into the Melody[ ] array and adjust the defaultduration, defaultoctave and beat_speed to suit. If you have a Nokia F-Bus cable, you can download your favourite tunes from your phone using software such as Logomanager or the Oxygen Phone Manager. You can then save them as .rtl files and paste it into your source code.
/*****************************************************************************/
/* */
/* RTTTL Ring Tone Player for Microchip PIC16F87x Microcontrollers */
/* Copyright Craig.Peacock@beyondlogic.org */
/* Version 1.0 17th August 2003 */
/* */
/*****************************************************************************/
#include <pic.h>
#define TONE RB0
void InitTimer(void);
void delayms(unsigned char cnt);
void PlayNote(unsigned short note, unsigned char octave, unsigned int duration);
unsigned char beep;
unsigned char preloadTMR1L;
unsigned char preloadTMR1H;
unsigned short TMR0Count;
unsigned char beat_speed;
#define MissionImpossible
void main(void)
{
unsigned int pointer = 0;
unsigned int octave = 0;
unsigned int duration = 0;
unsigned short note = 0;
unsigned int defaultoctave = 0;
unsigned int defaultduration = 0;
#ifdef AxelF
/* 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,86,
8a#,8g,8d6,8g6,16g,16f,16p,16f,8d,8a#,2g"};
defaultoctave = 5;
defaultduration = 4;
beat_speed = 125;
#endif
#ifdef HappyBirthday
/* 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 = 125;
#endif
#ifdef Itchy
/* Itchy & Scratcy */
const unsigned char static Melody[] = {"8c,8a5,4p,8c,8a,4p,8c,a5,8c,a5,8c,8a,4p,8p,8c,8d,8e,8p,8e,8f,8g,4p,8d,
8c,4d,8f,4a#,4a,2c7"};
defaultoctave = 6;
defaultduration = 8;
beat_speed = 198;
#endif
#ifdef MissionImpossible
/* Mission Impossible */
const unsigned char static Melody[] = {"16d5,16d#5,16d5,16d#5,16d5,16d#5,16d5,16d5,16d#5,16e5,16f5,16f#5,16g5,
8g5,4p,8g5,4p,8a#5,8p,8c6,8p,8g5,4p,8g5,4p,8f5,8p,8p,8g5,4p,4p,8a#5,8p,
8c6,8p,8g5,4p,4p,8f5,8p,8f#5,8p,8a#5,8g5,1d5"};
defaultoctave = 6;
defaultduration = 4;
beat_speed = 150;
#endif
TRISB0 = 0; /* Make TONE an output */
beep = 0;
InitTimer();
PEIE = 1;
GIE = 1; /* Enable General Purpose Interrupts */
do {
octave = defaultoctave; /* Set Default Octave */
if ((Melody[pointer] == '3') && (Melody[pointer+1] == '2')) {
duration = 32;
pointer += 2;
}
else if ((Melody[pointer] == '1') && (Melody[pointer+1] == '6')) {
duration = 16;
pointer += 2;
}
else if (Melody[pointer] == '8') {
duration = 8;
pointer++;
}
else if (Melody[pointer] == '4') {
duration = 4;
pointer++;
}
else if (Melody[pointer] == '2') {
duration = 2;
pointer++;
}
else if (Melody[pointer] == '1') {
duration = 1;
pointer++;
} else duration = defaultduration;
if (Melody[pointer + 1] == '#') {
/* Process Sharps */
switch (Melody[pointer]) {
case 'a' : note = 10726;
break;
case 'c' : note = 9019;
break;
case 'd' : note = 8035;
break;
case 'f' : note = 6757;
break;
case 'g' : note = 6024;
break;
}
pointer +=2;
} else {
switch (Melody[pointer]) {
case 'a' : note = 11364;
break;
case 'b' : note = 10123;
break;
case 'c' : note = 9555;
break;
case 'd' : note = 8513;
break;
case 'e' : note = 7584;
break;
case 'f' : note = 7158;
break;
case 'g' : note = 6378;
break;
case 'p' : note = 0;
break;
}
pointer++;
}
if (Melody[pointer] == '.') {
/* Duration 1.5x */
duration = duration + 128;
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 + 128;
pointer++;
}
PlayNote(note, octave, duration);
} while (Melody[pointer++] == ',');
/* Wait until last note has played */
while(TMR0Count) { };
beep = 0;
/* Loop */
while(1) {};
}
void PlayNote(unsigned short note, unsigned char octave, unsigned int duration)
{
/* Process octave */
switch (octave) {
case 4 : /* Do noting */
break;
case 5 : /* %2 */
note = note >> 1;
break;
case 6 : /* %4 */
note = note >> 2;
break;
case 7 : /* %8 */
note = note >> 4;
break;
}
/* Wait until last note has played */
while(TMR0Count) { };
beep = 0;
/* Process New Note Frequency */
if (note) {
note = ~note;
preloadTMR1L = (note & 0xFF);
preloadTMR1H = ((note & 0xFF00) >> 8);
}
/* Process Note Duration */
TMR0Count = 255/(duration & 0x7F);
/* If duration is 1.5x add .5 to duration */
if (duration & 0x80) TMR0Count = (TMR0Count + (TMR0Count >> 1));
if (note) beep = 1;
}
void InitTimer(void)
{
/* Initialise Timer 0 */
OPTION = 0b11010111; /* Set TMR0 to Internal CLk, 1:256 */
T0IF = 0; /* Clear TMR0 Flag, ready for use */
T0IE = 1; /* Enable Timer Overflow Interrupt */
/* Initialise Timer 1 */
T1CON = 0b00000001; /* Counter Enabled, Using Ext Pin 1:1 Prescaler */
TMR1IF = 0; /* Clear Flag */
TMR1IE = 1; /* Enable Interrupt */
}
void interrupt interr(void)
{
if (T0IF) {
TMR0 = beat_speed;
if (TMR0Count) TMR0Count--;
T0IF = 0;
}
if (TMR1IF) {
if (beep) TONE = !TONE;
else TONE = 0;
TMR1H = preloadTMR1H;
TMR1L = preloadTMR1L;
TMR1IF = 0; /* Clear Flag */
}
}
The above example compiled with the Mission Impossible theme takes a modest 1K of memory. .
Memory Usage Map:
Program ROM $0000 - $004D $004E ( 78) words
Program ROM $006F - $01BA $014C ( 332) words
Program ROM $05B9 - $07FF $0247 ( 583) words
$03E1 ( 993) words total Program ROM
Bank 0 RAM $0020 - $0038 $0019 ( 25) bytes
Bank 0 RAM $0071 - $0078 $0008 ( 8) bytes
$0021 ( 33) bytes total Bank 0 RAM
Program statistics:
Total ROM used 993 words (12.1%)
Total RAM used 33 bytes (9.0%)
Downloading the Source Code
|