RDA5807M FM Radio library with RDS.

Dependents:   RDA5807M-FM-Radio EFM32 RDA5807M RDS Radio

Example program here:

https://developer.mbed.org/users/star297/code/RDA5807M-FM-Radio/

RDA5807M.cpp

Committer:
star297
Date:
2015-06-19
Revision:
4:3e7968bd455d
Parent:
3:bdd691977de4

File content as of revision 4:3e7968bd455d:

#include "RDA5807M.h"


unsigned int RDA5807M_WriteRegDef[6] ={0xC004,0x0000,0x0100,0x84D4,0x4000,0x0000}; // initial data

RDA5807M::RDA5807M(PinName sda, PinName scl) : i2c(sda, scl)
{   
    i2c.frequency(400000);
    Init();
}

RDA5807M::~RDA5807M()
{
}

void RDA5807M::WriteAll()
{ 
    char buf[30];  
    int i,x = 0;
    for(i=0; i<12; i=i+2){
        buf[i] = RDA5807M_WriteReg[x] >> 8;
        x++;}
    x = 0;
    for(i=1; i<13; i=i+2){
        buf[i] = RDA5807M_WriteReg[x] & 0xFF;
        x++;}
    i2c.write(0x20, buf, 14);
}

void RDA5807M::Init(){
    int i;
    for(i=0; i<6; i++){
        RDA5807M_WriteReg[i] = RDA5807M_WriteRegDef[i];
        WriteAll();
    }
}

void RDA5807M::PowerOn(){
    RDA5807M_WriteReg[1] = RDA5807M_WriteReg[1] | RDA_TUNE_ON;
    RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] | RDA_POWER;
    WriteAll(); power=1;
    RDA5807M_WriteReg[1] = RDA5807M_WriteReg[1] & 0xFFEF;   //Disable tune after PowerOn operation
}

void RDA5807M::PowerOff(){
    RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] ^ RDA_POWER;
    WriteAll();power=0;
}

void RDA5807M::Reset(){
    Init();
    PowerOn();
    RDSinit();
    RDS();
    Volume(InitialVolume);
    Frequency(DefaultFreq);  // set default start frequency.
}

void RDA5807M::Volume(int vol){
    if(vol > 15){
        vol = 15;
    }
    if(vol < 0){
        vol = 0;
    }
    RDA5807M_WriteReg[3] = (RDA5807M_WriteReg[3] & 0xFFF0)| vol;   // Set New Volume
    volume=vol;
    WriteAll();
}

void RDA5807M::BassBoost(){
    if ((RDA5807M_WriteReg[0] & 0x1000)==0){
        RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] | RDA_BASS_ON;
        bass=1;
    }
    else{
        RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] & RDA_BASS_OFF;
        bass=0;
    }
    WriteAll();
}

void RDA5807M::Mono(){
    if ((RDA5807M_WriteReg[0] & 0x2000)==0){
        RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] | RDA_MONO_ON;
        mono=1;
    }
    else{
        RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] & RDA_MONO_OFF;
        mono=0;
    }
    WriteAll();
}

void RDA5807M::Mute(){
    if ((RDA5807M_WriteReg[0] & 0x8000)==0){
        RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] | 0x8000;
        mute=0;
    }
    else{
        RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] & 0x7FFF;
        mute=1;
    }
    WriteAll();
}

void RDA5807M::Softmute(){
    if ((RDA5807M_WriteReg[2] & 0x0200)==0){
        RDA5807M_WriteReg[2] = RDA5807M_WriteReg[2] | 0x0200;
        softmute=1;
    }
    else{
        RDA5807M_WriteReg[2] = RDA5807M_WriteReg[2] & 0xFDFF;
        softmute=0;
    }
    WriteAll();
}

void RDA5807M::SoftBlend(){
    if ((RDA5807M_WriteReg[6] & 0x0001)==0){
        RDA5807M_WriteReg[6] = RDA5807M_WriteReg[6] | 0x0001;
        softblend=1;
    }
    else{
        RDA5807M_WriteReg[6] = RDA5807M_WriteReg[6] & 0xFFFE;
        softblend=0;
    }
    WriteAll();
}

