[098] Budujemy arduinowe radio cz. 2

[098] Budujemy arduinowe radio cz. 2

Zabawa, nauka i tworzenie czegoś praktycznego – dziś spotykamy się pod takim hasłem. Z prostego, acz działającego radia przedstawionego w poprzednim artykule będę chciał zrobić coś bardziej użytecznego. Równie ważną rzeczą, którą poznamy, a o którą padały już nieraz pytania, jest nieulotna pamięć EEPROM, która zachowuje swoją zawartość po odłączeniu zasilania. Aż się prosi, by w oparciu o nią zrobić programator – ale to za chwilę.


Pozostaje jeszcze jeden problem: co znaczy: radio użyteczne i w którą stronę podążać? Można oczywiście stworzyć pełen panel z wyświetlaniem częstotliwości, poziomów, komunikatów RDS i tak dalej. Ale to się w sieci znajdzie, a poza tym takie małe płytki raczej nie służą budowie tunerów wieżowych, tylko podręcznych radyjek. Bardziej użyteczny będzie prosty interfejs i taki właśnie zaprojektowałem. Zresztą zawsze może powstać trzecia część radiowej sagi.

Rozbudowę będziemy czynić etapami, dzięki czemu łatwiej będzie się w tym połapać. Kto nie czytał poprzedniego artykułu, zapraszam na wstępie tam, ponieważ wykorzystamy szkic tamże powstały i nie będę już omawiał elementów, o którym mówiłem poprzednio.

1

Na początek zróbmy użytek z potencjometru. Kręcenie gałką celem zmiany głośności jest o wiele wygodniejsze niż wciskanie przycisków. Na płytce edukacyjnej TME, której używam w projekcie, osadzono potencjometr, podłączając ślizgacz do portu A1. Kto takiej płytki nie ma, może sobie oczywiście podpiąć potencjometr do dowolnego wejścia analogowego z wyjątkiem dwóch ostatnich, które tworzą magistralę I2C, z której korzysta płytka tunera. Wróćmy do naszego szkicu i dopiszmy tylko dwie linie.

#include <radio.h>      // Biblioteka obsługi serii tunerów FM
#include <RDA5807FP.h>  // Biblioteka obsługi konkretnej wersji tunera.
// #include <RDA5807M.h>
// #include <SI4705.h>
// #include <SI47xx.h>
// #include <TEA5767.h>

RDA5807FP radio;  // Nadaj układowi nazwę "radio"
// RDA5807M radio;
// SI4705   radio;
// SI47xx radio;
// TEA5767  radio;

const byte poprzednia = 4;  // Pstryczek "Poprzednia stacja"
const byte nastepna = 7;    // Pstryczek "Następna stacja"
const byte ciszej = 5;      // Pstryczek "Zmniejsz głośność"
const byte glosniej = 6;    // Pstryczek "Zwiększ głośność"
const byte mono = 8;        // Pstryczek "Mono/Stereo"

const byte potencjometr = A1;  // Potencjometr 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.setMute(false);                                // Wyłącz wyciszenie.
  radio.setVolume(4);                                  // Ustaw poziom głośności na 1/4 zakresu.

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

