Si4735 Library with RDS/RBDS control functions

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Si4735.cpp Source File

Si4735.cpp

00001 /* mbed Si4735 Library
00002  * Brett Wilson and Brett Berry
00003  * Georgia Tech ECE 4180
00004  * Note: this code has only been tested for FM.
00005  * Ported from ...
00006 
00007  * Arduino Si4735 Library
00008  * Written by Ryan Owens for SparkFun Electronics
00009  * 5/17/11
00010  *
00011  * This library is for use with the SparkFun Si4735 Shield
00012  * Released under the 'Buy Me a Beer' license
00013  * (If we ever meet, you buy me a beer)
00014  *
00015  * See the header file for better function documentation.
00016  *
00017  * See the example sketches to learn how to use the library in your code.
00018 */
00019 
00020 #include "Si4735.h"
00021 #include "mbed.h"
00022 
00023 //This is just a constructor.
00024 Si4735::Si4735(PinName sda, PinName scl, PinName RST_, Serial *pc) :
00025     _RST_(RST_)
00026 {
00027     i2c_ = new I2C(sda, scl);
00028     i2c_->frequency(100000);
00029     this->pc = pc;
00030 
00031     strcpy(pty_prev, " ");
00032     _locale = NA;
00033     _ab = 0;
00034     _year = 0;
00035     _month = 0;
00036     _day = 0;
00037     _hour = 0;
00038     _minute = 0;
00039     _newRadioText = 0;
00040     clearRDS();
00041 
00042 }
00043 
00044 
00045 void Si4735::begin(char mode)
00046 {
00047     _mode = mode;
00048     //Set the initial volume level.
00049     _currentVolume=63;
00050 
00051     // Reset the Si4735
00052     _RST_ = 0;
00053     wait(.2);
00054     _RST_ = 1;
00055     wait(.2);
00056 
00057 
00058     //Send the POWER_UP command
00059     switch(_mode) {
00060         case FM:
00061             sprintf(command, "%c%c%c", 0x01, 0x10, 0x05);
00062             break;
00063         case AM:
00064         case SW:
00065         case LW:
00066             sprintf(command, "%c%c%c", 0x01, 0x11, 0x05);
00067             break;
00068         default:
00069             return;
00070     }
00071     sendCommand(command, 3);
00072     wait(.2);
00073 
00074     // Set the RCLK to 32768Hz
00075     sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x02, 0x01, 0x80, 0x00);
00076     sendCommand(command, 6);
00077     wait(.2);
00078 
00079     // Set default frequency to 99.7 MHz FM
00080     sprintf(command, "%c%c%c%c", 0x20, 0x00, 0x26, 0xF2);
00081     sendCommand(command, 4);
00082     wait(.2);
00083 
00084     //Set the volume to the current value.
00085     sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x00, 0x00, _currentVolume);
00086     sendCommand(command, 6);
00087     wait(.2);
00088 
00089     //Disable Mute
00090     sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x01, 0x00, 0x00);
00091     sendCommand(command, 6);
00092     wait(.2);
00093 
00094     setProperty(0x1502, 0xAA01);
00095 
00096     //Set the seek band for the desired mode (AM and FM can use default values)
00097     switch(_mode) {
00098         case SW:
00099             //Set the lower band limit for Short Wave Radio to 2300 kHz
00100             sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x34, 0x00, 0x08, 0xFC);
00101             sendCommand(command, 6);
00102             wait(.001);
00103             //Set the upper band limit for Short Wave Radio to 23000kHz
00104             sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x34, 0x01, 0x59, 0xD8);
00105             sendCommand(command, 6);
00106             wait(.001);
00107             break;
00108         case LW:
00109             //Set the lower band limit for Long Wave Radio to 152 kHz
00110             sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x34, 0x00, 0x00, 0x99);
00111             sendCommand(command, 6);
00112             wait(.001);
00113             //Set the upper band limit for Long Wave Radio to 279 kHz
00114             sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x34, 0x01, 0x01, 0x17);
00115             sendCommand(command, 6);
00116             wait(.001);
00117             break;
00118         default:
00119             break;
00120     }
00121 
00122 }
00123 
00124 /*
00125 * Description: Tunes the Si4735 to a frequency
00126 *
00127 * Params: int frequency - The desired frequency in kHz
00128 *
00129 * Returns: True if tune was successful
00130 *            False if tune was unsuccessful
00131 */
00132 bool Si4735::tuneFrequency(int frequency)
00133 {
00134     //Split the desired frequency into two character for use in the
00135     //set frequency command.
00136     char highByte = frequency >> 8;
00137     char lowByte = frequency & 0x00FF;
00138 
00139     //Depending on the current mode, set the new frequency.
00140     switch(_mode) {
00141         case FM:
00142             sprintf(command, "%c%c%c%c", 0x20, 0x00, highByte, lowByte);
00143             break;
00144         case AM:
00145         case SW:
00146         case LW:
00147             sprintf(command, "%c%c%c%c", 0x40, 0x00, highByte, lowByte);
00148             break;
00149         default:
00150             break;
00151     }
00152     sendCommand(command, 4);
00153     wait(.1);
00154     clearRDS();
00155     return true;
00156 }
00157 
00158 //This function does not work unless you bypass the buffer chip!
00159 int Si4735::getFrequency(void)
00160 {
00161     char data[8];
00162     int freq = 0;
00163     // FM only
00164     i2c_->start();
00165     i2c_->write(address_write);
00166     i2c_->write(0x22);
00167     i2c_->write(0x02);
00168     i2c_->stop();
00169     i2c_->start();
00170     i2c_->write(address_read);
00171     for (int i=0; i<8; i++)
00172         data[i] = i2c_->read(1);
00173     //i2c_->read(address_read, data, 8, false);
00174     i2c_->stop();
00175     freq = data[2];
00176     freq = (freq << 8) | data[3];
00177     return freq;
00178 }
00179 
00180 bool Si4735::seekUp(void)
00181 {
00182     //Use the current mode selection to seek up.
00183     switch(_mode) {
00184         case FM:
00185             sprintf(command, "%c%c", 0x21, 0x0C);
00186             sendCommand(command, 2);
00187             break;
00188         case AM:
00189         case SW:
00190         case LW:
00191             sprintf(command, "%c%c%c%c%c%c", 0x41, 0x0C, 0x00, 0x00, 0x00, 0x00);
00192             sendCommand(command, 6);
00193             break;
00194         default:
00195             break;
00196     }
00197     wait(.001);
00198     clearRDS();
00199     return true;
00200 }
00201 
00202 bool Si4735::seekDown(void)
00203 {
00204     //Use the current mode selection to seek down.
00205     switch(_mode) {
00206         case FM:
00207             sprintf(command, "%c%c", 0x21, 0x04);
00208             sendCommand(command, 2);
00209             break;
00210         case AM:
00211         case SW:
00212         case LW:
00213             sprintf(command, "%c%c%c%c%c%c", 0x41, 0x04, 0x00, 0x00, 0x00, 0x00);
00214             sendCommand(command, 6);
00215             break;
00216         default:
00217             break;
00218     }
00219     wait(.001);
00220     clearRDS();
00221     return true;
00222 }
00223 
00224 void Si4735::volumeUp(void)
00225 {
00226     //If we're not at the maximum volume yet, increase the volume
00227     if(_currentVolume < 63) {
00228         _currentVolume+=1;
00229         //Set the volume to the current value.
00230         sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x00, 0x00, _currentVolume);
00231         sendCommand(command, 6);
00232         wait(.01);
00233     }
00234 }
00235 
00236 void Si4735::volumeDown(void)
00237 {
00238     //If we're not at the maximum volume yet, increase the volume
00239     if(_currentVolume > 0) {
00240         _currentVolume-=1;
00241         //Set the volume to the current value.
00242         sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x00, 0x00, _currentVolume);
00243         sendCommand(command, 6);
00244         wait(.01);
00245     }
00246 }
00247 
00248 void Si4735::mute(void)
00249 {
00250     //Disable Mute
00251     sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x01, 0x00, 0x03);
00252     sendCommand(command, 6);
00253     wait(.001);
00254 }
00255 
00256 void Si4735::unmute(void)
00257 {
00258     //Disable Mute
00259     sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, 0x40, 0x01, 0x00, 0x00);
00260     sendCommand(command, 6);
00261     wait(.001);
00262 }
00263 
00264 void Si4735::end(void)
00265 {
00266     sprintf(command, "%c", 0x11);
00267     sendCommand(command, 1);
00268     wait(.001);
00269 }
00270 
00271 
00272 void Si4735::setProperty(int address, int value)
00273 {
00274     sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, (address>>8)&255, address&255, (value>>8)&255, value&255);
00275     sendCommand(command, 6);
00276     wait(0.01);
00277 }
00278 
00279 bool Si4735::readRDS()
00280 {
00281     char R[16];
00282     sprintf(command, "%c%c", 0x24, 0x00);
00283     sendCommand(command, 2);
00284     i2c_->start();
00285     i2c_->write(address_read);
00286     for (int i=0; i<16; i++)
00287         R[i] = i2c_->read(1);
00288     i2c_->stop();
00289 
00290     bool ps_rdy = false;
00291     char pty = ((R[6]&3) << 3) | ((R[7] >> 5)&7);
00292     ptystr(pty);
00293     char type = (R[6]>>4) & 15;
00294     bool version = bitRead(R[6], 4);
00295     bool tp = bitRead(R[5], 4);
00296     int pi;
00297     if (version == 0) {
00298         pi = R[4];
00299         pi = (pi << 8) | R[5];
00300     } else {
00301         pi = R[8];
00302         pi = (pi << 8) | R[9];
00303     }
00304     if(pi>=21672) {
00305         _csign[0]='W';
00306         pi-=21672;
00307     } else if(pi<21672 && pi>=4096) {
00308         _csign[0]='K';
00309         pi-=4096;
00310     } else {
00311         pi=-1;
00312     }
00313     if(pi>=0) {
00314         _csign[1]=char(pi/676+65);//char(pi/676);
00315         _csign[2]=char((pi - 676*int(pi/676))/26+65);//char((pi-676*(_csign[1]))/26+65);
00316         _csign[3]=char(((pi - 676*int(pi/676))%26)+65);//char((pi-676*(_csign[1]))%26+65);
00317 //_csign[1]+=65;
00318         _csign[4]='\0';
00319     } else {
00320         _csign[0]='U';
00321         _csign[1]='N';
00322         _csign[2]='K';
00323         _csign[3]='N';
00324         _csign[4]='\0';
00325     }
00326     if (type == 0) {
00327         bool ta = bitRead(R[7], 4);
00328         bool ms = bitRead(R[7], 3);
00329         char addr = R[7] & 3;
00330         bool diInfo = bitRead(R[7], 2);
00331 
00332 // Groups 0A & 0B: to extract PS segment we need blocks 1 and 3
00333         if (addr >= 0 && addr<= 3) {
00334             if (R[10] != '\0')
00335                 _ps[addr*2] = R[10];
00336             if (R[11] != '\0')
00337                 _ps[addr*2+1] = R[11];
00338             ps_rdy=(addr==3);
00339         }
00340         printable_str(_ps, 8);
00341     }
00342 
00343     else if (type == 2) {
00344         int addressRT = R[7] & 0xf; // Get rightmost 4 bits
00345         bool ab = bitRead(R[7], 4);
00346         bool cr = 0; //indicates that a carriage return was received
00347         char len = 64;
00348         if (version == 0) {
00349             if (addressRT >= 0 && addressRT <= 15) {
00350                 if (R[8] != 0x0D )
00351                     _disp[addressRT*4] = R[8];
00352                 else {
00353                     len=addressRT*4;
00354                     cr=1;
00355                 }
00356                 if (R[9] != 0x0D)
00357                     _disp[addressRT*4+1] = R[9];
00358                 else {
00359                     len=addressRT*4+1;
00360                     cr=1;
00361                 }
00362                 if (R[10] != 0x0D)
00363                     _disp[addressRT*4+2] = R[10];
00364                 else {
00365                     len=addressRT*4+2;
00366                     cr=1;
00367                 }
00368                 if (R[11] != 0x0D)
00369                     _disp[addressRT*4+3] = R[11];
00370                 else {
00371                     len=addressRT*4+3;
00372                     cr=1;
00373                 }
00374             }
00375         } else {
00376             if (addressRT >= 0 && addressRT <= 7) {
00377                 if (R[10] != '\0')
00378                     _disp[addressRT*2] = R[10];
00379                 if (R[11] != '\0')
00380                     _disp[addressRT*2+1] = R[11];
00381             }
00382         }
00383         if(cr) {
00384             for (char i=len; i<64; i++) _disp[i] = ' ';
00385         }
00386         if (ab != _ab) {
00387              for (char i=0; i<64; i++) _disp[i] = ' ';
00388             _disp[64] = '\0';
00389             _newRadioText=1;
00390         } else {
00391             _newRadioText=0;
00392         }
00393         _ab = ab;
00394         printable_str(_disp, 64);
00395     }
00396 
00397     else if (type == 4 && version == 0) {
00398         unsigned long MJD = (R[7]&3<<15 | (unsigned int)(R[8])<<7 | (R[9]>>1)&127)&0x01FFFF;
00399         unsigned int Y=(MJD-15078.2)/365.25;
00400         unsigned int M=(MJD-14956.1-(unsigned int)(Y*365.25))/30.6001;
00401         char K;
00402         if(M==14||M==15)
00403             K=1;
00404         else
00405             K=0;
00406         _year=(Y+K)%100;
00407         _month=M-1-K*12;
00408         _day=MJD-14956-(unsigned int)(Y*365.25)-(unsigned int)(M*30.6001);
00409 
00410         char sign=(((R[11]>>5)&1)/(-1));
00411         if(sign==0)sign=1; //Make sign a bipolar variable
00412         char offset=sign*(R[11]&31);
00413 
00414         _hour=((R[9]&1)<<4) | ((R[10]>>4)&15);
00415         _minute=((R[10]&15)<<2 | (R[11]>>6)&3);
00416         _hour= (_hour + (offset/2) + 24)%24;
00417         _minute=(_minute + (offset)%2*30 + 60)%60;
00418     }
00419     wait(0.04);
00420     return ps_rdy;
00421 }
00422 
00423 void Si4735::getRDS()
00424 {
00425     strcpy(tuned.programService, _ps);
00426     strcpy(tuned.radioText, _disp);
00427     strcpy(tuned.programType, _pty);
00428     strcpy(tuned.callSign, _csign);
00429     tuned.newRadioText=_newRadioText;
00430 }
00431 
00432 void Si4735::getTime()
00433 {
00434     date.year=_year;
00435     date.month=_month;
00436     date.day=_day;
00437     date.hour=_hour;
00438     date.minute=_minute;
00439 }
00440 
00441 bool Si4735::getIntStatus(void)
00442 {
00443     char response;
00444     i2c_->start();
00445     i2c_->write(address_write);
00446     i2c_->write(0x14);
00447     i2c_->stop();
00448 
00449     i2c_->start();
00450     i2c_->write(address_read);
00451     response = i2c_->read(1);
00452     i2c_->stop();
00453     (*pc).printf("Int Status Response: %c\n", response);
00454 
00455     if(response == NULL) {
00456         return false;
00457     }
00458     return true;
00459 }
00460 
00461 void Si4735::clearRDS(void)
00462 {
00463     char i;
00464     for(i=0; i<64; i++) _disp[i] ='\0';
00465     for(i=0; i<8; i++) _ps[i] ='\0';
00466     for(i=0; i<16; i++) _pty[i] ='\0';
00467     for(i=0; i<4; i++) _csign[i]='\0';
00468 }
00469 
00470 void Si4735::setLocale(char locale)
00471 {
00472     _locale=locale;
00473 //Set the deemphasis to match the locale
00474     switch(_locale) {
00475         case NA:
00476             setProperty(0x1100, 0x0002);
00477             break;
00478         case EU:
00479             setProperty(0x1100, 0x0001);
00480             break;
00481         default:
00482             break;
00483     }
00484 }
00485 
00486 char Si4735::getLocale(void)
00487 {
00488     return _locale;
00489 }
00490 
00491 void Si4735::ptystr(char pty)
00492 {
00493 // Translate the Program Type bits to the RBDS 16-character fields
00494     if(pty>=0 && pty<32) {
00495         char* pty_LUT[51] = {
00496             " None ",
00497             " News ",
00498             " Information ",
00499             " Sports ",
00500             " Talk ",
00501             " Rock ",
00502             " Classic Rock ",
00503             " Adult Hits ",
00504             " Soft Rock ",
00505             " Top 40 ",
00506             " Country ",
00507             " Oldies ",
00508             " Soft ",
00509             " Nostalgia ",
00510             " Jazz ",
00511             " Classical ",
00512             "Rhythm and Blues",
00513             " Soft R & B ",
00514             "Foreign Language",
00515             "Religious Music ",
00516             " Religious Talk ",
00517             " Personality ",
00518             " Public ",
00519             " College ",
00520             " Reserved -24- ",
00521             " Reserved -25- ",
00522             " Reserved -26- ",
00523             " Reserved -27- ",
00524             " Reserved -28- ",
00525             " Weather ",
00526             " Emergency Test ",
00527             " !!!ALERT!!! ",
00528             "Current Affairs ",
00529             " Education ",
00530             " Drama ",
00531             " Cultures ",
00532             " Science ",
00533             " Varied Speech ",
00534             " Easy Listening ",
00535             " Light Classics ",
00536             "Serious Classics",
00537             " Other Music ",
00538             " Finance ",
00539             "Children's Progs",
00540             " Social Affairs ",
00541             " Phone In ",
00542             "Travel & Touring",
00543             "Leisure & Hobby ",
00544             " National Music ",
00545             " Folk Music ",
00546             " Documentary "
00547         };
00548         if(_locale==NA) {
00549             strcpy(_pty, pty_LUT[pty]);
00550         } else if(_locale==EU) {
00551             char LUT[32] = {0, 1, 32, 2,
00552                             3, 33, 34, 35,
00553                             36, 37, 9, 5,
00554                             38, 39, 40, 41,
00555                             29, 42, 43, 44,
00556                             20, 45, 46, 47,
00557                             14, 10, 48, 11,
00558                             49, 50, 30, 31
00559                            };
00560             strcpy(_pty, pty_LUT[LUT[pty]]);
00561         } else {
00562             strcpy(_pty, " LOCALE UNKN0WN ");
00563         }
00564     } else {
00565         strcpy(_pty, " PTY ERROR ");
00566     }
00567 }
00568 
00569 void Si4735::refreshDisplay(int next_stn)
00570 {
00571     bool ps_rdy = readRDS();
00572     if(!ps_rdy)
00573         return;
00574     getRDS();
00575     //getTime();
00576     bool refresh = false;
00577     if(strcmp(tuned.programType,pty_prev)) {
00578         //refresh = true;
00579         strcpy(pty_prev,tuned.programType);
00580     }
00581     if (strlen(tuned.programService) == 8) {
00582         if(ps_rdy) {
00583             if(strcmp(tuned.programService,ps_prev)) {
00584                 refresh = true;
00585                 strcpy(ps_prev,tuned.programService);
00586             }
00587         }
00588     } else if(strcmp(tuned.programType," None ")) {
00589         if(!strcmp("01234567",ps_prev)) {
00590             strcpy(tuned.programService, "mBed-Rdo");
00591             strcpy(ps_prev,"01234567");
00592             //refresh = true;
00593         }
00594     }
00595     if(!tuned.newRadioText) {
00596         strcpy(RadioText, tuned.radioText);
00597     }
00598 
00599     if(refresh) {
00600         (*pc).printf("\x1B[2J");
00601         (*pc).printf("\x1B[H");
00602         (*pc).printf("Current Station: %3.2f MHz FM\n", float(getFrequency())/100.0);
00603         (*pc).printf("Program Type    = %s\n", tuned.programType);
00604         (*pc).printf("Program Service = %s\n", tuned.programService);
00605         (*pc).printf("Radio Text      = %s\n", RadioText);
00606         (*pc).printf("Call Sign       = %s\n", tuned.callSign);
00607         //(*pc).printf("Time = %2d:%2d\n", date.hour, date.minute);
00608     }
00609 }
00610 
00611 
00612 /*******************************************
00613 *
00614 * Private Functions
00615 *
00616 *******************************************/
00617 
00618 void Si4735::sendCommand(char * command, int length)
00619 {
00620     //ack = i2c_->write(address_write, command, length, false);
00621     i2c_->start();
00622     bool ack = i2c_->write(address_write);
00623     for(int i=0; i<length; i++)
00624         ack = i2c_->write(command[i]);
00625     char CTS = i2c_->read(1);
00626     i2c_->stop();
00627 }
00628 
00629 
00630 bool Si4735::bitRead(char value, int index)
00631 {
00632     return ((value >> index) & 1) == 1;
00633 }
00634 void Si4735::printable_str(char * str, int length)
00635 {
00636     for(int i=0; i<length; i++) {
00637         if( (str[i]!=0 && str[i]<32) || str[i]>126 ) str[i]=' ';
00638     }
00639 }