void RDA5807M::AFC(){
    if ((RDA5807M_WriteReg[2] & 0x0100)==0){
        RDA5807M_WriteReg[2] = RDA5807M_WriteReg[2] | 0x0100;
        afc=1;
    }
    else{
        RDA5807M_WriteReg[2] = RDA5807M_WriteReg[2] & 0xFEFE;
        afc=0;
    }
    WriteAll();
}

void RDA5807M::SeekUp(){
    RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] | RDA_SEEK_UP;   // Set Seek Up
    WriteAll();
    RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] & RDA_SEEK_STOP;   // Disable Seek
}

void RDA5807M::SeekDown(){
    RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] | RDA_SEEK_DOWN;   // Set Seek Down
    WriteAll();
    RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] & RDA_SEEK_STOP;   // Disable Seek
}

void RDA5807M::Frequency(float Freq){
    int Channel;
    Channel = ((Freq-StartingFreq)/0.1)+0.05;
    Channel = Channel & 0x03FF;
    RDA5807M_WriteReg[1] = Channel*64 + 0x10;  // Channel + TUNE-Bit + Band=00(87-108) + Space=00(100kHz)
    WriteAll();
    RDA5807M_WriteReg[1] = RDA5807M_WriteReg[1] & RDA_TUNE_OFF;
}

void RDA5807M::RDS(){
    if ((RDA5807M_WriteReg[0] & RDA_RDS_ON)==0){
        RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] | RDA_RDS_ON;
        rds=1;
    }
    else{
        RDA5807M_WriteReg[0] = RDA5807M_WriteReg[0] & RDA_RDS_OFF;
        rds=0;
    }
    WriteAll();
}

void RDA5807M::Readregisters(){
   int i;
   char rcv[12];    
   i2c.read(0x20, rcv,12);   // read 12 bytes for reg 0x0A .. reg 0x0F 
        for(i=0; i<6; i++){
            RDA5807M_ReadReg[i] = ((rcv[i *2] << 8) | rcv [(i*2) +1] );
        }
    block1 = RDA5807M_ReadReg[2];
    block2 = RDA5807M_ReadReg[3];
    block3 = RDA5807M_ReadReg[4];
    block4 = RDA5807M_ReadReg[5];    
   
    rdsready = RDA5807M_ReadReg[0] & 0x8000;            //if rdsready != 0 rds data are ready
    tuneok = RDA5807M_ReadReg[0] & 0x4000;              //if tuneok != 0 seek/tune completed
    nochannel = RDA5807M_ReadReg[0] & 0x2000;           //if nochannel != 0 no channel found
    rdssynchro = RDA5807M_ReadReg[0] & 0x1000;          //if rdssynchro = 1000 rds decoder syncrhonized
    stereo = RDA5807M_ReadReg[0] & 0x0400;              //if stereo = 0 station is mono else stereo
    freq = (((RDA5807M_ReadReg[0] & 0x03FF) * 100) + 87000);    //return freq ex 102600KHz > 102.6MHz
    signal = RDA5807M_ReadReg[1] >> 10;                 //return signal strength rssi
    fmready = RDA5807M_ReadReg[1] & 0x0008;             //if fmready = 8 > fm is ready
    fmstation = RDA5807M_ReadReg[1] & 0x0100;           //if fmstation = 100 fm station is true
    rdsblockerror = RDA5807M_ReadReg[1] & 0x000C;       //check for rds blocks errors
                                                        //00= 0 errors,01= 1~2 errors requiring correction
                                                        //10= 3~5 errors requiring correction
                                                        //11= 6+ errors or error in checkword, correction not possible.
}

void RDA5807M::RDSinit() {
  strcpy(StationName, "        ");
  strcpy(PSName, "        ");
  strcpy(PSName1, "        ");
  strcpy(PSName2, "        ");
  memset(RDSText, '\0', sizeof(RDSText));
  memset(RDSTxt, '\0', sizeof(RDSTxt));
  lastTextIDX = 0;
  mins=0;
  sprintf(CTtime, "CT --:--");
} 

