Андрей Смирнов
Время чтения: ~23 мин.
Просмотров: 1

Еще одно FM радио на RDA5807 под управлением Ардуино

1pcs-FM-Stereo-Radio-Module-RDA5807M-Wireless-Module-Profor-For-Arduino-RRD-102V2-0.jpg $0.48 Ссылка на товар Данная плата уже несколько раз встречалась в обзорах на этом сайте в составе каких-либо поделок. Я опишу наиболее быстрый способ проверки этой платы после ее получения, т.к. наверняка многие их тех у кого она есть (или будет) просто докинули ее в корзину увидев низкую цену (20-30 руб.) Плата очень миниатюрная, примерно 10*10 мм, но с богатым функционалом — будучи подключена к микроконтроллеру по I2C интерфейсу она позволяет слушать FM радио на частотах от 50 МГц до 115 МГц. В описании указаны такие функции как RDS/RBDS, синтезатор частот, автоматическая регулировка усиления, цифровое адаптивное подавление шума, индикатор уровня сигнала, переключение режимов моностерео, усилитель низких частот, регулировка звука с функцией «mute». Хочешь, не хочешь, но чтобы все это проверить, сначала придется припаять к данной плате минимум восемь ножек, ввиду их полного отсутствия. Я использовал проводники от витой пары, получилось так:9eacac.jpg После этого необходимо подключить плату к микроконтроллеру (я использовал Arduino Nano) по следующей схеме:71344c.jpg Как видим соединений тут минимум, в качестве антенны выступил тот же проводок от витой пары, длиной 10-20 см. Подключаем Arduino к компьютеру, запускаем Arduino IDE, качаем библиотеку для радиомодуля здесь. Скачанный .zip файл добавляем в Arduino IDE: «Скетч» — «Подключить библиотеку» — «Добавить .ZIP библиотеку…» Открываем появившийся пример программы: «Файл» — «Примеры» — «Radio» — «SerialRadio» и ничего не меняя заливаем его в нашу Arduino. После успешной заливки скетча радио сразу начинает работать на запрограммированной частоте 89.30 MHz. В любом случае вы должны услышать хотя бы шипение. Если ничего не слышно, значит что-то выполнено не верно, либо плата не исправна. Для управления используются команды отправляемые через Com-порт. Для этого откройте монитор порта, если в окне отображаются «кракозябры», то необходимо в нижней части окна выбрать скорость порта — 57600 Бод. После этого вы увидите краткую подсказку по имеющимся командам, кроме того подсказку можно получить если отправить команду «?» (без кавычек) Имеющиеся команды: + добавить громкость — убавить громкость > следующая заранее запрограммированная станция< предыдущая заранее запрограммированная станция . поиск станции вверх , поиск станции вниз fnnnnn переключение на нужную станцию, например f9140 — станция 91.40 MHz i информация о станции s переключение mono/stereo b усиление басов u mute/unmute К сожалению команды не всегда срабатывают с первого раза, иногда их необходимо повторить. Если и это не помогает и радио не реагирует на команды (и в мониторе порта ничего не меняется), нужно закрыть и снова отрыть монитор порта — все заработает! Скриншот из монитора порта:90152f.jpg Итак, плата довольно интересная, ловит станции неплохо, громкость воспроизведения высокая, осталось придумать ей реальное применение, тем более, что готовых схем (с кнопками и экранами) в интернете хватает. Надеюсь мой обзор был для вас полезен.

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

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

FM-приемник RDA5807M

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

RDA5807M.jpg
RDA5807M

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

Распиновка RDA5807M

RDA5807M-pinout.jpg
Распиновка RDA5807M

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

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

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

shema-fm-radio-680x345.png

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

RDA5807M-shema-680x351.jpg

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

RDA5807M-arduino-680x453.jpg

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

Исходный код

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

Скачать arduino-fm-radio.ino

