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.