By Alvin Alexander. Last updated: May 8, 2014
Source code for an Arduino “radio” project. Per the author, “The base code is from Spark Fun for Si4703 radio hardware. It uses a serial interface to run the radio. I use hardware to run it.” I’m about to do something similar with a Raspberry Pi, and I’m using this code as reference. I saw this code referenced in the comments on this Sparkfun page.
The code:
/* //************************************************************ // Radio project 2 * // Version 1.1 * // 30 Dec 2012 * // Use Serial LCD for display * // See http://www.arduino.cc/playground/Learning/SparkFunSerLCD // Michael R. Wild * //************************************************************ The base code is from Spark Fun for Si4703 radio hardware. It uses a serial interface to run the radio. I use hardware to run it. 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). To use this code, connect the following 5 wires: Arduino : Si470x board 3.3V : VCC GND : GND A5 : SCLK A4 : SDIO D2 : RST A0 : Trimpot (optional) MRW--Not used. Look for serial output at 57600bps. MRW--9600 is better for me. 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. MRW--Have one now. 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// MRW #include int STATUS_LED = 13; int resetPin = 2; int SDIO = A4; //SDA/A4 on Arduino int SCLK = A5; //SCL/A5 on Arduino char printBuffer[50]; uint16_t si4703_registers[16]; //There are 16 registers, each 16 bits large #define FAIL 0 #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). #define SEEK_DOWN 0 //Direction used for seeking. Default is down #define SEEK_UP 1 //Define the register names #define DEVICEID 0x00 #define CHIPID 0x01 #define POWERCFG 0x02 #define CHANNEL 0x03 #define SYSCONFIG1 0x04 #define SYSCONFIG2 0x05 #define STATUSRSSI 0x0A #define READCHAN 0x0B #define RDSA 0x0C #define RDSB 0x0D #define RDSC 0x0E #define RDSD 0x0F //Register 0x02 - POWERCFG #define SMUTE 15 #define DMUTE 14 #define SKMODE 10 #define SEEKUP 9 #define SEEK 8 //Register 0x03 - CHANNEL #define TUNE 15 //Register 0x04 - SYSCONFIG1 #define RDS 12 #define DE 11 //Register 0x05 - SYSCONFIG2 #define SPACE1 5 #define SPACE0 4 //Register 0x0A - STATUSRSSI #define RDSR 15 #define STC 14 #define SFBL 13 #define AFCRL 12 #define RDSS 11 #define STEREO 8 //#define TRACE 1 //Use this define create TRACE code //************************************************************************ // Pins 7 for display #define txPin 7 #define LCDMax 16 SoftwareSerial LCD = SoftwareSerial(0, txPin); const int LCDdelay=10; #define RefreshRate 1024 //************************************************************************ // Physical Controls #define seekUpButton 8 #define seekDownButton 9 #define volumeNob A3 //************************************************************************ // House keeping char version_code[ ] = "1.5"; char project_name[ ] = "Wild's Radio"; //******************************** //* Serial LCD * //******************************** void lcdPosition(int col, int row) { LCD.write(0xFE); //command flag LCD.write((col + row*64 + 128)); //position delay(LCDdelay); } void clearLCD(){ LCD.write(0xFE); //command flag LCD.write(0x01); //clear command. delay(LCDdelay); } void backlightOn() { //turns on the backlight LCD.write(0x7C); //command flag for backlight stuff LCD.write(157); //light level. delay(LCDdelay); } void backlightOff(){ //turns off the backlight LCD.write(0x7C); //command flag for backlight stuff LCD.write(128); //light level for off. delay(LCDdelay); } void serCommand(){ //a general function to call the command flag for issuing all other commands LCD.write(0xFE); } //************************************************************************ void setup() { pinMode(13, OUTPUT); pinMode(A0, INPUT); //Optional trimpot for analog station control Serial.begin(9600); //MRW 9600 works better for me Serial.println(); si4703_init(); //Init the Si4703 - we need to toggle SDIO before Wire.begin takes over. si4703_readRegisters(); si4703_registers[POWERCFG] ^= (1<<dmute); toggle="" mute="" bit="" si4703_updateregisters();="" *************************************************************************="" set="" up="" the="" lcd's="" number="" of="" columns="" and="" rows:="" pinmode(txpin,="" output);="" lcd.begin(9600);="" clearlcd();="" backlighton();="" lcdposition(0,0);="" print="" a="" message="" to="" lcd.="" this="" lets="" us="" know="" about="" reset="" lcd.print(project_name);="" lcdposition(0,1);="" lcd.print("version="" ");="" lcd.print(version_code);="" delay(500);="" lcd.print("="" ****************************************************************************="" physical="" controls="" pinmode(seekupbutton,="" input);="" pinmode(seekdownbutton,="" pinmode(volumenob,="" volume="" control="" }="" void="" loop()="" {="" define="" values="" used="" for="" rsd="" display="" int="" pi_code="0," previouspi="-1;" pi="" code="" radio="" station="" uint16_t="" breg[lcdmax+1],="" creg[lcdmax+1],="" dreg[lcdmax+1];="" bdata,="" cdata,="" ddata;="" char="" message0_buffer[lcdmax+1]=" " ;="" 1234567890123456="" message0_lots[100],="" hashrsd;="" volumesetting,="" previousvolumesetting;="" use="" these="" refreshcount="0;" cause="" refresh="" every="" once="" in="" while="" option;="" currentchannel,="" previouschannel;="" currentchannel="971;//Default" unit="" known="" good="" local="" gotochannel(currentchannel);="" si4703_readregisters();="" si4703_registers[powercfg]="" ^="(1< RefreshRate) { refreshCount = 0; previousChannel = - 1; //Force a restart } else ++refreshCount; // Get channel and check if change currentChannel = readChannel(); if (currentChannel != previousChannel) {//print channel previousChannel = currentChannel; clearLCD(); backlightOn(); sprintf(printBuffer, "FM %02d.%01dMHz", currentChannel / 10, currentChannel % 10); lcdPosition(0,0); LCD.print(printBuffer); PI_Code = -1; // force change }// currentChannel != // Check for RSD information si4703_readRegisters(); if(si4703_registers[STATUSRSSI] & (1< > 8; Al = (si4703_registers[RDSA] & 0x00FF); Bh = (si4703_registers[RDSB] & 0xFF00) >> 8; Bl = (si4703_registers[RDSB] & 0x00FF); Ch = (si4703_registers[RDSC] & 0xFF00) >> 8; Cl = (si4703_registers[RDSC] & 0x00FF); Dh = (si4703_registers[RDSD] & 0xFF00) >> 8; Dl = (si4703_registers[RDSD] & 0x00FF); // Used for spottin duplcate messages BData = si4703_registers[RDSB]; CData = si4703_registers[RDSC]; DData = si4703_registers[RDSD]; hashRSD = (BData * 1000) + (CData * 100) + DData;//should be unique for each message // Break out into useful values PI_Code = si4703_registers[RDSA]; byte groupTypeCode, B_code, trafficCode, PTY_code, extraCode; groupTypeCode = (Bh & 0xF0) >> 4; B_code = (Bh & 0x08) >> 3; trafficCode = (Bh & 0x04) >> 2; PTY_code = (Bh & 0x03) << 3; PTY_code = PTY_code + ((Bl & 0xE0) >> 5); extraCode = (Bl & 0x0F); // Not sure what to call this #ifdef TRACE sprintf(printBuffer, "PI: %05d", PI); Serial.println(printBuffer); sprintf(printBuffer, "Group Type Code: %02x", groupTypeCode); Serial.println(printBuffer); sprintf(printBuffer, "B_code: %01d", B_code); Serial.println(printBuffer); sprintf(printBuffer, "Traffic code: %01d", trafficCode); Serial.println(printBuffer); sprintf(printBuffer, "PTY code: %01d", PTY_code); Serial.println(printBuffer); sprintf(printBuffer, "Extra code: %02x", extraCode); Serial.println(printBuffer); sprintf(printBuffer, "RDS: %02x %02x %02x %02x %02x %02x Hex !", Bh, Bl, Ch, Cl, Dh, Dl); Serial.println(printBuffer); #endif // Detect message change for 0A and 0B only and only in Western Characters if (groupTypeCode == 0x00) { if (PI_Code != previousPI) { previousPI = PI_Code; for (int i=0; i > 8; byte DLow = (DReg[i] & 0x00FF); sprintf(printBuffer, "%c%c", char(DHigh), char(DLow) ); Serial.print(printBuffer); } Serial.println(); } Serial.println(); #endif }//Found == FAIL else {// SUCCESS } //here lcdPosition(0,1); LCD.print(message0_Buffer); #ifdef TRACE Serial.println(message0_Buffer); #endif }// if group 0x00 } // If we have and RSD // Check if we have a button pressed if (digitalRead(seekUpButton) != 0) { seek(SEEK_UP); }//seekup if (digitalRead(seekDownButton) != 0) { seek(SEEK_DOWN); }//seekdown volumeSetting = (analogRead(volumeNob)/ 64) ; if (previousVolumeSetting != volumeSetting) { si4703_registers[SYSCONFIG2] &= 0xFFF0; //Clear volume bits si4703_registers[SYSCONFIG2] |= volumeSetting; //Set new volume si4703_updateRegisters(); //Update previousVolumeSetting = volumeSetting; }//volume change } // check for serial input option = Serial.read(); // read input }//While(1) } //************************************************************************************************* //this is more SparkFUn Code //Given a channel, tune to it //Channel is in MHz, so 973 will tune to 97.3MHz //Note: gotoChannel will go to illegal channels (ie, greater than 110MHz) //It's left to the user to limit these if necessary //Actually, during testing the Si4703 seems to be internally limiting it at 87.5. Neat. void gotoChannel(int newChannel){ //Freq(MHz) = 0.200(in USA) * Channel + 87.5MHz //97.3 = 0.2 * Chan + 87.5 //9.8 / 0.2 = 49 newChannel *= 10; //973 * 10 = 9730 newChannel -= 8750; //9730 - 8750 = 980 #ifdef IN_EUROPE newChannel /= 10; //980 / 10 = 98 #else newChannel /= 20; //980 / 20 = 49 #endif //These steps come from AN230 page 20 rev 0.5 si4703_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="" si4703_updateregisters();="" delay(60);="" wait="" 60ms="" -="" you="" can="" use="" or="" skip="" this="" delay="" poll="" see="" if="" stc="" is="" while(1)="" {="" si4703_readregisters();="" if(="" (si4703_registers[statusrssi]="" &="" (1>="" 8;="" byte="" low_byte="si4703_registers[regSpot]" &="" 0x00ff;="" wire.write(high_byte);="" upper="" 8="" bits="" wire.write(low_byte);="" lower="" }="" end="" transmission="" ack="Wire.endTransmission();" if(ack="" !="0)" we="" have="" a="" problem!="" serial.print("write="" fail:");="" no="" ack!="" serial.println(ack,="" dec);="" i2c="" error:="" 0="success," 1="data" too="" long,="" 2="rx" nack="" on="" address,="" 3="rx" data,="" 4="other" error="" return(fail);="" return(success);="" read="" entire="" register="" control="" from="" 0x00="" 0x0f="" void="" si4703_readregisters(void){="" si4703="" begins="" reading="" of="" 0x0a="" and="" reads="" 0x0f,="" then="" loops="" 0x00.="" wire.requestfrom(si4703,="" 32);="" want="" 0x09="32" bytes.="" while(wire.available()="" <="" 32)="" ;="" for="" 16="" words="" 32="" bytes="" come="" back="" slave="" device="" may="" some="" time-out="" here="" remember,="" comes="" in="" first="" so="" shuffle="" array="" around="" for(int="" x="0x0A" x++)="" these="" if(x="=" 0x10)="" loop="" zero="" si4703_registers[x]="Wire.read()" <<="" |="Wire.read();" 0x09)="" break;="" we're="" done!="" si4703_printregisters(void)="" registers="" print="" response="" debugging="" sprintf(printbuffer,="" "reg="" 0x%02x="0x%04X"," x,="" si4703_registers[x]);="" serial.println(printbuffer);=""