#include      #include <wire>  de <spi>  #includafruit>  //#incl #include <adafruit>  #dM>  #include <rdsparser>  #include <extern const="" uint8_t="" uinon_volume="" rda5807m="" radio="" create="" an="" instance="" of="" a="" rda5807="" chip="" debug="" oled_reset="" adafruit_ssd1306="" tft="" rdsparser="" rds="" enum="" radio_state="" state_parsecommand="" waiting="" for="" new="" command="" character.="" state_parseint="" digits="" the="" parameter.="" state_exec="" executing="" command.="" state="" variable="" is="" used="" parsing="" input="" characters.="" int="" entrada="A0;" entradav="0;" menu="" maxmenu="" menux="" maxmenux="" static="" char="" menus="" tune="" volumen="" volumenold="7;" frecuencia="" signal_level="" unsigned="" z="" byte="" xfrecu="" estado="" long="" time="" buffer="" seg_rds="" seg_rds1="" indexrds1="" hora="" julian="" mezcla="" void="" rds_process="" block1="" uint16_t="" block2="" block3="" block4="" rds.processdata="" setup="" wire.begin="" serial.begin="" tft.begin="" radio.init="" radio.debugenable="" tft.display="" delay="" clear="" buffer.="" tft.cleardisplay="" read="" value="" last="" frequency="" if="">15) volumen = 15;  if (frecuencia <0) frecuencia = 0;  if (frecuencia >210) frecuencia = 210;        tft.sotation(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)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)menAXmenux;      #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(daV);      menux--;      if(menuxnux=1;       #if DEBUG         Serial.print("menux = " );  Serial.println(menux);      #ef         switch(menu)        {          case 1:              frecuencia--;              if(frecuencia<0)frecuencia=0;                  delay(130);          break;           se 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ñ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ñ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  Direccion11 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.endTransmion();                     }      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(100);tft.setTextColor(BLUE, BLACK);         for (i=0;i<8;i++)        {          #if DEBUG           Serial.write(seg_RDS[i]);             #endif                   // if(seg_RDS>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-");                #endi                   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                    ;                      }                                    }                        */      }  </extern></rdsparser></adafruit></spi></wire>

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

arduino-fm-itog.jpg

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

RRD102v2.jpg Микросхема RDA5807M — это FM радиоприемник нового поколения с поддержкой RDS/RBDS и цифровым управлением по I2C. Микросхема выполнена по CMOS технологии, что определяет ее минимальное энергопотребление. RDA5807M уже содержит все необходимые узлы и требует лишь небольшого числа внешних компонентов. А мощный аудиопроцессор обеспечивает оптимальное качество звука при различных условиях приема. Все это делает RDA5807M удачным выбором для носимых, портативных устройств. В интернет магазинах распространен модуль RRD-102v2, на котором распаяны RDA5807M, кварцевый резонатор и пара компонентов обвязки. В данной статье я опишу как подключить этот модуль к Ардуино и что нужно знать для создания радиоприемника на его основе.

Характеристики RDA5807M

Сразу даю ссылку на даташит: RDA5807M_datasheet_v1.1, наиболее полную информацию о характеристиках RDA5807M вы можете найти в нем. Я перечислю некоторые из них:

  • Напряжение питания 2.7 — 3.3В
  • Потребляемый ток (при напряжении питания 3В):
    • в рабочем режиме — не более 20мА
    • в режиме сна — не более 15мкА
  • Диапазон принимаемых частот 50 — 115МГц
  • Выбираемый шаг изменения частоты: 200кГц, 100кГц, 50кГц, 25кГц
  • Выбираемый источник тактового сигнала: внешний или внутренний генератор (для внутреннего генератора требуется резонатор 32.768кГц)
  • Поддержка RDS/RBDS
  • Управление по шине I2C
  • Возможность прямого подключения нагрузки от 32Ом

Диапазон напряжения питания не самый удобный, например, от лития без стабилизации запитать не получится. Зато наушники можно подключать прямо к выводам микросхемы, дополнительное усиление не требуется. Также можно отметить небольшой потребляемый ток микросхемы, что позволяет запитывать ее от цифрового вывода микроконтроллера (по крайней мере AVR) в тех случаях, когда требуется отключение питания радио в целях энергосбережения.

Распиновка и подключение к Ардуино

RRD102v2%2BRDA5807M%2Bpinout.png
Распиновка RRD-102v2
RDA5807M%2BArduino.png
Подключение RRD-102v2 (RDA5807M) к Ардуино

Выводы SDA и SCL модуля подключаются к одноименным выводам Ардуино. Для платы Uno это пины A4 и A5 соответственно. Их уровни превышают напряжение питания RDA5807M, но это не критично, микросхема отлично работает без преобразователя уровней. Питание берем с вывода 3v3.

Интерфейс управления

