Dependents:   Adafruit_Si4173_Demo Radio_Transmitter_Working

Files at this revision

API Documentation at this revision

Comitter:
JLarkin
Date:
Tue Jan 24 06:55:43 2017 +0000
Commit message:
Translated Adafruit library from Arduino to mbed. Need extra delays between I2C commands because of mbed's faster speed.

Changed in this revision

Adafruit_Si4713.cpp Show annotated file Show diff for this revision Revisions of this file
Adafruit_Si4713.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 6643c5542090 Adafruit_Si4713.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Adafruit_Si4713.cpp	Tue Jan 24 06:55:43 2017 +0000
@@ -0,0 +1,404 @@
+/*************************************************** 
+  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());
+}
+*/
diff -r 000000000000 -r 6643c5542090 Adafruit_Si4713.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Adafruit_Si4713.h	Tue Jan 24 06:55:43 2017 +0000
@@ -0,0 +1,145 @@
+/*************************************************** 
+  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
+******************************************************/
+
+//!Library for the Adafruit Si4713.
+/*!
+The Si4713 is an FM radio transmitter with RDS
+*/
+
+#include "mbed.h"
+
+//#define SI4713_CMD_DEBUG 
+
+#define SI4710_ADDR0 0x11<<1  // if SEN is low
+#define SI4710_ADDR1 0x63<<1  // if SEN is high, default!
+#define SI4710_STATUS_CTS 0x80
+
+/* COMMANDS */
+#define SI4710_CMD_POWER_UP     0x01
+#define SI4710_CMD_GET_REV      0x10
+#define SI4710_CMD_POWER_DOWN   0x11
+#define SI4710_CMD_SET_PROPERTY 0x12
+#define SI4710_CMD_GET_PROPERTY 0x13
+#define SI4710_CMD_GET_INT_STATUS 0x14
+#define SI4710_CMD_PATCH_ARGS 0x15
+#define SI4710_CMD_PATCH_DATA 0x16
+#define SI4710_CMD_TX_TUNE_FREQ 0x30
+#define SI4710_CMD_TX_TUNE_POWER 0x31
+#define SI4710_CMD_TX_TUNE_MEASURE 0x32
+#define SI4710_CMD_TX_TUNE_STATUS 0x33
+#define SI4710_CMD_TX_ASQ_STATUS 0x34
+#define SI4710_CMD_TX_RDS_BUFF 0x35
+#define SI4710_CMD_TX_RDS_PS 0x36
+#define SI4710_CMD_TX_AGC_OVERRIDE 0x48
+#define SI4710_CMD_GPO_CTL 0x80
+#define SI4710_CMD_GPO_SET 0x81
+
+/* Parameters */
+
+#define SI4713_PROP_GPO_IEN 0x0001
+#define SI4713_PROP_DIGITAL_INPUT_FORMAT 0x0101
+#define SI4713_PROP_DIGITAL_INPUT_SAMPLE_RATE 0x0103
+#define SI4713_PROP_REFCLK_FREQ 0x0201
+#define SI4713_PROP_REFCLK_PRESCALE 0x0202
+#define SI4713_PROP_TX_COMPONENT_ENABLE 0x2100
+#define SI4713_PROP_TX_AUDIO_DEVIATION 0x2101
+#define SI4713_PROP_TX_PILOT_DEVIATION 0x2102
+#define SI4713_PROP_TX_RDS_DEVIATION 0x2103
+#define SI4713_PROP_TX_LINE_LEVEL_INPUT_LEVEL 0x2104
+#define SI4713_PROP_TX_LINE_INPUT_MUTE 0x2105
+#define SI4713_PROP_TX_PREEMPHASIS 0x2106
+#define SI4713_PROP_TX_PILOT_FREQUENCY 0x2107
+#define SI4713_PROP_TX_ACOMP_ENABLE 0x2200
+#define SI4713_PROP_TX_ACOMP_THRESHOLD 0x2201
+#define SI4713_PROP_TX_ATTACK_TIME 0x2202
+#define SI4713_PROP_TX_RELEASE_TIME 0x2203
+#define SI4713_PROP_TX_ACOMP_GAIN 0x2204
+#define SI4713_PROP_TX_LIMITER_RELEASE_TIME 0x2205
+#define SI4713_PROP_TX_ASQ_INTERRUPT_SOURCE 0x2300
+#define SI4713_PROP_TX_ASQ_LEVEL_LOW 0x2301
+#define SI4713_PROP_TX_ASQ_DURATION_LOW 0x2302
+#define SI4713_PROP_TX_AQS_LEVEL_HIGH 0x2303
+#define SI4713_PROP_TX_AQS_DURATION_HIGH 0x2304
+
+#define SI4713_PROP_TX_RDS_INTERRUPT_SOURCE 0x2C00
+#define SI4713_PROP_TX_RDS_PI 0x2C01
+#define SI4713_PROP_TX_RDS_PS_MIX 0x2C02
+#define SI4713_PROP_TX_RDS_PS_MISC 0x2C03
+#define SI4713_PROP_TX_RDS_PS_REPEAT_COUNT 0x2C04
+#define SI4713_PROP_TX_RDS_MESSAGE_COUNT 0x2C05
+#define SI4713_PROP_TX_RDS_PS_AF 0x2C06
+#define SI4713_PROP_TX_RDS_FIFO_SIZE 0x2C07
+
+
+/* REGISTERS */
+
+
+int min(int a, int b);
+
+class Adafruit_Si4713  {
+ public:
+   //!Creates an instance of the class.
+  /*!
+  Connect module at I2C address addr using I2C port pins sda and scl.  A digital output is used to reset the device.
+  TMP102
+  \param i2c An I2C object created elsewhere
+  \param rstpin A digital out pin used to toggle the device reset.
+  \param addr The I2C address the device.  
+  */
+  Adafruit_Si4713(I2C i2c, PinName rstpin, uint8_t addr = SI4710_ADDR1);
+  bool begin();
+  void reset();
+  
+  void powerUp(void);
+  void configure(void);
+  uint8_t getRev(void);
+
+  void tuneFM(uint16_t freqKHz);
+  uint8_t getStatus(void);
+  void readTuneStatus(void);
+  void readTuneMeasure(uint16_t freq);
+  void setTXpower(uint8_t pwr, uint8_t antcap = 0);
+  void readASQ(void);
+  void setProperty(uint16_t p, uint16_t v);
+
+  // RDS stuff
+  void beginRDS(uint16_t programID = 0xADAF);
+  void setRDSstation(char *s);
+  void setRDSbuffer(char *s);
+
+  uint16_t currFreq;
+  uint8_t currdBuV, currAntCap, currNoiseLevel, currASQ;
+  int8_t currInLevel;
+
+
+  void setGPIO(uint8_t x);
+  void setGPIOctrl(uint8_t x);
+
+ private:
+
+  void sendCommand(uint8_t len);
+
+  I2C _i2c;
+  DigitalOut _rst;
+  char _i2ccommand[10];  // holds the command buffer
+  char _i2caddr;
+};
+