[103] Radio z RDS-em

[103] Radio z RDS-em

Mając już radio z wyświetlaczem, koniecznie trzeba oprogramować możliwość prezentowania napisów RDS. Cóż to takiego? Nie będę wyjaśniał dokładnie jak działa ten system, bo to materiał na osobny artykuł. W skrócie powiem tylko, że w zasadzie tekst sprowadza się do ośmiu znaków widocznych jednocześnie i jest traktowany bardzo niekonsekwentnie. Teoretycznie mając dłuższy wyświetlacz, można by go składać w bardziej czytelne, dłuższe komunikaty, lecz w praktyce co stacja, to własny pomysł i czasem ta czytelność stawałaby się dyskusyjna. Dlatego postanowiłem się ograniczyć do ośmiu znaków, wyświetlanych w dolnym wierszu wyświetlacza użytego w poprzednim projekcie.


#include <RDSParser.h>  // Biblioteka obsługująca protokół RDS
RDSParser rds;

Do dekodowania tekstów użyjemy kolejnej biblioteki, która nie jest zbyt czytelna w opisach, ale działa. Jak to zwykle bywa, musimy nazwać obiekt, więc użyłem oczywistej nazwy: rds

void rdsOdbierz(unsigned int rds1, unsigned int rds2, unsigned int rds3, unsigned int rds4) {  // Procedura pozyskująca dane RDS
  rds.processData(rds1, rds2, rds3, rds4);
}

void rdsWyswietl(const char* rdsDane) {  // Pozyskaj dane RDS z tunera.
  lcd.setCursor(0, 1);                   // Ustaw kursor na dole wyświetlacza.
  lcd.print(rdsDane);                    // Wyświetl pozyskane dane.
}

Procedura pracuje nietypowo, nie odwołując się prost w programie do kolejnych elementów. Tutaj definiujemy parę podprogramów – jeden odbierający dane, drugi – formujący je.

W głównej pętli znajduje się tylko niejawne odwołanie do podprogramów. Dostęp do danych RDS jest zawiły i prawdę powiedziawszy nie znalazłem opisu, dlaczego akurat w ten sposób należy to robić. Ale nie wszystko trzeba w życiu wiedzieć, jeśli coś działa dobrze – może pozostać czarną skrzynką. Najważniejsze, że w ostatniej linii możemy wysłać już uformowany tekst na wyświetlacz.

Prawdopodobnie komplikacja tej procedury wynika z rozbudowanego systemu komunikatów RDS, które w praktyce nie mają znaczenia, ponieważ nadawcy wysyłają co chcą, bez ładu i składu. Z tego powodu system ten pełni bardziej formę bajeru niż użyteczną, choć z pewnością dałoby się to wszystko uporządkować i – jak w przypadku programu drugiego polskiego radia – uzyskać istotne informacje o emitowanej muzyce. Ale to niestety jest wyjątek w regule.

W zasadzie wszystko jest już gotowe, lecz wypadałoby jeszcze zapiąć ostatni guzik. Spójrzmy na procedurę zapisu stacji w pamięci. Dopiszmy do niej rozkazy, które wyślą komunikat o dokonaniu zapisu stacji w pamięci, po czym, po sekundzie przywrócą standardowy wygląd wyświetlacza.

