|
SW emulace sériové audio sbernice pro I2S D/A převodník
Při pokusech s audio signály na malých MCU existuje několik možností, jak zrealizovat D/A převodník. Velmi často používaný je PWM kanál, který spolu s integrátorem dává poměrně poslouchatelné výsledky a je velmi levný. Pro kvalitnější výstup lze použít externí D/A převodník. Nabízí se zde sice nejeden převodník s paralelním vstupem, ale zabere příliš mnoho pinů (pro více než 8 bitů). Na relativně výkonných jednočipech AVR naštěstí lze provozovat i klasické D/A převodníky se sériovou I2S sběrnicí nebo podobnou. Nejznámější typy jsou TDA1543 (I2S sběrnice) nebo kvalitnější TDA1545 (dle datasheetu "sběrnice kompatibilní s většinou japonských audio formátů: časový multiplex, TTL, dvojkový doplněk"). Oba lze získat i ze starých CD mechanik nebo přehrávačů. V kombinaci s vyrovnávací pamětí (FIFO) lze získat kvalitní audio výstup při relativně malém vytížení MCU. TDA1543 D/A komunikace
TDA1543 je 16-ti bitový stereo D/A převodník. Výstupy jsou proudové, takže je třeba doplnit ještě převodník proud-napětí s dolní propustí. Maximální vzorkovací kmitočet je 192kHz, kmitočet hodin sběrnice I2S je až 9.2MHz. Převodník je napájen jediným napětím 5V (měl by jet i na 3V3), odběr je poměrně vysoký - asi 50mA.
Formát I2S sběrnice je naznačen v grafu. Jak je vidět, 16-ti bitový vzorek ve formátu dvojkového doplňku je odesílán od nejvýznamnějšího bitu (MSB first), data jsou čteny při vzestupné hraně hodin. Signál WS slouží k synchronizaci a multiplexování kanálů. Jeho hodnota se musí změnit vždy PŘED odesláním posledního bitu vzorku, což nepříjemně komplikuje emulaci. Na stejný kanál D/A nelze odeslat více vzorků po sobě. TDA1545 D/A komunikace
TDA1545 je velice podobný předchozímu, ale mnohem kvalitnější (alespoň dle datasheetu :-). Může pracovat jen na 5V napájení a jeho odběr by měl být jen 6mA pro fullscale na obou výstupech. Má nižší zkreslení a nižší šum. Vzorkovací kmitočet může být až 384kHz, kmitočet hodin až 18.4MHz.
Vstupní formát zřejmě nemá žádný název. Dle datasheetu "kompatibilní s většinou japonských audio formátů: časový multiplex, TTL, dvojkový doplněk". Z grafu je vidět, že jde vlastně o identický formát, pouze WS signál pro multiplex kanálů je zpozděn o 1 clock - snáze se realizuje. Vyrovnávací paměť FIFO
Samotná emulace I2S je celkem k ničemu, pokud MCU musí audio data teprve od někud získat nebo
dokonce sám generovat. To zabere poměrně hodně strojového času - často mnohonásobně víc,
než jedna vzorkovací perioda. V průměru sice audio signál musí být generován dostatečně rychle pro přehrávání na daném
vzorkovacím kmitočtu, ale
některé operace krátkodobě brání odesílání vzorků do D/A, což se podepíše na kvalitě výstupu.
Proto je třeba nějakým způsobem přenechat odesílání vzorků na přerušení procesoru, které může téměř libovolně a
hlavně pravidelně přerušovat vlastní generování signálu v hlavní programové smyčce. Je zřejmé,
že přerušení nemůže odeslat do D/A vzorek, který se právě počítá, případně ještě ani nezačal počítat.
Proto je třeba vytvořit vyrovnávací paměť, do které se v hlavní smyčce ukládají vypočítané vzorky maximální možnou rychlostí
a na druhé straně procesor přes přerušení tyto vzorky zase vyčítá.
Pakliže už ve vyrovnávací paměti nejaké předpočítané vzorky jsou, tak i v případě, že je MCU
zaneprázdněn generováním dalších, nebo nečím úplně jiným, tak přes přerušní může tyto vzorky z vyrovnávací paměti
vybírat a odesílat do D/A. Pokud hlavní smyčka zase začne "dodávat" další vzorky dříve, než se vyrovnávací paměť zcela
vyprázdní, tak se buffer zase začne plnit a vše běží bez problému. Hlavní programová smyčka pochopitelně
před zápisem dat do vyrovnávací paměti musí zjistit, zda je v ní ješte volné místo.
Jinými slovy stav vyrovnávací paměti (v tomto případě množství nepřehraných audio vzorků) může libovolně kolísat,
ale nesmí se zcela vyprázdnit.
Zjednodušeně řečeno si FIFO lze představit jako "černou skříňku", do které jednou stranou ukládáme data v náhodných intervalech (pakliže je v ní místo) a druhou stranou v pravidelných intervalech vybíráme. Toto platí třeba pro případ D/A, ale samozřejmě to může fungovat i opačně - třeba z A/D převodníku dostáváme pravidleně data a na výstupu FIFO je zpracováváme jak právě zbývá MCU čas. V obou případech však musí platit, že průměrný tok dat do FIFO se musí rovnat toku dat z FIFO. Zároveň je také zřejmé, že FIFO působí jako zpožďovací linka. Velikost zpoždění je přímo uměrná velikosti FIFO a nepřímo úměrna datovému toku skrz FIFO. To samozřejmě platí pro případ, že stav FIFO je víceméně neměnný a téměř maximální.
FIFO se sice vyrábí jako samostatné obvody, ale pro tyto účely jsou nevhodné. Daleko častější jsou SW FIFO vytvořené v operační paměti. Jde o velmi jednoduchý princip - vyhradí se potřebný úsek RAM a vytvoří se 2 pointery. Jeden pro zápis a druhý pro čtení dat. Takto vytvořené FIFO pracuje jako kruhový buffer - jakmile kterýkoliv z pointerů přesáhne horní hranici FIFO, resetuje se zase na jeho začátek. Banální, ale důležitou operací je zjištění volného prostoru ve FIFO. To lze provést prostým odečtením pointerů. V případě, že jejich pozice je taková, že vyjde záporné číslo, přičte se k němu velikost FIFO.
Emulace popsaných sběrnic na AVR
Jak jsem už naznačil v úvodu, AVR na nějakých 16MHz jsou dostatečně
výkonné, aby na vzorkovacím kmitočtu 22050Hz pseudo DMA výstup na tyto D/A zabíral jen asi 13%.
Jako MCU jsem pro tuto ukázku zvolil můj oblíbený ATmega32. Nechtěl jsem zapojení nijak zvlášť komplikovat, takže audio není v žádné externí paměti, ale
přijímá se přes virtuální COM port realizovaný pomocí FT232. Navíc je zde doplněno LCD, ale není nutné ho připojovat. Zobrazuje mono/stereo a vzorkovací kmitočet. Navíc je zde rotační enkodér pro změnu vzorkovací frekvence.
K emulaci I2S jsem použil SPI interface taktovaný na maximálních 8MHz, takže odeslání jednoho bytu trvá jen 16 cyklů. Odesílá se vždy jeden stereo
vozrek najednou. Vlastní rutinu jsem vyřešil jako pseudo DMA - je volaná timerem
2 nastaveným na vzorkovací kmitočet. Vzorky jsou odebírány z vyrovnávací paměti
nastavené na maximum co se vejde - 1900B. Podtečení FIFO není nijak kontrolováno - při zastavení toku dat do FIFO se opakuje poslední úsek stále dokola.
Všechny operace jsem se snažil dostat do doby, kdy by MCU pouze čekal na odeslání jednotlivých bytů, takže je tato rutinka poměrně krátká.
Navíc místo push/pop jsem použil in/out do nevyužitých registrů (mírné zrychlení). Přes preprocesor si lze vybrat variantu pro TDA1545 nebo TDA1543 a
variantu mono/stereo. V případě mono je vždy 1 vzorek odeslán na oba kanály a
ušetří se tak polovina paměti pro FIFO. Pak je zde ještě varianta, která
umožňuje přepínat mono/stereo za běhu programu pomocí proměnné I2Schannels. To
samozřejmě zabere pár cyklů navíc, ale je to ještě únosné.
Jednoduchý ovládací prográmek můžete stáhnout
zde. V ini souboru
je třeba nastavit číslo portu, kde je připojený FT232 převodník. V ovladačích
virtuálního COM portu je ještě vhodné nastavit latency na 2ms (nebo 1ms na strojích od 1GHz - na pomalejších dojde spíš ke zpomalení toku dat). Poslední aktualizace: 28.7.2007
|