mbed version of the Adafruit Si4713 library (FM transmitter)

Dependents:   Adafruit_Si4173_Demo Radio_Transmitter_Working

Adafruit_Si4713.cpp

Committer:
JLarkin
Date:
2017-01-24
Revision:
0:6643c5542090

File content as of revision 0:6643c5542090:

/*************************************************** 
  This is a library for the Si4713 FM Radio Transmitter with RDS

  Designed specifically to work with the Si4713 breakout in the
  adafruit shop
  ----> https://www.adafruit.com/products/1958

  These transmitters use I2C to communicate, plus reset pin. 
  3 pins are required to interface
  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/
/***************************************************
    Modified to work with ARM mbed by John M. Larkin
    January 2017
******************************************************/

#include "Adafruit_Si4713.h"

float dt = 0.1; // An extra delay between I2C commands is essential

int min(int a, int b) {
	return a > b ? a:b;
}

Adafruit_Si4713::Adafruit_Si4713(I2C i2c, PinName resetpin, uint8_t addr):_i2c(i2c),_rst(resetpin) {
	_i2caddr = addr;
}


bool Adafruit_Si4713::begin() {
  reset();
  powerUp();

  // check for Si4713
  if (getRev() != 13) return false;

  return true;
}

void Adafruit_Si4713::reset() {
	_rst = 1;
	wait(dt);
	_rst = 0;
	wait(dt);
	_rst = 1;
}


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

void Adafruit_Si4713::setProperty(uint16_t property, uint16_t value) {
    _i2ccommand[0] = SI4710_CMD_SET_PROPERTY;
    _i2ccommand[1] = 0;
    _i2ccommand[2] = property >> 8;
    _i2ccommand[3] = property & 0xFF;
    _i2ccommand[4] = value >> 8;
    _i2ccommand[5] = value & 0xFF;
    sendCommand(6);

#ifdef SI4713_CMD_DEBUG
    printf("Set Prop 0x%x = %d\r\n", property,value);
#endif
}

void Adafruit_Si4713::sendCommand(uint8_t len) {
#ifdef SI4713_CMD_DEBUG
  printf("\r\n*** Command:");
  for (uint8_t i = 0; i<len; i++)
  	printf(" 0x%x",_i2ccommand[i]);
  printf("\r\n");
#endif
  _i2c.write(_i2caddr, _i2ccommand, len,true);
  wait(dt);

  // Wait for status CTS bit
  char status = 0;
  while (! (status & SI4710_STATUS_CTS)) {
    status = _i2c.read(0);
#ifdef SI4713_CMD_DEBUG
    printf("status: %x\r\n", status);
#endif
  }
  _i2c.stop();
  wait(dt);
}

void Adafruit_Si4713::tuneFM(uint16_t freqKHz) {
  _i2ccommand[0] = SI4710_CMD_TX_TUNE_FREQ;
  _i2ccommand[1] = 0;
  _i2ccommand[2] = freqKHz >> 8;
  _i2ccommand[3] = freqKHz;
  sendCommand(4);
  while ((getStatus() & 0x81) != 0x81) {
    wait(0.01);
  }
}

void Adafruit_Si4713::setTXpower(uint8_t pwr, uint8_t antcap) {
  _i2ccommand[0] = SI4710_CMD_TX_TUNE_POWER;
  _i2ccommand[1] = 0;
  _i2ccommand[2] = 0;
  _i2ccommand[3] = pwr;
  _i2ccommand[4] = antcap;
  sendCommand(5);
}

void Adafruit_Si4713::readASQ(void) {
  _i2ccommand[0] = SI4710_CMD_TX_ASQ_STATUS;
  _i2ccommand[1] = 0x1;
  sendCommand(2);

  char rawBytes[5];
  _i2c.read(_i2caddr, rawBytes, 5);
  currASQ = rawBytes[1];
  currInLevel = rawBytes[4];
}