if (digitalRead(programZapisz) == HIGH) {         // Jeśli wciśnięto przycisk "programZapisz"...
  EEPROM.put(program * 2, radio.getFrequency());  // Zapisz w pamięci EEPROM aktualną częstotliwość.
  lcd.clear();                                    // Wyczyść wyświetlacz i ustaw kursor na początku.
  lcd.setCursor(1, 0);                            // Wyświetl komunikat.
  lcd.print(F("Stacja zostala"));
  lcd.setCursor(4, 1);
  lcd.print(F("zapisana"));
  delay(1000);  // Zaczekaj chwilę...
  lcd.clear();  // Odśwież wyświetlacz.
  wyswietlCzestotliwosc();
  wyswietlProgram();
  wyswietlGlosnosc();
  while (digitalRead(programZapisz) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
}

I to już wszystko. Teraz mamy już radyjko pełnoprawne, które będzie wyświetlało teksty, czasem użyteczne, a czasem nie. I jak zwykle pragnę przypomnieć, że jest to tylko pomysł wstępny, który winien nie być kopiowany, a adaptowany do własnych potrzeb. A już na koniec – raz jeszcze, kompletny i sprawdzony listing radia z RDS-em

#include <radio.h>      // Biblioteka obsługi serii tunerów FM
#include <RDA5807FP.h>  // Biblioteka obsługi konkretnej wersji tunera.
RDA5807FP radio;        // Nadaj układowi nazwę "radio"
// #include <RDA5807M.h>
// RDA5807M radio;
// #include <SI4705.h>
// SI4705   radio;
// #include <SI47xx.h>
// SI47xx radio;
// #include <TEA5767.h>
// TEA5767  radio;

#include <RDSParser.h>  // Biblioteka obsługująca protokół RDS
RDSParser rds;

#include <EEPROM.h>  // Biblioteka obsługi pamięci nieulotnej.

#include <Wire.h>                                                      // Biblioteka obsługująca magistralę I2C
#include <hd44780.h>                                                   // Biblioteka obsługująca wyświetlacze 44780
#include <hd44780ioClass/hd44780_I2Cexp.h>                             // Dodatek obsługujący wyświetlacze podłączone do ekspandera I2C
hd44780_I2Cexp lcd(0x20, I2Cexp_MCP23008, 7, 6, 5, 4, 3, 2, 1, HIGH);  // Konfiguracja połączeń wyświetlacza LCD

const byte poprzednia = 4;     // Pstryczek "Poprzednia stacja"
const byte nastepna = 7;       // Pstryczek "Następna stacja"
const byte programMinus = 5;   // Pstryczek "Zmniejsz numer programu"
const byte programPlus = 6;    // Pstryczek "Zwiększ numer programu"
const byte programZapisz = 8;  // Pstryczek "Zapisz program"

const byte potencjometr = A1;  // Potencjometr głośności.
int napiecieStare = 0;         // Poprzednie zmierzone napięcie ustawione potencjometrem.
int napiecieNowe = 1023;       // Aktualne zmierzone napięcie ustawione potencjometrem.

byte program = 1;                   // Numer aktualnego programu (1-29)
int czestotliwosc;                  // Aktualna częstotliwość stacji.
char czestotliwoscWyswietlana[11];  // Zmienna tekstowa, która reprezentuje częstotliwość pobraną z płytki tunera.
byte glosnosc;                      // Poziom aktualnej głośności.

void setup() {
  radio.initWire(Wire);                                // Inicjuj tuner.
  radio.setup(RADIO_FMSPACING, RADIO_FMSPACING_100);   // Ustaw krok strojenia równy 100 kHz.
  radio.setup(RADIO_DEEMPHASIS, RADIO_DEEMPHASIS_50);  // Ustaw deemfazę obowiązującą w Europie.
  radio.setBandFrequency(RADIO_BAND_FM, 9800);         // Ustaw częstotliwość początkową na środek zakresu.
  radio.setMono(false);                                // Ustaw tryb stereo.

  radio.attachReceiveRDS(rdsOdbierz);          // Zdefiniuj podprogram odbierający dane RDS z płytki tunera.
  rds.attachServiceNameCallback(rdsWyswietl);  // Zdefiniuj podprogram wyświetlający dane RDS

  pinMode(poprzednia, INPUT_PULLUP);  // Deklaruj linie pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
  pinMode(nastepna, INPUT_PULLUP);
  pinMode(programMinus, INPUT_PULLUP);
  pinMode(programPlus, INPUT_PULLUP);
  pinMode(programZapisz, INPUT_PULLUP);

  lcd.begin(16, 2);  // Inicjalizacja wyświetlacza LCD
  lcd.clear();       // Wyczyść wyświetlacz i ustaw kursor na początku.
  zmienProgram();    // Pobierz częstotliwość pierwszego programu z pamięci EEPROM i wyślij ją do tunera.
}

void loop() {

  if (digitalRead(poprzednia) == HIGH) {        // Jeśli wciśnięto przycisk "Poprzednia stacja"...
    radio.seekDown(0);                          // Przestrój się w górę o 100 kHz (jedynka skacze do najbliższej stacji).
    wyswietlCzestotliwosc();                    // Wyświetl częstotliwość.
    wyswietlKreski();                           // Wyświetl kreski zamiast numeru programu.
    while (digitalRead(poprzednia) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(nastepna) == HIGH) {        // Jeśli wciśnięto przycisk "Następna stacja"...
    radio.seekUp(0);                          // Przestrój się w dół o 100 kHz (jedynka skacze do najbliższej stacji).
    wyswietlCzestotliwosc();                  // Wyświetl częstotliwość.
    wyswietlKreski();                         // Wyświetl kreski zamiast numeru programu.
    while (digitalRead(nastepna) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(programMinus) == HIGH) {  // Jeśli wciśnięto przycisk "Zmniejsz numer programu"...
    --program;                              // Zmniejsz numer programu.
    if (program < 1) {                      // Jeśli numer programu jest mniejszy od 1...
      program = 29;                         // Numer programu będzie równy 29
    }
    zmienProgram();                               // Reszta procedury.
    while (digitalRead(programMinus) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(programPlus) == HIGH) {  // Jeśli wciśnięto przycisk "Zwiększ numer programu"...
    ++program;                             // Zwiększ numer programu.
    if (program > 29) {                    // Jeśli numer programu jest większy od 29...
      program = 1;                         // Numer programu będzie równy 1
    }
    zmienProgram();                              // Reszta procedury.
    while (digitalRead(programPlus) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(programZapisz) == HIGH) {         // Jeśli wciśnięto przycisk "programZapisz"...
    EEPROM.put(program * 2, radio.getFrequency());  // Zapisz w pamięci EEPROM aktualną częstotliwość.
    lcd.clear();                                    // Wyczyść wyświetlacz i ustaw kursor na początku.
    lcd.setCursor(1, 0);                            // Wyświetl komunikat.
    lcd.print(F("Stacja zostala"));
    lcd.setCursor(4, 1);
    lcd.print(F("zapisana"));
    delay(1000);  // Zaczekaj chwilę...
    lcd.clear();  // Odśwież wyświetlacz.
    wyswietlCzestotliwosc();
    wyswietlProgram();
    wyswietlGlosnosc();
    while (digitalRead(programZapisz) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  napiecieNowe = analogRead(potencjometr);           // Mierzymy napięcie ustawione ślizgaczem potencjometru.
  if (abs(napiecieNowe - napiecieStare) > 3) {       // Odejmujemy tę wartość od wartości zmierzonej poprzednio i jeśli wartość bezwzględna wyniku jest większa od 3, to...
    napiecieStare = napiecieNowe;                    // Przepisujemy wartość zmierzonego napięcia do zmiennej napiecieStare.
    if (napiecieNowe < 4) {                          // Jeśli napięcie jest mniejsze od 4...
      radio.setMute(1);                              // Wycisz radio.
      glosnosc = 0;                                  // Na potrzeby wyświetlacza ustaw wartość głośności równą zero.
    } else {                                         // W przeciwnym razie...
      radio.setMute(0);                              // Wyłącz wyciszenie oraz...
      glosnosc = map(napiecieNowe, 4, 1020, 0, 15);  // Przemapuj pozycję potencjometru do zakresu (0-15)
      radio.setVolume(glosnosc);                     // Wyślij tę wartość do rejestru głośności.
      glosnosc++;                                    // Na potrzeby wyświetlacza przesuń wartość głośności o jeden, bo zero to cisza.
    }
    wyswietlGlosnosc();  // Odśwież wyświetlacz.
  }

  radio.checkRDS();  // Odśwież dane RDS
}

void zmienProgram() {
  EEPROM.get(program * 2, czestotliwosc);                // Pobierz z pamięci EEPROM częstotliwość aktualnego kanału.
  radio.setBandFrequency(RADIO_BAND_FM, czestotliwosc);  // Wyślij tę częstotliwość do tunera.
  wyswietlCzestotliwosc();                               // Odśwież wyświetlacz.
  wyswietlProgram();
}

void wyswietlCzestotliwosc() {
  lcd.setCursor(0, 0);                                  // Ustaw kursor.
  delay(100);                                           // Niezbędne opóźnienie celem ustabilizowania się układu tunera po zmianie częstotliwości.
  radio.formatFrequency(czestotliwoscWyswietlana, 11);  // Pobierz wartość częstotliwości z tunera.
  lcd.print(czestotliwoscWyswietlana);                  // Wyświetl ją.
}

void wyswietlProgram() {
  lcd.setCursor(11, 0);  // Ustaw kursor.
  lcd.print(F("Pr:"));   // Wyświetl "PR:"
  if (program < 10) {    // Gdy numer programu jest mniejszy od 10...
    lcd.print(F("0"));   // Wyświetl zero wiodące.
  }
  lcd.print(String(program));  // Wyświetl numer programu.
}

void wyswietlGlosnosc() {
  lcd.setCursor(10, 1);  // Ustaw kursor.
  lcd.print(F("Vol:"));  // Wyświetl "VOL:"
  if (glosnosc < 10) {   // Gdy poziom głośności jest mniejszy od 10...
    lcd.print(F("0"));   // Wyświetl zero wiodące.
  }
  lcd.print(String(glosnosc));  // Wyświetl wartość głośności.
}

void wyswietlKreski() {
  lcd.setCursor(14, 0);  // Ustaw kursor.
  lcd.print(F("--"));    // Wyświetl "--"
}

void rdsOdbierz(unsigned int rds1, unsigned int rds2, unsigned int rds3, unsigned int rds4) {  // Procedura pozyskująca dane RDS
  rds.processData(rds1, rds2, rds3, rds4);
}

void rdsWyswietl(const char* rdsDane) {  // Pozyskaj dane RDS z tunera.
  lcd.setCursor(0, 1);                   // Ustaw kursor na dole wyświetlacza.
  lcd.print(rdsDane);                    // Wyświetl pozyskane dane.
}
Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2025 arduino.pl