Decode RDS clock from FM SI4703

本帖最後由 hon829 於 2018-7-8 00:57 編輯

淘左件SI4703 FM 模塊, 斗左成個星期編程至有D成果!可能本身coding 是有限公司及網上參考少,多數library分享流於FM調台等操作.

RDS 用Binary PSK傳送, 1187.5 bps, 每Packet(104 bit) 分4個block(A,B,C,D), 每個block含16 bit data + 10 bit Check & Offset, 即約每87.5mS 讀一次RDS register, 可用polling 或 Interrupt待接收1個packet完結才讀取.

SI4703.jpg

RDS Group list ref: http://www.g.laroche.free.fr/eng ... listeGroupesRDS.htm

但RDS資訊分16個group,再分A/B版本,即32組合,而日期及時間是4A group的:
ref:http://www.g.laroche.free.fr/english/rds/groupes/4/groupe4A.htm
Group4A.jpg


要近窗邊至收倒RDS訊息:
a.jpg
b.jpg
c.jpg

d.jpg
e.jpg
f.jpg

g.jpg
h.jpg

唔知是否用PSK問題 error rate都幾高, SI4703說明最好接收幾次對比確認無誤!
zError1.jpg
zError2.jpg

由於傳送資訊沒秒數, 故4A packet (time)只在踏正分鐘數時傳送一次,如果錯失左就要等下一分鐘了
與天文台的網上時鐘對比,CR2 慢了約12秒, RTHK2慢了約3秒, 吾知是否數碼化的Delay

SI4703會將接收訊號強度由0~75dBμV表示 (Received Signal Strength Indicator), 初步發覺要到15dBμV倒至收倒RDS部份,屋內全無反應,或許是這模塊沒天線,只靠headphone cable而已!

請問師兄會否公開程式碼讓大家學習?

TOP

CR2 慢了約12秒, RTHK2慢了約3秒,
個鐘咁有咩用?

TOP

本帖最後由 hon829 於 2018-7-11 21:06 編輯

回覆 2# chingkit

沒問題
part I:   (回貼最多只可 34995 字,超過了只好分兩部份)


/*
ref: https://github.com/packetgeek/si4703/blob/master/rds.c
RDS: http://www.g.laroche.free.fr/english/rds/groupes/4/groupe4A.htm

uno    |   SI4703          //Connection
----------------
pin 3  |   GPIO2            RDS Interrupt
pin 2  |   reset
I2C



Si4703
<< must read/write all register at one time >> (2byte register)
-76 ~ 108MHz
-Automatic frequency control (AFC)
-Automatic gain control (AGC)
-RDS/RBDS Processor
-Volume control
-2.7 to 5.5 V
-RDS Binary PSK, 1187.5 bps, 4 block (each 16 bit data +10 bit Check + Offset), 87.5mS receive a packet to have an Interrupt to reading


*/



  //#include <SparkFunSi4703.h>
#include <Wire.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);                                   //select the pins used on the LCD panel, pin 10 for black light control


#define resetPin  2
#define SDIO  A4
#define SCLK  A5
  //Si4703_Breakout radio(resetPin, SDIO, SCLK);

#define RDS_Interrupt 3                                               //pin 3, generate a 5 ms low pulse on GPIO2 when the RDSR 0Ah[15] bit is set
boolean RDS_Ready = 0;                                                //for Interrupt to reading

#define Si4703_add 0x10                                               //0b._001.0000 = I2C address of Si4703 - note that the Wire function assumes non-left-shifted I2C address, not 0b.0010.000W
uint16_t Si4703_registers[16];                                        //There are 16 registers, each 16 bits large,

float RDS_year = 0;                                                   //RDS readout UTC
byte RDS_hour = 0;                                                    //RDS readout UTC
byte RDS_minute = 0;                                                  //RDS readout UTC


byte RDS_2A_text[65] = {'\0',84,101,115,116};                         //2A group allows to transmit data of radiotext, with a maximum of 64 characters
unsigned int RDS_2A_text_count = 0;                                   //max. 0~15, b3, b2, b1 and b0

