Si4735 Library with RDS/RBDS control functions
Si4735.cpp
- Committer:
- kpatel70
- Date:
- 2012-10-11
- Revision:
- 0:ab340864b251
File content as of revision 0:ab340864b251:
/* 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_, Serial *pc) : _RST_(RST_) { i2c_ = new I2C(sda, scl); i2c_->frequency(100000); this->pc = pc; strcpy(pty_prev, " "); _locale = NA; _ab = 0; _year = 0; _month = 0; _day = 0; _hour = 0; _minute = 0; _newRadioText = 0; clearRDS(); } 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); setProperty(0x1502, 0xAA01); //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); clearRDS(); 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(0x02); 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); clearRDS(); 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); clearRDS(); 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); } void Si4735::end(void) { sprintf(command, "%c", 0x11); sendCommand(command, 1); wait(.001); } void Si4735::setProperty(int address, int value) { sprintf(command, "%c%c%c%c%c%c", 0x12, 0x00, (address>>8)&255, address&255, (value>>8)&255, value&255); sendCommand(command, 6); wait(0.01); } bool Si4735::readRDS() { char R[16]; sprintf(command, "%c%c", 0x24, 0x00); sendCommand(command, 2); i2c_->start(); i2c_->write(address_read); for (int i=0; i<16; i++) R[i] = i2c_->read(1); i2c_->stop(); bool ps_rdy = false; char pty = ((R[6]&3) << 3) | ((R[7] >> 5)&7); ptystr(pty); char type = (R[6]>>4) & 15; bool version = bitRead(R[6], 4); bool tp = bitRead(R[5], 4); int pi; if (version == 0) { pi = R[4]; pi = (pi << 8) | R[5]; } else { pi = R[8]; pi = (pi << 8) | R[9]; } if(pi>=21672) { _csign[0]='W'; pi-=21672; } else if(pi<21672 && pi>=4096) { _csign[0]='K'; pi-=4096; } else { pi=-1; } if(pi>=0) { _csign[1]=char(pi/676+65);//char(pi/676); _csign[2]=char((pi - 676*int(pi/676))/26+65);//char((pi-676*(_csign[1]))/26+65); _csign[3]=char(((pi - 676*int(pi/676))%26)+65);//char((pi-676*(_csign[1]))%26+65); //_csign[1]+=65; _csign[4]='\0'; } else { _csign[0]='U'; _csign[1]='N'; _csign[2]='K'; _csign[3]='N'; _csign[4]='\0'; } if (type == 0) { bool ta = bitRead(R[7], 4); bool ms = bitRead(R[7], 3); char addr = R[7] & 3; bool diInfo = bitRead(R[7], 2); // Groups 0A & 0B: to extract PS segment we need blocks 1 and 3 if (addr >= 0 && addr<= 3) { if (R[10] != '\0') _ps[addr*2] = R[10]; if (R[11] != '\0') _ps[addr*2+1] = R[11]; ps_rdy=(addr==3); } printable_str(_ps, 8); } else if (type == 2) { int addressRT = R[7] & 0xf; // Get rightmost 4 bits bool ab = bitRead(R[7], 4); bool cr = 0; //indicates that a carriage return was received char len = 64; if (version == 0) { if (addressRT >= 0 && addressRT <= 15) { if (R[8] != 0x0D ) _disp[addressRT*4] = R[8]; else { len=addressRT*4; cr=1; } if (R[9] != 0x0D) _disp[addressRT*4+1] = R[9]; else { len=addressRT*4+1; cr=1; } if (R[10] != 0x0D) _disp[addressRT*4+2] = R[10]; else { len=addressRT*4+2; cr=1; } if (R[11] != 0x0D) _disp[addressRT*4+3] = R[11]; else { len=addressRT*4+3; cr=1; } } } else { if (addressRT >= 0 && addressRT <= 7) { if (R[10] != '\0') _disp[addressRT*2] = R[10]; if (R[11] != '\0') _disp[addressRT*2+1] = R[11]; } } if(cr) { for (char i=len; i<64; i++) _disp[i] = ' '; } if (ab != _ab) { for (char i=0; i<64; i++) _disp[i] = ' '; _disp[64] = '\0'; _newRadioText=1; } else { _newRadioText=0; } _ab = ab; printable_str(_disp, 64); } else if (type == 4 && version == 0) { unsigned long MJD = (R[7]&3<<15 | (unsigned int)(R[8])<<7 | (R[9]>>1)&127)&0x01FFFF; unsigned int Y=(MJD-15078.2)/365.25; unsigned int M=(MJD-14956.1-(unsigned int)(Y*365.25))/30.6001; char K; if(M==14||M==15) K=1; else K=0; _year=(Y+K)%100; _month=M-1-K*12; _day=MJD-14956-(unsigned int)(Y*365.25)-(unsigned int)(M*30.6001); char sign=(((R[11]>>5)&1)/(-1)); if(sign==0)sign=1; //Make sign a bipolar variable char offset=sign*(R[11]&31); _hour=((R[9]&1)<<4) | ((R[10]>>4)&15); _minute=((R[10]&15)<<2 | (R[11]>>6)&3); _hour= (_hour + (offset/2) + 24)%24; _minute=(_minute + (offset)%2*30 + 60)%60; } wait(0.04); return ps_rdy; } void Si4735::getRDS() { strcpy(tuned.programService, _ps); strcpy(tuned.radioText, _disp); strcpy(tuned.programType, _pty); strcpy(tuned.callSign, _csign); tuned.newRadioText=_newRadioText; } void Si4735::getTime() { date.year=_year; date.month=_month; date.day=_day; date.hour=_hour; date.minute=_minute; } bool Si4735::getIntStatus(void) { char response; i2c_->start(); i2c_->write(address_write); i2c_->write(0x14); i2c_->stop(); i2c_->start(); i2c_->write(address_read); response = i2c_->read(1); i2c_->stop(); (*pc).printf("Int Status Response: %c\n", response); if(response == NULL) { return false; } return true; } void Si4735::clearRDS(void) { char i; for(i=0; i<64; i++) _disp[i] ='\0'; for(i=0; i<8; i++) _ps[i] ='\0'; for(i=0; i<16; i++) _pty[i] ='\0'; for(i=0; i<4; i++) _csign[i]='\0'; } void Si4735::setLocale(char locale) { _locale=locale; //Set the deemphasis to match the locale switch(_locale) { case NA: setProperty(0x1100, 0x0002); break; case EU: setProperty(0x1100, 0x0001); break; default: break; } } char Si4735::getLocale(void) { return _locale; } void Si4735::ptystr(char pty) { // Translate the Program Type bits to the RBDS 16-character fields if(pty>=0 && pty<32) { char* pty_LUT[51] = { " None ", " News ", " Information ", " Sports ", " Talk ", " Rock ", " Classic Rock ", " Adult Hits ", " Soft Rock ", " Top 40 ", " Country ", " Oldies ", " Soft ", " Nostalgia ", " Jazz ", " Classical ", "Rhythm and Blues", " Soft R & B ", "Foreign Language", "Religious Music ", " Religious Talk ", " Personality ", " Public ", " College ", " Reserved -24- ", " Reserved -25- ", " Reserved -26- ", " Reserved -27- ", " Reserved -28- ", " Weather ", " Emergency Test ", " !!!ALERT!!! ", "Current Affairs ", " Education ", " Drama ", " Cultures ", " Science ", " Varied Speech ", " Easy Listening ", " Light Classics ", "Serious Classics", " Other Music ", " Finance ", "Children's Progs", " Social Affairs ", " Phone In ", "Travel & Touring", "Leisure & Hobby ", " National Music ", " Folk Music ", " Documentary " }; if(_locale==NA) { strcpy(_pty, pty_LUT[pty]); } else if(_locale==EU) { char LUT[32] = {0, 1, 32, 2, 3, 33, 34, 35, 36, 37, 9, 5, 38, 39, 40, 41, 29, 42, 43, 44, 20, 45, 46, 47, 14, 10, 48, 11, 49, 50, 30, 31 }; strcpy(_pty, pty_LUT[LUT[pty]]); } else { strcpy(_pty, " LOCALE UNKN0WN "); } } else { strcpy(_pty, " PTY ERROR "); } } void Si4735::refreshDisplay(int next_stn) { bool ps_rdy = readRDS(); if(!ps_rdy) return; getRDS(); //getTime(); bool refresh = false; if(strcmp(tuned.programType,pty_prev)) { //refresh = true; strcpy(pty_prev,tuned.programType); } if (strlen(tuned.programService) == 8) { if(ps_rdy) { if(strcmp(tuned.programService,ps_prev)) { refresh = true; strcpy(ps_prev,tuned.programService); } } } else if(strcmp(tuned.programType," None ")) { if(!strcmp("01234567",ps_prev)) { strcpy(tuned.programService, "mBed-Rdo"); strcpy(ps_prev,"01234567"); //refresh = true; } } if(!tuned.newRadioText) { strcpy(RadioText, tuned.radioText); } if(refresh) { (*pc).printf("\x1B[2J"); (*pc).printf("\x1B[H"); (*pc).printf("Current Station: %3.2f MHz FM\n", float(getFrequency())/100.0); (*pc).printf("Program Type = %s\n", tuned.programType); (*pc).printf("Program Service = %s\n", tuned.programService); (*pc).printf("Radio Text = %s\n", RadioText); (*pc).printf("Call Sign = %s\n", tuned.callSign); //(*pc).printf("Time = %2d:%2d\n", date.hour, date.minute); } } /******************************************* * * 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(); } bool Si4735::bitRead(char value, int index) { return ((value >> index) & 1) == 1; } void Si4735::printable_str(char * str, int length) { for(int i=0; i<length; i++) { if( (str[i]!=0 && str[i]<32) || str[i]>126 ) str[i]=' '; } }