SHARE
TWEET

Software defined FM radio transmitter (Arduino + AD9850)

a guest Oct 5th, 2013 1,193 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * ------------------------------------------------------------------------
  3.  * "THE BEER-WARE LICENSE" (Revision 42):
  4.  * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice
  5.  * you can do whatever you want with this stuff. If we meet some day,
  6.  * and you think this stuff is worth it, you can buy me a beer in return
  7.  * Poul-Henning Kamp
  8.  * ------------------------------------------------------------------------
  9.  */
  10. //#ifndef AD9850_H
  11. //#define AD9850_H
  12. #include <Arduino.h>
  13. #include <stdint.h>
  14. #define EX_CLK 125.0e6
  15. // change if ad9850 is connected to other frequency crystal oscillator
  16.  
  17. class AD9850
  18. {
  19.     private:
  20.         const char W_CLK; // word load clock pin
  21.         const char FQ_UD; // frequency update pin
  22.         const char D7; // serial input pin
  23.         uint32_t frequency; // delta phase
  24.         uint8_t phase; // phase offset
  25.         void update();
  26.     public:
  27.         AD9850(char w_clk, char fq_ud, char d7);
  28.         /* NOTE: For device start-up in serial mode,
  29.         hardwire pin 2 at 0, pin 3 at 1, and pin 4 at 1 */
  30.         void setfreq(double f);
  31.         // set frequency in Hz
  32.         void setphase(uint8_t p);
  33.         // for flexibility, p is an int value and 0 <= p <= 32,
  34.         // as input, 360 degree devide into 32 parts,
  35.         // you will get phase increments for 360/32*p degree
  36.         void down();
  37.         // power down
  38.         void up();
  39.         // power on
  40. };
  41.  
  42. #define pulse(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW);}
  43.  
  44. AD9850::AD9850(char w_clk, char fq_ud, char d7)
  45.     : W_CLK(w_clk), FQ_UD(fq_ud), D7(d7) {
  46.     frequency = 0;
  47.     phase = 0;
  48.     pinMode(W_CLK, OUTPUT);
  49.     pinMode(FQ_UD, OUTPUT);
  50.     pinMode(D7, OUTPUT);
  51.     pulse(W_CLK);
  52.     pulse(FQ_UD);
  53. }
  54.  
  55. void AD9850::update() {
  56.     uint32_t f = frequency;
  57.     for (int i = 0; i < 32; i++, f >>= 1) {
  58.         digitalWrite(D7, f & (uint32_t)0x00000001);
  59.         pulse(W_CLK);
  60.     }
  61.     uint8_t p = phase;
  62.     for (int i = 0; i < 8; i++, p >>= 1) {
  63.         digitalWrite(D7, p & (uint8_t)0x01);
  64.         pulse(W_CLK);
  65.     }
  66.     pulse(FQ_UD);
  67. }
  68.  
  69. void AD9850::setfreq(double f) {
  70.     frequency = f * 4294967296.0 / EX_CLK;
  71.     update();
  72. }
  73. void AD9850::setphase(uint8_t p) {
  74.     phase = p << 3;
  75.     update();
  76. }
  77.  
  78. void AD9850::down() {
  79.     pulse(FQ_UD);
  80.     uint8_t p = 0x04;
  81.     for (int i = 0; i < 8; i++, p >>= 1) {
  82.         digitalWrite(D7, p & (uint8_t)0x01);
  83.         pulse(W_CLK);
  84.     }
  85.     pulse(FQ_UD);
  86. }
  87.  
  88. void AD9850::up() { update(); }
  89.  
  90. AD9850 ad(5, 6, 7); // w_clk, fq_ud, d7
  91.  
  92. // A fun sketch to demonstrate the use of the tone() function written by Brett Hagman.
  93.  
  94. // This plays RTTTL (RingTone Text Transfer Language) songs using the
  95. // now built-in tone() command in Wiring and Arduino.
  96. // Written by Brett Hagman
  97. // http://www.roguerobotics.com/
  98.  
  99. // To play the output on a small speaker (i.e. 8 Ohms or higher), simply use
  100. // a 1K Ohm resistor from the output pin to the speaker, and connect the other
  101. // side of the speaker to ground.
  102.  
  103. // You can get more RTTTL songs from
  104. // http://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation
  105.  
  106. const int tonePin = 27;  // for rEDI board
  107. //const int tonePin = 13;  // arbitrary for arduino boards, set this to whatever you want
  108.  
  109. #define OCTAVE_OFFSET 0
  110.  
  111. // These values can also be found as constants in the Tone library (Tone.h)
  112. unsigned int notes[] = { 0,
  113. 10000000/262, 10000000/277, 10000000/294, 10000000/311, 10000000/330, 10000000/349, 10000000/370, 10000000/392, 10000000/415, 10000000/440, 10000000/466, 10000000/494,
  114. 10000000/523, 10000000/554, 10000000/587, 10000000/622, 10000000/659, 10000000/698, 10000000/740, 10000000/784, 10000000/831, 10000000/880, 10000000/932, 10000000/988,
  115. 10000000/1047, 10000000/1109, 10000000/1175, 10000000/1245, 10000000/1319, 10000000/1397, 10000000/1480, 10000000/1568, 10000000/1661, 10000000/1760, 10000000/1865, 10000000/1976,
  116. 10000000/2093, 10000000/2217, 10000000/2349, 10000000/2489, 10000000/2637, 10000000/2794, 10000000/2960, 10000000/3136, 10000000/3322, 10000000/3520, 10000000/3729, 10000000/3951
  117. };
  118.  
  119. //char *song = "The Simpsons:d=4,o=5,b=160:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p,8f#,8f#,8f#,8g,a#.,8c6,8c6,8c6,c6";
  120. //char *song = "Indiana:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6";
  121. //char *song = "TakeOnMe:d=4,o=4,b=160:8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5,8f#5,8e5,8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5";
  122. //char *song = "Entertainer:d=4,o=5,b=140:8d,8d#,8e,c6,8e,c6,8e,2c.6,8c6,8d6,8d#6,8e6,8c6,8d6,e6,8b,d6,2c6,p,8d,8d#,8e,c6,8e,c6,8e,2c.6,8p,8a,8g,8f#,8a,8c6,e6,8d6,8c6,8a,2d6";
  123. //char *song = "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c";
  124. //char *song = "Xfiles:d=4,o=5,b=125:e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,g6,f#6,e6,d6,e6,2b.,1p,g6,f#6,e6,d6,f#6,2b.,1p,e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,e6,2b.";
  125. //char *song = "Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f";
  126. //char *song = "20thCenFox:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p,8e,8g#,8b,1c#6,8f#,8a,8c#6,1e6,8a,8c#6,8e6,1e6,8b,8g#,8a,2b";
  127. //char *song = "Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6";
  128. //char *song = "MASH:d=8,o=5,b=140:4a,4g,f#,g,p,f#,p,g,p,f#,p,2e.,p,f#,e,4f#,e,f#,p,e,p,4d.,p,f#,4e,d,e,p,d,p,e,p,d,p,2c#.,p,d,c#,4d,c#,d,p,e,p,4f#,p,a,p,4b,a,b,p,a,p,b,p,2a.,4p,a,b,a,4b,a,b,p,2a.,a,4f#,a,b,p,d6,p,4e.6,d6,b,p,a,p,2b";
  129. //char *song = "StarWars:d=4,o=5,b=45:32p,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#.6,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#6";
  130. //char *song = "GoodBad:d=4,o=5,b=56:32p,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,d#,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,c#6,32a#,32d#6,32a#,32d#6,8a#.,16f#.,32f.,32d#.,c#,32a#,32d#6,32a#,32d#6,8a#.,16g#.,d#";
  131. //char *song = "TopGun:d=4,o=4,b=31:32p,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,16f,d#,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,g#";
  132. //char *song = "A-Team:d=8,o=5,b=125:4d#6,a#,2d#6,16p,g#,4a#,4d#.,p,16g,16a#,d#6,a#,f6,2d#6,16p,c#.6,16c6,16a#,g#.,2a#";
  133. //char *song = "Flinstones:d=4,o=5,b=40:32p,16f6,16a#,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,d6,16f6,16a#.,16a#6,32g6,16f6,16a#.,32f6,32f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,a#,16a6,16d.6,16a#6,32a6,32a6,32g6,32f#6,32a6,8g6,16g6,16c.6,32a6,32a6,32g6,32g6,32f6,32e6,32g6,8f6,16f6,16a#.,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#6,16c7,8a#.6";
  134. //char *song = "Jeopardy:d=4,o=6,b=125:c,f,c,f5,c,f,2c,c,f,c,f,a.,8g,8f,8e,8d,8c#,c,f,c,f5,c,f,2c,f.,8d,c,a#5,a5,g5,f5,p,d#,g#,d#,g#5,d#,g#,2d#,d#,g#,d#,g#,c.7,8a#,8g#,8g,8f,8e,d#,g#,d#,g#5,d#,g#,2d#,g#.,8f,d#,c#,c,p,a#5,p,g#.5,d#,g#";
  135. //char *song = "Gadget:d=16,o=5,b=50:32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,32d#,32f,32f#,32g#,a#,d#6,4d6,32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,8d#";
  136. //char *song = "Smurfs:d=32,o=5,b=200:4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8f#,p,8a#,p,4g#,4p,g#,p,a#,p,b,p,c6,p,4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8b,p,8f,p,4f#";
  137. //char *song = "MahnaMahna:d=16,o=6,b=125:c#,c.,b5,8a#.5,8f.,4g#,a#,g.,4d#,8p,c#,c.,b5,8a#.5,8f.,g#.,8a#.,4g,8p,c#,c.,b5,8a#.5,8f.,4g#,f,g.,8d#.,f,g.,8d#.,f,8g,8d#.,f,8g,d#,8c,a#5,8d#.,8d#.,4d#,8d#.";
  138. //char *song = "LeisureSuit:d=16,o=6,b=56:f.5,f#.5,g.5,g#5,32a#5,f5,g#.5,a#.5,32f5,g#5,32a#5,g#5,8c#.,a#5,32c#,a5,a#.5,c#.,32a5,a#5,32c#,d#,8e,c#.,f.,f.,f.,f.,f,32e,d#,8d,a#.5,e,32f,e,32f,c#,d#.,c#";
  139. char *song = "MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d";
  140.  
  141. void playTone(unsigned int note, unsigned int duration )  
  142. {
  143.   const double freq = 90 * 1e6;
  144.  
  145.   if ( note == 0)
  146.   {
  147.     ad.setfreq(freq);
  148.     delay(duration);    
  149.     return;
  150.   }
  151.    
  152.   unsigned long currentMillis = millis() + duration;
  153.   while( millis()< currentMillis)
  154.   {  
  155.       ad.setfreq(freq + 75e3/2);
  156.       //digitalWrite(12,LOW);
  157.       delayMicroseconds(note);
  158.       ad.setfreq(freq - 75e3/2);
  159.       //digitalWrite(12,HIGH);
  160.       delayMicroseconds(note);
  161.   }
  162.  
  163.   ad.setfreq(freq );
  164. }
  165.  
  166. void setup(void)
  167. {
  168.   Serial.begin(9600);
  169. }
  170.  
  171. #define isdigit(n) (n >= '0' && n <= '9')
  172.  
  173. void play_rtttl(char *p)
  174. {
  175.   // Absolutely no error checking in here
  176.  
  177.   byte default_dur = 4;
  178.   byte default_oct = 6;
  179.   int bpm = 63;
  180.   int num;
  181.   long wholenote;
  182.   long duration;
  183.   byte note;
  184.   byte scale;
  185.  
  186.   // format: d=N,o=N,b=NNN:
  187.   // find the start (skip name, etc)
  188.  
  189.   while(*p != ':') p++;    // ignore name
  190.   p++;                     // skip ':'
  191.  
  192.   // get default duration
  193.   if(*p == 'd')
  194.   {
  195.     p++; p++;              // skip "d="
  196.     num = 0;
  197.     while(isdigit(*p))
  198.     {
  199.       num = (num * 10) + (*p++ - '0');
  200.     }
  201.     if(num > 0) default_dur = num;
  202.     p++;                   // skip comma
  203.   }
  204.  
  205.   Serial.print("ddur: "); Serial.println(default_dur, 10);
  206.  
  207.   // get default octave
  208.   if(*p == 'o')
  209.   {
  210.     p++; p++;              // skip "o="
  211.     num = *p++ - '0';
  212.     if(num >= 3 && num <=7) default_oct = num;
  213.     p++;                   // skip comma
  214.   }
  215.  
  216.   Serial.print("doct: "); Serial.println(default_oct, 10);
  217.  
  218.   // get BPM
  219.   if(*p == 'b')
  220.   {
  221.     p++; p++;              // skip "b="
  222.     num = 0;
  223.     while(isdigit(*p))
  224.     {
  225.       num = (num * 10) + (*p++ - '0');
  226.     }
  227.     bpm = num;
  228.     p++;                   // skip colon
  229.   }
  230.  
  231.   Serial.print("bpm: "); Serial.println(bpm, 10);
  232.  
  233.   // BPM usually expresses the number of quarter notes per minute
  234.   wholenote = (60 * 1000L / bpm) * 4;  // this is the time for whole note (in milliseconds)
  235.  
  236.   Serial.print("wn: "); Serial.println(wholenote, 10);
  237.  
  238.   // now begin note loop
  239.   while(*p)
  240.   {
  241.     // first, get note duration, if available
  242.     num = 0;
  243.     while(isdigit(*p))
  244.     {
  245.       num = (num * 10) + (*p++ - '0');
  246.     }
  247.    
  248.     if(num) duration = wholenote / num;
  249.     else duration = wholenote / default_dur;  // we will need to check if we are a dotted note after
  250.  
  251.     // now get the note
  252.     note = 0;
  253.  
  254.     switch(*p)
  255.     {
  256.       case 'c':
  257.         note = 1;
  258.         break;
  259.       case 'd':
  260.         note = 3;
  261.         break;
  262.       case 'e':
  263.         note = 5;
  264.         break;
  265.       case 'f':
  266.         note = 6;
  267.         break;
  268.       case 'g':
  269.         note = 8;
  270.         break;
  271.       case 'a':
  272.         note = 10;
  273.         break;
  274.       case 'b':
  275.         note = 12;
  276.         break;
  277.       case 'p':
  278.       default:
  279.         note = 0;
  280.     }
  281.     p++;
  282.  
  283.     // now, get optional '#' sharp
  284.     if(*p == '#')
  285.     {
  286.       note++;
  287.       p++;
  288.     }
  289.  
  290.     // now, get optional '.' dotted note
  291.     if(*p == '.')
  292.     {
  293.       duration += duration/2;
  294.       p++;
  295.     }
  296.  
  297.     // now, get scale
  298.     if(isdigit(*p))
  299.     {
  300.       scale = *p - '0';
  301.       p++;
  302.     }
  303.     else
  304.     {
  305.       scale = default_oct;
  306.     }
  307.  
  308.     scale += OCTAVE_OFFSET;
  309.  
  310.     if(*p == ',')
  311.       p++;       // skip comma for next note (or we may be at the end)
  312.  
  313.     // now play the note
  314.  
  315.     if(note)
  316.     {
  317.       Serial.print("Playing: ");
  318.       Serial.print(scale, 10); Serial.print(' ');
  319.       Serial.print(note, 10); Serial.print(" (");
  320.       Serial.print(notes[(scale - 4) * 12 + note], 10);
  321.       Serial.print(") ");
  322.       Serial.println(duration, 10);
  323.       //tone(tonePin, notes[(scale - 4) * 12 + note]);
  324.       playTone(notes[(scale - 4) * 12 + note], duration );
  325.       //delay(duration);
  326.       noTone(tonePin);
  327.     }
  328.     else
  329.     {
  330.       Serial.print("Pausing: ");
  331.       Serial.println(duration, 10);
  332.       //delay(duration);
  333.       playTone(0, duration );
  334.     }
  335.   }
  336. }
  337.  
  338. void loop(void)
  339. {
  340.   play_rtttl(song);
  341.   Serial.println("Done.");
  342.   while(1);
  343. }
RAW Paste Data
Top