![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
Si4703 sample code ECE 4180 Georgia Tech
Fork of Si4735 by
Revision 1:563a11fe39e0, committed 2015-10-20
- Comitter:
- Gjika
- Date:
- Tue Oct 20 14:58:05 2015 +0000
- Parent:
- 0:42c032fc907a
- Commit message:
- Sample Code for Si4703 Digital FM Radio Receiver
Changed in this revision
diff -r 42c032fc907a -r 563a11fe39e0 Si4735.cpp --- a/Si4735.cpp Wed Oct 12 17:01:38 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,271 +0,0 @@ -/* mbed Si4735 Library - * Brett Wilson and Brett Berry - * Georgia Tech ECE 4180 - * Note: this code has only been tested for FM. - * Ported from ... - - * Arduino Si4735 Library - * Written by Ryan Owens for SparkFun Electronics - * 5/17/11 - * - * This library is for use with the SparkFun Si4735 Shield - * Released under the 'Buy Me a Beer' license - * (If we ever meet, you buy me a beer) - * - * See the header file for better function documentation. - * - * See the example sketches to learn how to use the library in your code. -*/ - -#include "Si4735.h" -#include "mbed.h" - -//This is just a constructor. -Si4735::Si4735(PinName sda, PinName scl, PinName RST_) : - _RST_(RST_) { - i2c_ = new I2C(sda, scl); - i2c_->frequency(100000); -} - - -void Si4735::begin(char mode){ - _mode = mode; - //Set the initial volume level. - _currentVolume=63; - - // Reset the Si4735 - _RST_ = 0; - wait(.2); - _RST_ = 1; - wait(.2); - - - //Send the POWER_UP command - switch(_mode){ - case FM: - sprintf(command, "%c%c%c", 0x01, 0x10, 0x05); - break; - case AM: - case SW: - case LW: - sprintf(command, "%c%c%c", 0x01, 0x11, 0x05); - break; - default: - return; - } - sendCommand(command, 3); - wait(.2); - - // Set the RCLK to 32768Hz - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x02, 0x01, 0x80, 0x00); - sendCommand(command, 6); - wait(.2); - - // Set default frequency to 99.7 MHz FM - sprintf(command, "%c%c%c%c", 0x20, 0x00, 0x26, 0xF2); - sendCommand(command, 4); - wait(.2); - - //Set the volume to the current value. - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x00, 0x00, _currentVolume); - sendCommand(command, 6); - wait(.2); - - //Disable Mute - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x01, 0x00, 0x00); - sendCommand(command, 6); - wait(.2); - - //Set the seek band for the desired mode (AM and FM can use default values) - switch(_mode){ - case SW: - //Set the lower band limit for Short Wave Radio to 2300 kHz - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x34, 0x00, 0x08, 0xFC); - sendCommand(command, 6); - wait(.001); - //Set the upper band limit for Short Wave Radio to 23000kHz - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x34, 0x01, 0x59, 0xD8); - sendCommand(command, 6); - wait(.001); - break; - case LW: - //Set the lower band limit for Long Wave Radio to 152 kHz - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x34, 0x00, 0x00, 0x99); - sendCommand(command, 6); - wait(.001); - //Set the upper band limit for Long Wave Radio to 279 kHz - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x34, 0x01, 0x01, 0x17); - sendCommand(command, 6); - wait(.001); - break; - default: - break; - } - -} - -/* -* Description: Tunes the Si4735 to a frequency -* -* Params: int frequency - The desired frequency in kHz -* -* Returns: True if tune was successful -* False if tune was unsuccessful -*/ -bool Si4735::tuneFrequency(int frequency){ - //Split the desired frequency into two character for use in the - //set frequency command. - char highByte = frequency >> 8; - char lowByte = frequency & 0x00FF; - - //Depending on the current mode, set the new frequency. - switch(_mode){ - case FM: - sprintf(command, "%c%c%c%c", 0x20, 0x00, highByte, lowByte); - break; - case AM: - case SW: - case LW: - sprintf(command, "%c%c%c%c", 0x40, 0x00, highByte, lowByte); - break; - default: - break; - } - sendCommand(command, 4); - wait(.1); - - return true; -} - -//This function does not work unless you bypass the buffer chip! -int Si4735::getFrequency(void){ - char data[8]; - int freq = 0; - // FM only - i2c_->start(); - i2c_->write(address_write); - i2c_->write(0x22); - i2c_->write(0x00); - i2c_->stop(); - i2c_->start(); - i2c_->write(address_read); - for (int i=0; i<8; i++) - data[i] = i2c_->read(1); - //i2c_->read(address_read, data, 8, false); - i2c_->stop(); - freq = data[2]; - freq = (freq << 8) | data[3]; - return freq; -} - -bool Si4735::seekUp(void){ - //Use the current mode selection to seek up. - switch(_mode){ - case FM: - sprintf(command, "%c%c", 0x21, 0x0C); - sendCommand(command, 2); - break; - case AM: - case SW: - case LW: - sprintf(command, "%c%c%c%c%c%c", 0x41, 0x0C, 0x00, 0x00, 0x00, 0x00); - sendCommand(command, 6); - break; - default: - break; - } - wait(.001); - return true; -} - -bool Si4735::seekDown(void){ - //Use the current mode selection to seek down. - switch(_mode){ - case FM: - sprintf(command, "%c%c", 0x21, 0x04); - sendCommand(command, 2); - break; - case AM: - case SW: - case LW: - sprintf(command, "%c%c%c%c%c%c", 0x41, 0x04, 0x00, 0x00, 0x00, 0x00); - sendCommand(command, 6); - break; - default: - break; - } - wait(.001); - return true; -} - -void Si4735::volumeUp(void){ - //If we're not at the maximum volume yet, increase the volume - if(_currentVolume < 63){ - _currentVolume+=1; - //Set the volume to the current value. - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x00, 0x00, _currentVolume); - sendCommand(command, 6); - wait(.01); - } -} - -void Si4735::volumeDown(void){ - //If we're not at the maximum volume yet, increase the volume - if(_currentVolume > 0){ - _currentVolume-=1; - //Set the volume to the current value. - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x00, 0x00, _currentVolume); - sendCommand(command, 6); - wait(.01); - } -} - -void Si4735::mute(void){ - //Disable Mute - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x01, 0x00, 0x03); - sendCommand(command, 6); - wait(.001); -} - -void Si4735::unmute(void){ - //Disable Mute - sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x01, 0x00, 0x00); - sendCommand(command, 6); - wait(.001); -} - -/*char Si4735::getStatus(void){ - char response; - bool ack1, ack2; - i2c_->start(); - ack1 = i2c_->write(address_); //Send i2c address and wait for ack (true) to proceed - response = spi_->write(0xA0); //read a single byte - _cs_ = 1; - wait(.001); - return response; -}*/ -/*void Si4735::getResponse(char * response){ - -}*/ - -void Si4735::end(void){ - sprintf(command, "%c", 0x11); - sendCommand(command, 1); - wait(.001); -} - -/******************************************* -* -* Private Functions -* -*******************************************/ - -void Si4735::sendCommand(char * command, int length){ - //ack = i2c_->write(address_write, command, length, false); - i2c_->start(); - bool ack = i2c_->write(address_write); - for(int i=0; i<length; i++) - ack = i2c_->write(command[i]); - char CTS = i2c_->read(1); - i2c_->stop(); - }
diff -r 42c032fc907a -r 563a11fe39e0 Si4735.h --- a/Si4735.h Wed Oct 12 17:01:38 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ -/* mbed Si4735 Library - * Brett Wilson and Brett Berry - * Georgia Tech ECE 4180 - * Ported from ... - - * Arduino Si4735 Library - * Written by Ryan Owens for SparkFun Electronics - * 5/17/11 - * - * This library is for use with the SparkFun Si4735 Shield - * Released under the 'Buy Me a Beer' license - * (If we ever meet, you buy me a beer) - * - * See the example sketches to learn how to use the library in your code. -*/ - -#include "mbed.h" - -#ifndef Si4735_h -#define Si4735_h - -//Assign the radio pin numbers -#define POWER_PIN 8 -#define RADIO_RESET_PIN 9 -#define INT_PIN 2 - -//Define the SPI Pin Numbers -#define DATAOUT 11 //MOSI -#define DATAIN 12 //MISO -#define SPICLOCK 13 //sck -#define SS 10 //ss - -//List of possible modes for the Si4735 Radio -#define AM 0 -#define FM 1 -#define SW 2 -#define LW 3 - -#define ON true -#define OFF false - -#define address_write 34 -#define address_read 35 - -class Si4735 -{ - public: - //This is just a constructor. - Si4735(PinName sda, PinName scl, PinName RST_); - /* - * Description: - * Initializes the Si4735, powers up the radio in the desired mode and limits the bandwidth appropriately. - * This function must be called before any other radio command. - * The bands are set as follows: - * FM - 87.5 - 107.9 MHz - * AM - 520 - 1710 kHz - * SW - 2300 - 23000 khz - * LW - 152 - 279 kHz - * Parameters: - * mode - The desired radio mode. Use AM(0), FM(1), SW(2) or LW(3). - */ - void begin(char mode); - /* - * Description: - * Used to send an ascii command string to the radio. - * Parameters: - * myCommand - A null terminated ascii string limited to hexidecimal characters - * to be sent to the radio module. Instructions for building commands can be found - * in the Si4735 Programmers Guide. - */ - void sendCommand(char * myCommand); - /* - * Description: - * Used to to tune the radio to a desired frequency. The library uses the mode indicated in the - * begin() function to determine how to set the frequency. - * Parameters: - * frequency - The frequency to tune to, in kHz (or in 10kHz if using FM mode). - * Returns: - * True - * TODO: - * Make the function return true if the tune was successful, else return false. - */ - bool tuneFrequency(int frequency); - /* - * Description: - * This function currently does not work! - * TODO: - * Make this function work. - */ - int getFrequency(void); - /* - * Description: - * Commands the radio to seek up to the next valid channel. If the top of the band is reached, the seek - * will continue from the bottom of the band. - * Returns: - * True - * TODO: - * Make the function return true if a valid channel was found, else return false. - */ - bool seekUp(void); - /* - * Description: - * Commands the radio to seek down to the next valid channel. If the bottom of the band is reached, the seek - * will continue from the top of the band. - * Returns: - * True - * TODO: - * Make the function return true if a valid channel was found, else return false. - */ - bool seekDown(void); - /* - * Description: - * Increasese the volume by 1. If the maximum volume has been reached, no increase will take place. - */ - void volumeUp(void); - /* - * Description: - * Decreases the volume by 1. If the minimum volume has been reached, no decrease will take place. - */ - void volumeDown(void); - /* - * Description: - * Mutes the audio output - */ - void mute(void); - /* - * Description: - * Disables the mute. - */ - void unmute(void); - /* - * Description: - * Gets the current status of the radio. Learn more about the status in the Si4735 datasheet. - * Returns: - * The status of the radio. - */ - char getStatus(void); - /* - * Description: - * Gets the long response (16 characters) from the radio. Learn more about the long response in the Si4735 datasheet. - * Parameters: - * response - A string for the response from the radio to be stored in. - */ - void getResponse(char * response); - /* - * Description: - * Powers down the radio - */ - void end(void); - - private: - // Pointer for the SPI bus - I2C* i2c_; - // Declare digital out pins - DigitalOut _RST_; - /* - * A variable that is assigned the current mode of the radio (AM, FM, SW or LW) - */ - char _mode; - /* - * A variable the keeps the current volume level. - */ - char _currentVolume; - /* - * Command string that holds the binary command string to be sent to the Si4735. - */ - char command[9]; - /* - * Description: - * Sends a binary command string to the Si4735. - * Parameters: - * command - Binary command to be sent to the radio. - * length - The number of characters in the command string (since it can't be null terminated!) - * TODO: - * Make the command wait for a valid CTS response from the radio before releasing control of the CPU. - */ - void sendCommand(char * command, int length); - /* - * Description: - * Sends/Receives a character from the SPI bus. - * Parameters: - * value - The character to be sent to the SPI bus. - * Returns: - * The character read from the SPI bus during the transfer. - */ - char spiTransfer(char value); - -}; - -#endif \ No newline at end of file
diff -r 42c032fc907a -r 563a11fe39e0 SparkFun-Si4703.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SparkFun-Si4703.h Tue Oct 20 14:58:05 2015 +0000 @@ -0,0 +1,147 @@ +/* +Library for Sparkfun Si4703 breakout board. +Simon Monk. 2011-09-09 + +This is a library wrapper and a few extras to the excellent code produced +by Nathan Seidle from Sparkfun (Beerware). + +Nathan's comments...... + +Look for serial output at 57600bps. + +The Si4703 ACKs the first byte, and NACKs the 2nd byte of a read. + +1/18 - after much hacking, I suggest NEVER write to a register without first reading the contents of a chip. +ie, don't updateRegisters without first readRegisters. + +If anyone manages to get this datasheet downloaded +http://wenku.baidu.com/view/d6f0e6ee5ef7ba0d4a733b61.html +Please let us know. It seem to be the latest version of the programming guide. It had a change on page 12 (write 0x8100 to 0x07) +that allowed me to get the chip working.. + +Also, if you happen to find "AN243: Using RDS/RBDS with the Si4701/03", please share. I love it when companies refer to +documents that don't exist. + +1/20 - Picking up FM stations from a plane flying over Portugal! Sweet! 93.9MHz sounds a little soft for my tastes,s but +it's in Porteguese. + +ToDo: +Display current status (from 0x0A) - done 1/20/11 +Add RDS decoding - works, sort of +Volume Up/Down - done 1/20/11 +Mute toggle - done 1/20/11 +Tune Up/Down - done 1/20/11 +Read current channel (0xB0) - done 1/20/11 +Setup for Europe - done 1/20/11 +Seek up/down - done 1/25/11 + +The Si4703 breakout does work with line out into a stereo or other amplifier. Be sure to test with different length 3.5mm +cables. Too short of a cable may degrade reception. +*/ + +#ifndef SparkFun_Si4703_h +#define SparkFun_Si4703_h + +#include "mbed.h" + + +class Si4703_Breakout +{ + public: + Si4703_Breakout(PinName sdioPin, PinName sclkPin, PinName resetPin, Serial *pc); + void powerOn(); // call in setup + void powerOff(); // call in de-setup + void setChannel(int channel); // 3 digit channel number + int getChannel(); // returns the current channel + int seekUp(); // returns the tuned channel or 0 + int seekDown(); + void setVolume(int volume); // 0 to 15 + uint8_t getVolume(); // Returns The Current Volume (0 .. 15) + +/* void readRDS(char* message, long timeout); */ + // message should be at least 9 chars + // result will be null terminated + // timeout in milliseconds + uint16_t getRegister(uint8_t regNum); // Returns The Value of a Single Register (0x00 .. 0x0F) + void printRegs(); // Prints All of The Registers To The Serial Console + + private: + PinName _resetPin; + PinName _sdioPin; + PinName _sclkPin; + + mbed::I2C *i2c_; + mbed::Serial *pc; + DigitalOut *_reset_; + DigitalOut *_sdio_; + + void si4703_init(); + uint8_t readRegisters(); + uint8_t updateRegisters(); + int seek(bool seekDirection); + + uint16_t si4703_registers[16]; //There are 16 registers, each 16 bits large + static const uint16_t FAIL = 0; + static const uint16_t SUCCESS = 1; + + static const int SI4703 = 0x20; //0b._001.0000 = I2C address of Si4703 - note that the Wire function assumes non-left-shifted I2C address, not 0b.0010.000W + static const uint16_t I2C_FAIL_MAX = 10; //This is the number of attempts we will try to contact the device before erroring out + static const uint16_t SEEK_DOWN = 0; //Direction used for seeking. Default is down + static const uint16_t SEEK_UP = 1; + + //Define the register names + static const uint16_t DEVICEID = 0x00; + static const uint16_t CHIPID = 0x01; + static const uint16_t POWERCFG = 0x02; + static const uint16_t CHANNEL = 0x03; + static const uint16_t SYSCONFIG1 = 0x04; + static const uint16_t SYSCONFIG2 = 0x05; + static const uint16_t SYSCONFIG3 = 0x06; + static const uint16_t TEST1 = 0x07; + static const uint16_t STATUSRSSI = 0x0A; + static const uint16_t READCHAN = 0x0B; + static const uint16_t RDSA = 0x0C; + static const uint16_t RDSB = 0x0D; + static const uint16_t RDSC = 0x0E; + static const uint16_t RDSD = 0x0F; + + //Register 0x00 - DeviceID +// part# = 15:12, mfgid = 11:0 + + //Register 0x02 - POWERCFG + static const uint16_t SMUTE = 15; + static const uint16_t DMUTE = 14; + static const uint16_t SKMODE = 10; + static const uint16_t SEEKUP = 9; + static const uint16_t SEEK = 8; + static const uint16_t DISABLE = 6; + static const uint16_t ENABLE = 0; + + //Register 0x03 - CHANNEL + static const uint16_t TUNE = 15; + + //Register 0x04 - SYSCONFIG1 + static const uint16_t RDS = 12; + static const uint16_t DE = 11; + + //Register 0x05 - SYSCONFIG2 + static const uint16_t SPACE1 = 5; + static const uint16_t SPACE0 = 4; + static const uint16_t SEEKTH = 8; + + //Register 0x06 - SYSCONFIG3 + static const uint16_t SKSNR = 4; + static const uint16_t SKCNT = 0; + + //Register 0x0A - STATUSRSSI + static const uint16_t RDSR = 15; + static const uint16_t STC = 14; + static const uint16_t SFBL = 13; + static const uint16_t AFCRL = 12; + static const uint16_t RDSS = 11; + static const uint16_t BLERA = 9; + static const uint16_t STEREO = 8; + static const uint16_t RSSI = 0; +}; + +#endif \ No newline at end of file
diff -r 42c032fc907a -r 563a11fe39e0 TextLCD.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TextLCD.lib Tue Oct 20 14:58:05 2015 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/simon/code/TextLCD/#e4cb7ddee0d3
diff -r 42c032fc907a -r 563a11fe39e0 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Oct 20 14:58:05 2015 +0000 @@ -0,0 +1,87 @@ +#include "mbed.h" +#include "SparkFun-Si4703.h" +#include "TextLCD.h" + +Serial pc(USBTX, USBRX); +Si4703_Breakout radio(p28, p27, p26, &pc); // (sda, scl, rst, Serial) +DigitalIn chanUp(p17); +DigitalIn chanDown(p18); +DigitalIn volUp(p19); +DigitalIn volDown(p20); +TextLCD lcd(p16, p15, p14, p13, p12, p11); // rs, e, d4-d7 + + + +int main(int argc, char** argv) { + + int chan = 901; + int vol = 9; + radio.powerOn(); + radio.setVolume(vol); // range: 0-15 + radio.setChannel(chan); // 90.1 WABE + + lcd.cls(); + lcd.printf("channel = '%d'", radio.getChannel()); + lcd.printf(" volume = '%d'\n", radio.getVolume()); + pc.printf("channel = '%d'", radio.getChannel()); + pc.printf(" volume = '%d'\n", radio.getVolume()); + + + while(1){ + if(chanUp) { + chan++; + radio.setChannel(chan); + lcd.cls(); + lcd.printf("channel = '%d'", radio.getChannel()); + lcd.printf(" volume = '%d'\n", radio.getVolume()); + pc.printf("channel = '%d'", radio.getChannel()); + pc.printf(" volume = '%d'\n", radio.getVolume()); + wait(.25); + } + if(chanDown) { + chan--; + radio.setChannel(chan); + lcd.cls(); + lcd.printf("channel = '%d'", radio.getChannel()); + lcd.printf(" volume = '%d'\n", radio.getVolume()); + pc.printf("channel = '%d'", radio.getChannel()); + pc.printf(" volume = '%d'\n", radio.getVolume()); + wait(.25); + } + if(volUp && vol <= 14) { + vol++; + radio.setVolume(vol); + lcd.cls(); + lcd.printf("channel = '%d'", radio.getChannel()); + lcd.printf(" volume = '%d'\n", radio.getVolume()); + pc.printf("channel = '%d'", radio.getChannel()); + pc.printf(" volume = '%d'\n", radio.getVolume()); + wait(.25); + } + if(volUp && vol == 15) { + lcd.cls(); + lcd.printf("channel = '%d'", radio.getChannel()); + lcd.printf(" Max Volume\n", radio.getVolume()); + pc.printf("Max Volume\n"); + wait(.25); + } + if(volDown && vol >= 1) { + vol--; + radio.setVolume(vol); + lcd.cls(); + lcd.printf("channel = '%d'", radio.getChannel()); + lcd.printf(" volume = '%d'\n", radio.getVolume()); + pc.printf("channel = '%d'", radio.getChannel()); + pc.printf(" volume = '%d'\n", radio.getVolume()); + wait(.25); + } + if(volDown && vol == 0) { + lcd.cls(); + lcd.printf("channel = '%d'", radio.getChannel()); + lcd.printf(" Min Volume\n", radio.getVolume()); + pc.printf("Min Volume\n"); + wait(.25); + } + + } +} \ No newline at end of file
diff -r 42c032fc907a -r 563a11fe39e0 parkFun-Si4703.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/parkFun-Si4703.cpp Tue Oct 20 14:58:05 2015 +0000 @@ -0,0 +1,306 @@ +#include "mbed.h" +#include "SparkFun-Si4703.h" + +Si4703_Breakout::Si4703_Breakout(PinName sdioPin, PinName sclkPin, PinName resetPin, Serial *pc) +{ + _resetPin = resetPin; + _sdioPin = sdioPin; + _sclkPin = sclkPin; + + this->pc = pc; +} + +void Si4703_Breakout::powerOn() +{ + si4703_init(); +} + +void Si4703_Breakout::powerOff() +{ +// a Minimal Power-Down Sequence - According To SI AN230 (rev. 0.9), p.13 - Table 4 + +readRegisters(); + +si4703_registers[POWERCFG] &= ~(1<<DMUTE); // 'Enable Mute' +si4703_registers[POWERCFG] |= (1<<ENABLE); // 'Enable IC' +si4703_registers[POWERCFG] |= (1<<DISABLE); // & 'Disable IC' + // To Init. Power-Down Sequence + +updateRegisters(); +// Notice : This Does NOT Perform a Reset of The IC. +} + +void Si4703_Breakout::setChannel(int channel) +{ + uint8_t ack; + //Freq(MHz) = 0.1 (in Europe) * Channel + 87.5MHz + //97.3 = 0.1 * Chan + 87.5 + //9.8 / 0.1 = 98 + int newChannel = channel * 10; //973 * 10 = 9730 + newChannel -= 8750; //9730 - 8750 = 980 + newChannel /= 10; //980 / 10 = 98 + + //These steps come from AN230 page 20 rev 0.5 + readRegisters(); + si4703_registers[CHANNEL] &= 0xFE00; //Clear out the channel bits + si4703_registers[CHANNEL] |= newChannel; //Mask in the new channel + si4703_registers[CHANNEL] |= (1<<TUNE); //Set the TUNE bit to start + updateRegisters(); + + wait_ms(60); //Wait 60ms - you can use or skip this delay + + //Poll to see if STC is set + while(1) { + ack = readRegisters(); + wait_ms(1); // Just In Case... + if (( (si4703_registers[STATUSRSSI] & (1<<STC)) != 0) || (ack != SUCCESS)) break; //Tuning complete! (or FAILED) + } + + readRegisters(); + si4703_registers[CHANNEL] &= ~(1<<TUNE); //Clear the tune after a tune has completed + updateRegisters(); + + //Wait for the si4703 to clear the STC as well + while(1) { + ack = readRegisters(); + wait_ms(1); // Just In Case... + if (( (si4703_registers[STATUSRSSI] & (1<<STC)) == 0) || (ack != SUCCESS)) break; //Tuning complete! (or FAILED) + } +} + +int Si4703_Breakout::seekUp() +{ + return seek(SEEK_UP); +} + +int Si4703_Breakout::seekDown() +{ + return seek(SEEK_DOWN); +} + +void Si4703_Breakout::setVolume(int volume) +{ + readRegisters(); //Read the current register set + if(volume < 0) volume = 0; + if (volume > 15) volume = 15; + si4703_registers[SYSCONFIG2] &= 0xFFF0; //Clear volume bits + si4703_registers[SYSCONFIG2] |= volume; //Set new volume + updateRegisters(); //Update +} + +uint8_t Si4703_Breakout::getVolume() +{ + readRegisters(); //Read the current register set + + return (si4703_registers[SYSCONFIG2] & 0x000F); +} + +/* +void Si4703_Breakout::readRDS(char* buffer, long timeout) +{ + long endTime = millis() + timeout; + boolean completed[] = {false, false, false, false}; + int completedCount = 0; + while(completedCount < 4 && millis() < endTime) { + readRegisters(); + if(si4703_registers[STATUSRSSI] & (1<<RDSR)){ + // ls 2 bits of B determine the 4 letter pairs + // once we have a full set return + // if you get nothing after 20 readings return with empty string + uint16_t b = si4703_registers[RDSB]; + int index = b & 0x03; + if (! completed[index] && b < 500) + { + completed[index] = true; + completedCount ++; + char Dh = (si4703_registers[RDSD] & 0xFF00) >> 8; + char Dl = (si4703_registers[RDSD] & 0x00FF); + buffer[index * 2] = Dh; + buffer[index * 2 +1] = Dl; + // Serial.print(si4703_registers[RDSD]); Serial.print(" "); + // Serial.print(index);Serial.print(" "); + // Serial.write(Dh); + // Serial.write(Dl); + // Serial.println(); + } + delay(40); //Wait for the RDS bit to clear + } + else { + delay(30); //From AN230, using the polling method 40ms should be sufficient amount of time between checks + } + } + if (millis() >= endTime) { + buffer[0] ='\0'; + return; + } + + buffer[8] = '\0'; +} +*/ + + + +//To get the Si4703 inito 2-wire mode, SEN needs to be high and SDIO needs to be low after a reset +//The breakout board has SEN pulled high, but also has SDIO pulled high. Therefore, after a normal power up +//The Si4703 will be in an unknown state. RST must be controlled +void Si4703_Breakout::si4703_init() +{ + _reset_ = new DigitalOut(_resetPin); + _sdio_ = new DigitalOut(_sdioPin); + + _sdio_->write(0); //A low SDIO indicates a 2-wire interface + _reset_->write(0); //Put Si4703 into reset + wait_ms(1); //Some delays while we allow pins to settle + _reset_->write(1); //Bring Si4703 out of reset with SDIO set to low and SEN pulled high with on-board resistor + wait_ms(1); //Allow Si4703 to come out of reset + + //Now that the unit is reset and I2C inteface mode, we need to begin I2C + i2c_ = new I2C(_sdioPin, _sclkPin); + i2c_->frequency(100000); + /// + + readRegisters(); //Read the current register set + si4703_registers[0x07] = 0x8100; //Enable the oscillator, from AN230 page 9, rev 0.61 (works) + updateRegisters(); //Update + + wait_ms(500); //Wait for clock to settle - from AN230 page 9 + + readRegisters(); //Read the current register set + si4703_registers[POWERCFG] = 0x4001; //Enable the IC + // si4703_registers[POWERCFG] |= (1<<SMUTE) | (1<<DMUTE); //Disable Mute, disable softmute + si4703_registers[SYSCONFIG1] |= (1<<RDS); //Enable RDS + + si4703_registers[SYSCONFIG1] |= (1<<DE); //50μS Europe setup + si4703_registers[SYSCONFIG2] |= (1<<SPACE0); //100kHz channel spacing for Europe + + si4703_registers[SYSCONFIG2] &= 0xFFF0; //Clear volume bits + si4703_registers[SYSCONFIG2] |= 0x0001; //Set volume to lowest + + // SI AN230 page 40 - Table 23 ('Good Quality Stations Only' Settings) + si4703_registers[SYSCONFIG2] |= (0xC<<SEEKTH); + si4703_registers[SYSCONFIG3] |= (0x7<<SKSNR); + si4703_registers[SYSCONFIG3] |= (0xF<<SKCNT); + /// + updateRegisters(); //Update + + wait_ms(110); //Max powerup time, from datasheet page 13 + +} + +//Read the entire register control set from 0x00 to 0x0F +uint8_t Si4703_Breakout::readRegisters(){ + + //Si4703 begins reading from register upper register of 0x0A and reads to 0x0F, then loops to 0x00. +// Wire.requestFrom(SI4703, 32); //We want to read the entire register set from 0x0A to 0x09 = 32 uint8_ts. + char data[32]; + uint8_t ack = i2c_->read(SI4703, data, 32); //Read in these 32 uint8_ts + + if (ack != 0) { //We have a problem! + return(FAIL); + } + +//Remember, register 0x0A comes in first so we have to shuffle the array around a bit + for (int y=0; y<6; y++) + { + si4703_registers[0x0A+y] = 0; + si4703_registers[0x0A+y] = data[(y*2)+1]; + si4703_registers[0x0A+y] |= (data[(y*2)] << 8); + } + + for (int y=0; y<10; y++) + { + si4703_registers[y] = 0; + si4703_registers[y] = data[(12)+(y*2)+1]; + si4703_registers[y] |= (data[(12)+(y*2)] << 8); + } +//We're done! +/// + return(SUCCESS); +} + +//Write the current 9 control registers (0x02 to 0x07) to the Si4703 +//It's a little weird, you don't write an I2C address +//The Si4703 assumes you are writing to 0x02 first, then increments +uint8_t Si4703_Breakout::updateRegisters() { + + char data[12]; + + //First we send the 0x02 to 0x07 control registers + //In general, we should not write to registers 0x08 and 0x09 + for(int regSpot = 0x02 ; regSpot < 0x08 ; regSpot++) { + data[(regSpot-2)*2] = si4703_registers[regSpot] >> 8; + data[((regSpot-2)*2)+1] = si4703_registers[regSpot] & 0x00FF; + } + + uint8_t ack = i2c_->write(SI4703, data, 12); // a write command automatically begins with register 0x02 so no need to send a write-to address + + if(ack != 0) { //We have a problem! + return(FAIL); + } + + return(SUCCESS); +} + +//Returns The Value of a Register +uint16_t Si4703_Breakout::getRegister(uint8_t regNum) +{ + readRegisters(); + return si4703_registers[regNum]; +// No Error Status Checking +} + +//Seeks out the next available station +//Returns the freq if it made it +//Returns zero if failed +int Si4703_Breakout::seek(bool seekDirection){ + uint8_t ack; + readRegisters(); + //Set seek mode wrap bit + si4703_registers[POWERCFG] |= (1<<SKMODE); //Disallow wrap - if you disallow wrap, you may want to tune to 87.5 first + //si4703_registers[POWERCFG] &= ~(1<<SKMODE); //Allow wrap + if(seekDirection == SEEK_DOWN) si4703_registers[POWERCFG] &= ~(1<<SEEKUP); //Seek down is the default upon reset + else si4703_registers[POWERCFG] |= 1<<SEEKUP; //Set the bit to seek up + + si4703_registers[POWERCFG] |= (1<<SEEK); //Start seek + updateRegisters(); //Seeking will now start + + //Poll to see if STC is set + while(1) { + ack = readRegisters(); + wait_ms(1); // Just In Case... + if (((si4703_registers[STATUSRSSI] & (1<<STC)) != 0) || (ack != SUCCESS)) break; //Tuning complete! (or FAILED) + } + + readRegisters(); + int valueSFBL = si4703_registers[STATUSRSSI] & (1<<SFBL); //Store the value of SFBL + si4703_registers[POWERCFG] &= ~(1<<SEEK); //Clear the seek bit after seek has completed + updateRegisters(); + + //Wait for the si4703 to clear the STC as well + while(1) { + ack = readRegisters(); + wait_ms(1); // Just In Case... + if (((si4703_registers[STATUSRSSI] & (1<<STC)) == 0) || (ack != SUCCESS)) break; //Tuning complete! (or FAILED) + } + + if(valueSFBL) { //The bit was set indicating we hit a band limit or failed to find a station + return(0); + } +return getChannel(); +} + +//Reads the current channel from READCHAN +//Returns a number like 973 for 97.3MHz +int Si4703_Breakout::getChannel() { + readRegisters(); + int channel = (si4703_registers[READCHAN] & 0x03FF); //Mask out everything but the lower 10 bits + //Freq(MHz) = 0.100(in Europe) * Channel + 87.5MHz + //X = 0.1 * Chan + 87.5 + channel += 875; //98 + 875 = 973 ( for 97.3 MHz ) + return(channel); +} + +void Si4703_Breakout::printRegs() { + readRegisters(); + for (int x=0; x<16; x++) { pc->printf("Reg# 0x%X = 0x%X\r\n",x,si4703_registers[x]); wait_ms(1); } +} \ No newline at end of file