Si4703 - Example Code Si4703 FM Tuner
Si4703 - Example Code Si4703 FM Tuner
pde
/*
1-6-2011
Spark Fun Electronics 2011
Nathan Seidle
This code is public domain but you buy me a beer if you use this and we meet someday (Beerware
license).
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.
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.
*/
#include <Wire.h>
#define FAIL 0
www.geeetech.com/Documents/Si4703_Example.pde 1/10
6/7/2021 www.geeetech.com/Documents/Si4703_Example.pde
#define SUCCESS 1
#define SI4703 0x10 //0b._001.0000 = I2C address of Si4703 - note that the Wire function assumes non-
left-shifted I2C address, not 0b.0010.000W
#define I2C_FAIL_MAX 10 //This is the number of attempts we will try to contact the device before
erroring out
//#define IN_EUROPE //Use this define to setup European FM reception. I wuz there for a day during
testing (TEI 2011).
void setup() {
pinMode(13, OUTPUT);
pinMode(A0, INPUT); //Optional trimpot for analog station control
Serial.begin(57600);
Serial.println();
si4703_init(); //Init the Si4703 - we need to toggle SDIO before Wire.begin takes over.
void loop() {
www.geeetech.com/Documents/Si4703_Example.pde 2/10
6/7/2021 www.geeetech.com/Documents/Si4703_Example.pde
char option;
char vol = 15;
int currentChannel = 973; //Default the unit to a known good local radio station
gotoChannel(currentChannel);
while(1) {
Serial.println();
Serial.println("Si4703 Configuration");
currentChannel = readChannel();
sprintf(printBuffer, "Current station: %02d.%01dMHz", currentChannel / 10, currentChannel % 10);
Serial.println(printBuffer);
#ifdef IN_EUROPE
Serial.println("Configured for European Radio");
#endif
while (!Serial.available());
option = Serial.read();
if(option == '1') {
Serial.println("Tune to 97.3");
currentChannel = 973;
//currentChannel = 1023;
gotoChannel(currentChannel);
}
else if(option == '2') {
Serial.println("Mute toggle");
si4703_readRegisters();
si4703_registers[POWERCFG] ^= (1<<DMUTE); //Toggle Mute bit
si4703_updateRegisters();
}
else if(option == '3') {
si4703_readRegisters();
Serial.println();
Serial.println("Radio Status:");
www.geeetech.com/Documents/Si4703_Example.pde 3/10
6/7/2021 www.geeetech.com/Documents/Si4703_Example.pde
if(si4703_registers[STATUSRSSI] & (1<<STC)) Serial.print(" (Tune Complete)");
if(si4703_registers[STATUSRSSI] & (1<<SFBL))
Serial.print(" (Seek Fail)");
else
Serial.print(" (Seek Successful!)");
if(si4703_registers[STATUSRSSI] & (1<<AFCRL)) Serial.print(" (AFC/Invalid Channel)");
if(si4703_registers[STATUSRSSI] & (1<<RDSS)) Serial.print(" (RDS Synch)");
si4703_readRegisters();
if(si4703_registers[STATUSRSSI] & (1<<RDSR)){
Serial.println("We have RDS!");
byte Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl;
Ah = (si4703_registers[RDSA] & 0xFF00) >> 8;
Al = (si4703_registers[RDSA] & 0x00FF);
Serial.print("RDS: ");
Serial.print(Ah);
Serial.print(Al);
Serial.print(Bh);
Serial.print(Bl);
Serial.print(Ch);
Serial.print(Cl);
Serial.print(Dh);
Serial.print(Dl);
Serial.println(" !");
int trimpot = 0;
for(int x = 0 ; x < 16 ; x++)
trimpot += analogRead(A0);
trimpot /= 16; //Take average of trimpot reading
Serial.print("Trim: ");
Serial.print(trimpot);
trimpot = map(trimpot, 0, 1023, 875, 1079); //Convert the trimpot value to a valid station
Serial.print(" station: ");
Serial.println(trimpot);
if(trimpot != currentChannel) {
currentChannel = trimpot;
gotoChannel(currentChannel);
}
delay(100);
}
}
else if(option == 'v') {
byte current_vol;
Serial.println();
Serial.println("Volume:");
Serial.println("+) Up");
Serial.println("-) Down");
Serial.println("x) Exit");
while(1){
if(Serial.available()){
option = Serial.read();
if(option == '+') {
si4703_readRegisters(); //Read the current register set
current_vol = si4703_registers[SYSCONFIG2] & 0x000F; //Read the current volume level
if(current_vol < 16) current_vol++; //Limit max volume to 0x000F
si4703_registers[SYSCONFIG2] &= 0xFFF0; //Clear volume bits
si4703_registers[SYSCONFIG2] |= current_vol; //Set new volume
si4703_updateRegisters(); //Update
Serial.print("Volume: ");
Serial.println(current_vol, DEC);
}
if(option == '-') {
si4703_readRegisters(); //Read the current register set
current_vol = si4703_registers[SYSCONFIG2] & 0x000F; //Read the current volume level
www.geeetech.com/Documents/Si4703_Example.pde 5/10
6/7/2021 www.geeetech.com/Documents/Si4703_Example.pde
if(current_vol > 0) current_vol--; //You can't go lower than zero
si4703_registers[SYSCONFIG2] &= 0xFFF0; //Clear volume bits
si4703_registers[SYSCONFIG2] |= current_vol; //Set new volume
si4703_updateRegisters(); //Update
Serial.print("Volume: ");
Serial.println(current_vol, DEC);
}
else if(option == 'x') break;
}
}
}
#ifdef IN_EUROPE
newChannel /= 10; //980 / 10 = 98
#else
newChannel /= 20; //980 / 20 = 49
#endif
while(1) {
si4703_readRegisters();
if( (si4703_registers[STATUSRSSI] & (1<<STC)) != 0) break; //Tuning complete!
Serial.println("Tuning");
}
si4703_readRegisters();
si4703_registers[CHANNEL] &= ~(1<<TUNE); //Clear the tune after a tune has completed
si4703_updateRegisters();
#ifdef IN_EUROPE
//Freq(MHz) = 0.100(in Europe) * Channel + 87.5MHz
//X = 0.1 * Chan + 87.5
channel *= 1; //98 * 1 = 98 - I know this line is silly, but it makes the code look uniform
#else
//Freq(MHz) = 0.200(in USA) * Channel + 87.5MHz
//X = 0.2 * Chan + 87.5
channel *= 2; //49 * 2 = 98
#endif
Serial.print("Trying station:");
Serial.println(readChannel());
}
www.geeetech.com/Documents/Si4703_Example.pde 7/10
6/7/2021 www.geeetech.com/Documents/Si4703_Example.pde
si4703_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
si4703_updateRegisters();
if(valueSFBL) { //The bit was set indicating we hit a band limit or failed to find a station
Serial.println("Seek limit hit"); //Hit limit of band during seek
return(FAIL);
}
//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_init(void) {
Serial.println("Initializing I2C and Si4703");
pinMode(resetPin, OUTPUT);
pinMode(SDIO, OUTPUT); //SDIO is connected to A4 for I2C
digitalWrite(SDIO, LOW); //A low SDIO indicates a 2-wire interface
digitalWrite(resetPin, LOW); //Put Si4703 into reset
delay(1); //Some delays while we allow pins to settle
digitalWrite(resetPin, HIGH); //Bring Si4703 out of reset with SDIO set to low and SEN pulled high
with on-board resistor
delay(1); //Allow Si4703 to come out of reset
Wire.begin(); //Now that the unit is reset and I2C inteface mode, we need to begin I2C
#ifdef IN_EUROPE
si4703_registers[SYSCONFIG1] |= (1<<DE); //50kHz Europe setup
si4703_registers[SYSCONFIG2] |= (1<<SPACE0); //100kHz channel spacing for Europe
#else
si4703_registers[SYSCONFIG2] &= ~(1<<SPACE1 | 1<<SPACE0) ; //Force 200kHz channel spacing for USA
#endif
Wire.beginTransmission(SI4703);
//A write command automatically begins with register 0x02 so no need to send a write-to address
//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++) {
byte high_byte = si4703_registers[regSpot] >> 8;
byte low_byte = si4703_registers[regSpot] & 0x00FF;
return(SUCCESS);
}
//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
bytes.
while(Wire.available() < 32) ; //Wait for 16 words/32 bytes to come back from slave I2C device
//We may want some time-out error here
//Remember, register 0x0A comes in first so we have to shuffle the array around a bit
for(int x = 0x0A ; ; x++) { //Read in these 32 bytes
if(x == 0x10) x = 0; //Loop back to zero
si4703_registers[x] = Wire.receive() << 8;
si4703_registers[x] |= Wire.receive();
if(x == 0x09) break; //We're done!
}
}
void si4703_printRegisters(void) {
//Read back the registers
si4703_readRegisters();
www.geeetech.com/Documents/Si4703_Example.pde 9/10
6/7/2021 www.geeetech.com/Documents/Si4703_Example.pde
www.geeetech.com/Documents/Si4703_Example.pde 10/10