本帖最後由 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;
} |