Schematic Diagram for popular VS1053 breakout board

Sorry for not many updates as i have been busy on few additional projects. Expect a few additional posts soon based on the vs1053 chipset. One reader wanted to schematic of the popular vs1053 board available on ebay shown below.

Vs1053_icdev

Here is the schematic diagram.  It is in most aspects similar to the one described in the vs1053 datasheet except that the headphone outputs have decoupling capacitors instead of a common GBUF as in the datasheet.

Click here to download:
AU1053P_sch.pdf (211 KB)
(download)

DIY USB or Solar powered Lithium ion or Lithium polymer battery charger

Lithium ion and Lithium Polymer batteries are very popular because of the very high energy storage capacity to weight ratio. Charging the batteries is very technically demanding as they must be precisely charged to 4.2 Volts other wise they can explode or catch fire.  In this article I will show a circuit based on the CN3083 from consonance electrical company which you can easily build to make your own charger which will charge a single cell lithium ion battery. These are the batteries found on most mobile phones and also sold in shops. The CN3083 is a very good charging IC and is also far cheaper than most battery charger IC's. It is easily available on ebay.

The source for power for charging the battery could be a USB port or a solar panel. This IC is ideally suited for charging from a solar panel as it can automatically adjust it's current draw to prevent drawing too much current from the solar panel and causing too much voltage drop and charge interruption when there is low power from the solar panel.

Here is the  schematic,

Noname

 

The IC is a SOP8, ie small outline package which is small but it is easily soldered if you have good soldering skills.

 

The circuits charging current is set to 1800 / R1 , which in our circuit comes to 1800/3.6 = 450 ma. A usb port supports a maximum of 500 ma so we are within spec with such values. It doesnt matter if your solar panel cant supply 450 ma, if so the CN3083 will automatically reduce the current so as to prevent overloading the solar panel.

 

Please note that although the attached datasheet is for CN3063, it is identical to CN3083's datasheet.

Click here to download:
DSE-CN3063.pdf (278 KB)
(download)

 

I will be adding the necessary schematic files shortly.

 

DIY MP3/OGG/FLAC music player using Arduino and VS1053

In this article I will illustrate the building of your own DIY music player. The music player described can play almost any audio file including MP3's, OGG, AAC, FLAC, WMA. The sound quality is actually better than a ipod. Hopefully this article will provide a starting point for you to build your own player.

This humble arduino AVR based music player has no problem playing even 320kbp's MP3's or even 500kbp's OGG's, it can amazingly play FLAC files as well! This is no mean feat because FLAC decoding is at 1000Kbps !!!

The circuit will incorporate a Arduino as the micro controller, which will be reading files from a SD card and sending it to a music decoder which is the VS1053 chip which incorporates a mp3 decoder and earphone driver as well.

The VS1053 is very small and difficult to solder so it is advisable to get a breakout board, I used one avaiable on ebay built by ic-dev.com.cn which is also available at MDFly as well. It's a excellent breakout board featuring a microphone and line input as well should you want to use the VS1053 as a digital recorder as well. However we are going to use VS1053 in it's decoding mode, we will read the data from the SD card and feed it to the VS1053 which will give us music :) The breakout board can be seen below.

Vs1053_icdev

 

SD cards talk to the Arduino through a interface called SPI. It incorporates 3 lines, CLK – for the clock, MISO (Master In Slave Out) – The slave sends data through this and the master receives it, and MOSI (Master Out Slave In) – The master sends data to the slave through this line.

There is a additional line called CS (Chip select), if this line is LOW that means that that chip is active. This is important as the 3 lines mentioned (CLK, MISO, MOSI) are shared between the VS1053 and the SD card. If the Arduino wants to send data to the VS1053 chip it will pull the VS1053's CS (Chip select) line low thus activating it and at the same time it will make the SD card's CS line high thus disabling it. Now when the Arduino sends data through the SPI bus it will be going to the VS1053 and not the SD. Should the Arduino want to talk to the SD, it will reverse the process disabling the VS1053 and activating the SD. Remember when it comes to chip selects in SPI, they are active LOW, that means that they are active only if they are low.

The other issue is that VS1053 runs at 3.3v and the Arduino is at 5v, this we need to reduce the voltage fed to the VS through the SPI digital interface otherwise the chip can get damaged. So we will use a resistor based voltage divider. The MISO (master in slave out) can be directly connected as only the SD card or the VS1053 running at 3.3v will be activating this, the Arduino is just listening so this line will only see 3.3v maximum.

Here is a example of using resistor based shiftors to reduce the voltage i found on the web.......

Schematicuo7

 