byte RDS_2B_text[33] = {'\0'};                                        //2B group radiotext, with a maximum of 32 characters
unsigned int RDS_2B_text_count = 0;                                   //max. 0~15, b3, b2, b1 and b0, 6 groups 2B must be transmitted every 2 seconds

byte RDS_PS_Name[9] = {'\0'};                                         //0A group name of radio
byte RDS_old_PS_Name[9] = {'\0'};                                     //compare multiple PS messages
unsigned int RDS_PS_Name_count = 0;                                   //0~3, C1/C0

unsigned int RDS_AF = 0;                                              //16 bits allows to transmit alternative frequencies, 1> Frequency 87.6 MHz, 2 > Frequency87.7 MHz
byte RDS_program_type= 0;                                             //5 bit
unsigned int RDS_PI = 0;                                              //Program Identification Code (PI code)

int RDS_textDisplay = 0;                                              //RDS text received to display, 1: 2A group text, 2: time, 3:PS name , 4: RDS_2B_text[32]

int reflash_lcd=0;                                                  //earch 0.5 second reflash the LCD
unsigned long delay_start_time = 0;                                 //for millis(), unsigned long 1mS

int channel = 948;
int volume;


#define POWERCFG  0x02                                                //Define the register names
#define CHANNEL  0x03
#define SYSCONFIG1  0x04
#define SYSCONFIG2  0x05
#define STATUSRSSI  0x0A
#define READCHAN  0x0B


#define TUNE  15                                                      //Register 0x03 - CHANNEL

#define RDS  12                                                       //Register 0x04 - SYSCONFIG1
#define DE  11

#define SPACE1  5                                                     //Register 0x05 - SYSCONFIG2
#define SPACE0  4

#define RDSR  15                                                      //Register 0x0A - STATUSRSSI
#define STC  14
#define SFBL  13
#define AFCRL  12
#define RDSS  11
#define STEREO  8

#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 SKMODE  10
#define SEEKUP  9
#define SEEK  8

#define RDSA  0x0c
#define RDSB  0x0d
#define RDSC  0x0e
#define RDSD  0x0f




int lcd_key     = 0;                                                  //button pin A0
int adc_key_in  = 0;                                                  //adc value

#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5





const char program_type[][8] = {
"",                                                 // 0
"News",                                             // 1
"Current",                                          // 2
"Information",                                      // 3
"Sport",                                            // 4
"Education",                                        // 5
"Drama",                                            // 6
"Culture",                                          // 7
"Science",                                          // 8
"Varied",                                           // 9
"Pop",                                              // 10
"Rock",                                             // 11
"Easy listening",                                   // 12
"Light classical",                                  // 13
"Serious classical",                                // 14
"Music",                                            // 15
"Weather",                                          // 16
"Finance",                                          // 17
"Children",                                         // 18
"Social",                                           // 19
"Religion",                                         // 20
"Phone-in",                                         // 21
"Travel",                                           // 22
"Leisure",                                          // 23
"Jazz",                                             // 24
"Country",                                          // 25
"National",                                         // 26
"Oldies",                                           // 27
"Folk",                                             // 28
"Documentary",                                      // 29
"Alarm test",                                       // 30
"Alarm"                                             // 31
};





void RDS_ISR(){
  RDS_Ready=1;}                                         //measured Interrupt frequency 11.4 Hz (87.7mS), generate a 5 ms low pulse on GPIO2 when the RDSR