Здесь я хочу обратить внимание на имеющуюся в технической документации неточность (даташит на эту микросхему вообще очень мутный): в ней говорится, что I2C адрес микросхемы 0x10h, что внутренние адреса ее регистров не видны и что чтение и запись выполняются последовательно, начиная с фиксированного стартового адреса (0x0Ah для чтения, 0x02h для записи). После каждой операции чтения/записи происходит инкремент внутреннего счетчика и очередная операция будет выполняться уже для следующего регистра. Так до тех пор, пока внутренний счетчик не дойдет до верхней границы 0x3Ah, после этого он вернется к своему начальному значению. На самом деле RDA5807M отзывается на три I2C адреса, в чем легко убедиться, воспользовавшись I2C сканером:

RDA5807_I2C_Addr.png
I2C адреса RDA5807M

Адрес 0x10h используется для последовательного обращения к регистрам, как было описано выше.Адрес 0x11h позволяет обращаться к произвольным регистрам.Адрес 0x60h позволяет работать с RDA5807M в режиме совместимости с TEA5767. Упоминание адреса 0x11h можно найти в документе RDA5807P_ProgManual_1.0. Хоть он и предназначен для другой микросхемы, но практически всё применимо и для RDA5807M. Ниже приведен фрагмент из данного документа, описывающий формат I2C обмена при использовании адреса 0x11h:

RDA5807_I2C_Format.png
Формат обмена с RDA5807M по I2C адресу 0x11h

Как можно видеть, при записи в режиме произвольного доступа первым передается адрес интересующего регистра (REGISTER ADDRESS), затем старший и младший байты данных. Для чтения содержимого регистра из RDA5807M микроконтроллер сначала передает его адрес, затем считывает старший и младший байты. Чуть позже я приведу пример чтения/записи регистров, а пока разберемся с их назначением.

Регистры RDA5807M

Управление работой RDA5807M заключается в обращении к его регистрам: изменяя одни регистры, мы производим необходимые нам настройки; из других можно читать различную информацию (флаги, данные RDS и т.д.). Регистры 16-разрядные, их адреса и назначение приведены в даташите. Описание весьма скудное, поэтому я решил сам «пощупать» каждый регистр, чтобы понять какой бит за что отвечает. Для этого была написана следующая программа:

RDA5807M_software.png
Управление работой RDA5807M с компьютера

Данная программа читает значения регистров RDA5807M, отображает в удобном виде и позволяет изменять их, щелкая мышью по элементам управления. Ардуино при этом выступает в роли посредника между программой на компьютере и RDA5807M, для этого в нее должен быть загружен соответствующий скетч (вы найдёте его в программе по кнопке «Скетч для Ардуино»). Очень рекомендую попробовать данную программу, чтобы разобраться с назначением регистров. Скачать ее можно здесь. И, чтобы совсем не осталось вопросов по управлению RDA5807M, привожу описание регистров на понятном языке.

