DIY Arduino FM Radio (Part 2).

 Updates on 24 Oct. 2012.

If you have read my first blog on the topic, than you already know what I’m experimenting with. Low price FM Radio, build with TDA7088 / YD 9088. It was obvious, that technology from the early 90-x is outdated. I mean, simple “search and hold” function of the radio.

  • Scan function works only one way, up in the frequency.
  • After switching power “on/off” you have to tune it again on your preferred radio wave.
  • You have no means to know, what is the frequency it’s on.
  •  Occasionally you can’t “push” a tuner to the next station on the air, and have to send multiple commends.

The problem is that radio luck intellectuality, you can’t do much with  RS trigger.  Arduino, even it’s small and 8-bits only, just a monster on this matter, with CPU, ADC and EEPROM. Now it’s time to use them!  Radio-2 able to automatically scan all radio station in the area, store them in non-volatile memory, and plus do a manual “tunning” – mostly for debugging purposes.

This version of the shield requires 4 wires (plus a cap, 4 resistors, and radio, of course). Two power lines, the same like in the first project, and other two connected to pin 1 of the IC and to variable capacitance diode. You also have to cut a trace, which connects Varicap and pin 16. Look at the schematic above.

Digital pin 9 of the arduino, internally driven by TIMER 1, is PWM-ed frequency control voltage to tune a heterodyne.  FAST PWM mode, 10-bit is a compromise between PWM frequency and voltage resolution. We can’t get both, for example 16-bit PWM would drop a frequency down to 16 MHz / 65536 = 244 Hz, which is too low to be filtered by primitive  RC filter, and plus not adequate to do a fast “sweep” or changes in tunning. The same time 8-bit PWM has low voltage resolution, and consequently creates too big “steps” in bandwidth. Measurements show, that radio has about 1 V / 20 MHz sensitivity of the VCO, which would imply to have a resolution at least:  1 V / 20 MHz / 200 kHz = 10 mV, where 200 kHz is a “spacing” in FM broadcasting schedule.  8 bit analogWrite has a resolution 5 V / 256 = .19.53 mV. Even resistors divider improves this value on 0.6 times (3 v / 256 = 11.72 mV), it’s still bigger than necessary minimum. With 10-bits I have 3V / 1024 =   2.93 mV.   Using of external DAC isn’t an option for a few backs radio, but may be worse to try. PWM frequency with 10-bit equals to 16 kHz, which could be easily filtered out with 1-st order RC network.

Analog pin 3 (AN3 ADC), reading the “signal strength” output line from the YD9088.

uint16_t read_tuner()
  {
   uint16_t temp = 0;
      for( uint8_t i = 0; i < 16; i++ ) {
        ADCSRA |= (1<<ADSC);
        while(!(ADCSRA & 0x10));
        temp += ADC;
       }
    temp = 5 * temp / 16;
    return temp;
  }

As you can see, readings averaged over 16 values, which increases a resolution on two additional bits.

Search algorithm has a “window” of 5 samples, to be able to recognize a peaks in the incoming data.

for( uint16_t i = 1023, prn_dot = 0; i > 500; i-- ) {
     OCR1A = i;
     delay(200); // T = R x C = 1 M x 0.1 uF = 0.1 seconds.
     lpf[i%5] = read_tuner();

  uint16_t accum = 0;
       for( uint8_t jp = 0; jp < 5; jp++ ) {
         accum += lpf[jp];
         }
        accum /= 5;
        if ((prn_dot++) % 32 == 0) Serial.print("\n");
          Serial.print(" *");
        if ( accum > maxim )
         {
          maxim = accum;
          trigg = 1;
         }
        if ( accum < maxim )
         { 
          if ( trigg )
           { 
            if ( accum > BARRIER )
             {
              store_entry( posit, (i + 1));
                Serial.print("\n");
                print_transl( posit );
                prn_dot = 0;
              posit++;
              if ( posit >=20 ) return;
              }
             }
          maxim = accum;
          trigg = 0;
     }
   }
 }

All I have to do, is to set a threshold for radio station, in order to be distinguished from the background noise and interference. Tunning data are stored in 40 EEPROM bytes, list includes 20 radio stations overall.