void setup(){
  pinMode(RDS_Interrupt, INPUT_PULLUP);
  lcd.begin(16, 2);   
  lcd.clear();                     
  lcd.print("Si4703 FM radio");
  lcd.setCursor(0, 1);
  lcd.print("76 ~ 108MHz 7/18 ");
  
  Serial.begin(9600);
  Serial.println("SI4703 RDS radio...");
  si4703_init();                                       //powerOn Init the Si4703 - we need to toggle SDIO before Wire.begin takes over.

  si4703_readRegisters();                              //read SI4703 all register data

  Si4703_registers[02]  = 0x4801;                      //default 0x4001 by lib.      RDS Mode[11]
  Si4703_registers[03]  = 0x802e;                      //default 0x0049                 (ch*100-8750)/10,  CR2=2e,   RTHK2: (96.9x100-8750)/10 = 73 > 0x005e
  Si4703_registers[04]  = 0x98c4;                      //default 0x1800,  RDS Interrupt Enable[15], Seek/Tune Complete Interrupt Enable[14], RDS Enable[12], De-emphasis[11], BLNDADJ[7:6]                  
  Si4703_registers[05]  = 0x0518;                      //default 0x0018,  Seek Threshold.[15:8] 00~7F  ,BAND[7:6], SPACE[5:4], VOLUME[3:0]
  Si4703_registers[06]  = 0x0011;                      //default 0x0000,  SMUTER[15:14], SMUTEA[13:12],
  //Si4703_registers[07]  = 0xbc04;                    //default 0xbc04  bit13~0 If written, these bits should be read first and then written with their pre-existing values. Do not write during powerup.
  
  si4703_updateRegisters();
  check_Tune_Complete();
  
  delay(1000);
  lcd.clear();
  displayInfo();

  attachInterrupt(1, RDS_ISR, LOW);                     //attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
  
}









void loop(){

if(CheckKey() != 5){                                                        //no key pressed
  if(CheckKey()==1){                                                            //up Volume
      volume = Si4703_registers[SYSCONFIG2] & 0x000f;
      volume ++;
      if (volume >= 16) volume = 15;
      Si4703_registers[SYSCONFIG2] = (Si4703_registers[SYSCONFIG2] & 0xfff0) | volume;
      si4703_updateRegisters();
      displayInfo();
      delay(150);}


  if(CheckKey()==2){                                                            //down Volume
      volume = Si4703_registers[SYSCONFIG2] & 0x000f;
      volume --;
      if (volume <= 0) volume = 0;
      Si4703_registers[SYSCONFIG2] = (Si4703_registers[SYSCONFIG2] & 0xfff0) | volume;
      si4703_updateRegisters();
      displayInfo();
      delay(150);}


  if(CheckKey()==0){                                                            //right seekUp
      lcd.clear();
      si4703_seek(SEEK_UP);
      displayInfo();
      RDS_2A_text_count = 0;                                                   //reset RDS buffer
      RDS_2B_text_count = 0;                                          
      RDS_PS_Name_count = 0;
      RDS_Ready=0;
      delay(100);}


  if(CheckKey()==3){                                                            //left seekDown
      lcd.clear();
      si4703_seek(SEEK_DOWN);
      displayInfo();
      RDS_2A_text_count = 0;
      RDS_2B_text_count = 0;                                          
      RDS_PS_Name_count = 0;
      RDS_Ready=0;
      delay(100);}


  if(CheckKey()==4){                                                            //select
    lcd.clear();
    displayInfo();
      delay(100);}
      
}





  
  if( RDS_Ready ){                                                                //set the RDS ready (RDSR) bit for a minimum of 40 ms when a valid RDS group has been received
      RDS_Ready = 0;
      si4703_readRegisters();
      si4703_readRDS();
      
      lcd.setCursor(15, 0);                                                       //check RDS ready
      lcd.print("RDS  ");
    }

  delay_start_time = millis();
  while( millis() < (delay_start_time + 10) ){}                                   ////delay(10);
                                                                     


  
  reflash_lcd++;                                                                 //reflash display 0.5 second
  if (reflash_lcd >= 50){
    reflash_lcd=0;
    displayInfo();}                                                           

}











