Иван Иванов   4 ноября в 22:25

Простой FM-радиоприемник на Arduino UNO и RDA5807M

Узнаем, как вы можете слушать местные радиостанции с помощью RDA5807M, Arduino UNO, OLED-экрана и динамика.

В этом проекте мы будем использовать FM-приемник RDA5807M для прослушивания некоторых местных радиостанций с помощью Arduino. Кроме того, мы также отобразим текущую радиостанцию на OLED-экране.

FM-приемник RDA5807M

RDA5807M - это простой стереофонический радиочип, который поддерживает FM-диапазоны во всем мире от 50 МГц до 115 МГц. Он имеет полностью интегрированный синтезатор, селективность промежуточной частоты (IF), RDS / RBDS и декодер MPX.

RDA5807M
RDA5807M

Этот чип имеет мощный цифровой аудиопроцессор с низким ПЧ, а это значит, что вы можете напрямую слушать звук через наушники. Однако если вы хотите подключить его к динамикам большего размера, вам может потребоваться схема усилителя звука. Я буду использовать простой портативный динамик со встроенным усилителем звука для этого проекта.

Распиновка RDA5807M

Распиновка RDA5807M
Распиновка RDA5807M

Оборудование

  • Arduino UNO (или любая другая плата)
  • RDA5807M
  • I2C OLED Дисплей
  • Радио- или ТВ-антенна
  • Резисторы 10 кОм

Программное обеспечение

Схема соединения

Подключите радиоприемник RDA5807M к Arduino UNO, как показано ниже.

Вы можете использовать одножильный провод или телевизионную/радиоантенну для данного ресивера в качестве антенны. Rout (правый аудиовыход) и Lout (левый аудиовыход) модуля RDA5807M можно подключить через гнездовой разъем аудиоразъема 3,5 мм. Для этого вы можете сделать дополнительный кабель, как я сделал на картинке ниже.

Используя макет, подключите OLED, как показано ниже, и соедините три кнопки последовательно, используя резисторы по 10 кОм. Кнопки подключены к аналоговому выводу A0 Arduino. Дисплей OLED и модуль FM-приемника используют те же SDA и SCL, что и Arduino.

Когда все оборудование подключено примерно так должно выглядеть ваше устройство:

Исходный код

После проверки всех соединений загрузите исходный код, скопировав и вставив приведенный ниже код в Arduino IDE.

#include <EEPROM.h> 

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
//#include <TFT_ILI9163C.h>
#include <Adafruit_SSD1306.h>
#include <RDA5807M.h>
#include <RDSParser.h>
#include <radio.h>
//extern const uint8_t icon_radio[];
//extern const uint8_t icon_volume[];
RDA5807M radio;    ///< Create an instance of a RDA5807 chip radio
#define DEBUG 0
#define OLED_RESET 4

Adafruit_SSD1306 tft(OLED_RESET);
RDSParser rds;

enum RADIO_STATE {
  STATE_PARSECOMMAND, ///< waiting for a new command character.
  
  STATE_PARSEINT,     ///< waiting for digits for the parameter.
  STATE_EXEC          ///< executing the command.
};

RADIO_STATE state; ///< The state variable is used for parsing input characters.


const int entrada = A0; 
int entradaV = 0; 

int menu;
#define MAXmenu  3
int menux;
#define MAXmenux  3
static char* menuS[]= {" ","MANUAL TUNE","VOLUME     ","AUTO TUNE","INFO        "};


int volumen, volumenOld=7;
int frecuencia,frecuenciaOld;
int signal_level;

unsigned int z,z1;
byte xfrecu,xfrecuOld;
unsigned int estado[6];

unsigned long time,time1,time2,time3;


char buffer[30];
unsigned int RDS[4];
char seg_RDS[8];
char seg_RDS1[64];
char indexRDS1;

char hora,minuto,grupo,versio;
unsigned long julian;

 int mezcla;