void loop() {

  if (digitalRead(poprzednia) == HIGH) {        // Jeśli wciśnięto przycisk "Poprzednia stacja"...
    radio.seekDown(1);                          // Przestrój się w górę do najbliższej stacji (zero skacze 100 kHz do góry).
    while (digitalRead(poprzednia) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(nastepna) == HIGH) {        // Jeśli wciśnięto przycisk "Następna stacja"...
    radio.seekUp(1);                          // Przestrój się w dół do najbliższej stacji (zero skacze 100 kHz w dół).
    while (digitalRead(nastepna) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(ciszej) == HIGH) {        // Jeśli wciśnięto przycisk "Zmniejsz głośność"...
    byte x = radio.getVolume();             // Pobierz z tunera poziom głośności.
    radio.setVolume(--x);                   // Wyślij do tunera wartość pobraną, pomniejszoną o jeden.
    while (digitalRead(ciszej) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(glosniej) == HIGH) {        // Jeśli wciśnięto przycisk "Zwiększ głośność"...
    byte x = radio.getVolume();               // Pobierz z tunera poziom głośności.
    radio.setVolume(++x);                     // Wyślij do tunera wartość pobraną, powiększoną o jeden.
    while (digitalRead(glosniej) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(mono) == HIGH) {        // Jeśli wciśnięto przycisk "Mono/Stereo"...
    radio.setMono(!radio.getMono());      // Zmień tryb: stereo <> mono.
    while (digitalRead(mono) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  radio.setVolume(map(analogRead(potencjometr), 0, 1023, 0, 15));  // Wyślij do rejestru głośności przeliczoną wartość zmierzonego napięcia z potencjometru.
}

Zadeklarujemy port woltomierza, który nazwiemy potencjometr. Całe sedno siedzi w tej linii, którą dopisałem na końcu głównej pętli:

radio.setVolume(map(analogRead(potencjometr), 0, 1023, 0, 15));

Używając instrukcji, którą mamy już wyżej i obsługują one tam dwa bloki regulujące głośność za pomocą przycisków, wysyłamy do tunera zmierzone napięcie, przemapowane na zakres od zera do piętnastu, bo w takim okienku porusza się interpreter poziomu głośności w radyjku. O instrukcji map pisałem już wielokrotnie, jest ona przyjemna, bo w locie zamienia jeden przedział proporcjonalnie na drugi. Jak wiadomo, rozdzielczość woltomierza w Arduino wynosi 10 bitów, stąd wartość: 1023, bo to właśnie dwa do potęgi dziesiątej minus jeden. Po skompilowaniu będziemy mogli już kręcić gałką jak w starożytnym radiu wyprodukowanym w Polsce Ludowej.

Wróćmy do programu i przyjrzyjmy się wszystkim blokom w głównej pętli. Fragmenty te wykonają się tylko wtedy, gdy zajdzie warunek wciśnięcia odpowiedniego przycisku. Do tego dane wyjdą raz i program pozostanie zawieszony, dopóki przycisk związany z blokiem nie zostanie zwolniony. Inaczej jest w przypadku nowo dopisanej linii. Ona jest wykonywana w każdym przejściu pętli loop, czyli wartość potencjometru aktualizowana jest bez przerwy. Niby to działa, ale nie jest to eleganckie z kilku względów.

  • Po pierwsze, nie usunąłem bloków zmiany głośności przyciskami. A przestały działać, ponieważ cokolwiek tam wciśniemy, zaraz potem program uaktualni głośność zgodnie z pozycją potencjometru.

  • Po drugie, mamy ciągły ruch na magistrali, co w jakiś sposób może powodować wzrost zakłóceń. Zasada mówi, że należy minimalizować ruch danych do niezbędnego minimum. Wysyłanie w kółko informacji o głośności, która się nie zmienia, nie ma sensu.

  • Po trzecie wreszcie, brak tutaj zabezpieczeń przed niestabilnymi odczytami. W pewnych pozycjach ślizgacza kolejne odczyty mogą się zmieniać o jeden, co w pewnych przypadkach będzie mapowane także o jeden, a to – jeśli nastąpi szybką serią - będzie już słychać w postaci trzasków. Trzeba coś z tym zrobić.

#include <radio.h>      // Biblioteka obsługi serii tunerów FM
#include <RDA5807FP.h>  // Biblioteka obsługi konkretnej wersji tunera.
// #include <RDA5807M.h>
// #include <SI4705.h>
// #include <SI47xx.h>
// #include <TEA5767.h>

RDA5807FP radio;  // Nadaj układowi nazwę "radio"
// RDA5807M radio;
// SI4705   radio;
// SI47xx radio;
// TEA5767  radio;

const byte poprzednia = 4;  // Pstryczek "Poprzednia stacja"
const byte nastepna = 7;    // Pstryczek "Następna stacja"
const byte ciszej = 5;      // Pstryczek "Zmniejsz głośność"
const byte glosniej = 6;    // Pstryczek "Zwiększ głośność"
const byte mono = 8;        // Pstryczek "Mono/Stereo"

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

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.setMute(false);                                // Wyłącz wyciszenie.
  radio.setVolume(4);                                  // Ustaw poziom głośności na 1/4 zakresu.

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

void loop() {

  if (digitalRead(poprzednia) == HIGH) {        // Jeśli wciśnięto przycisk "Poprzednia stacja"...
    radio.seekDown(1);                          // Przestrój się w górę do najbliższej stacji (zero skacze 100 kHz do góry).
    while (digitalRead(poprzednia) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(nastepna) == HIGH) {        // Jeśli wciśnięto przycisk "Następna stacja"...
    radio.seekUp(1);                          // Przestrój się w dół do najbliższej stacji (zero skacze 100 kHz w dół).
    while (digitalRead(nastepna) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(ciszej) == HIGH) {        // Jeśli wciśnięto przycisk "Zmniejsz głośność"...
    byte x = radio.getVolume();             // Pobierz z tunera poziom głośności.
    radio.setVolume(--x);                   // Wyślij do tunera wartość pobraną, pomniejszoną o jeden.
    while (digitalRead(ciszej) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(glosniej) == HIGH) {        // Jeśli wciśnięto przycisk "Zwiększ głośność"...
    byte x = radio.getVolume();               // Pobierz z tunera poziom głośności.
    radio.setVolume(++x);                     // Wyślij do tunera wartość pobraną, powiększoną o jeden.
    while (digitalRead(glosniej) == HIGH) {}  // Czekaj aż przycisk zostanie puszczony.
  }

  if (digitalRead(mono) == HIGH) {        // Jeśli wciśnięto przycisk "Mono/Stereo"...
    radio.setMono(!radio.getMono());      // Zmień tryb: stereo <> mono.
    while (digitalRead(mono) == 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...
    napiecieStare = napiecieNowe;                        // Przepisujemy wartość zmierzonego napięcia do zmiennej napiecieStare.
    radio.setVolume(map(napiecieNowe, 0, 1020, 0, 15));  // Wysyłamy ją także do rejestru głośności, po przemapowaniu do zakresu (0-15)
  }
}

Zmienimy ostatni blok na modłę pozostałych – z warunkiem wejścia. Zaczniemy od pomiaru napięcia, ale zamiast go wysyłać od razu, przekażemy je do zmiennej napiecieNowe. Teraz odejmiemy od siebie owo świeżo zmierzone napięcie od zmierzonego w poprzedniej pętli – albo równego zero, jeśli właśnie nastąpił reset. Z wyniku weźmiemy wartość bezwzględną, bo interesuje nas fakt, czy oba odczyty różnią się o pewien próg, a już w którą stronę – to nie ma znaczenia.

Próg ten ustawiłem na 3, co daje redukcję rozdzielczości przetwornika o dwa bity. To i tak o wiele więcej od rozdzielczości rejestru głośności tunera, który jest czterobitowy. Im wyższy próg, tym większy odstęp od zakłóceń i śmiało można go zwiększyć nawet do kilkudziesięciu, jeśli mamy zamiar używać starych potencjometrów z Telpodu.

Jeśli powyższy warunek zajdzie, teraz dopiero przekazujemy zmiennej napięcieStare aktualną wartość napięcia i wysyłamy je – po przemapowaniu do tunera, jak poprzednio.

Podsumowując: wysyłanie danych nastąpi dopiero po przekroczeniu progu czterech jednostek napięcia, zabezpieczając nas przed drganiami potencjometru. Zwie się to histerezą i jest podstawą poprawnej pracy układów z analogowym odczytem manipulatorów. Mała uwaga – wartość map należy ograniczyć od góry o wartość progu.

Jest już dobrze, ale nieidealnie. Potencjometr w lewej skrajnej pozycji nie wycisza radia zupełnie, bo wartość zero to nie cisza, ale najniższa głośność. Spróbujmy i z tym wygrać. Zmienimy ostatni blok programu:

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.
  } else {                                               // W przeciwnym razie...
    radio.setMute(0);                                    // Wyłącz wyciszenie oraz...
    radio.setVolume(map(napiecieNowe, 4, 1020, 0, 15));  // Wyślij wartość napięcia do rejestru głośności, po przemapowaniu do zakresu (0-15)
  }
}

 Dołożymy kolejny warunek, identyfikujący owo skrajne położenie potencjometru. Ustaliłem sobie przedział wąziutki, ograniczony czwórką, czyli w praktyce ten warunek zajdzie tylko dla potencjometru skrajnie skręconego w lewo. W tym wypadku nie będziemy wysyłać komunikatu o głośności, a inny – wyciszający tuner.

Jeśli ten warunek nie zajdzie, wracamy do procedury z poprzedniego szkicu, z tym że dokładamy jeszcze instrukcję zdjęcia wyciszenia. Teraz już potencjometr działa jak w prawdziwym radiu. Czas więc zająć się programatorem, ale o tym napiszę w kolejnym artykule.

Powiązane tematy

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2025 arduino.pl