void Adafruit_Si4713::readTuneStatus(void) {
  _i2ccommand[0] = SI4710_CMD_TX_TUNE_STATUS;
  _i2ccommand[1] = 0x1; // INTACK
  sendCommand(2);


  char rawBytes[8];
  _i2c.read(_i2caddr, rawBytes, 8);
  currFreq = ((uint16_t)rawBytes[2] << 8) | (uint16_t)rawBytes[3];
  currdBuV = rawBytes[5];
  currAntCap = rawBytes[6];
  currNoiseLevel = rawBytes[7];
}


void Adafruit_Si4713::readTuneMeasure(uint16_t freq) {
  // check freq is multiple of 50khz
  if (freq % 5 != 0) {
    freq -= (freq % 5);
  }
  _i2ccommand[0] = SI4710_CMD_TX_TUNE_MEASURE;
  _i2ccommand[1] = 0;
  _i2ccommand[2] = freq >> 8;
  _i2ccommand[3] = freq;
  _i2ccommand[4] = 0;

  sendCommand(5);
  while (getStatus() != 0x81) wait(0.01);
}

void Adafruit_Si4713::beginRDS(uint16_t programID) {
  // 66.25KHz (default is 68.25)
  setProperty(SI4713_PROP_TX_AUDIO_DEVIATION, 6625); 
  
  // 2KHz (default)  
  setProperty(SI4713_PROP_TX_RDS_DEVIATION, 200); 

  // RDS IRQ 
  setProperty(SI4713_PROP_TX_RDS_INTERRUPT_SOURCE, 0x0001); 
  // program identifier
  setProperty(SI4713_PROP_TX_RDS_PI, programID);
  // 50% mix (default)
  setProperty(SI4713_PROP_TX_RDS_PS_MIX, 0x03);
  // RDSD0 & RDSMS (default)
  setProperty(SI4713_PROP_TX_RDS_PS_MISC, 0x1808); 
  // 3 repeats (default)
  setProperty(SI4713_PROP_TX_RDS_PS_REPEAT_COUNT, 3); 

  setProperty(SI4713_PROP_TX_RDS_MESSAGE_COUNT, 1);
  setProperty(SI4713_PROP_TX_RDS_PS_AF, 0xE0E0); // no AF
  setProperty(SI4713_PROP_TX_RDS_FIFO_SIZE, 0);

  setProperty(SI4713_PROP_TX_COMPONENT_ENABLE, 0x0007);
}

void Adafruit_Si4713::setRDSstation(char *s) {
  uint8_t i, len = strlen(s);
  uint8_t slots = (len+3) / 4;

  for (uint8_t i=0; i<slots; i++) {
    memset(_i2ccommand, ' ', 6); // clear it with ' '
    memcpy(_i2ccommand+2, s, min(4, strlen(s)));
    s+=4;
    _i2ccommand[6] = 0;
    //Serial.print("Set slot #"); Serial.print(i); 
    //char *slot = (char *)( _i2ccommand+2);
    //Serial.print(" to '"); Serial.print(slot); Serial.println("'");
    _i2ccommand[0] = SI4710_CMD_TX_RDS_PS;
    _i2ccommand[1] = i; // slot #
    sendCommand(6);
  }
}