void RDA5807M::ProcessData()
{
    Readregisters();        
    if (rdssynchro != 0x1000){  // reset all the RDS info.
        RDSinit();
        return;
    }   
  // analyzing Block 2
    rdsGroupType = 0x0A | ((block2 & 0xF000) >> 8) | ((block2 & 0x0800) >> 11);
    rdsTP = (block2 & 0x0400);
    rdsPTY = (block2 & 0x0400);

    switch (rdsGroupType) {
        case 0x0A:
        case 0x0B:
        // The data received is part of the Service Station Name  
            idx = 2 * (block2 & 0x0003);
            // new data is 2 chars from block 4
            c1 = block4 >> 8;
            c2 = block4 & 0x00FF;
        // check that the data was received successfully twice
        // before sending the station name
        if ((PSName1[idx] == c1) && (PSName1[idx + 1] == c2)) {
            // retrieve the text a second time: store to _PSName2
            PSName2[idx] = c1;
            PSName2[idx + 1] = c2;
            PSName2[8] = '\0';
            if (strcmp(PSName1, PSName2) == 0) {
                // populate station name          
                n=0;
                for(i=0;i<(8);i++){ // remove non-printable error ASCCi characters               
                    if(PSName2[i] > 31 && PSName2[i] < 127){          
                        StationName[n] = PSName2[i];         
                        n++;
                    }                  
                } 
            }
        }
        if ((PSName1[idx] != c1) || (PSName1[idx + 1] != c2)) {
            PSName1[idx] = c1;
            PSName1[idx + 1] = c2;
            PSName1[8] = '\0';
        } 
    break;
    
    case 0x2A:
        // RDS text
    textAB = (block2 & 0x0010);
    idx = 4 * (block2 & 0x000F);  
    if (idx < lastTextIDX) {
      // The existing text might be complete because the index is starting at the beginning again.
      // Populate RDS text array.
        n=0;       
        for(i=0;i<strlen(RDSTxt);i++){
            if(RDSTxt[i] > 31 && RDSTxt[i] < 127){    // remove any non printable error charcters      
                RDSText[n] = RDSTxt[i];         
                n++;
            }
        }
      }
    lastTextIDX = idx;
    if (textAB != lasttextAB) {
      // when this bit is toggled the whole buffer should be cleared.
      lasttextAB = textAB;
      memset(RDSTxt, 0, sizeof(RDSTxt));
      memset(RDSText, '\0', sizeof(RDSText));
    }    
    if(rdsblockerror < 4){     
        // new data is 2 chars from block 3
        RDSTxt[idx] = (block3 >> 8);     idx++;
        RDSTxt[idx] = (block3 & 0x00FF); idx++;
        // new data is 2 chars from block 4
        RDSTxt[idx] = (block4 >> 8); idx++;
        RDSTxt[idx] = (block4 & 0x00FF); idx++;
    }             
    break;

  case 0x4A:
    // Clock time and date
    if(rdsblockerror <3){ // allow limited RDS data errors as we have no correctioin code 
        offset = (block4) & 0x3F; // 6 bits
        mins = (block4 >> 6) & 0x3F; // 6 bits
        mins += 60 * (((block3 & 0x0001) << 4) | ((block4 >> 12) & 0x0F));
    }
    // adjust offset
    if (offset & 0x20) {
      mins -= 30 * (offset & 0x1F);
    }
    else {
      mins += 30 * (offset & 0x1F);
    }    
    if(mins == lastmins+1){ // get CT time twice before populating time
        minutes=mins;}
        lastmins=mins;    
    if(rdssynchro == 0x1000){
        if(minutes>0 && minutes<1500){sprintf(CTtime, "CT %2d:%02d",(minutes/60),(minutes%60));}
        }
        else{minutes=0;sprintf(CTtime, "CT --:--");} // CT time formatted string         
    break;

  case 0x6A: 
    // IH
    break;

  case 0x8A:
    // TMC
    break;

  case 0xAA:
    // TMC
    break;

  case 0xCA:
    // TMC
    break;

  case 0xEA:
    // IH
    break;

  default:
    
    break;
  }
}