//radio.debugEnable();
void RDS_process(uint16_t block1, uint16_t block2, uint16_t block3, uint16_t block4) {
  rds.processData(block1, block2, block3, block4);
}
void setup() 
{
  Wire.begin();   
  //Serial.begin(9600); 
  Serial.begin(57600);
 tft.begin(SSD1306_SWITCHCAPVCC, 0x3C);
 radio.init();
 radio.debugEnable();
 tft.display();
  delay(20);

  // Clear the buffer.
  tft.clearDisplay();

// read value of last frequency
frecuencia = EEPROM.read(201);
volumen = EEPROM.read(202);

if (volumen < 0) volumen = 0;
if (volumen >15) volumen = 15;
if (frecuencia <0) frecuencia = 0;
if (frecuencia >210) frecuencia = 210;
  
  tft.setRotation(0);
  tft.setTextColor(WHITE);
 
tft.setCursor(95,0);
// tft.print("Nivel semnal :");
  tft.print(signal_level);tft.display();
   tft.print("/15 ");
   // draw an antenna
   tft.drawLine(103, 9, 103, 13, WHITE);
   tft.drawCircle(103,9,2,WHITE);

 
   WriteReg(0x02,0xC00d); // write 0xC00d into Reg.2 ( soft reset, enable,RDS, )
   WriteReg(0x05,0x84d8);  // write ,0x84d8 into Reg.3 
   
  time3=time2=time1=time = millis();
  menu=3;
  
  canal(frecuencia);
 // clearRDS;

  state = STATE_PARSECOMMAND;
 
  // setup the information chain for RDS data.
  radio.attachReceiveRDS(RDS_process);
  rds.attachServicenNameCallback(tftServiceName);
//  rds.attachTimeCallback(tftTime);
}
/*
void tftTime(uint8_t hour, uint8_t minute) {
  tft.setCursor(60, 100); 
  tft.setTextSize(2);    
  tft.setTextColor(GREENYELLOW, BLACK);
  if (hour < 10) tft.print('0');
  if (hour > 30) tft.print(' ');
  tft.print(hour);
  tft.print(':');
  if (minute < 10) tft.print('0');
  tft.println(minute); 
  tft.setTextColor(GREENYELLOW, BLACK);
} // tftTime()
*/
void tftServiceName(char *name)
{
  size_t len = strlen(name);
  tft.setCursor(0, 25);
  tft.setTextSize(1.5);    
  tft.setTextColor(WHITE, BLACK); 
  tft.print(name);tft.setTextColor(WHITE, BLACK);  tft.display();
  while (len < 8) {
    tft.print(' ');tft.setTextColor(WHITE, BLACK); tft.display();
    len++;  
  } // while
} // tftServiceName()
void loop() {
  int newPos;
  unsigned long now = millis();
 // static unsigned long nextFreqTime = 0;
  static unsigned long nextRadioInfoTime = 0;
  char c;
  entradaV = analogRead(entrada);
  
   #if DEBUG  
      Serial.print("sensor = " );  Serial.println(entradaV);delay(50);
   #endif
   
// Boton menu   
 if(entradaV>500 && entradaV<524)
   {
    Serial.print(entradaV);
    menu++;
    if(menu>MAXmenu)menu=1;
    Visualizar();

    #if DEBUG 
      Serial.print("menu = " );  Serial.println(menu); 
    #endif   
    while(1020>analogRead(entrada))delay(5);
   }
            
// Boton derecho
 if( entradaV<50)
   {
    Serial.print(entradaV);
    menux++;
    if(menux>MAXmenux)menux=MAXmenux;
    #if DEBUG 
      Serial.print("menux = " );  Serial.println(menux);
    #endif
    switch(menu)
      {
        case 1:
          frecuencia++;
          if(frecuencia>210)frecuencia=210; // верхняя граница частот
          delay(130);
        break;  
        case 2:
           volumen++;
           if(volumen>15)volumen=15;
           while(1020>analogRead(entrada))delay(5);
        break; 
        case 3:
           busqueda(0);
           while(1020>analogRead(entrada))delay(5);
        break; 
        case 4:
           // LcdClear();
            tft.clearDisplay();
           // visualPI();
            delay(3000);
           // LcdClear();
            tft.clearDisplay();
            frecuenciaOld=-1;
        break; 
      }              
   }
   
// Boton izquierdo
 if( entradaV<700 && entradaV>660)
 //if( entradaV<700 && entradaV>660)
   {
    Serial.print(entradaV);
    menux--;
    if(menux<1)menux=1; 
    #if DEBUG 
      Serial.print("menux = " );  Serial.println(menux);
    #endif   
    switch(menu)
      {
        case 1:
            frecuencia--;
            if(frecuencia<0)frecuencia=0;    
            delay(130);
        break;  
        case 2:
            volumen--;
            if(volumen<0)volumen=0;
            while(1020>analogRead(entrada))delay(5);
        break; 
        case 3:
            busqueda(1);
            while(1020>analogRead(entrada))delay(5);
        break; 
        case 4:
           // LcdClear();
          tft.clearDisplay();
            //tft.print(frecuencia);
            //visualPTY();
            delay(3000);
           // LcdClear();
            tft.clearDisplay();
            frecuenciaOld=-1;
        break; 
      }
    
   }
      
      if( millis()-time2>50)
          {
           ReadEstado();
           time1 = millis(); 
            //RDS   
           if ((estado[0] & 0x8000)!=0) {get_RDS();}
          }
     if( millis()-time3>500)
          {
            time3 = millis();
            Visualizar(); 

          }

    if( frecuencia!=frecuenciaOld)
          {  
            frecuenciaOld=frecuencia;                        
            z=870+frecuencia;
            EEPROM.write(201,frecuencia);
         #if DEBUG  
            Serial.print("Frecuencia = " );  Serial.println(frecuencia);
         #endif 
            sprintf(buffer,"%04d ",z);
  // tft.drawBitmap(80, 2,  icon_volume, 24, 10, WHITE);
  //tft.display();
            tft.setCursor(0,0); 
            tft.setTextSize(2);    
            tft.setTextColor(WHITE, BLACK); 
          for(z=0;z<5;z++)
               {
          if (z==0) {
          if (frecuencia < 130) tft.print(" ");
          else tft.print(buffer[0]);tft.setTextColor(WHITE, BLACK);
          
       
          }
                     
             if(z==3)  tft.print(",");tft.setTextColor(WHITE, BLACK); 
             
           if (z>0) tft.print(buffer[z]);tft.setTextColor(WHITE, BLACK); 
               }
               
               
       //   LcdString("MHz"); 
          tft.setCursor(65,5);
          tft.setTextSize(1);
          tft.setTextColor(WHITE, BLACK);  
          tft.print("MHz");
          tft.setTextColor(WHITE, BLACK); 
           tft.display();
        
          canal(frecuencia);
        //  clearRDS();
       }     

    //Cambio de volumen        
    if(volumen!=volumenOld)
        { 
          volumenOld=volumen;
          sprintf(buffer,"Volum %02d",volumen); tft.setCursor(62,25); tft.setTextSize(1); tft.setTextColor(WHITE, BLACK); tft.print(buffer);tft.print(" ");       
          tft.display();tft.print("/15");
          WriteReg(5, 0x84D0 | volumen);
          EEPROM.write(202,volumen);
        }    
         // check for RDS data
  radio.checkRDS();

  
}