void displayInfo(){   
  si4703_readRegisters();                                                           //read SI4703 all register data
  
/* for (int i=0; i<=0x0f; i++){                                                      //for debug, printout all register
    Serial.print(i, HEX);
    Serial.print("  ");
    Serial.println(Si4703_registers, HEX);}
    Serial.println(" ");   */

    float playCH = ((float)((Si4703_registers[0x0b] & 0x03ff)*10) + 8750)/100;     //9:0 READCHAN[9:0] Read Channel. If BAND 05h[7:6] = 00, then Freq (MHz) = Spacing (MHz) x Channel + 87.5 MHz
    lcd.setCursor(0, 0);   
    lcd.print(playCH, 1);                                                           //display play channel
    lcd.print("   ");
   
    lcd.setCursor(6, 0);
    if ((Si4703_registers[0x0a] & 0x0100) == 0x0100){ lcd.print("St   ");}          //Stereo / Mono
    else{ lcd.print("Mo   ");}

    int Signal_level = Si4703_registers[0x0a] & 0x00ff;                             //Received Signal Strength Indicator  <= 75 dBμV.
    lcd.setCursor(9, 0);
    lcd.print("s");
    lcd.print(Signal_level);                                                        //signal level
    lcd.print("   ");

    int volume = Si4703_registers[0x05] & 0x000f;
    lcd.setCursor(12, 0);                                                           //display volume
    lcd.print("v");
    lcd.print(volume);
    lcd.print("   ");

    lcd.setCursor(15, 0);                                                           
    if( (Si4703_registers[0x0a] & 0x8000) == 0x8000){                               //check RDS ready
    lcd.print("RDS  ");
    RDS_Ready=1;}
    else{lcd.print("   ");}
   


    if( RDS_textDisplay == 1){                                                          //display RDS_2A_text[64]
      RDS_textDisplay=0;
      lcd.clear();
      for (int i=0; i<=31; i++){                                                        //i<=63                        
        if ( RDS_2A_text == '\n' ){break;}
        if (i==16){lcd.setCursor(0, 1);}
        if (i==32){
          delay(2000);
          lcd.clear();}
      lcd.print( (char)RDS_2A_text);}
      
      delay_start_time = millis();
      while( millis() < (delay_start_time + 1000) ){                                    //delay(1000);
          if( RDS_Ready ){                                                              //set the RDS ready (RDSR) bit for a minimum of 40 ms when a valid RDS group has been received
            RDS_Ready = 0;
            si4703_readRegisters();
            si4703_readRDS();}
       }                                   
    }



    if( RDS_textDisplay == 4){                                                          //display RDS_2B_text[32]
      RDS_textDisplay=0;
      lcd.clear();
      for (int i=0; i<=31; i++){
        if ( RDS_2B_text == '\n' ){break;}
        if (i==16){lcd.setCursor(0, 1);}
      lcd.print( (char)RDS_2B_text);}
    }



    if( RDS_textDisplay == 2){                                                          //display Time and day
      RDS_textDisplay=0;
      if (RDS_year >= 58000){                                                           //check error, 58303 = 2018-7-4

      lcd.setCursor(0, 1);  
      if(RDS_hour < 10){ lcd.print(" ");}                                                     
      lcd.print(RDS_hour);
      lcd.print(":");
      if(RDS_minute < 10){ lcd.print("0");}   
      lcd.print(RDS_minute);
      lcd.print(" ");

      lcd.setCursor(6, 1);                                                             //ref. https://github.com/mastmees/silicon_radio
      int32_t yp = ( (float)RDS_year-15078.2 )/365.25;                                 // Y' = int [ (MJD - 15 078,2) / 365,25 ]
      int32_t mp = ( (float)RDS_year-14956.1 - (int32_t)(yp*365.25) )/30.6001;         // M' = int { [ MJD - 14 956,1 - int (Y' × 365,25) ] / 30,6001 }
      int16_t d = RDS_year - 14956 - (int32_t)(yp*365.25) - (int32_t)(mp*30.6001);     // D = MJD - 14 956 - int ( Y' × 365,25 ) - int ( M' × 30,6001 )
      int16_t y, m;
      if (mp == 14 || mp == 15) {                                                       // If M' = 14 or M' = 15, then K = 1; else K = 0
        y = yp + 1;                                                                     // Y = Y' + K
        m = mp-1-12; }                                                                  // M = M' - 1 - K × 12
      else {
        y=yp;
        m=mp-1;}
      y+=1900;
      
      lcd.print(y);
      lcd.print("-");
      lcd.print(m);
      lcd.print("-");
      lcd.print(d);
      lcd.print("  ");
      
      delay_start_time = millis();
      while( millis() < (delay_start_time + 5000) ){                                    //delay(5000);
          if( RDS_Ready ){                                                              //set the RDS ready (RDSR) bit for a minimum of 40 ms when a valid RDS group has been received
            RDS_Ready = 0;
            si4703_readRegisters();
            si4703_readRDS();}
       }
      
      }
    }



    if( RDS_textDisplay == 3){                                                            //display PS name
      RDS_textDisplay=0;
      lcd.clear();
      displayInfo();
      lcd.setCursor(0, 1);  
      for (int i=0; i<=7; i++){
      lcd.print( (char)RDS_PS_Name);}

      if ( (RDS_AF > 0) && (RDS_AF < 205) ){
        float i = (float)RDS_AF/10 + 87.5;                                                //Alternative Frequencies, 1=87.6 MHz, 204=107.9 MHz
        lcd.setCursor(8, 1);
        lcd.print( "AF");
        lcd.print(i, 1);
        lcd.print( "MHz ");
        RDS_AF=0;}
      else{
        lcd.setCursor(8, 1);
        //lcd.print( "PT:");
        
        for (int i=0; i<20; i++){
          if ( program_type[RDS_program_type] == '\0'){break;}
          lcd.print( (char)program_type[RDS_program_type] );                         //program_type
        }
        RDS_old_PS_Name[0] = '\0';
        RDS_program_type=0;}
    }








}











                                                        //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

  si4703_readRegisters();                                                 //Read the current register set
  //si4703_registers[0x07] = 0xBC04;                                      //Enable the oscillator, from AN230 page 9, rev 0.5 (DOES NOT WORK, wtf Silicon Labs datasheet?)
  Si4703_registers[0x07] = 0x8100;                                        //Enable the oscillator, from AN230 page 9, rev 0.61 (works)
  si4703_updateRegisters();                                               //Update

  delay(500);                                                             //Wait for clock to settle - from AN230 page 9

  si4703_readRegisters();                                                 //Read the current register set
  Si4703_registers[POWERCFG] = 0x4001;                                    //Enable the IC
        //  si4703_registers[POWERCFG] |= (1<<SMUTE) | (1<<DMUTE);        //Disable Mute, disable softmute
  Si4703_registers[SYSCONFIG1] |= (1<<RDS);                               //Enable RDS

