[097] Budujemy arduinowe radio cz. 1
![[097] Budujemy arduinowe radio cz. 1](/_next/image?url=https%3A%2F%2Farduino.pl%2Fimgproxy%2FzIihuOL0enoavqdN_9w4gHSc6aTKCfEzCLcenko2tO4%2Ff%3Awebp%2Fw%3A1200%2FbG9jYWw6Ly8vaW1hZ2VzLzAtNjdjLzBjYTZjL2JiMDIxLzAtNjdjMGNhNmNiYjAyMTEzMzU5Njg0Ny5wbmc%3D.webp&w=3840&q=75)
Kto z nas nie budował kiedyś odbiornika radiowego? Lata temu wymagało to wiedzy, środków i czasu, bo dobrej klasy odbiornik fal ultrakrótkich, zwłaszcza stereofoniczny, to nie była migająca lampka do złożenia w dziesięć minut, ale skomplikowane urządzenie wymagające zestrojenia i zwykle ten etap był najtrudniejszy. Wielu w ogóle rezygnowało z budowy części odpowiedzialnej za największą częstotliwość, czyli z głowicy, przystosowując fabryczne rozwiązania do własnych potrzeb.
Czasy jednak się zmieniły. Po drodze odbiorniki upraszczały się, nastąpiła integracja całości w jednej strukturze, synteza częstotliwości i cyfryzacja. Czas płynął dalej, z rozbudowanych modułów kompletny odbiornik radiowy, stereofoniczny, z RDS-em i rewelacyjną czułością zawędrował do malusieńkiego układu osadzonego na płytce o powierzchni nieco ponad centymetra kwadratowego. Oczywiście sposób sterowania także się zmienił i już nie podłączymy tutaj nigdzie gałki strojenia ani potencjometrów. Z radyjkiem rozmawia się w języku I2C.

Oto przykłady takich odbiorników. Układów powstało kilka, są ze sobą częściowo kompatybilne i umożliwiają zbudowanie odbiorników kompletnych, z wyjściem wprost na słuchawki o rezystancji 32 omów. Nasuwa się od razu podstawowe zastosowanie tych odbiorniczków, jako wyposażenie urządzeń przenośnych. Lecz jakość dźwięku jest na tyle dobra, że umożliwia zbudowanie dużego tunera radiowego, o ile tylko zadbamy o porządną antenę i układy dopasowujące. W podstawowej wersji stanowi ją po prostu kawałek przewodu bądź masowy przewód słuchawkowy, po założeniu filtrów.
Odbiorniczki mają znormalizowane wymiary. Nieszczęśliwie rozstaw wyprowadzeń zrywa z miarą calową, więc w przypadku zabaw z płytką testową należy stosować sprytne lutowanie, kompensujące standard dwumilimetrowy do rastra równego 1/10 cala. Nie jest to rzecz trudna w realizacji, ale nieelegancka.

Rzućmy okiem na parametry przykładowego radyjka tej klasy. Po pierwsze: zasilamy to napięciem standardu trzywoltowego. Na szczęście Arduino dysponuje tym napięciem i na szczęście również szyna I2C może być podłączona do logiki pięciowoltowej. Trzeba jednak pamiętać, że podanie pięciu woltów na wejście zasilające może zniszczyć układ.
Pobór energii nie jest śladowy – to 20 mA, czyli blisko 70 mW – jak na standardy współczesne niemało, ale to jest jednak układ częściowo analogowy i służy do odbioru częstotliwości radiowych.
A te mogą wynosić od 50 do 115 MHz, znacznie więcej niż potrzeba do odbioru komercyjnego pasma. Skok częstotliwości może zejść nawet do 25 kHz. Mamy też elementy pomagające przy odbiorze słabszych stacji: od przełącznika mono, przez różne ograniczniki pasma. Czułość jest bardzo przyzwoita, sięga poniżej półtora mikrowolta. Typowe radio kuchenne z peerelu dysponowało zwykle czułością przynajmniej rząd wielkości słabszą.
Selektywność także jest wysoka, niedostępna prostym klasycznym odbiornikom, podobnie jak dynamika: 55 dB. Bezwzględnie – to nie dużo wobec obecnych standardów, ale emisja FM po prostu więcej nie potrafi. Zniekształcenia nie przekraczają 2 promili, co także ociera się o kres technologii, jakby nie było, projektowanej w okresie okołowojennym jeszcze.
Jak widać, ta śmieszna, mała płytka umożliwia zbudowanie tunera stereofonicznego o całkiem przyzwoitych parametrach. I taki dzisiaj zbudujemy, ale najpierw w wersji minimalistycznej, czyli oferującej tylko możliwość regulacji głośności i skoku do kolejnych stacji wyszukiwanych automatycznie. Zanim jednak zabierzemy się do programowania, krótka chwila na montaż.