Регистр Биты Имя Назначение
00h 15:0 CHIP_ID Chip ID — Идентификатор микросхемы. Есть у меня основания полагать, что значение ChipID состоит именно из двух байт, а не одного, как это указано в даташите.  0x5804
02h 15 DHIZ Audio Output High-Z Disable. Управляет состоянием аудио выводов: 0 — выводы находятся в высокоимпедансном состоянии; 1 — переводит выводы в рабочий режим.  0
14 DMUTE  Mute Disable — отключение режима mute, который по умолчанию включен (значение 0). Для отключения mute в этот бит следует записать 1.  0
13 MONO Принудительное моно, включается записью в данный бит значения 1   0
12 BASS Bass Boost — усиление басов. Для включения данной опции необходимо записать 1  0
11 Если я правильно понял, этот бит отключает температурную компенсацию тактового генератора, в результате чего RDA5807M не сможет работать в заявленном температурном диапазоне (-20..70C) и сможет поддерживать колебания температуры только на +/- 20C от точки настройки.  0
10 Бит RCLK Direct Input Mode следует установить в 1, если используется внешний тактовый сигнал  0
9 SEEKUP Seek Up — направление поиска радиостанций: 0 — к нижней границе диапазона; 1 — вверх.  0
8 SEEK Запись 1 в этот бит запускает процесс поиска радиостанции. Поиск ведется в направлении, заданном битом SEEKUP, до нахождения радиостанции или до прохождения всего диапазона частот, после чего данный бит сбрасывается и устанавливается бит STC.  0
7 SKMODE Seek Mode. Определяет поведение при достижении границы диапазона во время поиска радиостанций: 0 — продолжить поиск с другой границы; 1 — прекратить поиск
6:4 CLK_MODE 000
3 RDS_EN RDS/RBDS Enable. Запись 1 в этот бит включает прием RDS/RBDS сообщений.
2 NEW_METHOD New Demodulation Method Enable — установка этого бита задействует новый метод демодуляции, способный улучшить чувствительность приемника
1 SOFT_RESET  Программный сброс RDA5807M. Установка бита в 1 приведет к сбросу всех внутренних регистров к значениям по умолчанию. Сброс выполняется автоматически при включении питания микросхемы, нет необходимости сбрасывать устройство дополнительно.
ENABLE  Power Up Enable — разрешение работы. Установка в 1 переводит приемник в рабочий режим; 0 — спящий режим — отключает питание внутренних узлов, состояние регистров при этом сохраняется, после возвращения в рабочий режим необходимо выполнить TUNE для настройки на радиостанцию.
03h 15:6 CHAN Channel Select — выбор канала. Частота радиостанции устанавливается не явно, а путем изменения значения CHAN, которое при умножении на SPACE и прибавления нижней границы диапазона дает итоговую частоту. Для записи CHAN необходимо также установить бит TUNE, в противном случае CHAN не изменится.  0x00
5 DIRECT_MODE Режим прямого управления, который используется только при тестировании — это описание из даташита, не уверен, что данный бит имеет отношение к RDA5807M.
4 TUNE Запись в этот бит значения 1 запускает процесс настройки. По окончании настройки устанавливается бит STC, бит TUNE при этом сбрасывается.
3:2 BAND 00 
1-0 SPACE   00
 04h 15:12 RSVD Биты зарезервированы   0000
11 DE  0
10 RSVD Зарезервирован 1
9 SOFTMUTE_EN Soft Mute Enable — приглушение звука, может быть использовано для минимизации шумов в условиях слабого приема. Функция включается установкой бита в 1.
8 AFCD  0
05h 15 INT_MODE Режим генерации прерывания при завершении поиска/настройки. Данный бит определен в даташите, но не имеет отношения к RDA5807M. Актуален для микросхем с дополнительными выводами GPIO, например, RDA5807P.  1
14:12 RSVD Биты зарезервированы 000 
11:8 SEEKTH Seek Threshold. Данные биты задают порог отношения сигнал/шум при выполнении поиска радиостанций.   1000
7:6 LNA_PORT_SEL  10 
5:4 RSVD Биты зарезервированы 00 
3:0 VOLUME Регулировка громкости 1011
06h 15 RSVD Зарезервирован
14:13 OPEN_MODE Данные биты указаны в даташите, но они неактуальны для RDA5807M. В других микросхемах серии установка этих битов в 11 разрешает изменение остальных битов регистра, отвечающих за настройку I2S (Audio Data Interface). 00
07h 15 RSVD Зарезервирован
14:10 TH_SOFTBLEND Soft Blend Thershold — настройка уровня шумоподавления. 10000
9 65M_50M MODE 1
8 RSVD Зарезервирован
7:2 SEEK_TH_OLD Seek Threshold Old — по аналогии с SEEKTH данные биты определяют порог при поиске радиостанций, но актуальны только при SEEK_MODE (биты 14:12 регистра 0x20h) = 001 — «старый» метод поиска. 000000
1 SOFTBLEND_EN Soft Blend Enable. Данный бит разрешает шумоподавление, уровень которого задан битами TH_SOFTBLEND. Помогает здорово очистить сигнал от помех. 1
FREQ_MODE Режим задания частоты. Когда данный бит сброшен в 0, результирующая частота определяется как BAND + CHAN * STEP. При FREQ_MODE = 1 частота определяется как BAND + содержимое регистра 08h.
08h 15:0 FREQ_DIRECT 0x0h
0Ah 15 RDSR RDS Ready — флаг готовности данных RDS/RBDS (1 — данные готовы)
14 STC Seek/Tune Complete — флаг завершения поиска/настройки на заданную частоту (1 — операция завершена). 
13 SF Seek Fail — флаг, сигнализирующий о неуспешном выполнении поиска, когда не удалось найти сигнал с RSSI большим порога SEEKTH
12 RDSS
11 BLK_E Данный флаг сообщает о получении E блока.
10 ST 1
9:0 READCHAN Read Channel. Эти биты содержат значение CHAN, доступны только для чтения. В режиме последовательного доступа к регистрам RDA5807M стартовый адрес для чтения — 0Ah, таким образом нет возможности прочитать значение CHAN регистра 03h. Этим и обусловлено наличие битов READCHAN. 0x0h
0Bh 15:9 RSSI Received Signal Strength Indicator — показатель уровня принимаемого сигнала.
8 FM_TRUE Данный флаг сигнализирует о наличии передачи на текущей частоте. То есть приемник настроен на радиостанцию.
7 FM_READY Насколько я могу судить, данный флаг идентичен флагу STC
6:5 RSVD Биты зарезервированы 00
4 ABCD_E
3:2 BLERA
1:0 BLERB Block Errors Level Of B — уровень ошибок в блоке B (RDS) или E (RBDS, когда ABCD_E флаг установлен в 1). Значения битов аналогичны BLERA.
0Ch 15:0 RDSA Блок A (в режиме RDS) или E (в режиме RBDS при ABCD_E = 1). 0x5803h
0Dh 15:0 RDSB Блок B (в режиме RDS) или E (в режиме RBDS при ABCD_E = 1). 0x5804h
0Eh 15:0 RDSC Блок C (в режиме RDS) или E (в режиме RBDS при ABCD_E = 1). 0x5808h
0Fh 15:0 RDSD Блок D (в режиме RDS) или E (в режиме RBDS при ABCD_E = 1). 0x5804h
10h 15:14 BLERC Block Errors Level Of C — уровень ошибок в блоке C (RDS) или E (RBDS, когда ABCD_E флаг установлен в 1). Значения битов аналогичны BLERA.
13:12 BLERD Block Errors Level Of D — уровень ошибок в блоке D (RDS) или E (RBDS, когда ABCD_E флаг установлен в 1). Значения битов аналогичны BLERA.