#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

  Si4703_registers[SYSCONFIG2] &= 0xFFF0;                                 //Clear volume bits
  Si4703_registers[SYSCONFIG2] |= 0x0001;                                 //Set volume to lowest
  si4703_updateRegisters();                                               //Update

  delay(110);                                                             //Max powerup time, from datasheet page 13
}







void si4703_readRegisters(){                                              //Read the entire register control set from 0x00 to 0x0F
                                                                        //Si4703 begins reading from register upper register of 0x0A and reads to 0x0F, then loops to 0x00.
  Wire.requestFrom(Si4703_add, 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.read() << 8;
    Si4703_registers[x] |= Wire.read();
    if(x == 0x09) break;}                                                  //We're done!
}




                                    
                                      
void si4703_updateRegisters(){                                    //Write the current 6 control registers (0x02 to 0x07) to the Si4703. It's a little weird, you don't write an I2C addres
  Wire.beginTransmission(Si4703_add);                               //The Si4703 assumes you are writing to 0x02 first, then increments
  for(int regSpot = 0x02 ; regSpot < 0x08 ; regSpot++){                 //A write command automatically begins with register 0x02 so no need to send a write-to address
    byte high_byte = Si4703_registers[regSpot] >> 8;                    //First we send the 0x02 to 0x07 control registers
    byte low_byte = Si4703_registers[regSpot] & 0x00FF;                 //In general, we should not write to registers 0x08 and 0x09

    Wire.write(high_byte);                                              //Upper 8 bits
    Wire.write(low_byte);}                                              //Lower 8 bits
  byte ack = Wire.endTransmission();                                    //End this transmission
// 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 NACK on data, 4 = other error
  //return;}                                                         //fail write to registers
// return;                                                          //SUCCESS
}