void busqueda(byte direc)
{
  byte i;
  if(!direc) WriteReg(0x02,0xC30d); else  WriteReg(0x02,0xC10d);
  
  for(i=0;i<10;i++)
    {
      delay(200);      
      ReadEstado();      
      if(estado[0]&0x4000)
        {
          //Serial.println("Emisora encontrada");
          frecuencia=estado[0] & 0x03ff;  
          break;
        }       
    }
}

void Visualizar(void)
{ 
     
   //  tft.setCursor(3, 13); tft.setTextSize(2); tft.setTextColor(WHITE, BLACK); tft.print("FM"); 
    // tft.setCursor(27, 20); tft.setTextSize(1); tft.setTextColor(WHITE, BLACK); tft.print("RDA5807-"); 
      sprintf(buffer,"%s",menuS[menu]); tft.setCursor(5,17); tft.setTextSize(1); tft.setTextColor(WHITE,BLACK); tft.print(buffer); 
       //Detectar se&#241;al stereo
       tft.display();
       tft.setCursor(85,15);
       tft.setTextColor(BLACK, WHITE);
       if((estado[0] & 0x0400)==0)  tft.print("");   else     tft.setTextColor(BLACK, WHITE); tft.print("Stereo");  tft.setTextColor(BLACK, WHITE);      
       tft.display();
       //Se&#241;al 
     //  z=estado[1]>>10; sprintf(buffer,"S-%02d",z); tft.setCursor(58,0); tft.setTextColor(WHITE, BLACK); tft.print(buffer); tft.setCursor(25,0);tft.setTextColor(WHITE, BLACK);tft.print("Canal");
      sprintf(buffer,"Volum %02d",volumen); tft.setCursor(62,25);tft.setTextSize(1); tft.setTextColor(WHITE, BLACK); tft.print(buffer); //DUBLARE AFISARE VOLUM
       tft.display();tft.print("/15");
      frecuencia=estado[0] & 0x03ff;  
    
  }