void Adafruit_Si4713::setRDSbuffer(char *s) {
  uint8_t i, len = strlen(s);
  uint8_t slots = (len+3) / 4;
  char slot[5];

  for (uint8_t i=0; i<slots; i++) {
    memset(_i2ccommand, ' ', 8); // clear it with ' '
    memcpy(_i2ccommand+4, s, min(4, strlen(s)));
    s+=4;
    _i2ccommand[8] = 0;
    //Serial.print("Set buff #"); Serial.print(i); 
    //char *slot = (char *)( _i2ccommand+4);
    //Serial.print(" to '"); Serial.print(slot); Serial.println("'");
    _i2ccommand[0] = SI4710_CMD_TX_RDS_BUFF;
    if (i == 0)
      _i2ccommand[1] = 0x06;
    else
      _i2ccommand[1] = 0x04;

    _i2ccommand[2] = 0x20;
    _i2ccommand[3] = i;
    sendCommand(8);
  }


  /*
  // set time
   _i2ccommand[0] = SI4710_CMD_TX_RDS_BUFF;
   _i2ccommand[1] = 0x84;
   _i2ccommand[2] = 0x40; // RTC
   _i2ccommand[3] = 01;
   _i2ccommand[4] = 0xA7;
   _i2ccommand[5] = 0x0B;
   _i2ccommand[6] = 0x2D;
   _i2ccommand[7] = 0x6C;
   sendCommand(8);

   Wire.requestFrom((uint8_t)_i2caddr, (uint8_t)6);  
   Wire.read(); // status
   Serial.print("FIFO overflow: 0x"); Serial.println(Wire.read(), HEX);
   Serial.print("Circ avail: "); Serial.println(Wire.read());
   Serial.print("Circ used: "); Serial.println(Wire.read());
   Serial.print("FIFO avail: "); Serial.println(Wire.read());
   Serial.print("FIFO used: "); Serial.println(Wire.read());
   */

   // enable!
  //Serial.println("Enable RDS");
  setProperty(SI4713_PROP_TX_COMPONENT_ENABLE, 0x0007); // stereo, pilot+rds
  /*
  // wait till ready
  while (getStatus() != 0x80) {
    Serial.println(getStatus(), HEX);
    delay(100);
  }
  delay(500);
  _i2ccommand[0] = SI4710_CMD_TX_RDS_BUFF;
  _i2ccommand[1] = 0x01; // clear RDSINT
  _i2ccommand[2] = 0x0;
  _i2ccommand[3] = 0x0;
  _i2ccommand[4] = 0x0;
  _i2ccommand[5] = 0x0;
  _i2ccommand[6] = 0x0;
  _i2ccommand[7] = 0x0;
   sendCommand(8);
   // get reply!
   Wire.requestFrom((uint8_t)_i2caddr, (uint8_t)6);  
   for (uint8_t x=0; x<7; x++) {
     Wire.read();
   }
   */
   /*   or you can read the status
   Wire.read(); // status
   Serial.print("FIFO overflow: 0x"); Serial.println(Wire.read(), HEX);
   Serial.print("Circ avail: "); Serial.println(Wire.read());
   Serial.print("Circ used: "); Serial.println(Wire.read());
   Serial.print("FIFO avail: "); Serial.println(Wire.read());
   Serial.print("FIFO used: "); Serial.println(Wire.read());
   */
}

uint8_t Adafruit_Si4713::getStatus(void) {
  _i2c.start();
  _i2c.write(_i2caddr);
  _i2c.write(SI4710_CMD_GET_INT_STATUS);
  _i2c.stop();
  
  uint8_t status;
  _i2c.start();
  _i2c.write(_i2caddr|0x01);
  status = _i2c.read(0);
  _i2c.stop();
  return status;
}

void Adafruit_Si4713::powerUp(void) {
    _i2ccommand[0] = SI4710_CMD_POWER_UP;
    _i2ccommand[1] = 0x12; 
    // CTS interrupt disabled
    // GPO2 output disabled
    // Boot normally
    // xtal oscillator ENabled
    // FM transmit
    _i2ccommand[2] = 0x50;  // analog input mode
    sendCommand(3);

    // configuration! see page 254
    setProperty(SI4713_PROP_REFCLK_FREQ, 32768);  // crystal is 32.768
    setProperty(SI4713_PROP_TX_PREEMPHASIS, 0); // 74uS pre-emph (USA std)
    setProperty(SI4713_PROP_TX_ACOMP_GAIN, 10); // max gain?
    setProperty(SI4713_PROP_TX_ACOMP_ENABLE, 0x02); // turn on limiter, but no dynamic ranging
    //setProperty(SI4713_PROP_TX_ACOMP_ENABLE, 0x0); // turn on limiter and AGC
}