void check_Tune_Complete(){
  while ( (Si4703_registers[0x0a] & 0x4000) == 0){                        //check STC flat, The seek/tune complete flag is set when the seek or tune operation completes. Setting the SEEK 02h[8] or TUNE 03h[15] bit low will clear STC.
  si4703_readRegisters();}
  Si4703_registers[CHANNEL] &= ~(1<<TUNE);                                //Clear the tune after a tune has completed
  si4703_updateRegisters();
}







void si4703_seek(byte seekDirection){                                                   //Set seek mode wrap bit                                                                       
  Si4703_registers[POWERCFG] |= (1<<SKMODE);                                            //Allow wrap  
  if(seekDirection == SEEK_DOWN) Si4703_registers[POWERCFG] &= ~(1<<SEEKUP);            //Seek down is the default upon reset
  else Si4703_registers[POWERCFG] |= 1<<SEEKUP;                                         //Set the bit to seek up

  Si4703_registers[POWERCFG] |= (1<<SEEK);                                              //Start seek
  si4703_updateRegisters();                                                             //Seeking will now start

  while(1) {                                                                            //Poll to see if STC is set
    si4703_readRegisters();
    if((Si4703_registers[STATUSRSSI] & (1<<STC)) != 0) break;}                          //Tuning complete!

  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();


  while(1) {                                                                           //Wait for the si4703 to clear the STC as well
    si4703_readRegisters();
    if( (Si4703_registers[STATUSRSSI] & (1<<STC)) == 0) break;}                        //Tuning complete!

  if(valueSFBL) {                                                                      //The bit was set indicating we hit a band limit or failed to find a station
    return;}

return ;
}





void compare_PS_Name(){
  if ( strncmp(RDS_PS_Name, RDS_old_PS_Name, 8) == 0 ) {                                                  
    RDS_textDisplay = 3;}                                                                     //equal
   
  for (int i=0; i<=8; i++){                                                                   //backup PS name                                         
    RDS_old_PS_Name = RDS_PS_Name;}

  RDS_PS_Name_count=0;
}

TOP

本帖最後由 hon829 於 2018-7-11 20:57 編輯

回覆 2# chingkit


Part II:







