Difference between revisions of "Grove - I2C FM Receiver v1.1"
(Created page with "Grove - I2C FM Receiver v1.1 === Créer un lien === === Créer un lien === === Créer un lien ===") |
(→Créer un lien) |
||
| Line 5: | Line 5: | ||
=== Créer un lien === | === Créer un lien === | ||
| + | <pre> | ||
| + | /* | ||
| + | * I2C_FM.ino | ||
| + | * Demo code for the Grove-I2C_FM_Receiver module | ||
| + | * | ||
| + | * Copyright (c) 2012 seeed technology inc. | ||
| + | * Website : www.seeed.cc | ||
| + | * Author : Jack Shao (jacky.shaoxg@gmail.com) | ||
| + | * Create Time: Jul 2014 | ||
| + | * Change Log : | ||
| + | * | ||
| + | * The MIT License (MIT) | ||
| + | * | ||
| + | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| + | * of this software and associated documentation files (the "Software"), to deal | ||
| + | * in the Software without restriction, including without limitation the rights | ||
| + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| + | * copies of the Software, and to permit persons to whom the Software is | ||
| + | * furnished to do so, subject to the following conditions: | ||
| + | * | ||
| + | * The above copyright notice and this permission notice shall be included in | ||
| + | * all copies or substantial portions of the Software. | ||
| + | * | ||
| + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| + | * THE SOFTWARE. | ||
| + | */ | ||
| + | // | ||
| + | /* | ||
| + | * Modifications to the I2C_FM.ino by Mel Patrick - Wabbit Wanch Design | ||
| + | * Modified routines for scanning UP or DOWN through the FM band | ||
| + | * Modified routine to test for signal strength of received station | ||
| + | * Modified routines to support bass boost and MONO signal | ||
| + | * RSSI, read it too soon after setting a station and you get a small value | ||
| + | * so it's better to wait a bit (50ms) and try it. minSignalStrength will | ||
| + | * skip locking on a station with a weak signal (you could set the MONO bit) to get | ||
| + | * better reception on these stations. | ||
| + | */ | ||
| + | #include <Arduino.h> | ||
| + | #include <Wire.h> | ||
| + | #include <EEPROM.h> | ||
| + | |||
| + | #define BTNUP 2// used for seeking UP (normally CLOSED push button) | ||
| + | #define VOL_POT A0// volume POT LOG taper 10K | ||
| + | #define BTNDN 3// used for seeking DOWN (normally CLOSED push button) | ||
| + | |||
| + | uint16_t gChipID = 0; | ||
| + | uint8_t RDA5807P_REGW[10]; | ||
| + | |||
| + | #define I2C_ADDR 0x10 | ||
| + | |||
| + | #define READ 1 | ||
| + | #define WRITE 0 | ||
| + | |||
| + | #define ADRW 0x20 | ||
| + | #define ADRR 0x21 | ||
| + | // | ||
| + | |||
| + | //#define _SHARE_CRYSTAL_24MHz_ | ||
| + | //#define _SHARE_CRYSTAL_12MHz_ | ||
| + | #define _SHARE_CRYSTAL_32KHz_ | ||
| + | //#define _FM_STEP_50K_ | ||
| + | |||
| + | //5807M,5807FP,5807NN,5807NP | ||
| + | uint8_t RDA5807N_initialization_reg[]={ | ||
| + | #if defined(_SHARE_CRYSTAL_24MHz_) | ||
| + | 0xC4, 0x51, //02H: | ||
| + | #elif defined(_SHARE_CRYSTAL_12MHz_) | ||
| + | 0xC4, 0x11, //02H: | ||
| + | #elif defined(_SHARE_CRYSTAL_32KHz_) | ||
| + | 0xC4, 0x01,//change 01 to 05 enables the RDS/RBDS | ||
| + | #else | ||
| + | 0xC0, 0x01, | ||
| + | #endif | ||
| + | 0x00, 0x00, | ||
| + | 0x04, 0x00, | ||
| + | 0xC3, 0xad, //05h | ||
| + | 0x60, 0x00, | ||
| + | 0x42, 0x12, | ||
| + | 0x00, 0x00, | ||
| + | 0x00, 0x00, | ||
| + | 0x00, 0x00, //0x0ah | ||
| + | 0x00, 0x00, | ||
| + | 0x00, 0x00, | ||
| + | 0x00, 0x00, | ||
| + | 0x00, 0x00, | ||
| + | 0x00, 0x00, | ||
| + | 0x00, 0x00, //0x10h | ||
| + | 0x00, 0x19, | ||
| + | 0x2a, 0x11, | ||
| + | 0xB0, 0x42, | ||
| + | 0x2A, 0x11, // | ||
| + | 0xb8, 0x31, //0x15h | ||
| + | 0xc0, 0x00, | ||
| + | 0x2a, 0x91, | ||
| + | 0x94, 0x00, | ||
| + | 0x00, 0xa8, | ||
| + | 0xc4, 0x00, //0x1ah | ||
| + | 0xF7, 0xcF, | ||
| + | 0x12, 0x14, //0x1ch | ||
| + | 0x80, 0x6F, | ||
| + | 0x46, 0x08, | ||
| + | 0x00, 0x86, //10000110 | ||
| + | 0x06, 0x61, //0x20H | ||
| + | 0x00, 0x00, | ||
| + | 0x10, 0x9E, | ||
| + | 0x23, 0xC8, | ||
| + | 0x04, 0x06, | ||
| + | 0x0E, 0x1C, //0x25H //0x04 0x08 | ||
| + | }; | ||
| + | |||
| + | int16_t freq = 10110; | ||
| + | uint16_t vol = 1; | ||
| + | // | ||
| + | // added items - Mel | ||
| + | boolean bassBit = true;// bass boost | ||
| + | boolean monoBit = false;// force MONO not stereo | ||
| + | const boolean seekUP = true; | ||
| + | const boolean seekDN = false; | ||
| + | uint8_t minSignalStrength = 36;// anything below this probably set a MONO flag for better reception | ||
| + | uint8_t signalStrength; | ||
| + | long previousMillis = 0;// last time the function was called | ||
| + | long interval = 2000;// interval for the signal level function (2 seconds) | ||
| + | int8_t stationStep = 10;// kHz steps bewteen the stations (North America = 10) | ||
| + | boolean hasVolumePot = true;// flag if you have a POT attached or not | ||
| + | // | ||
| + | void setup() | ||
| + | { | ||
| + | Wire.begin(); | ||
| + | loadDefaults();// load any defaults from previous radio settings | ||
| + | Serial.begin(9600); | ||
| + | Serial.println("Started"); | ||
| + | //======================= | ||
| + | //rda5807 power on | ||
| + | RDA5807P_PowerOnReset(); | ||
| + | RDA5807P_SetMute(false); | ||
| + | |||
| + | //======================= | ||
| + | pinMode(BTNUP, INPUT_PULLUP); | ||
| + | pinMode(VOL_POT, INPUT); | ||
| + | pinMode(BTNDN, INPUT_PULLUP); | ||
| + | //======================= | ||
| + | RDA5807P_SetVolumeLevel(vol);// use this if you don't have a POT for volume attached (0-15) | ||
| + | RDA5807P_SetFreq( freq ); | ||
| + | } | ||
| + | |||
| + | void loop() | ||
| + | { | ||
| + | unsigned long currentMillis = millis(); | ||
| + | |||
| + | if(currentMillis - previousMillis > interval) { | ||
| + | // save the last time you blinked the LED | ||
| + | previousMillis = currentMillis; | ||
| + | showSignalStrength(); | ||
| + | } | ||
| + | // | ||
| + | if (digitalRead(BTNUP) == 1) | ||
| + | { | ||
| + | delay(100); | ||
| + | if (digitalRead(BTNUP) == 1) | ||
| + | fmSeek(seekUP); | ||
| + | while(digitalRead(BTNUP) == 1); | ||
| + | } | ||
| + | if (digitalRead(BTNDN) == 1) | ||
| + | { | ||
| + | delay(100); | ||
| + | if (digitalRead(BTNDN) == 1) | ||
| + | fmSeek(seekDN); | ||
| + | while(digitalRead(BTNDN) == 1); | ||
| + | } | ||
| + | if (hasVolumePot == true) setVolume();// use this to read the POT | ||
| + | } | ||
| + | // | ||
| + | void setVolume() { | ||
| + | unsigned int temp_vol; | ||
| + | temp_vol = analogRead( VOL_POT ); | ||
| + | if (abs(temp_vol - vol)>5) | ||
| + | { | ||
| + | if (vol != temp_vol) {// don't bother changing the volume if unless the pot moves | ||
| + | vol = temp_vol; | ||
| + | unsigned char hex_vol = map(vol, 0, 1023, 0, 0xf); | ||
| + | RDA5807P_SetVolumeLevel(hex_vol); | ||
| + | saveDefaults();// save new volume to EEPROM | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | // | ||
| + | void fmSeek(boolean theDir) { | ||
| + | int signalStrength; | ||
| + | if (!theDir) { | ||
| + | Serial.println("Start seeking down..."); | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | Serial.println("Start seeking up..."); | ||
| + | } | ||
| + | do { | ||
| + | do{ | ||
| + | if (theDir == seekUP) { | ||
| + | freq += stationStep; | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | freq -= stationStep; | ||
| + | } | ||
| + | if (freq > 10800) freq = 8800; | ||
| + | if (freq < 8800) freq = 10800; | ||
| + | //Serial.println(freq); | ||
| + | } | ||
| + | while(!RDA5807P_ValidStop(freq)); | ||
| + | delay(50); | ||
| + | signalStrength = RDA5807P_GetSigLvl(freq);// max is 63 according to Data sheet, but I've seen more | ||
| + | } | ||
| + | while (signalStrength < minSignalStrength);// minimum signal strength, keep looking | ||
| + | showRadioStation(); | ||
| + | saveDefaults();// save new station selection to EEPROM | ||
| + | } | ||
| + | // | ||
| + | void showRadioStation() { | ||
| + | Serial.print("Stable Freq:"); | ||
| + | Serial.print(((float)freq)/100.0f); | ||
| + | Serial.println("MHz"); | ||
| + | } | ||
| + | // | ||
| + | void showSignalStrength() { | ||
| + | signalStrength = RDA5807P_GetSigLvl(freq);// max is 63...as noted | ||
| + | Serial.print("Signal Strength: "); | ||
| + | Serial.println(signalStrength); | ||
| + | } | ||
| + | |||
| + | //=========================================================== | ||
| + | // FM functions | ||
| + | //=========================================================== | ||
| + | unsigned char OperationRDAFM_2w(unsigned char operation, unsigned char *data, int numBytes) | ||
| + | { | ||
| + | if(operation == READ) | ||
| + | { | ||
| + | Wire.requestFrom(I2C_ADDR, numBytes); | ||
| + | for(int i=0;i<numBytes;i++) | ||
| + | { | ||
| + | *data++ = Wire.read(); | ||
| + | } | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | Wire.beginTransmission(I2C_ADDR); | ||
| + | for(int i=0;i<numBytes;i++) | ||
| + | { | ||
| + | Wire.write(*data++); | ||
| + | } | ||
| + | Wire.endTransmission(); | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | |||
| + | |||
| + | /** | ||
| + | * @brief Reset RDA5807P while power on RDA5807P | ||
| + | * @author RDA RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param void | ||
| + | * @return void | ||
| + | * @retval | ||
| + | */ | ||
| + | void RDA5807P_PowerOnReset(void) | ||
| + | { | ||
| + | RDA5807P_Intialization(); | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * @brief RDA5807P power off function | ||
| + | * @author RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param void | ||
| + | * @return void | ||
| + | * @retval | ||
| + | */ | ||
| + | void RDA5807P_PowerOffProc(void) | ||
| + | { | ||
| + | RDA5807P_REGW[1] &= (~1); | ||
| + | OperationRDAFM_2w(WRITE, &(RDA5807P_REGW[0]), 2); | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * @brief Set RDA5807P into mute mode | ||
| + | * @author RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param bool mute: if mute is true,then set mute; if mute is false,then set no mute | ||
| + | * @return void | ||
| + | * @retval | ||
| + | */ | ||
| + | void RDA5807P_SetMute(boolean mute) | ||
| + | { | ||
| + | if(mute) | ||
| + | RDA5807P_REGW[0] &= ~(1<<6); | ||
| + | else | ||
| + | RDA5807P_REGW[0] |= 1<<6; | ||
| + | RDA5807P_REGW[0] |= monoBit<<5; | ||
| + | RDA5807P_REGW[0] |= bassBit<<4; | ||
| + | OperationRDAFM_2w(WRITE, &(RDA5807P_REGW[0]), 2);//RDA5807M_REGW | ||
| + | delay(50); //Dealy 50 ms | ||
| + | } | ||
| + | // | ||
| + | /************************************************* | ||
| + | * @brief Set frequency function | ||
| + | * @author RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param int16_t curFreq:frequency value | ||
| + | * @return void | ||
| + | * @retval | ||
| + | ***********************************************/ | ||
| + | void RDA5807P_SetFreq(int16_t curFreq) | ||
| + | { | ||
| + | uint16_t curChan; | ||
| + | curChan=RDA5807P_FreqToChan(curFreq); | ||
| + | |||
| + | if((curFreq >= 6500)&&(curFreq < 7600)) | ||
| + | { | ||
| + | RDA5807P_REGW[3] = 0x0c; | ||
| + | } | ||
| + | else if((curFreq >= 7600)&&(curFreq < 10800)) | ||
| + | { | ||
| + | RDA5807P_REGW[3] = 0x08;// sets the BAND bits (00xx = 87-108, 01xx=76-91, 10xx=76-108, 11xx=65-76 | ||
| + | // for north america this must be set to 10xx for some unknown reason | ||
| + | } | ||
| + | //SetNoMute | ||
| + | RDA5807P_REGW[0] |= 1<<6; | ||
| + | RDA5807P_REGW[0] |= monoBit<<5; | ||
| + | RDA5807P_REGW[0] |= bassBit<<4; | ||
| + | //handleBits(); | ||
| + | RDA5807P_REGW[2]=curChan>>2; | ||
| + | RDA5807P_REGW[3]=(((curChan&0x0003)<<6)|0x10) | (RDA5807P_REGW[3]&0x0f); //set tune bit | ||
| + | |||
| + | OperationRDAFM_2w(WRITE, &(RDA5807P_REGW[0]), 4); | ||
| + | delay(50); //Delay five ms | ||
| + | showRadioStation(); | ||
| + | } | ||
| + | // | ||
| + | /** | ||
| + | * @brief Station judge for auto search | ||
| + | * @In auto search mode,uses this function to judge the frequency if has a station | ||
| + | * @author RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param int16_t freq:frequency value | ||
| + | * @return bool: if return true,the frequency has a true station;otherwise doesn't have a station | ||
| + | * @retval | ||
| + | */ | ||
| + | boolean RDA5807P_ValidStop(int freq) | ||
| + | { | ||
| + | uint8_t RDA5807P_reg_data[4]={ | ||
| + | 0 }; | ||
| + | uint8_t falseStation = 0; | ||
| + | uint8_t i=0; | ||
| + | uint16_t curChan; | ||
| + | |||
| + | if((freq >= 6500)&&(freq < 7600)) | ||
| + | { | ||
| + | RDA5807P_REGW[3] = 0x0c; | ||
| + | } | ||
| + | else if((freq >= 7600)&&(freq < 10800)) | ||
| + | { | ||
| + | RDA5807P_REGW[3] = 0x08;// sets the BAND bits (00xx = 87-108, 01xx=76-91, 10xx=76-108, 11xx=65-76 | ||
| + | // for north america this must be set to 10xx for some unknown reason | ||
| + | } | ||
| + | curChan=RDA5807P_FreqToChan(freq); | ||
| + | //SetNoMute bit 9 is seek direction (0=seek down, 1=seek up). | ||
| + | //02H 14 | ||
| + | RDA5807P_REGW[0] |= 1<<6;// reg zero is bits 15 to bit 8 (this shifts to bit 14) | ||
| + | RDA5807P_REGW[0] |= monoBit<<5; | ||
| + | RDA5807P_REGW[0] |= bassBit<<4; | ||
| + | //handleBits(); | ||
| + | RDA5807P_reg_data[0]=RDA5807P_REGW[0]; | ||
| + | RDA5807P_reg_data[1]=RDA5807P_REGW[1]; | ||
| + | RDA5807P_reg_data[2]=curChan>>2;//03H 15:8 CHAN | ||
| + | RDA5807P_reg_data[3]=(((curChan&0x0003)<<6)|0x10) | (RDA5807P_REGW[3]&0x0f);// | ||
| + | OperationRDAFM_2w(WRITE,&(RDA5807P_reg_data[0]), 4); | ||
| + | |||
| + | delay(50); //Dealy 25 ms | ||
| + | |||
| + | if (0x5808 == gChipID) | ||
| + | OperationRDAFM_2w(READ,&(RDA5807P_reg_data[0]), 4); // | ||
| + | else | ||
| + | { | ||
| + | do | ||
| + | { | ||
| + | i++; | ||
| + | if(i>5) return 0; | ||
| + | |||
| + | delay(30); | ||
| + | //read REG0A&0B | ||
| + | OperationRDAFM_2w(READ,&(RDA5807P_reg_data[0]), 4); | ||
| + | } | ||
| + | while((RDA5807P_reg_data[0]&0x40)==0); | ||
| + | } | ||
| + | |||
| + | //check FM_TRUE | ||
| + | if((RDA5807P_reg_data[2] &0x01)==0) falseStation=1;//0B 8 FM TRUE | ||
| + | |||
| + | if(freq==9600) falseStation=1;// North America - if scanning DOWN, the radio will lock on 9600 for some reason! | ||
| + | delay(50); | ||
| + | if (falseStation==1) | ||
| + | return 0; | ||
| + | else | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * @brief Get the signal level(RSSI) of the current frequency | ||
| + | * @author RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param int16_t curf:frequency value | ||
| + | * @return uint8_t: the signal level(RSSI) | ||
| + | * @retval | ||
| + | */ | ||
| + | uint8_t RDA5807P_GetSigLvl( int16_t curf ) | ||
| + | { | ||
| + | uint8_t RDA5807P_reg_data[4]={ | ||
| + | 0 }; | ||
| + | OperationRDAFM_2w(READ,&(RDA5807P_reg_data[0]), 4); | ||
| + | delay(50); //Delay 50 ms | ||
| + | return (RDA5807P_reg_data[2]>>1); /*??rssi*/ | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * @brief Set FM volume | ||
| + | * @It has better use the system volume operation to replace this function | ||
| + | * @author RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param uint8_t level: volume value | ||
| + | * @return void | ||
| + | * @retval | ||
| + | */ | ||
| + | void RDA5807P_SetVolumeLevel(uint8_t level) | ||
| + | { | ||
| + | uint8_t RDA5807P_reg_data[8]; | ||
| + | uint8_t i = 0; | ||
| + | |||
| + | for (i=0;i<8;i++) | ||
| + | RDA5807P_reg_data[i] = RDA5807P_REGW[i]; | ||
| + | |||
| + | RDA5807P_reg_data[7]=(( RDA5807P_REGW[7] & 0xf0 ) | (level & 0x0f)); | ||
| + | |||
| + | RDA5807P_reg_data[3] &= (~(0x10));//disable tune | ||
| + | |||
| + | OperationRDAFM_2w(WRITE, &(RDA5807P_reg_data[0]), 8); | ||
| + | delay(50); //Dealy 50 ms | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * @brief Initialize RDA5807P | ||
| + | * @author RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param void | ||
| + | * @return bool:if true,the operation is successful;otherwise is failed | ||
| + | * @retval | ||
| + | **/ | ||
| + | boolean RDA5807P_Intialization(void) | ||
| + | { | ||
| + | uint8_t error_ind = 0; | ||
| + | uint8_t RDA5807P_REGR[10]={ | ||
| + | 0x0 }; | ||
| + | uint8_t i = 0; | ||
| + | |||
| + | RDA5807P_REGW[0] = 0x00; | ||
| + | RDA5807P_REGW[0] |= monoBit<<5; | ||
| + | RDA5807P_REGW[0] |= bassBit<<4; | ||
| + | RDA5807P_REGW[1] = 0x02; | ||
| + | |||
| + | error_ind = OperationRDAFM_2w(WRITE, (uint8_t *)&RDA5807P_REGW[0], 2);//soft reset | ||
| + | delay(50); | ||
| + | |||
| + | error_ind = OperationRDAFM_2w(READ, (uint8_t *)&RDA5807P_REGR[0], 10); | ||
| + | delay(50); | ||
| + | |||
| + | gChipID = RDA5807P_REGR[8]; | ||
| + | gChipID = ((gChipID << 8) | RDA5807P_REGR[9]); | ||
| + | |||
| + | Serial.print("Chip ID: 0x"); | ||
| + | Serial.println(gChipID, HEX); | ||
| + | |||
| + | for (i=0;i<8;i++) { | ||
| + | RDA5807P_REGW[i] = RDA5807N_initialization_reg[i]; | ||
| + | } | ||
| + | |||
| + | error_ind = OperationRDAFM_2w(WRITE, (uint8_t *)&RDA5807N_initialization_reg[0], 2); //power up | ||
| + | delay(600); | ||
| + | //Serial.println(sizeof(RDA5807N_initialization_reg)); | ||
| + | error_ind = OperationRDAFM_2w(WRITE, (uint8_t *)&RDA5807N_initialization_reg[0], sizeof(RDA5807N_initialization_reg)); | ||
| + | |||
| + | delay(50); //Dealy 50 ms | ||
| + | |||
| + | if (error_ind ) | ||
| + | return 0; | ||
| + | else | ||
| + | return 1; | ||
| + | } | ||
| + | // | ||
| + | /** | ||
| + | * @brief Cover the frequency to channel value | ||
| + | * @author RDA Ri'an Zeng | ||
| + | * @date 2008-11-05 | ||
| + | * @param uint16 frequency:covered frequency | ||
| + | * @return uint16: channel value | ||
| + | * @retval | ||
| + | * In the United States, frequency-modulated broadcasting stations operate in a frequency band extending from 87.8 MHz to 108.0 MHz, | ||
| + | * for a total of 20.2 MHz. It is divided into 101 channels, each 0.2 MHz wide, designated "channel 200" through "channel 300." | ||
| + | * In actual practice, no one (except the FCC) uses these channel numbers; the frequencies are used instead. | ||
| + | */ | ||
| + | uint16_t RDA5807P_FreqToChan(uint16_t frequency) { | ||
| + | uint8_t channelSpacing = 10; | ||
| + | uint16_t channel = 0; | ||
| + | |||
| + | if((frequency >= 6500)&&(frequency < 7600)) | ||
| + | { | ||
| + | channel = (frequency - 6500)/channelSpacing; | ||
| + | } | ||
| + | else if((frequency >= 7600)&&(frequency < 10800)) | ||
| + | { | ||
| + | channel = (frequency - 7600)/channelSpacing; | ||
| + | } | ||
| + | return (channel); | ||
| + | } | ||
| + | // | ||
| + | void loadDefaults() { | ||
| + | char myCode[9] = "Grove_FM"; | ||
| + | char myInit[9] = "blank123"; | ||
| + | /* | ||
| + | * byte map in EEPROM | ||
| + | * 8, 9 the default frequency for a reboot | ||
| + | * 10, 11 current preset volume of the radio (used if no pot is attached) | ||
| + | */ | ||
| + | for (int i=0; i < 8; i++) { | ||
| + | myInit[i] = EEPROM.read(i);// read out to see if the thing is INITIALIZED | ||
| + | } | ||
| + | if (strcmp(myCode, myInit) == 0) {// if this is ZERO (we previously wrote some), then read the values | ||
| + | freq = epReadINT(8);// read back the INT for frequency from eeprom 8 and 9 (two bytes for an INT) | ||
| + | if (!hasVolumePot) vol = epReadINT(10);// read back the volume setting but don't use it unless flag is false | ||
| + | } | ||
| + | else// we don't have any defaults, so we have to save some first | ||
| + | { | ||
| + | for (int i=0; i < 8; i++) { | ||
| + | EEPROM.write(i, myCode[i]);// write this to EEPROM to show we have it saved | ||
| + | } | ||
| + | saveDefaults();// write the current default settings | ||
| + | } | ||
| + | } | ||
| + | // | ||
| + | void saveDefaults() { | ||
| + | epWriteINT(8, freq);// write the two bytes for INT for a reboot | ||
| + | epWriteINT(10, vol);// write the current volume POT setting | ||
| + | } | ||
| + | // | ||
| + | void epWriteINT(int where, int theVal) { | ||
| + | union uData | ||
| + | { | ||
| + | byte stuff[2]; | ||
| + | int f1;// 2 bytes of memory | ||
| + | } | ||
| + | u; | ||
| + | u.f1 = theVal;// copy into the union | ||
| + | for (int j=0; j < 2; j++) {// now we have to write out 2 bytes of memory | ||
| + | EEPROM.write(where + j, u.stuff[j]);// write it to EEPROM | ||
| + | } | ||
| + | } | ||
| + | // | ||
| + | long epReadINT(int where) { | ||
| + | union uData | ||
| + | { | ||
| + | byte stuff[2]; | ||
| + | int f1;// 2 bytes of memory | ||
| + | } | ||
| + | u; | ||
| + | for (int j=0; j < 2; j++) { | ||
| + | u.stuff[j]=EEPROM.read(where + j);// read back the 2 bytes at this memory location | ||
| + | } | ||
| + | return u.f1; | ||
| + | } | ||
| + | // | ||
| + | void epWriteLong(int where, long theVal) { | ||
| + | union uData | ||
| + | { | ||
| + | byte stuff[4]; | ||
| + | long f1;// 4 bytes of memory | ||
| + | } | ||
| + | u; | ||
| + | u.f1 = theVal;// copy into the union | ||
| + | for (int j=0; j < 4; j++) {// now we have to write out 4 bytes of memory | ||
| + | EEPROM.write(where + j, u.stuff[j]);// write it to EEPROM | ||
| + | } | ||
| + | } | ||
| + | // | ||
| + | long epReadLong(int where) { | ||
| + | union uData | ||
| + | { | ||
| + | byte stuff[4]; | ||
| + | long f1;// 4 bytes of memory | ||
| + | } | ||
| + | u; | ||
| + | for (int j=0; j < 4; j++) { | ||
| + | u.stuff[j]=EEPROM.read(where + j);// read back the 4 bytes to this memory location | ||
| + | } | ||
| + | return u.f1; | ||
| + | } | ||
| + | </pre> | ||
Revision as of 22:49, 12 August 2018
Grove - I2C FM Receiver v1.1
Créer un lien
Créer un lien
Créer un lien
/*
* I2C_FM.ino
* Demo code for the Grove-I2C_FM_Receiver module
*
* Copyright (c) 2012 seeed technology inc.
* Website : www.seeed.cc
* Author : Jack Shao (jacky.shaoxg@gmail.com)
* Create Time: Jul 2014
* Change Log :
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
//
/*
* Modifications to the I2C_FM.ino by Mel Patrick - Wabbit Wanch Design
* Modified routines for scanning UP or DOWN through the FM band
* Modified routine to test for signal strength of received station
* Modified routines to support bass boost and MONO signal
* RSSI, read it too soon after setting a station and you get a small value
* so it's better to wait a bit (50ms) and try it. minSignalStrength will
* skip locking on a station with a weak signal (you could set the MONO bit) to get
* better reception on these stations.
*/
#include <Arduino.h>
#include <Wire.h>
#include <EEPROM.h>
#define BTNUP 2// used for seeking UP (normally CLOSED push button)
#define VOL_POT A0// volume POT LOG taper 10K
#define BTNDN 3// used for seeking DOWN (normally CLOSED push button)
uint16_t gChipID = 0;
uint8_t RDA5807P_REGW[10];
#define I2C_ADDR 0x10
#define READ 1
#define WRITE 0
#define ADRW 0x20
#define ADRR 0x21
//
//#define _SHARE_CRYSTAL_24MHz_
//#define _SHARE_CRYSTAL_12MHz_
#define _SHARE_CRYSTAL_32KHz_
//#define _FM_STEP_50K_
//5807M,5807FP,5807NN,5807NP
uint8_t RDA5807N_initialization_reg[]={
#if defined(_SHARE_CRYSTAL_24MHz_)
0xC4, 0x51, //02H:
#elif defined(_SHARE_CRYSTAL_12MHz_)
0xC4, 0x11, //02H:
#elif defined(_SHARE_CRYSTAL_32KHz_)
0xC4, 0x01,//change 01 to 05 enables the RDS/RBDS
#else
0xC0, 0x01,
#endif
0x00, 0x00,
0x04, 0x00,
0xC3, 0xad, //05h
0x60, 0x00,
0x42, 0x12,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00, //0x0ah
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00, //0x10h
0x00, 0x19,
0x2a, 0x11,
0xB0, 0x42,
0x2A, 0x11, //
0xb8, 0x31, //0x15h
0xc0, 0x00,
0x2a, 0x91,
0x94, 0x00,
0x00, 0xa8,
0xc4, 0x00, //0x1ah
0xF7, 0xcF,
0x12, 0x14, //0x1ch
0x80, 0x6F,
0x46, 0x08,
0x00, 0x86, //10000110
0x06, 0x61, //0x20H
0x00, 0x00,
0x10, 0x9E,
0x23, 0xC8,
0x04, 0x06,
0x0E, 0x1C, //0x25H //0x04 0x08
};
int16_t freq = 10110;
uint16_t vol = 1;
//
// added items - Mel
boolean bassBit = true;// bass boost
boolean monoBit = false;// force MONO not stereo
const boolean seekUP = true;
const boolean seekDN = false;
uint8_t minSignalStrength = 36;// anything below this probably set a MONO flag for better reception
uint8_t signalStrength;
long previousMillis = 0;// last time the function was called
long interval = 2000;// interval for the signal level function (2 seconds)
int8_t stationStep = 10;// kHz steps bewteen the stations (North America = 10)
boolean hasVolumePot = true;// flag if you have a POT attached or not
//
void setup()
{
Wire.begin();
loadDefaults();// load any defaults from previous radio settings
Serial.begin(9600);
Serial.println("Started");
//=======================
//rda5807 power on
RDA5807P_PowerOnReset();
RDA5807P_SetMute(false);
//=======================
pinMode(BTNUP, INPUT_PULLUP);
pinMode(VOL_POT, INPUT);
pinMode(BTNDN, INPUT_PULLUP);
//=======================
RDA5807P_SetVolumeLevel(vol);// use this if you don't have a POT for volume attached (0-15)
RDA5807P_SetFreq( freq );
}
void loop()
{
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
showSignalStrength();
}
//
if (digitalRead(BTNUP) == 1)
{
delay(100);
if (digitalRead(BTNUP) == 1)
fmSeek(seekUP);
while(digitalRead(BTNUP) == 1);
}
if (digitalRead(BTNDN) == 1)
{
delay(100);
if (digitalRead(BTNDN) == 1)
fmSeek(seekDN);
while(digitalRead(BTNDN) == 1);
}
if (hasVolumePot == true) setVolume();// use this to read the POT
}
//
void setVolume() {
unsigned int temp_vol;
temp_vol = analogRead( VOL_POT );
if (abs(temp_vol - vol)>5)
{
if (vol != temp_vol) {// don't bother changing the volume if unless the pot moves
vol = temp_vol;
unsigned char hex_vol = map(vol, 0, 1023, 0, 0xf);
RDA5807P_SetVolumeLevel(hex_vol);
saveDefaults();// save new volume to EEPROM
}
}
}
//
void fmSeek(boolean theDir) {
int signalStrength;
if (!theDir) {
Serial.println("Start seeking down...");
}
else
{
Serial.println("Start seeking up...");
}
do {
do{
if (theDir == seekUP) {
freq += stationStep;
}
else
{
freq -= stationStep;
}
if (freq > 10800) freq = 8800;
if (freq < 8800) freq = 10800;
//Serial.println(freq);
}
while(!RDA5807P_ValidStop(freq));
delay(50);
signalStrength = RDA5807P_GetSigLvl(freq);// max is 63 according to Data sheet, but I've seen more
}
while (signalStrength < minSignalStrength);// minimum signal strength, keep looking
showRadioStation();
saveDefaults();// save new station selection to EEPROM
}
//
void showRadioStation() {
Serial.print("Stable Freq:");
Serial.print(((float)freq)/100.0f);
Serial.println("MHz");
}
//
void showSignalStrength() {
signalStrength = RDA5807P_GetSigLvl(freq);// max is 63...as noted
Serial.print("Signal Strength: ");
Serial.println(signalStrength);
}
//===========================================================
// FM functions
//===========================================================
unsigned char OperationRDAFM_2w(unsigned char operation, unsigned char *data, int numBytes)
{
if(operation == READ)
{
Wire.requestFrom(I2C_ADDR, numBytes);
for(int i=0;i<numBytes;i++)
{
*data++ = Wire.read();
}
}
else
{
Wire.beginTransmission(I2C_ADDR);
for(int i=0;i<numBytes;i++)
{
Wire.write(*data++);
}
Wire.endTransmission();
}
return 0;
}
/**
* @brief Reset RDA5807P while power on RDA5807P
* @author RDA RDA Ri'an Zeng
* @date 2008-11-05
* @param void
* @return void
* @retval
*/
void RDA5807P_PowerOnReset(void)
{
RDA5807P_Intialization();
}
/**
* @brief RDA5807P power off function
* @author RDA Ri'an Zeng
* @date 2008-11-05
* @param void
* @return void
* @retval
*/
void RDA5807P_PowerOffProc(void)
{
RDA5807P_REGW[1] &= (~1);
OperationRDAFM_2w(WRITE, &(RDA5807P_REGW[0]), 2);
}
/**
* @brief Set RDA5807P into mute mode
* @author RDA Ri'an Zeng
* @date 2008-11-05
* @param bool mute: if mute is true,then set mute; if mute is false,then set no mute
* @return void
* @retval
*/
void RDA5807P_SetMute(boolean mute)
{
if(mute)
RDA5807P_REGW[0] &= ~(1<<6);
else
RDA5807P_REGW[0] |= 1<<6;
RDA5807P_REGW[0] |= monoBit<<5;
RDA5807P_REGW[0] |= bassBit<<4;
OperationRDAFM_2w(WRITE, &(RDA5807P_REGW[0]), 2);//RDA5807M_REGW
delay(50); //Dealy 50 ms
}
//
/*************************************************
* @brief Set frequency function
* @author RDA Ri'an Zeng
* @date 2008-11-05
* @param int16_t curFreq:frequency value
* @return void
* @retval
***********************************************/
void RDA5807P_SetFreq(int16_t curFreq)
{
uint16_t curChan;
curChan=RDA5807P_FreqToChan(curFreq);
if((curFreq >= 6500)&&(curFreq < 7600))
{
RDA5807P_REGW[3] = 0x0c;
}
else if((curFreq >= 7600)&&(curFreq < 10800))
{
RDA5807P_REGW[3] = 0x08;// sets the BAND bits (00xx = 87-108, 01xx=76-91, 10xx=76-108, 11xx=65-76
// for north america this must be set to 10xx for some unknown reason
}
//SetNoMute
RDA5807P_REGW[0] |= 1<<6;
RDA5807P_REGW[0] |= monoBit<<5;
RDA5807P_REGW[0] |= bassBit<<4;
//handleBits();
RDA5807P_REGW[2]=curChan>>2;
RDA5807P_REGW[3]=(((curChan&0x0003)<<6)|0x10) | (RDA5807P_REGW[3]&0x0f); //set tune bit
OperationRDAFM_2w(WRITE, &(RDA5807P_REGW[0]), 4);
delay(50); //Delay five ms
showRadioStation();
}
//
/**
* @brief Station judge for auto search
* @In auto search mode,uses this function to judge the frequency if has a station
* @author RDA Ri'an Zeng
* @date 2008-11-05
* @param int16_t freq:frequency value
* @return bool: if return true,the frequency has a true station;otherwise doesn't have a station
* @retval
*/
boolean RDA5807P_ValidStop(int freq)
{
uint8_t RDA5807P_reg_data[4]={
0 };
uint8_t falseStation = 0;
uint8_t i=0;
uint16_t curChan;
if((freq >= 6500)&&(freq < 7600))
{
RDA5807P_REGW[3] = 0x0c;
}
else if((freq >= 7600)&&(freq < 10800))
{
RDA5807P_REGW[3] = 0x08;// sets the BAND bits (00xx = 87-108, 01xx=76-91, 10xx=76-108, 11xx=65-76
// for north america this must be set to 10xx for some unknown reason
}
curChan=RDA5807P_FreqToChan(freq);
//SetNoMute bit 9 is seek direction (0=seek down, 1=seek up).
//02H 14
RDA5807P_REGW[0] |= 1<<6;// reg zero is bits 15 to bit 8 (this shifts to bit 14)
RDA5807P_REGW[0] |= monoBit<<5;
RDA5807P_REGW[0] |= bassBit<<4;
//handleBits();
RDA5807P_reg_data[0]=RDA5807P_REGW[0];
RDA5807P_reg_data[1]=RDA5807P_REGW[1];
RDA5807P_reg_data[2]=curChan>>2;//03H 15:8 CHAN
RDA5807P_reg_data[3]=(((curChan&0x0003)<<6)|0x10) | (RDA5807P_REGW[3]&0x0f);//
OperationRDAFM_2w(WRITE,&(RDA5807P_reg_data[0]), 4);
delay(50); //Dealy 25 ms
if (0x5808 == gChipID)
OperationRDAFM_2w(READ,&(RDA5807P_reg_data[0]), 4); //
else
{
do
{
i++;
if(i>5) return 0;
delay(30);
//read REG0A&0B
OperationRDAFM_2w(READ,&(RDA5807P_reg_data[0]), 4);
}
while((RDA5807P_reg_data[0]&0x40)==0);
}
//check FM_TRUE
if((RDA5807P_reg_data[2] &0x01)==0) falseStation=1;//0B 8 FM TRUE
if(freq==9600) falseStation=1;// North America - if scanning DOWN, the radio will lock on 9600 for some reason!
delay(50);
if (falseStation==1)
return 0;
else
return 1;
}
/**
* @brief Get the signal level(RSSI) of the current frequency
* @author RDA Ri'an Zeng
* @date 2008-11-05
* @param int16_t curf:frequency value
* @return uint8_t: the signal level(RSSI)
* @retval
*/
uint8_t RDA5807P_GetSigLvl( int16_t curf )
{
uint8_t RDA5807P_reg_data[4]={
0 };
OperationRDAFM_2w(READ,&(RDA5807P_reg_data[0]), 4);
delay(50); //Delay 50 ms
return (RDA5807P_reg_data[2]>>1); /*??rssi*/
}
/**
* @brief Set FM volume
* @It has better use the system volume operation to replace this function
* @author RDA Ri'an Zeng
* @date 2008-11-05
* @param uint8_t level: volume value
* @return void
* @retval
*/
void RDA5807P_SetVolumeLevel(uint8_t level)
{
uint8_t RDA5807P_reg_data[8];
uint8_t i = 0;
for (i=0;i<8;i++)
RDA5807P_reg_data[i] = RDA5807P_REGW[i];
RDA5807P_reg_data[7]=(( RDA5807P_REGW[7] & 0xf0 ) | (level & 0x0f));
RDA5807P_reg_data[3] &= (~(0x10));//disable tune
OperationRDAFM_2w(WRITE, &(RDA5807P_reg_data[0]), 8);
delay(50); //Dealy 50 ms
}
/**
* @brief Initialize RDA5807P
* @author RDA Ri'an Zeng
* @date 2008-11-05
* @param void
* @return bool:if true,the operation is successful;otherwise is failed
* @retval
**/
boolean RDA5807P_Intialization(void)
{
uint8_t error_ind = 0;
uint8_t RDA5807P_REGR[10]={
0x0 };
uint8_t i = 0;
RDA5807P_REGW[0] = 0x00;
RDA5807P_REGW[0] |= monoBit<<5;
RDA5807P_REGW[0] |= bassBit<<4;
RDA5807P_REGW[1] = 0x02;
error_ind = OperationRDAFM_2w(WRITE, (uint8_t *)&RDA5807P_REGW[0], 2);//soft reset
delay(50);
error_ind = OperationRDAFM_2w(READ, (uint8_t *)&RDA5807P_REGR[0], 10);
delay(50);
gChipID = RDA5807P_REGR[8];
gChipID = ((gChipID << 8) | RDA5807P_REGR[9]);
Serial.print("Chip ID: 0x");
Serial.println(gChipID, HEX);
for (i=0;i<8;i++) {
RDA5807P_REGW[i] = RDA5807N_initialization_reg[i];
}
error_ind = OperationRDAFM_2w(WRITE, (uint8_t *)&RDA5807N_initialization_reg[0], 2); //power up
delay(600);
//Serial.println(sizeof(RDA5807N_initialization_reg));
error_ind = OperationRDAFM_2w(WRITE, (uint8_t *)&RDA5807N_initialization_reg[0], sizeof(RDA5807N_initialization_reg));
delay(50); //Dealy 50 ms
if (error_ind )
return 0;
else
return 1;
}
//
/**
* @brief Cover the frequency to channel value
* @author RDA Ri'an Zeng
* @date 2008-11-05
* @param uint16 frequency:covered frequency
* @return uint16: channel value
* @retval
* In the United States, frequency-modulated broadcasting stations operate in a frequency band extending from 87.8 MHz to 108.0 MHz,
* for a total of 20.2 MHz. It is divided into 101 channels, each 0.2 MHz wide, designated "channel 200" through "channel 300."
* In actual practice, no one (except the FCC) uses these channel numbers; the frequencies are used instead.
*/
uint16_t RDA5807P_FreqToChan(uint16_t frequency) {
uint8_t channelSpacing = 10;
uint16_t channel = 0;
if((frequency >= 6500)&&(frequency < 7600))
{
channel = (frequency - 6500)/channelSpacing;
}
else if((frequency >= 7600)&&(frequency < 10800))
{
channel = (frequency - 7600)/channelSpacing;
}
return (channel);
}
//
void loadDefaults() {
char myCode[9] = "Grove_FM";
char myInit[9] = "blank123";
/*
* byte map in EEPROM
* 8, 9 the default frequency for a reboot
* 10, 11 current preset volume of the radio (used if no pot is attached)
*/
for (int i=0; i < 8; i++) {
myInit[i] = EEPROM.read(i);// read out to see if the thing is INITIALIZED
}
if (strcmp(myCode, myInit) == 0) {// if this is ZERO (we previously wrote some), then read the values
freq = epReadINT(8);// read back the INT for frequency from eeprom 8 and 9 (two bytes for an INT)
if (!hasVolumePot) vol = epReadINT(10);// read back the volume setting but don't use it unless flag is false
}
else// we don't have any defaults, so we have to save some first
{
for (int i=0; i < 8; i++) {
EEPROM.write(i, myCode[i]);// write this to EEPROM to show we have it saved
}
saveDefaults();// write the current default settings
}
}
//
void saveDefaults() {
epWriteINT(8, freq);// write the two bytes for INT for a reboot
epWriteINT(10, vol);// write the current volume POT setting
}
//
void epWriteINT(int where, int theVal) {
union uData
{
byte stuff[2];
int f1;// 2 bytes of memory
}
u;
u.f1 = theVal;// copy into the union
for (int j=0; j < 2; j++) {// now we have to write out 2 bytes of memory
EEPROM.write(where + j, u.stuff[j]);// write it to EEPROM
}
}
//
long epReadINT(int where) {
union uData
{
byte stuff[2];
int f1;// 2 bytes of memory
}
u;
for (int j=0; j < 2; j++) {
u.stuff[j]=EEPROM.read(where + j);// read back the 2 bytes at this memory location
}
return u.f1;
}
//
void epWriteLong(int where, long theVal) {
union uData
{
byte stuff[4];
long f1;// 4 bytes of memory
}
u;
u.f1 = theVal;// copy into the union
for (int j=0; j < 4; j++) {// now we have to write out 4 bytes of memory
EEPROM.write(where + j, u.stuff[j]);// write it to EEPROM
}
}
//
long epReadLong(int where) {
union uData
{
byte stuff[4];
long f1;// 4 bytes of memory
}
u;
for (int j=0; j < 4; j++) {
u.stuff[j]=EEPROM.read(where + j);// read back the 4 bytes to this memory location
}
return u.f1;
}