Sketch, Arduino UNO:  FM_Radio_YD9088

 24 Oct. 2012

 In first part of this project, I already write about voltage – frequency non-linearity and how I get the coefficients to improve accuracy of the voltage-to-frequency (VTF) conversion formula, based on power regression. Formula in second part was slightly different, I used second degree polynomial (parabola) approximation, which provides better match to measurements data. Even LibreOffice helps a  lot with calculation, but in overall I spend a lot of time to figure out the values of the coefficients. Plus filling the data table in spreadsheet also quite time consuming. Can Arduino do this work, finding a coefficients to second degree polynomial that “fits” to experimental data? From mathematical / science point of view, the task is interesting itself, and could be a great part of any science project, not necessarily a radio tuner. The method I choose – Least Squares.

 To my great surprise, arduino solved a problem in a splits of second!  I upgrade a sketch, which includes a calibration procedure now, calling via “c” from the serial monitor console input-output interface (Hi, DOS era).

void calibrate()
 {
 //Least squares parabola (2-nd degree polynomial) coefficient calculation
 float arr[5][5] ={{0},{0}}, err[5] = {0}, coeff[5] = {0};

 err[0] = 5;
 for(uint8_t i = 1; i < 5; i++)
  {
   err[i] = 0;
   for(uint8_t j = 0; j < 5; j++)
    {
     err[i] += pow(calibration[j], i);
    }
  }
 for(uint8_t i = 0; i < 3; i++)
  {
   for(uint8_t j = 0; j < 3; j++)
    {
     arr[i][j] = err[i+j];
    }
  }
 for(uint8_t i = 0; i < 3; i++)
  {
   arr[i][3] = 0;
   for(uint8_t j = 0; j < 5; j++)
    {
     if (i==0) arr[i][3] += radio_table[j];
     else arr[i][3] += radio_table[j] * pow(calibration[j], i);
    }
  }
 for(uint8_t k = 0; k < 3; k++)
  {
   for(uint8_t i = 0; i < 3; i++)
    {
     if ( i != k )
      {
      for(uint8_t j = (k + 1); j < 4; j++)
       {
        arr[i][j] -= ((arr[i][k] * arr[k][j]) / arr[k][k]);
       }
      }
    }
  }

union split_float {
 uint16_t tegri[2];
 float loatf;
 } sf;

for(uint8_t i = 0; i < 3; i++)
 {
  coeff[i] = ( arr[i][3] / arr[i][i]);
  sf.loatf = coeff[i];
  store_entry(( 20 + 2 * i), sf.tegri[0] );
  store_entry(( 21 + 2 * i), sf.tegri[1] );
 }
}

In order to perform a calibration procedure, you would need 5 Radio Stations (RSts) available in your area, and their frequencies. (Calibration doesn’t have much sense, if there are less than 5).  Edit this line in the code:

const float radio_table[5] = { 92.5, 97.7, 100.7, 105.7, 107.3 };  

For better results, choose two RSts from both side of the band ( 88 and 108 for North America ) and others in the middle. The signal (RSSI) has to be strong, you may need an external antenna properly oriented for maximum RF field. Run a “scan” first, and see what you get in the list. After this part completed, run “c”.

Arduino starts / a scan : optional /, than would ask you to tune a radio  on the first RSt in the radio_table.  When calibration was done at least ones, print a list with “l” command (interface isn’t blocking, you can send any commands, except digits). If Rst in the list, than use “u” or “d” to tune a radio. Than use “v”  -  ”n” for fine tunning, plus “x”, pay attention to RSSI, find the best position. If you start calibration first time, you can’t use a radio_list, because MHz data would be far off or completely wrong. Use another radio with digital scale or you have to listen content and compare it to RSts broadcasting schedule.

Enter “y” and send. Follow the instructions on the screen. At the end, arduino would store coefficients in the EEPROM, print them out for your review and say “Finished”. Print a list again, and check if the data in front of MHz is correct.

CLI:

  1. “x” – print current settings, fine tunning / calibration / debug;
  2. “Dr” – read settings from the previously stored radio list. (D is digits 0 <-> 19).
  3. “Dw” – write settings to the radio list. (D is digits 0 <-> 19).
  4. “u” – change radio to next “UP” in the list;
  5. “d” – change radio to next “DOWN” in the list;
  6. “s” – full “SCAN” of the air, and save to EEPROM;
  7. “l” – print out a stations list from the memory;
  8. “v” – push tuner “up” on 1 step, debug (“ux”);
  9. “n” – push tuner “down” on 1 step, debug (“nx”);
  10. “c” and “y” – calibration of the VCO, voltage settings – frequency conversion.

Listing of commands isn’t final, and  may have some variation.

Sketch:  FM_Radio_calibration.


[original story: coolarduino]