Arduino FM receiver with TEA5767

Old wish to make digitally controlled FM tuner come true when I found on Ebay cheap module with TEA5767 (Low-power FM stereo radio for handheld applications).

This module size is only 11.2mm x 11mm. TEA 5767 supports I2C.  Pinout and wiring:

For antenna i have used just 75 cm long wire, because that is 1/4 of wavelength at 100 MHz. TEA5767 doesn’t have audio amplifier, sound output level is very low, headphone can not be connected directly. During testing i had connected audio output to PC audio system.

Writing to TEA5767

Radio chip is controlled by writing 5 bytes one by one. During writing TEA5767  IC2 address is 0x60, reading – 0x61. Arduino IDE supports only 7 bit address ant last bite is assigned automatically,  so  in code 0x60 is used as address.

   frequency=87.5; //starting frequency

   frequencyB=4*(frequency*1000000+225000)/32768; //calculating PLL word

   frequencyH=frequencyB>>8;

  frequencyL=frequencyB&0XFF;

  delay(100);

  Wire.beginTransmission(0x60);   //writing TEA5767

  Wire.send(frequencyH);
  Wire.send(frequencyL);
  Wire.send(0xB0);
  Wire.send(0x10);
  Wire.send(0x00);
  Wire.endTransmission();

This is code responsible for starting frequency of FM receiver.

Frequency is controlled by 14 bit word, that is written to two 8 bit registers.

Example of word calculating is given in datasheet.

3 byte (0xB0): high side LO injection is on,.

4 byte (0x10) : Xtal is 32.768 kHz

5 byte (0x00)

Reading TEA5767

Reading is performed every time in the loop

  Wire.requestFrom(0x60,5); //reading TEA5767

  if (Wire.available()) 

  {
    for (int i=0; i<5; i++) {

      buffer[i]= Wire.receive();
    }

    freq_available=(((buffer[0]&0x3F)<<8)+buffer[1])*32768/4-225000;

    lcd.print("FM ");

    lcd.print((freq_available/1000000));

    frequencyH=((buffer[0]&0x3F));

    frequencyL=buffer[1];

    if (search_mode) {

      if(buffer[0]&0x80) search_mode=0;

    }

    if (search_mode==1) lcd.print(" SCAN");
    else {
      lcd.print("       ");
    }

    lcd.setCursor(0, 1);

    lcd.print("Level: ");
    lcd.print((buffer[3]>>4));
    lcd.print("/16 ");

    if (buffer[2]&0x80) lcd.print("STEREO   ");
    else lcd.print("MONO   ");

  }

1 byte has information about search status and part of frequency word

2 byte has remaining information about frequency

3 byte – Stereo indicator

4 byte – Signal level

5 byte – reserved

Search

Search mode is on by pressing up or down buttons longer. TEA 5767 stars scanning up or down with 100kHz steps.  Search stop level can be selected.

1 byte (Search mode on/off)

2 byte (Search up/down , stop level)

If station if found ready flag is on (1 byte).

Demo

All code

/// Arduino FM receiver with TEA5767 https://www.electronicsblog.net/
#include <Wire.h>
#include <LiquidCrystal.h>

unsigned char search_mode=0;

int b=0;
int c=0;

#define Button_next 30
#define Button_prev 31

unsigned char frequencyH=0;
unsigned char frequencyL=0;

unsigned int frequencyB;
double frequency=0;

double freq_available=0; 

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); 

void setup()   { 

  Wire.begin();
  lcd.begin(16, 2);

  /// buttons  

  pinMode(Button_next, INPUT);
  digitalWrite(Button_next, HIGH); //pull up resistor

  pinMode(Button_prev, INPUT);
  digitalWrite(Button_prev, HIGH); //pull up resistor

  frequency=87.5; //starting frequency

  frequencyB=4*(frequency*1000000+225000)/32768; //calculating PLL word

  frequencyH=frequencyB>>8;

  frequencyL=frequencyB&0XFF;

  delay(100);

  Wire.beginTransmission(0x60);   //writing TEA5767

  Wire.send(frequencyH);
  Wire.send(frequencyL);
  Wire.send(0xB0);
  Wire.send(0x10);
  Wire.send(0x00);
  Wire.endTransmission();

  delay(100);

}

void loop()
{

  unsigned char buffer[5];

  lcd.setCursor(0, 0);

  Wire.requestFrom(0x60,5); //reading TEA5767

  if (Wire.available()) 

  {
    for (int i=0; i<5; i++) {

      buffer[i]= Wire.receive();
    }

    freq_available=(((buffer[0]&0x3F)<<8)+buffer[1])*32768/4-225000;

    lcd.print("FM ");

    lcd.print((freq_available/1000000));

    frequencyH=((buffer[0]&0x3F));

    frequencyL=buffer[1];

    if (search_mode) {

      if(buffer[0]&0x80) search_mode=0;

    }

    if (search_mode==1) lcd.print(" SCAN");
    else {
      lcd.print("       ");
    }

    lcd.setCursor(0, 1);

    lcd.print("Level: ");
    lcd.print((buffer[3]>>4));
    lcd.print("/16 ");

    if (buffer[2]&0x80) lcd.print("STEREO   ");
    else lcd.print("MONO   ");

  }

  ///// buttons read

  //////////// button_next////////// 
  if (!digitalRead(Button_next)&&!b) {

    frequency=(freq_available/1000000)+0.05;

    frequencyB=4*(frequency*1000000+225000)/32768+1;

    frequencyH=frequencyB>>8;
    frequencyL=frequencyB&0XFF;   

    Wire.beginTransmission(0x60);   

    Wire.send(frequencyH);
    Wire.send(frequencyL);
    Wire.send(0xB0);
    Wire.send(0x1F);
    Wire.send(0x00); 

    Wire.endTransmission(); 

    //////////////////////

    b=100;

  };

  if (!digitalRead(Button_next)&&b==1) {

    ///scannnn UP

    search_mode=1;

    Wire.beginTransmission(0x60);   

    Wire.send(frequencyH+0x40);
    Wire.send(frequencyL);
    Wire.send(0xD0);
    Wire.send(0x1F);
    Wire.send(0x00); 

    Wire.endTransmission();

    /////////////////

    b=100;

  };    

  if (!b==0) b--;

  //////////// button_prev////////// 
  if (!digitalRead(Button_prev)&&!c) {

    frequency=(freq_available/1000000)-0.05;

    frequencyB=4*(frequency*1000000+225000)/32768+1;

    frequencyH=frequencyB>>8;
    frequencyL=frequencyB&0XFF;

    Wire.beginTransmission(0x60);   

    Wire.send(frequencyH);
    Wire.send(frequencyL);
    Wire.send(0xB0);
    Wire.send(0x1F);
    Wire.send(0x00); 

    Wire.endTransmission(); 

    c=100;

  };

  if (!digitalRead(Button_prev)&&c==1) {

    ///scannnn DOWN

    search_mode=1;

    Wire.beginTransmission(0x60);   

    Wire.send(frequencyH+0x40);
    Wire.send(frequencyL); 

    Wire.send(0x50);
    Wire.send(0x1F);
    Wire.send(0x00);
    Wire.endTransmission();   

    c=100;

  };          

  if (!c==0) c--;

  ////////////////////

}

Code have some problem with frequency control/indication. Button press doesn’t always adds/subtracts exactly 0,05 MHz. Also later i found in application note, that after search frequency word must be rounded and sent back to tuner, because with 32768 Hz Xtal search step is not 100 kHz, but 98.304 kHz.