Here is the schematic of our circuit...Both the SD card and the arduino share the same SPI bus.

Vsmp3_schem

 

Regarding the SD card one can either by a SD card breakout board, or use a microSD card, and directly solder the wires to the microSD card's adapter.  Every microSD usually comes with a SD adapter which you can insert the microSD card.

Adapter

I directly soldered the wires to the microSD adapter. Here is the pin descriptions for the microSD adapter...

Sd_pinout

It is strongly advised to put a 0.1uf capacitor between pin's 3 and 4 of the SD card to improve stability, and please use short wires (3 to 4 inches maximum) between the SD card/Arduino and VS1053 SPI interface as long wires cause stray capacitance leading to instability.

What we do is to open a file on the SD card, read data from it (32 bytes) to a buffer, wait till VS1053's DREQ pin (Data request) is high which indicates it needs to receive data then we will write the data to the VS. The VS has two inputs CS (which when is low indicates that the VS is active) , and DS(Data select) which are mutually exclusive. When DS is low any data send to the SPI bus is read by the VS as data to be decoded, whereas is CS is active that means VS will read that as command data, ie to increase volume etc, once we send data we will deselect the VS, read more data from the SD etc etc....

The source code is available below, let me illustrate some key functions.

For the arduino there are certain pins already marked for  SPI which are

 MOSI - pin 11

MISO - pin 12

CLK - pin 13

CS - pin 10

 

So here are the additional definitions,

 

 



/* Control Chip Select Pin (for accessing SPI Control/Status registers)


*/   #define MP3_XCS 2


This means that  pin 2 of the arduino will be used to select the VS's command mode, if this pin is LOW then VS's command interface is selected.



/* Data Chip Select / BSYNC Pin */


#define MP3_XDCS 9


This means that pin 9 of the arduino will be used to select the VS's data mode, if this pin is LOW then VS's data interface is selected.



/* VS1053's Data select pin for sending data  is  Pin10 */


#define SD_CS 10   

The SD card's chip select is pin 10, when this is LOW then data will be sent/read from the SD card instead of the VS. This means of course that when SD_CS is active LOW, then the other pins MP3_XDCS and MP3_XCS must be disabled (ie HIGH).



/ * Data Request Pin: Player asks for more data */


#define MP3_DREQ 8   

This pin can be directly connected from the VS's DREQ to the arduino's pin 8. When VS needs more data to decode it will pull this pin HIGH.

Now for a few basic functions,



void  vs1053_select_control ()


{


fastDigitalWrite (MP3_XCS, LOW);


}   

When we want to select the VS control mode we call this function which will pull the XCS line low thus activating the VS's command mode.

 



/** Pull the VS10xx Control Chip Select line High */


void  vs1053_deselect_control ()


{


fastDigitalWrite (MP3_XCS, HIGH);


}


This will deselect the VS's command mode, by making the pin LOW.

Similar functions are there for the VS's data interface which deal with pin MP3_XDCS, and the SD card's chip select, pin SD_CS.

Now we will initialize the SPI bus,



void init_spi ()


{


SPI.setBitOrder (MSBFIRST);


SPI.setDataMode (SPI_MODE0);


SPI.setClockDivider (SPI_CLOCK_DIV16);      // slow SPI bus speed


SPI.transfer (0xFF);


}   

The Arduino runs at 16Mhz, while the maximum SPI speed is 8mhz, it is important to use a low SPI bus speed to write to the VS command interface. The manual recommends VS_CLOCK/7 at the start. Since the VS starts off with 12mhz clock, we need to reduce the Arduino's SPI rate to below 12/7= 1.5mhz. SPI_CLOCK_DIV16 means Arduino's default clock speed of 16 divided by 16, which is 1 mhz which is quite ok. After initializing the VS we can increase the VS's clock speed to around 55mhz.

Now we will get to a few basic VS functions. To command the VS we need to write/read from/to a 16 bit register and we will get a return value of 16bits.

 

unsigned int vs1053_read_register (unsigned char addressbyte)

The next function,

 

void vs1053_write_register (unsigned char addressbyte, unsigned char highbyte, unsigned char lowbyte)

 

will write a 16bit value to the VS1053 register adreesbyte. For example if you want to write 0xff32 to SPI_CLOCKF, you would invoke this function as,

vs1053_write_register(SPI_CLOCKF, 0xff, 0x32);

 

The most important function is the initialization of the VS1053 which has to be done precisely....



/** Soft Reset of VS10xx with patching the code */


void vs1053_softreset_and_patch ()