Jak wspomniałem, musimy niestety uśmiechnąć się do lutownicy, chyba że uda się nam zdobyć płytkę z już przylutowanymi goldpinami. Jeśli nie – należy najpierw przycięte goldpiny osadzić w płytce testowej, po czym mniej więcej symetrycznie umieścić płytkę i kroplami z cyny połączyć wyprowadzenia ze sobą – im bliżej krawędzi, tym bardziej skośnie. Taką płytką łatwo już manipulować, zatem podłączymy ją teraz do Arduino. Będę używał płytki edukacyjnej TME, ponieważ dublują mi się tutaj wyprowadzenia modułu Uno, a poza tym mam tu klawiaturkę, z której zaraz skorzystam.
I tak: masę i zasilanie łączymy odpowiednio z masą i wyjściem 3,3 V, SDA z A4, a SCL z A5. I to wszystko. No, jeszcze trzeba wyprowadzić dźwięki, wystawiając je na gniazdko słuchawkowe oraz podłączyć antenę, czyli kilkudziesięciocentymetrowy kawałek przewodu – tak na początek.

I to już cała praca sprzętowa, czas na program. Tutaj dodam, że tuner potrafi dużo: oferuje dane RDS, parametry sygnału, można go konfigurować w różnych trybach, ale to kiedy indziej. Po więcej dziś zapraszam do przykładowego szkicu SerialRadio z pakietu, który znajdziemy po ściągnięciu bibliotek, które zaraz omówię. Na pewno działa, bo sprawdzałem, ale jest napisany dość niedbale i trudno się w nim połapać. Tam jednak możemy porozmawiać z radiem za pomocą komend wpisywanych w oknie terminala.
#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; // Zadeklaruj nazwę dla tunera.
// RDA5807M radio;
// SI4705 radio;
// SI47xx radio;
// TEA5767 radio;
const byte poprzednia = 4; // Pstryczek "Poprzedni plik"
const byte nastepna = 7; // Pstryczek "Następny plik"
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"
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.
}
}
Tym razem skorzystamy z pary bibliotek, ponieważ jeden składnik radio.h dotyczy ogólnej komunikacji z całą serią tunerów, a drugi, tu: RDA5807FP.h już konkretnie tłumaczy polecenia na język zrozumiały dla konkretnego układu scalonego. Jak widać, mamy tutaj aż pięć układów do wyboru. Płytka którą posiadam wykorzystuje wariant pierwszy bądź drugi – nie zauważyłem różnic, acz pewno takie są w jakichś mało istotnych zakamarkach możliwości układu. Jeśli ktoś zdobędzie inny typ radyjka, niech sobie „odkomentuje” odpowiednią bibliotekę. Trzeba to także zrobić poniżej, w miejscu, w którym nadajemy radiu imię, w moim przypadku jak zwykle mało finezyjne: radio.
Następnie deklarujemy klawiaturę znajdującą się na płytce edukacyjnej TME, której używam w projektach i o tym mówiłem już wielokrotnie.
Zanim zakończymy omawianie wstępu, jedna uwaga: wcześniejsze wersje oprogramowania tunerów wymagały biblioteki I2C. Na którymś etapie elementy tej komunikacji zaimplementowano w bibliotece radio, więc stała się zbędna. Zaznaczam jednak, że może istnieć sytuacja, w której może się ona przydać. W obu przypadkach port komunikacyjny ustawiono na sztywno na pinach A4 i A5, a więc nietypowo. Tych rzeczy nie trzeba deklarować i nie można ich zmienić.
Na początku programu musimy zrobić porządek z płytką tunera. A więc zacząć należy od inicjalizacji zarówno układu jak i komunikacji. Następnie deklarujemy parametry odbiornika dla tej części świata, czyli skok strojenia równy 100 kHz, deemfazę równą 50 us – to parametr odpowiedzialny za tłumienie wysokich tonów, które są podbijane przed emisją, dzięki czemu zyskuje się na odstępie od szumów. Zakres pasma domyślnie jest ustawiony na europejski, czyli 87,5 – 108 MHz.
W linii radio.setBandFrequency(RADIO_BAND_FM, 9800) deklarujemy częstotliwość po włączeniu urządzenia. Wpisałem 98 MHz, co jest środkiem pasma, ale każdy może tutaj wrzucić częstotliwość swojej ulubionej stacji.
W końcu wymuszamy tryb stereo, wyłączamy wyciszenie i ustawiamy startową głośność na ¼ zakresu. Teraz można już przejść do pętli głównej.
Jak wspominałem, cudów nie będzie. Mając pięć przycisków postanowiłem je zagospodarować następująco: góra i dół – zmiana głośności, prawo i lewo – wyszukiwanie kolejnych stacji, a przycisk środkowy – naprzemienne wymuszanie trybu mono i stereo.
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.
}
Zacznijmy od strojenia. Te komendy umożliwiają przeskok do najbliższej stacji o częstotliwości niższej lub wyższej. Oczywiście przyjęto tutaj jakieś kryterium czułości i czasem kolejna złapana stacja będzie bardzo zaszumiona albo w ogóle nie będzie to stacja, tylko przypadkiem złapany warkot zakłóceń.
Po wymianie jedynek na zera, jak to opisałem w komentarzu, funkcja zmieni sposób pracy i przeskok nastąpi do wartości 100 kHz niższej lub wyższej, umożliwiając strojenie krokowe. Acz w tak uproszczonym radyjku jest to niewygodne, przestrojenie całego zakresu wymagałoby 205 wciśnięć przycisków.
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.
}
W przypadku regulacji głośności skorzystamy z faktu, iż można pozyskać informację o aktualnie ustawionym poziomie za pomocą instrukcji radio.getVolume() Przechowamy ją w lokalnej zmiennej x, którą wyślemy, uprzednio zmniejszając bądź zwiększając jej wartość o jeden. Zakres wynosi od 0 do 15, przy czym zero nie wycisza zupełnie radia.
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.
}
W końcu środkowy przycisk naprzemiennie będzie włączał tryb mono lub stereo. W tym wypadku także sięgniemy do statusu, który można wydobyć z układu i wyślemy go ponownie, po jego zanegowaniu.
W każdym wypadku po operacji należy zaczekać, aż przycisk zostanie puszczony. W przypadku użycia przycisków bezpośrednio związanych z portami, dobrze byłoby dodać jeszcze jakieś pętle opóźniające, acz tutaj – wykorzystując płytkę edukacyjną TME – przyciski mam podłączone przez inwertery Schmitta, co daje wystarczającą stabilność ich odczytów.
Na koniec kompilujemy szkic i możemy cieszyć się prostym radyjkiem, może nie aż tak użytecznym, bo pozbawionym skali i programatora, ale jak zwykle – dalszy ciąg nastąpi.