uint8_t Adafruit_Si4713::getRev(void) {
    _i2ccommand[0] = SI4710_CMD_GET_REV;
    _i2ccommand[1] = 0;

    sendCommand(2);
    
    char rawBytes[9];
    _i2c.read(_i2caddr, rawBytes, 9);
    uint8_t pn = rawBytes[1];
    uint16_t fw = ((uint16_t)rawBytes[2] << 8) | (uint16_t)rawBytes[3]; 
	uint16_t patch = ((uint16_t)rawBytes[4] << 8) | (uint16_t)rawBytes[5];
	uint16_t cmp = ((uint16_t)rawBytes[6] << 8) | (uint16_t)rawBytes[7];
	uint8_t chiprev = rawBytes[8];
    
    printf("Part #Si47%d - %x\r\n", pn, fw);
    printf("Patch 0x%x\r\n", patch);
    printf("Chip rev %d\r\n", chiprev);
    return pn;
}

void Adafruit_Si4713::setGPIOctrl(uint8_t x) {
  // Serial.println("GPIO direction");
  _i2ccommand[0] = SI4710_CMD_GPO_CTL;
  _i2ccommand[1] = x;
  
  sendCommand(2);
}

void Adafruit_Si4713::setGPIO(uint8_t x) {
  //Serial.println("GPIO set");
  _i2ccommand[0] = SI4710_CMD_GPO_SET;
  _i2ccommand[1] = x;

  sendCommand(2);
}


///

/*
void Adafruit_Si4713::rdstest() {
  _i2ccommand[0] = SI4710_CMD_TX_RDS_BUFF;
  _i2ccommand[1] = 0x02;
  _i2ccommand[2] = 0;
  _i2ccommand[3] = 0;
  _i2ccommand[4] = 0;
  _i2ccommand[5] = 0;
  _i2ccommand[6] = 0;
  _i2ccommand[7] = 0;
  sendCommand(8);
   Wire.requestFrom((uint8_t)_i2caddr, (uint8_t)6);  
   Wire.read(); // status
   Serial.print("FIFO overflow: 0x"); Serial.println(Wire.read(), HEX);
   Serial.print("Circ avail: "); Serial.println(Wire.read());
   Serial.print("Circ used: "); Serial.println(Wire.read());
   Serial.print("FIFO avail: "); Serial.println(Wire.read());
   Serial.print("FIFO used: "); Serial.println(Wire.read());

  _i2ccommand[0] = SI4710_CMD_TX_RDS_BUFF;
  _i2ccommand[1] = 0x04;
  _i2ccommand[2] = 0x20;
  _i2ccommand[3] = 0x00;
  _i2ccommand[4] = 0x54;
  _i2ccommand[5] = 0x65;
  _i2ccommand[6] = 0x73;
  _i2ccommand[7] = 0x74;
  sendCommand(8);
   Wire.requestFrom((uint8_t)_i2caddr, (uint8_t)6);  
   Wire.read(); // status
   Serial.print("FIFO overflow: 0x"); Serial.println(Wire.read(), HEX);
   Serial.print("Circ avail: "); Serial.println(Wire.read());
   Serial.print("Circ used: "); Serial.println(Wire.read());
   Serial.print("FIFO avail: "); Serial.println(Wire.read());
   Serial.print("FIFO used: "); Serial.println(Wire.read());


  _i2ccommand[0] = SI4710_CMD_TX_RDS_BUFF;
  _i2ccommand[1] = 0x04;
  _i2ccommand[2] = 0x20;
  _i2ccommand[3] = 0x01;
  _i2ccommand[4] = 0xD0;
  _i2ccommand[5] = 0x00;
  _i2ccommand[6] = 0x00;
  _i2ccommand[7] = 0x00;
  sendCommand(8);
   Wire.requestFrom((uint8_t)_i2caddr, (uint8_t)6);  
   Wire.read(); // status
   Serial.print("FIFO overflow: 0x"); Serial.println(Wire.read(), HEX);
   Serial.print("Circ avail: "); Serial.println(Wire.read());
   Serial.print("Circ used: "); Serial.println(Wire.read());
   Serial.print("FIFO avail: "); Serial.println(Wire.read());
   Serial.print("FIFO used: "); Serial.println(Wire.read());
}
*/