void si4703_readRDS(){
  
  for (int i=0x0c; i<= 0x0f; i++){                                                      //for debug, printout 4 Block RDS register
    Serial.print(i, HEX);
    Serial.print("  ");
    Serial.print(Si4703_registers, HEX);
    Serial.print("     ");}
    Serial.println(" ");  

                                                                                      
  if ( (Si4703_registers[0x0b] & 0x0c000) <= 0x4000){                                    //RDS Block B Errors < (01 = 1–2 errors requiring correction)
  
    int i, j;                                                                           //ref.  https://github.com/mastmees/sili ... ster/rdsdecoder.cpp
  switch( (Si4703_registers[RDSB] & 0x0f800) >> 11){                                 //check Group type 0-15, A/B version ref. http://www.g.laroche.free.fr/eng ... listeGroupesRDS.htm
    case 0:                                                                          // 0A
      i = Si4703_registers[RDSB] & 0x0003;                                              //c1,c0 bit    ref. http://www.g.laroche.free.fr/english/rds/groupes/0/groupe0A.htm
      j = ((Si4703_registers[RDSD] >> 8) & 0x00ff);                                     //A line feed " line feed " 0A ( Hex) can be used to indicate to the receiver a change of line.
      RDS_PS_Name[(i*2)] = j;
      j = Si4703_registers[RDSD] & 0x00ff;
      RDS_PS_Name[(i*2)+1] = j;
      if (i == 3){RDS_PS_Name[8] = '\0';}
      RDS_PS_Name_count = ( RDS_PS_Name_count | (1 << i) );                             //check to recivce 4 block data?
      if ( RDS_PS_Name_count == 15 ){
        compare_PS_Name();}                                                             //compare is it same as last one
   
      RDS_AF = Si4703_registers[RDSC];                                                  //16bits allows to transmit alternative frequencies
      RDS_program_type = (Si4703_registers[RDSB] & 0x03e0) >> 5;   
      break;

      
    case 1:                                                                           // 0B
      i = Si4703_registers[RDSB] & 0x0003;                                              //c1,c0 bit    ref. http://www.g.laroche.free.fr/english/rds/groupes/0/groupe0B.htm
      j = ((Si4703_registers[RDSD] >> 8) & 0x00ff);                                     //A line feed " line feed " 0A ( Hex) can be used to indicate to the receiver a change of line.
      RDS_PS_Name[(i*2)] = j;
      j = Si4703_registers[RDSD] & 0x00ff;
      RDS_PS_Name[(i*2)+1] = j;
      if (i == 3){RDS_PS_Name[8] = '\0';}
      RDS_PS_Name_count = ( RDS_PS_Name_count | (1 << i) );
      if ( RDS_PS_Name_count == 15 ){
        compare_PS_Name();}
        
      RDS_PI = Si4703_registers[RDSC];
      RDS_program_type = (Si4703_registers[RDSB] & 0x03e0) >> 5;   
      break;

      
    case 4:                                                                           // 2A group radiotext, with a maximum of 64 characters
      i = Si4703_registers[RDSB] & 0x000f;                                              //b3~b0 bit    ref. http://www.g.laroche.free.fr/english/rds/groupes/2/groupe2A.htm
      j = ((Si4703_registers[RDSC] >> 8) & 0x00ff);                                     //A line feed " line feed " 0A ( Hex) can be used to indicate to the receiver a change of line.
        if(j == "\r"){                                                                  //ended by the character " carriage return " 0D ( Hex)
          RDS_2A_text[(i*4)] = '\0';
          RDS_2A_text_count=0;
          RDS_textDisplay = 1;
          break;}                                                            
        RDS_2A_text[(i*4)] = j;                                                         
      j = Si4703_registers[RDSC] & 0x00ff;
        if(j == "\r"){                                                                  //ended by the character " carriage return " 0D ( Hex)
          RDS_2A_text[(i*4+1)] = '\0';
          RDS_2A_text_count=0;
          RDS_textDisplay = 1;
          break;}
        RDS_2A_text[(i*4+1)] = j;
      j = (Si4703_registers[RDSD] >> 8) & 0x00ff;
        if(j == "\r"){                                                                  //ended by the character " carriage return " 0D ( Hex)
          RDS_2A_text[(i*4+2)] = '\0';
          RDS_2A_text_count=0;
          RDS_textDisplay = 1;
          break;}
        RDS_2A_text[(i*4)+2] = j;
      j = Si4703_registers[RDSD] & 0x00ff;
        if(j == "\r"){                                                                  //ended by the character " carriage return " 0D ( Hex)
          RDS_2A_text[(i*4+3)] = '\0';
          RDS_2A_text_count=0;
          RDS_textDisplay = 1;
          break;}
        RDS_2A_text[(i*4)+3] = j;
        RDS_2A_text_count = ( RDS_2A_text_count | (1 << i) );
      if ( RDS_2A_text_count == 0x0ffff){
          RDS_2A_text[64] = '\0';
          RDS_2A_text_count=0;
          RDS_textDisplay = 1;}
      break;


      
    case 5:                                                                         // 2B 32 character radio text
        i= Si4703_registers[RDSB] & 0x000f;                                           //ref. http://www.g.laroche.free.fr/english/rds/groupes/2/groupe2B.htm
        j = ((Si4703_registers[RDSD] >> 8) & 0x00ff);                                
        if(j == "\r"){                                                                //Every message must be ended by the character " carriage return " 0D ( Hex)
          RDS_2B_text[(i*2)] = '\0';
          RDS_2B_text_count=0;
          RDS_textDisplay = 4;
          break;}
        RDS_2B_text[(i*2)] = j;
        j = ( Si4703_registers[RDSD] & 0x00ff);
        if(j == "\r"){                                                                //ended by the character " carriage return " 0D ( Hex)
          RDS_2B_text[(i*2)+1] = '\0';
          RDS_2B_text_count=0;
          RDS_textDisplay = 4;
          break;}
        RDS_2B_text[(i*2)+1] = j;
        RDS_2B_text_count = ( RDS_2B_text_count | (1 << i) );
        if ( RDS_2B_text_count == 0x0ffff){
          RDS_2B_text[32] = '\0';
          RDS_2B_text_count = 0;
          RDS_textDisplay = 4;}
      break;


  
    case 8:                                                                           //Group 4A, Clock-time and date only,  transmitted every minute, accurate to within 100 ms of UTC
      RDS_minute = (Si4703_registers[RDSD] & 0x0fc0) >> 6;                              //ref. http://www.g.laroche.free.fr/eng ... 4/groupe4A.htm#DATE
      RDS_hour = (Si4703_registers[RDSC] & 0x0001) << 4;                                //hour 5 bits,  from 0 to 23
      RDS_hour = RDS_hour | ((Si4703_registers[RDSD] & 0x0f000) >> 12);                 //minute 6 bits , 0 to 59
      RDS_year = (Si4703_registers[RDSB] & 0x0003) * 32768;                             //Local time offset :6 bit, multiple of half hour, sign +/- = "0" for a positive offset and "1" for a negative offset
      RDS_year = RDS_year + ((Si4703_registers[RDSC] & 0x0fffe) >> 1);                  //year since 1900, 17 bits to form a number from 0 to 99999

      RDS_textDisplay=2;
    break;


    case 20:                                                                             //Group 10A, Program Type Name
                                                                                            //ref. http://www.g.laroche.free.fr/eng ... ypeProgramme_TP.htm

    break;

   
    case 30:                                                                            //Group 15A, Program Type Name
                                                                                          //ref. http://www.g.laroche.free.fr/english/rds/groupes/15/groupe15A.htm
    break;

      
    default:
     break;
   
    }
     
  }
  else{return;}
  
}





