Radio Modul

http://www.voti.nl/docs/TEA5767.pdf

Funktionen

Auto Search

1. search tuning request (direction und „search stop level“ können eingestellt werden)

2. „ready flag“ checken; wenn gesetzt ist entweder ein Sender mit dem eingestellten Pegel gefunden oder das Frequenzband-Ende erreicht

Flag SM (search mode)

PLL[13:8][0:7] Synthesizer Counter Preset

SUD Search Up/Down

SSL[1:0] Search Stop Level (low,mid,high)

POR

Power on reset

Mute wird gesetzt; alle anderen Einstellungen müssen an das Modul übertragen werden








https://github.com/andykarpov/TEA5767/tree/master/examples/SimpleRadioFM
/* 
* TEA5767.h 
* defintions for TEA5767 FM radio module. 
* Created by Andrey Karpov 
* Based on this project: http://kalum.posterous.com/arduino-with-tea5767-single-chip-radio-and-no 
* 
* 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. 
*/ 

#ifndef _TEA5767_H_ 
#define _TEA5767_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 

/* internal constants */ 
#define TEA5767_SEARCH_DIR_UP 1 
#define TEA5767_SEARCH_DIR_DOWN 2 

#include <Arduino.h> 

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; 
}; 

class TEA5767 { 
private: 
tea5767_ctrl ctrl_data; 
int HILO; 
protected: 
int hilo_optimal (unsigned long freq); 
void set_frequency (int hilo, double freq); 
int ready (unsigned char *buf); 
int bl_reached (unsigned char *buf); 
public: 
TEA5767(); 
TEA5767(double initial_freq); 
void set_frequency (double freq); 
int read_status (unsigned char *buf); 
int signal_level (unsigned char *buf); 
int stereo (unsigned char *buf); 
double frequency_available (unsigned char *buf); 
void search_up (unsigned char *buf); 
void search_down (unsigned char *buf); 
int process_search (unsigned char *buf, int search_dir); 
void init(); 
}; 

#endif // _TEA5767_H_


#include <stdio.h>
#include <avr/pgmspace.h>
#include <Arduino.h>
#include <Wire.h>
#include "TEA5767.h"

TEA5767::TEA5767() {
        Wire.begin();
        HILO = 1;
}

TEA5767::TEA5767(double initial_freq) {
        Wire.begin();
        HILO = 1;
        set_frequency(initial_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 TEA5767::hilo_optimal (unsigned long freq) {

        int signal_high = 0;
        int signal_low = 0;
        unsigned char buf[5];

        set_frequency (1, (double) (freq + 450000) / 1000000);
        delay (30);
  
        // Read the signal level
        if (read_status (buf) == 1) {
                signal_high = signal_level (buf);
        }

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

        if (read_status (buf) == 1) {
                signal_low = signal_level (buf);
        }

        return (signal_high < signal_low) ? 1 : 0;
}

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

        memset (buffer, 0, 5);

        buffer[2] = 0;
        buffer[2] |= TEA5767_PORT1_HIGH;

        if (hilo == 1)
        buffer[2] |= TEA5767_HIGH_LO_INJECT;
        
        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.write (buffer[i]);

        Wire.endTransmission ();
}

/* Freq should be specifyed at X M hz */
void TEA5767::set_frequency (double freq) {
        HILO = hilo_optimal ((unsigned long) (freq * 1000000));
        set_frequency (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.read ();
                }
        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) {
        return (buf[0] & 0x80) ? 1 : 0;
}

//returns 1 if band limit is reached during searching
int TEA5767::bl_reached (unsigned char *buf) {
        return (buf[0] & 0x40) ? 1 : 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 = 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.write (buf[i]);

        Wire.endTransmission ();
        HILO = 1;
}

void TEA5767::search_down (unsigned char *buf)
{
        unsigned div;
        double freq_av;

        freq_av = 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_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.write (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 (ready (buf) == 1) {
        if (bl_reached (buf) == 1) {
                if (search_dir == TEA5767_SEARCH_DIR_UP) {
                        //wrap down
                        set_frequency (87.5);
                        read_status (buf);
                        search_up (buf);
                        return 0;
                } else if (search_dir == TEA5767_SEARCH_DIR_DOWN) {
                        //wrap up
                        set_frequency (108);
                        read_status (buf);
                        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 = frequency_available (buf);
                rounded_freq = floor (freq_available / 100000 + .5) / 10;
                set_frequency (rounded_freq);
                return 1;
        }
    }
}

void TEA5767::init() {
  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;

//  unsigned long freq = 87500000;
//  set_frequency((float) freq / 1000000);
  
}

Beispiel

#include <TEA5767.h>
#include <Wire.h>
#include <Button.h>
#include <LiquidCrystal.h>

TEA5767 Radio;
LiquidCrystal lcd(12,11,10,9,8,7);
double old_frequency;
double frequency;
int search_mode = 0;
int search_direction;
unsigned long last_pressed;

Button btn_forward(3, PULLUP);
Button btn_backward(2, PULLUP);

void setup() {  
  Wire.begin();
  Radio.init();
  Radio.set_frequency(105.4); 
  Serial.begin(9600);
  lcd.begin(16,2);
  lcd.clear();
}

void loop() {

  unsigned char buf[5];
  int stereo;
  int signal_level;
  double current_freq;
  unsigned long current_millis = millis();
  
  if (Radio.read_status(buf) == 1) {
    current_freq =  floor (Radio.frequency_available (buf) / 100000 + .5) / 10;
    stereo = Radio.stereo(buf);
    signal_level = Radio.signal_level(buf);
    lcd.setCursor(0,0);
    lcd.print("FM: "); lcd.print(current_freq);
    lcd.setCursor(0,1);
    if (stereo) lcd.print("STEREO "); else lcd.print("MONO ");
    //lcd.print(signal_level);
  }
  
  if (search_mode == 1) {
      if (Radio.process_search (buf, search_direction) == 1) {
          search_mode = 0;
      }
  }
  
  if (btn_forward.isPressed()) {
    last_pressed = current_millis;
    search_mode = 1;
    search_direction = TEA5767_SEARCH_DIR_UP;
    Radio.search_up(buf);
    delay(300);
  }
  
  if (btn_backward.isPressed()) {
    last_pressed = current_millis;
    search_mode = 1;
    search_direction = TEA5767_SEARCH_DIR_DOWN;
    Radio.search_down(buf);
    delay(300);
  } 
  //delay(20); 
  delay(50);
}