Przedstawiam prostą konstrukcję, która sprawiła mi sporo frajdy. Jest to odtwarzacz plików MP3 z użyciem RaspBerry Pi Pico na mikrokontrolerze RP2040.
Pliki wczytywane są z karty SD i odtwarzane z wykorzystaniem PWM.
Testy zacząłem od wykorzystania gotowych projektów. Zacząłem od przykładu z CircuitPythonem(https://learn.adafruit.com/mp3-playback-rp2040/pico-mp3), gdzie znalazłem prosty przykład odtwarzający,
mp3 z wewnętrznej pamięci za pomocą PWM. Głośnik podłączyłem bezpośrednio do pinu mikrokontrolera. I ku mojemu zdziwieniu okazało się,
że to bardzo ładnie gra, ale bardzo cicho. Potestowałem kilka godzin i nawet PIN się nie uszkodził od przepięć powstających w wyniku podłączenia cewki(głośnika).
Przebieg na GPIO wyglądał tak:
W kolejnym etapie dodałem kartę SD podłączoną po SPI, oraz dorobiłem płytkę z inwerterem i filtrem, aby nie przeciążać pinu GPIO i uzyskać większą głośność. Powstało coś w stylu końcówki mocy wzmacniacza klasy D. Jako bazę konstrukcji wykorzystałem starą płytę, która była podstawą wyświetlacza do drukarki 3D.
Płytkę z inwerterem najpierw zaprojektowałem na kartce papieru. Kropkami zaznaczone są miejsca lutowania nóżek tranzystorów. Następnie wyciąłem przerwy nożem do tapet. Wlutowałem dwa tranzystory NMOS i PMOS, a na wyjściu filtr LC. W ten sposób zbudowany inwerter steruje głośnikiem, a częstotliwość PWMu jest znacząco tłumiona filtrem. W ostatecznej wersji kondensator odsprzęgający DC jest nieobecny. Składowa stała jest mała, inwerter jest zasilany z 3.3V. Ja różnicy w dźwięku nie słyszę niezależnie czy kondensator jest obecny czy nie. Teraz mocy mam na tyle, że bez problemu przesterowuję, głośnik 0.5W, muszę redukować głośność.
Tranzystory jakich użyłem są w obudowie Sot-23. Modele to AO3400, AO3407, zakupione na aliexpress, jako kiedyś przyda się(i się przydały).
Inwerter wraz z filtrem przesymulowałem w LTSpice, aby sprawdzić jak to się zachowuje. W symulacji widoczny jest jeż modulator PWM na wzmacniaczu operacyjnym, aby zasymulować pin GPIO. Gdzie sygnałem modulujący czyli udawane audio jest sinusoida 1kHz. Na zrzucie ekranu widać przebieg odfiltrowanego sygnału wyjściowego.
Przetestowałem, też wersję gdy za pomocą RP2040 generuję PWmem sygnał 1kHz.
Przedstawiam, też przebieg na wyjściu inwertera zbudowanego na Mosach. Nie jest idealnie, ale wystarczająco dobrze, aby działało. Widać nawet na takim słabym
powiększeniu moment gdzie oba tranzystory przewodzą, no ale trudno. Nie jest to nawet po środku zbocza, no ale mają różne napięcia załączenia.
Nie udało mi się znaleźć symetrycznej pary NMOS PMOS aby miały takie same napięcia progowe.
W projekcie zmieniłem głośnik na trochę lepszy z obudową(na pierwszych zdjęciach był mniejszy). O mocy 0.5W. Ostatnio miałem go wyrzucić, ale jednak znalazłem dla niego zastosowanie.
Dodałem wyświetlacz sterowany na I2C SS1306, aby wyświetlał nazwę obecnie odtwarzanego pliku, obecny poziom głośności. Dodałem obsługę enkodera,
którego obrót zmienia głośność, a przyciśnięcie zmienia piosenkę na następną. Wyświetlacz też wyświetla numer obecnie odtwarzanego utworu, aby można było się łatwiej odnaleźć.Wszystkie elementy zostały razem połączone za pomocą taśmy dwustronnej.
Oprogramowanie kompilowane jest w środowisku arduino. Projekt dostępny jest pod linkiem https://github.com/mztulip/rp2040_pwm_mp3_player
Jako początki uruchamiania projektu dekodera MP3 z kartą sd wykorzystałem przykład z biblioteki BackgorundAudio. https://github.com/earlephilhower/BackgroundA...tSoSimpleMP3Shuffle/NotSoSimpleMP3Shuffle.ino
Musiałem go przerobić, aby w ogóle zadziałało, bo przykład używał DAca na I2S. Ale poszło szybko z bibliotekami arduino wystarczyło podmienić obiekt I2S na PWMAudio.
Ogólnie na tym etapie byłem w szoku, że jakość audio jest lepsza niż w moim laptopie Dell XPS.
Kolejnym etapem było dodanie wyświetlacza, co sprawiło sporo problemów nie chciało to działać. Były problemy z obsługa długich nazw plików na systemie Fat32.
były wycieki pamięci bufory się rozwalały, z audio leciały trzaski i procek się zawieszał. Choć na początku myślałem,że się nie wyrabiają dwie transmisje DMA jedna do audio a druga do wyświetlacza. Stworzyłem uproszczoną wersję biblioteki Wire, która nie używała DMA, aby I2C działał w pollingu. Co miało ułatwić analizę i weryfikację moich przypuszczeń, gdzie są problemy. Ostatecznie znalazłem miejsce, gdzie używałem obiektu, który był na stosie i już nie istniał, potem bufor audio go używał, a ja po nim pisałem używając wyświetlacza. Więc problemem nie było DMA, ale uproszczone wire już zostało.
Przerobiłem, też projekt na bezpośrednie użycie biblioteki Sdfat(https://github.com/greiman/SdFat), aby działały długie nazwy plików. Co ciekawe arduino core też używa tej biblioteki po spodem, ale z długimi nazwami kod przez chwile potrafił działać, czyli jakieś bufory się przepełniają. Ostatecznie po mojej modyfikacji z ominięciem biblioteki arduino działa bez problemu. Karta SD śmiga z zegarem 12MHz, który widać na screenie z oscyloskopu.
Dodanie enkodera poszło bezproblemowo, użyłem biblioteki https://github.com/gbr1/rp2040-encoder-library, która realizuje obsługę enkodera, wykorzystując maszynkę w PIO.
Jest tam licznik aktualnej pozycji enkodera i procesor odczytuje wartość.
Podsumowując działanie ostatecznego kodu to jest ono następujące.
Procesor pracuje z częstotliwością 133MHz. Choć czy jest to jego rzeczywista prędkość musiałbym zweryfikować, ale nie mam nawet pomysłu jak. Bo kod wykonywany jest z zewnętrznego flasha, który jest dużo wolniejszy. Procesor ma cache wykonywanego kodu, więc jest jakaś szansa, że moja aplikacja mieści się w tym cachu, który o ile pamiętam ma 16kB. Dodatkowym spowalniaczem może być to że domyślnie, kod wykonywany jest w trybie najwolniejszego odczytu po zwykłym SPI zamiast QuadSPI. Ale narazie prędkość mnie nie limitowała więc nic nie zmieniałem.
Rdzeń 1 zajmuje się czytaniem karty SD, przekazywaniem bloku 512 bajtów do dekodera MP3(którym jest biblioteka libmad https://github.com/sezero/libmad).
Tak zdekodowane audio jest kopiowane do bufora na który następnie nanoszona jest korekta regulacji głośności poprzez podzielenie próbek. Tak przygotowany bufor jest przekazywany do modułu PWMAudio z arduino. Moduł ten używa bloku PWM w mikrokontrolerze i dane z bufora o audio czyli wartościach PWMa wysyłane są przez DMA.
Jak odczyt pliku z karty SD zakończy się to otwierany jest kolejny.
Rdzeń 2
Zajmuje się odczytywaniem danych z enkodera, wysyłaniem danych, pisaniem po wyświetlaczu OLED oraz miganiem Ledem.
Cała aplikacja działa bez problemu na jednym rdzeniu, ale chciałem pobawić się drugim.
Tu jeszcze chciałbym wspomnieć, że PWM pracuje na 160kHz, domyślnie arduino uruchamia go na 49kHz. W przypadku większej częstotliwości uzyskuje się mniejszą rozdzielczość bitową. Co w efekcie powoduje że zmiany o małej amplitudzie nie są odtwarzane, ale jak na razie nie słyszę różnicy, ani dyskomfortu z tym związanego. Nawet odniosłem wrażenie, że na 160kHz gra lepiej, ale tego nie jestem pewien testowałem na jednym utworze w stylu techno.
Podsumowując
Powstały projekt/zabawka, testowałem już całymi dniami i gra dobrze. Plan jest użyć tego jako zamiennik elektroniki w zabawce, którą niedawno opisywałem, ale myślę czy nie połączyć tego jeszcze z bluetoothem, choć tu idea mi się jeszcze buduje.
Prace nad tym projektem trwały około miesiąca.
Fajne? Ranking DIY