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.
   Popsaný druh paměti se jmenuje FIFO (od slov First In First Out - první dovnitř, první ven). Méně vhodně, ale zato daleko častěji se označuje jako buffer a ještě méně vhodněji a ještě častěji třeba u discmanů jako "Antishock". Konkrétně u přenosných CD přehrávačů je snaha vytvořit co největší FIFO nejen pro případ otřesů, kdy se audio poměrně dlouho přehrává jen z FIFO, ale také kvůli spotřebě. Do FIFO se načte maximum a CD se může zastavit (především u MP3).


   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.
   V dolní části schématu jsou 2 varianty D/A převodníků. Jednak TDA1543 a pak TDA1545. Operační zesilovače mohou být napájeny klidně z +5V proti zemi - mělo by to být dostatečné.


   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é.
   Popsaná rutina je sice časově náročná, ale zdaleka ne nejkritičtější. Tou je rutina pro příjem dat z USARTu. Datový tok PCM WAV, který toto zapojení přehrává, je už poměrně vysoký, takže přenosová rychlost je nastavena naplno - 2Mbit/s. To znamená příjem bytu každých 80 cyklů procesoru a to už je opravdu extrém. Dostatečně rychlé zpracování se mi nepodařilo vyřešit v hlavní programové smyčce, takže je řešeno přes RXC přerušení. Takže i příjem dat je v tomto případě v podstatě pseudo DMA. USART je naštěstí na přijímací straně vybaven hardwarovým FIFO na 2B dat, takže lze obsluhu přerušení pozdržet ostatními operacemi. I tak ale musím v obsluze přerušení číst 2 byty najednou. Příjem dat z PC je pochopitelně bržděn HW řízením toku na potřebnou úroveň. Signál CTS, který obvodu FT232 oznámí plné FIFO v MCU, je ovládán v hlavní programové smyčce podle stavu FIFO. Jakmile ve FIFO zbývá jen 32B volného místa, je vyslán signál k zastavení toku dat. FT232 může po tomto signálu poslat ještě 4B, ale především programová smyčka může být blokována dalšími operacemi, takže proto 32B rezerva. Povolení toku dat (CTS - Clear To Send) je obnoveno až při 48 volných bytech.
   Aby toho nebylo málo, doplnil jsem ještě LCD pro zobrazování vzorkovacího kmitočtu a mono/stereo. LCD dost dobře nelze ovládat kalsicky, protože obnovení 20 znaků zabre najednou 1ms, takže i toto je vyřešeno obnovením přes pseudo DMA.
   Jakmile začnou přicházet data, MCU začne počítat byty. Až je příjmuto prvních 64B, pokusí se hlavní programová smyčka načíst z RIFF konzervy PCM WAVu frame "fmt " a v něm identifikovat, zda jde skutečně o PCM WAV, nastavit vzorkovací kmitočet a počet kanálů. Vzorkovací kmitočet lze měnit i ručně rotačním enkodérem v rozsahu 8kHz až 48kHz. 1s po dokončení přehrávání by měl vypršet timeout a MCU reinicializuje přehrávání.
Zdroje a bin.


   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).
   Otevřít by měl jít libovolný soubor, ale bezpečně přehrát lze jen 16bit PCM WAVy 8-48kHz. Pokud je nějaká zrada v HW zapojení, měl by vypršet timeout, takže program snad nevytuhne (to by se při HW řízení toku mohlo snadno stát).
   Celkem milé překvapení pro mě bylo, když jsem zjistil, že i na postarším P3 600MHz přenos jede tak rychle, že nedojde k žádným podtečením FIFO až do kvality výstupu 47500Hz 16bit stereo. Stačí ovšem pohnout myší a už je slyšet podtečení FIFO v podobě lupnutí. Na rychlejších strojích jede přenos nadoraz - 48kHz 16bit stereo a XP si poradí i s další zátěží bez podtečení FIFO. Tomuto formátu odpovídá datový tok 192kB/sec což je 96% z teoreticky maximálních 200kB/sec (2MBd, 10bit rámce) a to je docela slušný výsledek :-).

Poslední aktualizace: 28.7.2007

WebZdarma.cz