{


unsigned int sci_status = 0;


init_spi ();// slow speed


//software reset the VS


vs1053_write_register (SPI_MODE, 0x08, 0x04);


delay (1);


while (!fastDigitalRead (MP3_DREQ)); 


// upload patch   vs1053_load_patch ();


vs1053_write_register (SPI_CLOCKF, 0xc0, 0x00);  // VS1053


delay (1);


vs1053_write_register (SPI_MODE, B00001000, B10000000);


vs1053_set_volume (0x00, 0x00);


// ram up the SPI speed now that the VS internal clock has been set


SPI.setClockDivider (SPI_DEFAULT_SPEED);


}   

 

Let's analyze the function and see what it does.....



void vs1053_softreset_and_patch ()


{


unsigned int sci_status = 0;


init_spi ();   // slow speed   

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Set slow SPI speed for initialisation


Next software reset the VS by setting the necessary flags in the SPI_MODE register and we wait for the DREQ to become LOW to signify that the VS is now reset

 

// software reset the VS

 

vs1053_write_register (SPI_MODE, 0x08, 0x04);

delay (1);

while (!fastDigitalRead (MP3_DREQ));

 

// /upload patch

 

vs1053_load_patch ();

^^^^^^^^^^^^^^^^^^^^^^^

We upload any patch if needed, the current patch enables FLAC playback

 

vs1053_write_register (SPI_CLOCKF, 0xc0, 0x00); // VS1053

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This Set's the VS1053's clock speed to 4.5 x crystal speed, which is 4.5 * 12.28 Mhz

 

delay (1);

vs1053_write_register (SPI_MODE, B00001000, B10000000);

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This will set the SPI_MODE bits to SM_SDISNEW which is the default mode and also the low byte “ B10000000” enables spatial processing to give a better surround sound feel for headphones, if this is not desired just pass 0x00 as the low byte.

 

 

vs1053_set_volume (0x00, 0x00);

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Set's the volume to maximum

 

// ram up the SPI speed now that the VS internal clock has been set

SPI.setClockDivider (SPI_DEFAULT_SPEED);

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Now that we have the VS initialised and its clock set to high speed we can increase the Arduino's bus speed to the maximum which is 8Mhz. SPI_DEFAULT_SPEED is defined as SPI_CLOCK_DIV2, which is ½ of the arduino's clock speed, ie 16/2 = 8Mhz.

The next function will illustrate what we have to do to send data to the VS1053



void transfer_32bytes (unsigned char *buf)


{


deselect_sd ();


vs1053_deselect_control ();


vs1053_select_data ();


while (!fastDigitalRead (MP3_DREQ));    


for (unsigned char i = 0; i < 32; i++)    


{    


   SPI.transfer (buf[i]);     //Send a single byte 32 times


    }


vs1053_deselect_data ();


}   

 

Let's go through the steps.........

 

// Transfers 32 bytes of data

 

void

transfer_32bytes (unsigned char *buf)

{

 

deselect_sd ();

^^^^^^^^^^^^^^^^^^^^^^

This will deselect the SD card from the SPI bus

 

 

vs1053_deselect_control ();

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Since we want to send DATA we must disable the VS1053 control interface

 

vs1053_select_data ();

^^^^^^^^^^^^^^^^^^^

Enable the VS1053 Data select to tell the VS that we are sending data

 

 

while (!fastDigitalRead (MP3_DREQ));

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Wait till the VS1053 makes the DREQ line low, which tells us that it needs to be sent data.....

 

for (unsigned char i = 0; i < 32; i++)

{

SPI.transfer (buf[i]); // Send out a single byte 32 times

}

 

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Send the 32bits of data in the buffer via SPI to the VS

 

 

vs1053_deselect_data ();

^^^^^^^^^^^^^^^^^^^^^^^^^

Now that we have sent data, deselect the VS's Data interface.

}

The above example's illustrate the critical functions and how to send data and control commands to the VS. The other functions in the code are documented and should be able to be easily understood. They contain additional VS1053 routines such as volume setting etc, and a routine to play a file, and terminate a file. They are based on the VS1053 reference for playing and stopping a file.

The resulting Music player is quite impressive, the Arduino's atmega328p at only 16mhz together with the VS1053 decoder has no problems playing mp3's at 320kbp's it can even play FLAC files at 1000kbp's but that is really pushing it. The enclosed sketch will play all the files in the SD cards root folder. Here is the prototype...

Test

I hope this article will stimulate people to build there own mp3 players. This player only support's forward and next track with two buttons. Additional functionality like increasing and decreasing the volume can be easily implemented with the vs1053_set_volume functions and appropriate software code.

The source code is here..It's in a "doc" file, just open it and select-all and copy and paste the text to a arduino sketch...please note that this incorporates a optional 12kb patch to enable FLAC playback, if you are not interested you can just remove it, and not call the patching function at the start of the VS initiatlisation code. I actually incorporated to patch for testing purposes to verify that the arduino can actually handle high bitarates that FLAC requires. I plan to  be writing a function to load this patch from a file stored on the SD instead which will free a lot of the AVR's memory.

 

Click here to download:
test.doc (239 KB)
(download)

SD Card's and Arduino

There are several issues with interfacing SD card's with the Arduino. One issue is the need to shift the voltage as the Arduino operates at 5 volts and the SD card operates at 3.3v, so you need to reduce the voltage at the logic pins otherwise the SD card can get damaged. There are two ways, one is to use resistors as voltage dividers to reduce the voltage to the SD card. Here is a example I found on the web.

Schematicuo7

 

I use the same combination of 1.8k and 3.3k resistors and have had no problems whatsoever. The circuit is rock solid at high speeds such as 8mhz SPI bus speed.

The other is to use another IC between the SD and arduino to handle the voltage conversion. Obviously the resistor based method is cheaper and easier and is the one i recommend. Some people are worried about being unable to run at high speeds with the resistor based schematic but i have never come across this issue.

Another problem people run into is that the SD card is unstable with funny error messages. Suggested methods of resolving this errors are to keep the wiring as short as possible, say 3 to 4 inches maximum as long wires can act as stray capacitors. Also make sure that you have power supply decoupling capacitors across pins 3 and 4 as recommended.  I have found that a 0.1 uf capacitor together with a 100uf capacitor across pins 3 and 4 of the SD card (as close to the SD card's pins as possible) yielded the best results and improved stability a lot.

Rather than use SD breakout boards, i suggest direct soldering of the connections a micro SD adapter and then you can insert the micro SD card into that adapater when you need to use it. That way you dont need to worry about SD card adapters/breakout boards etc.......

These suggestions should  help you to improve the stability and have fun using SD cards as well.

Here is some useful information about SD card interfacing  such as power supply decoupling etc......

 

Click here to download:
SD-MMC_Integration.pdf (129 KB)
(download)

 

 

 

 

 

 

Arduino with TEA5767 single chip digital radio and Nokia 3310 lcd display

The TEA5767 is a single chip FM stereo digital radio which has a i2c interface. Cheaply priced breakout boards are available on ebay.

Tea5767pic

The modules connectors are as in the following diagram,

Module_pinout

I got such a TEA5767 breakout board and wanted to interface it with the Arduino and a nokia 3310 LCD.
The nokia 3310 LCD is a cheap easily available  LCD display whose 8 pins in the back connector are not difficult to solder.

The Schematic  diagram for my circuit is as follows.

Schem_final

As the Nokia 3310 LCD is a 3.3v device, voltage dividers using resistors were used to reduce the 5v logic outputs of the arduino to the 3.3v required for the LCD.


The TEA5767's pin 10 is connected to the antenna which in my case was a 3 foot wire :) It's outputs are not adequate to drive a headphone and need to be connected to a amplifier, i connected them to a 3.5mm jack which was connected to the computers speakers amplifier jack input.
The TEA5767 can be controlled via the i2c interface with frequency setting and auto searching being supported. The IC's data-sheet contains extensive information about various options available. I used the references extensively to write the driver for the TEA5767, it can set the frequency and auto search up or down. The Wire library makes interfacing i2c devices to the Arduino very easy.

The TEA5767 has 5 8-bit registers which can be read and written to with the wire library. The chips  is at 0x60. The source code has comments indicating the write and read mode register values.

The TEA5767/68 can be operated in two modes: High or Low side injection selectable with HILO bit (bit 4 of data byte 3) via the bus interface. Usually the setting can be anything, however in certain situation's a  strong neighouring station can interfere with a weak station. To prevent this one must select the optimal high or low injection mode by the following example.

Assume the frequency to be tuned is Fwanted, then

Set HILO to “1”.
Tune to Fwanted + 450 kHz: measure signal level LevelHigh
Tune to Fwanted – 450 kHz: measure signal level LevelLow
If LevelHigh < LevelLow then HILO is “1” else HILO is “0”.

Once either High or Low injection is selected as optimal using the above equation, then its time to set the frequency by calculating a PLL word, the method defers according to either high or low side injection mode.

Setting the frequency involves calculating a PLL word according to the following formula for High side injection

Pllword

for low side injection its a similar euqation, but the IF frequency of 225 is subtracted instead.

Autonomous searching is with the SEARCH and MUTE flags being set and the frequency being incremented by one grid step which is  32768khz * 3 in our case. It is important to note that as the IC is internally locked to 100khz grid steps that in our case with the 32768khz crystal, after the search is completed and we have found a new station,  we have to read the PLL word round it up and write it back to the TEA5767, as per the guidlines  "In that case, when performing a search and a station is found, the PLL word of the programmable divider will be read. The value of this word will be rounded and sent back to the tuner."

The PCD8544 library <http://code.google.com/p/pcd8544/ was used to interface with the nokia3310 easily. 
The new Chinese clones of the Nokia 3310 cause a weird display error causing garbage at the bottom of the screen because they are using the pcd8544 compatible Sitronics chip ST7576 which is actually a 66x102 LCD display driver.

Lcd

The solution is to adjust the temperature coefficient by the command
    this->send(PCD8544_CMD, 0x05); 
which will correct the offset.
As the default Vop causes too faint a display it has to be set to a high value like 0xE0 as follows.
    this->send(PCD8544_CMD, 0xE0); 

The Bounce library  http://www.arduino.cc/playground/Code/Bounce was used to bounce the two push buttons which are configure to search up or down.

Here is the completed Adruino circuit......

Ardu

 

Here is a screenshot of the LCD,

Nokia1

The Arduino sketch with comments is here

/*-----------------------------------------------------------------------------
 Source file for interfacing the Arduino microcontroller with TEA5767 single
 chip FM radio and a Nokia 3310 LCD display

             Written By -  Kalum <kalum_slk@gmx.com>

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


-----------------------------------------------------------------------------*/
#include <Bounce.h>

#include <stdio.h>
#include <avr/pgmspace.h>
#include <Wire.h>
#include <EEPROM.h>

#include <PCD8544.h>

/*****************************************************************************/

/******************************
 * Write mode register values *
 ******************************/

/* First register */
#define TEA5767_MUTE        0x80    /* Mutes output */
#define TEA5767_SEARCH        0x40    /* Activates station search */
/* Bits 0-5 for divider MSB */

/* Second register */
/* Bits 0-7 for divider LSB */

/* Third register */

/* Station search from botton to up */
#define TEA5767_SEARCH_UP    0x80

/* Searches with ADC output = 10 */
#define TEA5767_SRCH_HIGH_LVL    0x60

/* Searches with ADC output = 10 */
#define TEA5767_SRCH_MID_LVL    0x40

/* Searches with ADC output = 5 */
#define TEA5767_SRCH_LOW_LVL    0x20

/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */
#define TEA5767_HIGH_LO_INJECT    0x10

/* Disable stereo */
#define TEA5767_MONO        0x08

/* Disable right channel and turns to mono */
#define TEA5767_MUTE_RIGHT    0x04

/* Disable left channel and turns to mono */
#define TEA5767_MUTE_LEFT    0x02

#define TEA5767_PORT1_HIGH    0x01

/* Fourth register */
#define TEA5767_PORT2_HIGH    0x80
/* Chips stops working. Only I2C bus remains on */
#define TEA5767_STDBY        0x40

/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */
#define TEA5767_JAPAN_BAND    0x20

/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */
#define TEA5767_XTAL_32768    0x10

/* Cuts weak signals */
#define TEA5767_SOFT_MUTE    0x08

/* Activates high cut control */
#define TEA5767_HIGH_CUT_CTRL    0x04

/* Activates stereo noise control */
#define TEA5767_ST_NOISE_CTL    0x02

/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */
#define TEA5767_SRCH_IND    0x01

/* Fifth register */

/* By activating, it will use Xtal at 13 MHz as reference for divider */
#define TEA5767_PLLREF_ENABLE    0x80

/* By activating, deemphasis=50, or else, deemphasis of 50us */
#define TEA5767_DEEMPH_75    0X40

/*****************************
 * Read mode register values *
 *****************************/

/* First register */
#define TEA5767_READY_FLAG_MASK    0x80
#define TEA5767_BAND_LIMIT_MASK    0X40
/* Bits 0-5 for divider MSB after search or preset */

/* Second register */
/* Bits 0-7 for divider LSB after search or preset */

/* Third register */
#define TEA5767_STEREO_MASK    0x80
#define TEA5767_IF_CNTR_MASK    0x7f

/* Fourth register */
#define TEA5767_ADC_LEVEL_MASK    0xf0

/* should be 0 */
#define TEA5767_CHIP_ID_MASK    0x0f

/* Fifth register */
/* Reserved for future extensions */
#define TEA5767_RESERVED_MASK    0xff

/*****************************************************************************/

#define SEARCH_UP 1
#define SEARCH_DOWN 2

struct tea5767_ctrl
{
  unsigned int port1:1;
  unsigned int port2:1;
  unsigned int high_cut:1;
  unsigned int st_noise:1;
  unsigned int soft_mute:1;
  unsigned int japan_band:1;
  unsigned int deemph_75:1;
  unsigned int pllref:1;
  unsigned int xtal_freq;
};


tea5767_ctrl ctrl_data;
static PCD8544 lcd;
static int HILO = 1;        //variable holds the HI or LO injection state for the current freq


//calculate the optimial hi or lo injection mode for the freq is in hz
//return 1 if high is the best, or 0 for low injection
int
hilo_optimal (unsigned long freq)
{
  int signal_high = 0;
  int signal_low = 0;
  unsigned char buf[5];

  set_radio_freq (1, (double) (freq + 450000) / 1000000);
  delay (30);
  //Read the signal level
  if (tea5767_read_status (buf) == 1)
    {
      signal_high = tea5767_signal_level (buf);


    }


  set_radio_freq (0, (double) (freq - 450000) / 1000000);
  delay (30);

  if (tea5767_read_status (buf) == 1)
    {
      signal_low = tea5767_signal_level (buf);

    }

  //Serial.println("low level");
  //Serial.println(signal_low);
  //Serial.println("high level");
  //Serial.println(signal_high);

  if (signal_high < signal_low)
    return 1;
  else
    return 0;


}

void
set_radio_freq (int hilo, double freq)
{
  unsigned char buffer[5];
  unsigned div;
  int rc;

  Serial.println (freq, DEC);

  memset (buffer, 0, 5);

  buffer[2] = 0;

  //if (priv->ctrl.port1)
  buffer[2] |= TEA5767_PORT1_HIGH;

  if (hilo == 1)
    buffer[2] |= TEA5767_HIGH_LO_INJECT;

  //      buffer[2] |= TEA5767_MONO;

  buffer[3] = 0;

  if (ctrl_data.port2)
    buffer[3] |= TEA5767_PORT2_HIGH;

  if (ctrl_data.high_cut)
    buffer[3] |= TEA5767_HIGH_CUT_CTRL;

  if (ctrl_data.st_noise)
    buffer[3] |= TEA5767_ST_NOISE_CTL;

  if (ctrl_data.soft_mute)
    buffer[3] |= TEA5767_SOFT_MUTE;

  if (ctrl_data.japan_band)
    buffer[3] |= TEA5767_JAPAN_BAND;

  buffer[3] |= TEA5767_XTAL_32768;

  buffer[4] = 0;

  if (ctrl_data.deemph_75)
    buffer[4] |= TEA5767_DEEMPH_75;

  if (ctrl_data.pllref)
    buffer[4] |= TEA5767_PLLREF_ENABLE;

  if (hilo == 1)
    div = (4 * (freq * 1000 + 225)) / 32.768;
  else
    div = (4 * (freq * 1000 - 225)) / 32.768;

  buffer[0] = (div >> 8);    // & 0x3f;
  buffer[1] = div & 0xff;

  Wire.beginTransmission (0x60);

  for (int i = 0; i < 5; i++)
    Wire.send (buffer[i]);

  Wire.endTransmission ();

}

/* Freq should be specifyed at X M hz */
void
set_radio_freq (double freq)
{
  HILO = hilo_optimal ((unsigned long) (freq * 1000000));

  set_radio_freq (HILO, freq);

}

//read functions

int
tea5767_read_status (unsigned char *buf)
{

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

  if (Wire.available ())
    {
      for (int i = 0; i < 5; i++)
    {

      buf[i] = Wire.receive ();
    }
      return 1;
    }
  else
    return 0;

}

int
tea5767_signal_level (unsigned char *buf)
{
  int signal = ((buf[3] & TEA5767_ADC_LEVEL_MASK) >> 4);
  return signal;
}

int
tea5767_stereo (unsigned char *buf)
{
  int stereo = (buf[2] & TEA5767_STEREO_MASK);
  return stereo ? 1 : 0;
}

//returns 1 if tuning completed or BL reached
int
tea5767_ready (unsigned char *buf)
{
  if (buf[0] & 0x80)
    return 1;
  else
    return 0;
}

//returns 1 if band limit is reached during searching
int
tea5767_bl_reached (unsigned char *buf)
{
  if (buf[0] & 0x40)
    return 1;
  else
    return 0;
}

//returns freq available in Hz

double
tea5767_frequency_available (unsigned char *buf)
{
  double freq_available;

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

  return freq_available;
}

void
tea5767_search_up (unsigned char *buf)
{
  unsigned div;
  double freq_av;

  freq_av = tea5767_frequency_available (buf);

  div = (4 * (((freq_av + 98304) / 1000000) * 1000000 + 225000)) / 32768;

  buf[0] = (div >> 8);        // & 0x3f;
  buf[1] = div & 0xff;

  buf[0] |= TEA5767_SEARCH;

  buf[2] = 0;

  buf[2] |= TEA5767_SEARCH_UP;
  buf[2] |= TEA5767_SRCH_MID_LVL;
  buf[2] |= TEA5767_HIGH_LO_INJECT;

  //buf[3] = 0x18;
  buf[3] = 0;

  if (ctrl_data.port2)
    buf[3] |= TEA5767_PORT2_HIGH;

  if (ctrl_data.high_cut)
    buf[3] |= TEA5767_HIGH_CUT_CTRL;

  if (ctrl_data.st_noise)
    buf[3] |= TEA5767_ST_NOISE_CTL;

  if (ctrl_data.soft_mute)
    buf[3] |= TEA5767_SOFT_MUTE;

  if (ctrl_data.japan_band)
    buf[3] |= TEA5767_JAPAN_BAND;

  buf[3] |= TEA5767_XTAL_32768;

  buf[4] = 0;

  if (ctrl_data.deemph_75)
    buf[4] |= TEA5767_DEEMPH_75;

  if (ctrl_data.pllref)
    buf[4] |= TEA5767_PLLREF_ENABLE;

  Wire.beginTransmission (0x60);

  for (int i = 0; i < 5; i++)
    Wire.send (buf[i]);

  Wire.endTransmission ();
  HILO = 1;

}

void
tea5767_search_down (unsigned char *buf)
{
  unsigned div;
  double freq_av;

  freq_av = tea5767_frequency_available (buf);

  div = (4 * (((freq_av - 98304) / 1000000) * 1000000 + 225000)) / 32768;

  buf[0] = (div >> 8);        // & 0x3f;
  buf[1] = div & 0xff;

  buf[0] |= TEA5767_SEARCH;

  buf[2] = 0;

//  buf[2]|= TEA5767_SEARCH_UP;
  buf[2] |= TEA5767_SRCH_MID_LVL;
  buf[2] |= TEA5767_HIGH_LO_INJECT;

  buf[3] = 0;

  if (ctrl_data.port2)
    buf[3] |= TEA5767_PORT2_HIGH;

  if (ctrl_data.high_cut)
    buf[3] |= TEA5767_HIGH_CUT_CTRL;

  if (ctrl_data.st_noise)
    buf[3] |= TEA5767_ST_NOISE_CTL;

  if (ctrl_data.soft_mute)
    buf[3] |= TEA5767_SOFT_MUTE;

  if (ctrl_data.japan_band)
    buf[3] |= TEA5767_JAPAN_BAND;

  buf[3] |= TEA5767_XTAL_32768;

  buf[4] = 0;

  if (ctrl_data.deemph_75)
    buf[4] |= TEA5767_DEEMPH_75;

  if (ctrl_data.pllref)
    buf[4] |= TEA5767_PLLREF_ENABLE;

  Wire.beginTransmission (0x60);

  for (int i = 0; i < 5; i++)
    Wire.send (buf[i]);

  Wire.endTransmission ();

  HILO = 1;
}

//Returns 1 if search is finished, 0 if wrapped and new search initiated
//TODO - To prevent endless looping add a static variable to abort if it has searched for more than 2 loops
//
int
tea5767_process_search (unsigned char *buf, int search_dir)
{
  if (tea5767_ready (buf) == 1)
    {
      if (tea5767_bl_reached (buf) == 1)
    {
      Serial.println ("Bandlimit reached");
      if (search_dir == SEARCH_UP)
        {
          //wrap down
          set_radio_freq (87.5);
          tea5767_read_status (buf);
          tea5767_search_up (buf);

          return 0;

        }
      else if (search_dir == SEARCH_DOWN)
        {
          //wrap up
          set_radio_freq (108);
          tea5767_read_status (buf);
          tea5767_search_down (buf);

          return 0;

        }
    }
      else
    {
      // search finished - round up the pll word and feed it back as recommended

      double rounded_freq;
      double freq_available = tea5767_frequency_available (buf);

      Serial.println (freq_available);
      rounded_freq = floor (freq_available / 100000 + .5) / 10;
      Serial.println ("rounded_freq");
      Serial.println (rounded_freq);
      Serial.println ("Search finished");
      set_radio_freq (rounded_freq);
      return 1;

    }

    }

}

//lcd display routine

void
lcd_display_status (double freq, int stereo, int level, int search)
{
  lcd.setCursor (0, 0);
  lcd.print (freq);
  lcd.print (" Mhz ");
  if (search == 1)
    {
      lcd.setCursor (0, 1);
      lcd.clearLine ();
      lcd.print ("Searching...");
      lcd.setCursor (0, 2);
      lcd.clearLine ();
    }
  else
    {
      lcd.setCursor (0, 1);
      stereo ? lcd.print ("Stereo      ") : lcd.print ("Mono        ");
      lcd.setCursor (0, 2);
      lcd.print ("Level: ");
      lcd.print (level);
      lcd.print ("/16 ");
    }
  lcd.setCursor (0, 5);
  lcd.print ("by Kalum");

}


void
EEPROM_write_long (int offset, unsigned long val)
{
  byte *p = (byte *) (void *) &val;
  for (int i = 0; i < sizeof (val); i++)
    EEPROM.write (offset++, *p++);
}

unsigned long
EEPROM_read_long (int offset)
{
  unsigned long value = 0;
  byte *p = (byte *) (void *) &value;
  for (int i = 0; i < sizeof (value); i++)
    *p++ = EEPROM.read (offset++);
  return value;
}


const int FORWARD_PIN = 11;
const int BACK_PIN = 10;

Bounce forward_button = Bounce (FORWARD_PIN, 25);
Bounce backward_button = Bounce (BACK_PIN, 25);


int search_mode = 0;
int search_direction;

unsigned long prev_millis;


void
setup ()
{
  unsigned long freq;

  Serial.begin (9600);
  Wire.begin ();
  lcd.begin (102, 66, CHIP_ST7576);

  pinMode (FORWARD_PIN, INPUT);
  digitalWrite (FORWARD_PIN, HIGH);
  pinMode (BACK_PIN, INPUT);
  digitalWrite (BACK_PIN, HIGH);

  delay (100);

  ctrl_data.port1 = 1;
  ctrl_data.port2 = 1;
  ctrl_data.high_cut = 1;
  ctrl_data.st_noise = 1;
  ctrl_data.soft_mute = 1;
  ctrl_data.deemph_75 = 0;
  ctrl_data.japan_band = 0;
  ctrl_data.pllref = 0;

  prev_millis = millis ();

  //Read the eeprom frequency
  if (EEPROM_read_long (0) == 0xDA)
    {
      Serial.println ("Reading eeprom");
      freq = EEPROM_read_long (5);
      Serial.println (freq);
    }
  else
    {
      Serial.println ("First time init eeprom");
      EEPROM_write_long (0, 0xDA);
      EEPROM_write_long (5, 87500000);
      freq = 87500000;
    }


  set_radio_freq ((float) freq / 1000000);
}


void
loop ()
{

  unsigned char buf[5];
  int stereo;
  int incomingByte;
  double current_freq;
  unsigned long current_millis;

  delay (100);


  if (tea5767_read_status (buf) == 1)
    {
      current_freq =
    floor (tea5767_frequency_available (buf) / 100000 + .5) / 10;
      lcd_display_status (current_freq, tea5767_stereo (buf),
              tea5767_signal_level (buf), search_mode);


      //save the current frequency to EEPROM every 10 min, make sure we are not in search mode as well
      if (((current_millis = millis ()) - prev_millis > 300000)
      && search_mode != 1)
    {
      Serial.println ("saving cur freq");
      EEPROM_write_long (5, current_freq * 1000000);
      prev_millis = current_millis;
    }


      if (search_mode == 1)
    {
      if (tea5767_process_search (buf, search_direction) == 1)
        {
          search_mode = 0;
        }
    }

    }

  if (forward_button.update () && (forward_button.read () == LOW))
    {

      search_mode = 1;
      search_direction = SEARCH_UP;
      tea5767_search_up (buf);

    }


  if (backward_button.update () && (backward_button.read () == LOW))
    {

      search_mode = 1;
      search_direction = SEARCH_DOWN;
      tea5767_search_down (buf);

    }


  if (Serial.available () > 0)
    {
      // read the incoming byte:
      incomingByte = Serial.read ();

      // say what you got:
      Serial.print ("I received: ");
      Serial.println (incomingByte, DEC);

      if (incomingByte == 's')
    {
      search_mode = 1;
      search_direction = SEARCH_UP;
      tea5767_search_up (buf);

    }

      if (incomingByte == 'a')
    {
      search_mode = 1;
      search_direction = SEARCH_DOWN;
      tea5767_search_down (buf);
    }

    }

  return;

}

 

 

The TEA5767 apllication  data sheet is  as follow,

Click here to download:
application_note_tea5767-8(1).pdf (1.06 MB)
(download)