int CheckKey(){
  adc_key_in = analogRead(lcd_key);                                   // buttons reading
  // Serial.println(adc_key_in);                                        //for debug
if (adc_key_in > 900) return btnNONE;                                //1023,  We make this the 1st option for speed reasons since it will be the most likely result
if (adc_key_in < 40)   return btnRIGHT;                              //0
if (adc_key_in > 50 and adc_key_in < 150)  return btnUP;             //100
if (adc_key_in > 200 and adc_key_in < 300)  return btnDOWN;          //257
if (adc_key_in > 360 and adc_key_in < 460)  return btnLEFT;          //410
if (adc_key_in > 590 and adc_key_in < 690)  return btnSELECT;        //640
return btnNONE;                                                      // when all others fail, return this...
}

TOP

回覆 3# YES_MAN


無長期監察!如時差是恆常的都可調節返見有D政府部門用RDS時鐘的真是相差無幾的!
有RDS車機/HI FI的都可自行比較港台每半小的六下報時是否與RDS時鐘同步!

TOP

本帖最後由 hon829 於 2018-7-11 21:27 編輯

貼了兩次程序,答過我不是機械人,但初步看仍有三處地方少了"", 吾知點解

係所有中括號i 都消失了

Missing.jpg

TOP

提示: 作者被禁止或刪除 內容自動屏蔽

TOP

CR2 慢了約12秒, RTHK2慢了約3秒,
個鐘咁有咩用?
YES_MAN 發表於 11-7-2018 19:47


    無得上網但收到FM的地方,可用來參考吓個時間。

via HKEPC IR Pro 3.4.0 - iOS(2.3.3)

TOP

想跟樓主玩RDS , 我淘左塊P4用作顯示

TOP