void canal( int canal)
     {
       byte numeroH,numeroL;
       
       numeroH=  canal>>2;
       numeroL = ((canal&3)<<6 | 0x10); 
       Wire.beginTransmission(0x11);
       Wire.write(0x03);
         Wire.write(numeroH);                     // write frequency into bits 15:6, set tune bit         
         Wire.write(numeroL);
         Wire.endTransmission();
       }

//________________________ 
//RDA5807_adrr=0x11;       
// I2C-Address RDA Chip for random      Access
void WriteReg(byte reg,unsigned int valor)
{
  Wire.beginTransmission(0x11);
  Wire.write(reg); Wire.write(valor >> 8); Wire.write(valor & 0xFF);
  Wire.endTransmission();
  //delay(50);
}

//RDA5807_adrs=0x10;
// I2C-Address RDA Chip for sequential  Access
int ReadEstado()
{
 Wire.requestFrom(0x10, 12); 
 for (int i=0; i<6; i++) { estado[i] = 256*Wire.read ()+Wire.read(); }
 Wire.endTransmission();

}

//READ RDS  Direccion 0x11 for random access
void ReadW()
{
  // Wire.beginTransmission(0x11);            // Device 0x11 for random access
  // Wire.write(0x0C);                                // Start at Register 0x0C
  // Wire.endTransmission(0);                         // restart condition
  // Wire.requestFrom(0x11,8, 1);       // Retransmit device address with READ, followed by 8 bytes
  // for (int i=0; i<4; i++) {RDS[i]=256*Wire.read()+Wire.read();}        // Read Data into Array of Unsigned Ints
  // Wire.endTransmission();                  
 } 

 void get_RDS()
 {    
 /* int i;
  ReadW();      
  grupo=(RDS[1]>>12)&0xf;
      if(RDS[1]&0x0800) versio=1; else versio=0;  //Version A=0  Version B=1   
      if(versio==0)
      {
       #if DEBUG             
       sprintf(buffer,"Version=%d  Grupo=%02d ",versio,grupo); Serial.print(buffer);
   
    #endif 
    switch(grupo)
    {
     case 0:              
      #if DEBUG 
      Serial.print("_RDS0__");     
      #endif
      i=(RDS[1] & 3) <<1;
      seg_RDS[i]=(RDS[3]>>8);       
      seg_RDS[i+1]=(RDS[3]&0xFF);
      //tft.setCursor(20,100);tft.setTextColor(BLUE, BLACK); 
      for (i=0;i<8;i++)
      {
        #if DEBUG 
        Serial.write(seg_RDS[i]);   
        #endif
        
       // if(seg_RDS[i]>31 && seg_RDS[i]<128)
      //  tft.print(seg_RDS[i]);
       // else
      //  tft.setTextColor(BLUE, BLACK); 
        //tft.clearScreen();
    
      }  
       
      
      #if DEBUG                 
      Serial.println("---");
      #endif
      break;
     case 2:
      i=(RDS[1] & 15) <<2;              
      seg_RDS1[i]=(RDS[2]>>8);       
      seg_RDS1[i+1]=(RDS[2]&0xFF);
      seg_RDS1[i+2]=(RDS[3]>>8);       
      seg_RDS1[i+3]=(RDS[3]&0xFF);
      #if DEBUG 
      Serial.println("_RADIOTEXTO_");
              
              for (i=0;i<32;i++)  Serial.write(seg_RDS1[i]);                                    
              Serial.println("-TXT-");
              #endif      
              break;
              case 4:             
              i=RDS[3]& 0x003f;
              minuto=(RDS[3]>>6)& 0x003f;
              hora=(RDS[3]>>12)& 0x000f;
              if(RDS[2]&1) hora+=16;
              hora+=i;        
              z=RDS[2]>>1;
              julian=z;
              
              if(RDS[1]&1) julian+=32768;
              if(RDS[1]&2) julian+=65536;
              #if DEBUG 
              Serial.print("_DATE_");
              Serial.print(" Juliano=");Serial.print(julian);
             // sprintf(buffer," %02d:%02d ",hora,minuto); tft.setCursor(50,110); tft.setTextColor(CYAN, BLACK); tft.print(buffer); 
              Serial.println(buffer); 
              #endif            
              break;
              default:
              #if DEBUG 
              Serial.println("__"); 
              #endif    
              ;        
            }                        
          }                   
   */      }

Теперь настройтесь на популярные радиостанции в вашем районе, нажав правую кнопку. Вы сможете увидеть текущий диапазон FM на экране OLED. По умолчанию приемник будет использовать автонастройку.

Если качество звука низкое, вы также можете попробовать отрегулировать антенну, чтобы получить лучший сигнал.