Это не все регистры RDA5807M, по старшим адресам доступны другие. Возможно, среди них есть еще что-то интересное. И если вам о них известно, пишите, добавлю их в список.

Программирование RDA5807M

Давайте начнем с простенького скетча. Если вы попробуете управлять RDA5807M из моей программы, то обнаружите, что для того чтобы заставить его работать достаточно установить несколько битов: ENABLE, DHIZ, DMUTE, SEEK. Установка последнего запустит поиск радиостанции. Эти же действия можно выполнить программно при помощи следующего скетча:

voidsetup() {   Wire.begin();   setRegister(0x02, 0xC101);  }  voidloop() { }  void setRegister(uint8_t reg, const uint16_t value) {   Wire.beginTransmission(0x11);   Wire.write(reg);   Wire.write(highByte(value));   Wire.write(lowByte(value));   Wire.endTransmission(true); }

Подключите RDA5807M к Ардуино по приведенной ранее схеме и залейте в нее скетч. Приемник выполнит поиск и настроится на первую найденную радиостанцию. Бит Tune при этом сбрасывается. Нажатие кнопки Reset на Ардуино и повторное выполнение функции setup будут снова устанавливать этот бит, инициируя поиск следующей станции. Работает? Двигаемся дальше. В примере скетча выше мы записали в регистр 02h заранее определенное значение. На деле такое требуется редко, разве что для инициализации некоторых регистров. В основном же значения регистров формируются в процессе работы программы при изменении отдельных битов. В таких случаях удобно использовать константы, содержащие номера этих битов или маски для их установки. Ниже приведен пример такого скетча. Он позволяет настроиться на конкретную радиостанцию, установить громкость и получить RSSI.

                      uint8_t volume = 1;  uint16_t freq = 1073;  uint16_t reg02h, reg03h, reg05h, reg0Bh;  voidsetup() {   Serial.begin(9600);   Wire.begin();      reg02h = RDA5807M_FLG_ENABLE | RDA5807M_FLG_DHIZ | RDA5807M_FLG_DMUTE;   setRegister(RDA5807M_REG_CONFIG, reg02h);         reg02h |= RDA5807M_FLG_BASS;   setRegister(RDA5807M_REG_CONFIG, reg02h);               reg03h = (freq - 870) << RDA5807M_CHAN_SHIFT;    setRegister(RDA5807M_REG_TUNING, reg03h | RDA5807M_FLG_TUNE);         reg05h = getRegister(RDA5807M_REG_VOLUME);    reg05h &= ~RDA5807M_VOLUME_MASK;    reg05h |= volume << RDA5807M_VOLUME_SHIFT;    setRegister(RDA5807M_REG_VOLUME, reg05h); }  voidloop() {      reg0Bh = getRegister(RDA5807M_REG_RSSI);   uint8_t RSSI = (reg0Bh & RDA5807M_RSSI_MASK) >> RDA5807M_RSSI_SHIFT;   Serial.print("RSSI = ");   Serial.print(RSSI);   Serial.println(" (0-min, 127-max)");   delay(500); }  void setRegister(uint8_t reg, const uint16_t value) {   Wire.beginTransmission(0x11);   Wire.write(reg);   Wire.write(highByte(value));   Wire.write(lowByte(value));   Wire.endTransmission(true); }  uint16_t getRegister(uint8_t reg) {   uint16_t result;   Wire.beginTransmission(RDA5807M_RANDOM_ACCESS_ADDRESS);   Wire.write(reg);   Wire.endTransmission(false);   Wire.requestFrom(0x11, 2, true);   result = (uint16_t)Wire.read() << 8;   result |= Wire.read();   return result; }

В этом примере значения регистров получаются установкой отдельных разрядов. Для этого используются определенные в начале скетча флаги и маски. Я описал несколько из них для примера, остальные добавляются по аналогии. Чтобы настроить RDA5807M на интересующую частоту необходимо установить значения BAND и SPACE и затем изменять только значение CHAN. Итоговая частота определяется по формуле:F = BAND + CHAN * SPACE. В скетче используются определенные по умолчанию BAND и SPACE (87..108МГц  и 100кГц соответственно). По ним можно определить значение, которое должно быть записано в биты CHAN для получения интересующей частоты. Не забывайте при записи CHAN устанавливать также бит TUNE. Для изменения громкости значение регистра 05h считывается из RDA5807M в переменную. Затем осуществляется сброс битов VOLUME. И уже после этого можно устанавливать новое значение громкости и записывать результат в регистр. Для получения RSSI выполняются обратные действия: в считанном из регистра 0Bh значении сбрасываются все биты, кроме содержащих RSSI. Затем результат сдвигается вправо, чтобы младший бит RSSI оказался в младшем разряде переменной. Так мы получим нужное нам значение. Теперь, когда описаны основные приемы управления RDA5807M, можно приступить к программированию. Нужно лишь определиться с функционалом и интерфейсом.

Добавим LCD дисплей и энкодер

Да, я люблю использовать в своих проектах LCD2004 с I2C интерфейсом и энкодер вращения. Это уже привычные для меня элементы создания пользовательского интерфейса. Используя их, я могу сосредоточиться на текущей задаче, а не заморачиваться с изобретением велосипеда. Поэтому сейчас я добавил их в схему:

RDA5807M%2Bradio.png
Схема радио с дисплеем и энкодером
RDA5807%2BArduino%2BRadio.jpg
Макет радио с дисплеем и энкодером

Итак, моя текущая задача — это создание радио с базовым функционалом и индикацией. О законченном проекте речь пока не идет. Для меня это скорее знакомство с данным модулем. Поэтому в предлагаемом ниже скетче нет фишек вроде сохранения списка радиостанций в EEPROM. Нет и работы с RDS — использованию этой технологии в RDA5807M я посвящу следующую публикацию. В последствии я планирую сделать радио в оригинальном корпусе с OLED дисплеем. А пока можете оценить результат данного этапа, скетч доступен по ссылке. Для работы требуется библиотека LiquidCrystal_I2C_Menu, скачайте и установите ее. В скетче реализовано:

  • Поиск радиостанции вверх/вниз и отображение частоты
  • Регулировка громкости
  • Ввод значения частоты энкодером
  • Выбор поведения при повороте энкодера: регулировка громкости или поиск радиостанции
  • Установка ряда параметров, отвечающих за звук и шумоподавление
  • Сохранение настроек в EEPROM и чтение их при включении радио

На этом пока всё. Продолжение будет в следующей публикации с обзором RDS.RRD102v2.jpgИспользуемые источники:

  • https://mysku.ru/blog/diy/50158.html
  • https://arduinoplus.ru/fm-radiopriemnik-arduino-uno-rda5807m/
  • https://tsibrov.blogspot.com/2019/11/rda5807m-part1.html

Рейтинг автора
5
Подборку подготовил
Андрей Ульянов
Наш эксперт
Написано статей
168
Ссылка на основную публикацию
Похожие публикации