Úvod

Určitě už vás někdy napadla otázka jak náročné by bylo naučit Atmel mluvit. Nebo alespoň mňoukat. Odpověď vás jistě potěší - náročné to není a cesty jak toho dosáhnout jsou docela dobře prošlapané. Zvu vás na procházku jednou z nich.

Co budeme potřebovat

Potápíme se hlouběji

Přehled máme za sebou a je na čase podívat se zblízka na zvolený hardware. Spínaný audiozesilovač LM4667 by měl být schopen z 5V dodat do 8Ohm reproduktoru výkon až kolem 1W. Pracovat dokáže ale i s napětím 3.3V takže pokud budete aplikaci provozovat třeba z lion článku neměli by jste mít problém. O zkreslení nemá smysl moc hovořit, protože aplikace sama o sobě provede na audiosignálu docela hrubou kompresi. Pinem GAIN SELECT můžete volit zesílení 1x nebo 2x. Pin SHUT DOWN můžete směle ignorovat pokud neřešíte úspory energie. Protože má zesilovač diferenciální vstup (a my nebudeme mít diferenciální audio signál) je vhodné prohlédnout si v datasheetu figure 28 a figure 29 nadepsané jako "SINGLE-ENDED CIRCUIT CONFIGURATIONS". Je na nich znázorněn i způsob volby zesílení. Všimněte si že kromě filtračního kondenzátoru v napájení a dvou oddělovacích kondenzátorů na vstupech už nepotřebujete žádnou další součástku. V mém příkladu bude zisk nastavený na 1x, tedy pin GAIN SELECT přivedu na VDD. Nikdo vám ale přirozeně nebrání přivést jej na I/O Atmelu a přepínat si zisk dle potřeby programu nebo uživatele.

Trochu více pozornosti budeme věnovat DA převodníku. Neuškodí lehký přehled parametrů MCP4801. 8bitová hloubka jasně vypovídá o tom že máme k dispozici jen 256 úrovní výstupního signálu, kvalita zvuku tedy nebude nijak závratná. Teoreticky si můžete koupit i verzi s 10 nebo 12bitovou hloubkou, ale nezapomínejte že ten největší problém naší aplikace bude nedostatek paměti a tímto tahem bychom její spotřebu zvedli dvojnásobně. Výraz Rail-to-Rail Output nás informuje o tom že výstupní napětí DA převodníku je schopné pracovat přes celý rozsah napájecího napětí. Podpora až 20MHz SPI je také potěšující zpráva, protože čím rychleji data do převodníku napumpujeme tím více času zůstane Atmelu na jinou činnost. Settling time 4.5us naznačuje že výstup je možné měnit s frekvencí převyšující 200kHz, nám bude stačit desetina. Interní reference 2.048V a možnost softwarového výběru zisku 1x a 2x opět nahrává použití v 3V a 5V systémech. Zatímco při 3V provozu využijeme zisk 1x a výstupní napětí bude ležet v intervalu 0-2.048V, při 5V napájení můžeme zapnout zisk 2x a rozsah výstupních napětí se zdvojnásobí na 0-4.096V. Velice užitečnou funkci je interni "Latch" ovládaný pinem LDAC. Díky němu můžeme přesně specifikovat okamžik převodu a nemusíme se starat o to jestli se data do DAC převodníku dostanou o pár mikrosekund dříve nebo později. Náš SW bude této funkce přirozeně využívat. A teď si odskočíme k technickým detailům. Výrobce nás opět "obtěžuje" pinem SHDN (SHut DowN). Ten je nutné připojit na VDD jinak bude obvod ve vypnutém stavu. Funkce shut down je zdvojená, takže i přes to budete moci vypnout převodník softwarově. Vývody CS, SCK a SDI vám asi dávají smysl - tvoří jednosměrnou SPI sběrnici. S čipem se komunikuje buď v módu 0 nebo v módu 3. Já v programu používám mód 0. Komunikační protokol je jednoduchý. Odesílá se vždy 16 bitů (MSB jako první) a můžete si jejich význam prohlédnout v následující tabulce.

0 - GA SHDN D7 D6 D5 D4 D3 D2 D1 D0 - - - -
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

První bit zprávy (MSB) musí být vždy nulový. Bitem GA se řídí zisk (log.0 dává zisk 2x). Bitem SHDN se řídí vypínání a zapínání DA převodníku (log.0 vypnutý). Bity označené pomlčkou nemají význam a bity D7 až D0 tvoří byte určený k převodu. Všimněte si "nevhodného" zarovnání datových bitů (příčinou je eixstence 12bitové verze). Žádné další funkce, které by nám komplikovaly práci už nenajdete - jednoduchost sama.

Zapojení

Prohlédnete-li si schéma č.1 určitě uznáte že zapojení je v podstatě přímočaré. SPI sběrnici DA převodníku připojíme k SPI sběrnici Atmelu. Díky tomu, že z DA převodníku nevede do Atmelu žádný výstup (MISO), odpadají nám starosti se sloučením SPI sběrnice a programátoru využívajícího ISP (více zde). Jinak řečeno linky SCK i MOSI může sdílet programátor i DA převodník. Během nahrávání programu se ale budou z reproduktoru linou nepříjemné zvuky jak bude DA převodník interpretovat data tekoucí z programátoru do Atmelu. Zabránit tomu můžete přidáním pull-up rezistoru na CS pin (PB0). V takovém případě bude během nahrávání programu DA převodník odpojen od sběrnice. Připojení zesilovače je také jednoduché. Z výstupu DA převodníku je signál veden přes oddělovací kondenzátory (390nF nebo větší) do vstupů zesilovače. O funkcích vývodů GAIN a SHDN už byla řeč. Každý čip má přirozeně blokované napájení keramickým kondenzátorem. Ještě by se slušelo připomenout, že čip běží na frekvenci 16MHz. Ve schematu ale není zakreslen zdroj hodinového signálu, protože na Xplained desce je clock do čipu veden z debuggeru. Vy ale dost možná budete programovat jiný čip, pak je potřeba buď připojit krystal nebo se obecně s clockem vypořádat dle vaší potřeby. Po drobných úpravách může celá aplikace pracovat i s clockem 8MHz. Tuto eventualitu se pokusím rozebrat v komentáři k programu. Fotku zapojení pro inspiraci můžete vidět na obrázku č.1 a č.2.


Schema č.1 - zapojení DA převodníku a zesilovače.


Obrázek č.1 - ilustrační fotografie zapojení. DA převodník je připájen na Xplained board, zesilovač na vlastní destičce.


Obrázek č.2 - ilustrační fotografie zapojení.

Tvorba zvuku

Úkládáte-li nebo obecně pokoušíte-li se reprezentovat zvuk v digitální podobě pokoušíte se vlastně v paměti nějak popsat časový průběh akustického tlaku. Hodně zjednodušeně se dá říct, že chcete uložit graf. Graf, který může vypadat například tak jako na obrázku 3. Nejjednodušší způsob je dívat se na graf jako na pole hodnot a uložit ho celé do paměti. V takovém případě se musíme rozhodnout jak "jemně" chceme graf v paměti reprezentovat. Lidsky řečeno zvolit si kolik hodnot (vzorků) bude reprezentovat jednu vteřinu zvukového signálu. Tu je vhodné volit někde v rozsahu 5000-45000 vzorky za sekundu. Jemnější dělení s vyšší vzorkovací frekvencí je bezpředmětné, kvůli omezené schopnosti sylšet vysoké frekvence. Hrubší dělení naopak degraduje kvalitu "výšek" ve zvukovém signálu. Přirozeně s jemnějším dělením roste spotřeba paměti, takže je to kompromis. Kvalitní audiosignál na CD používá vzorkovací frekvenci 44.1 kHz. Další důležitou otázkou při ukládání našeho "grafu" bude hrát vertikální rozlišení. Jinak řečeno jak přesně chceme reprezentovat "výšku" signálu (tedy napětí, nebo akustický tlak). CD kvalita využívá 16bitové hloubky, takže po vertikální ose je graf rozdělen na 65535 hodnot. To je dostatečně jemné dělení, ale jeho použití klade nároky nejen na pamět, ale hlavně na HW. K jeho reprezentaci je přirozeně potřeba i 16bit DA převodník. A protože v našem příkladu používáme pouze 8bitový DA převodník, bude nám stačit ukládat data s 8bitovou hloubkou. Využijeme tedy pouze 256 vertikálních úrovní. Toto rozhodnutí má dle mého odhadu největší vliv na sníženou kvalitu reprodukovaného zvuku.


obrázek č.3 - "audio" signál (časový průběh napětí, odpovídají akustickému tlaku - zvuku"

Ukládat do paměti využitím knihovny progmem.h je celkem jednoduché, takže celé pole můžeme "uložit" tak že ho inkludujeme v *.h souboru s paměťovou třídou PROGMEM. Vyvstává ale otázka kde takový textový soubor vzít. Datový formát WAV se jeví jako nejvhodnější kandidát protože přesně takovým způsobem se v něm zvuk reprezentuje. Ale přirozeně ne v textové podobě - to by bylo velmi nehospodárné plýtvání pamětí. Je tedy na místě připravit si nějáký jednoduchý prográmek, který z data z WAV souboru upraví do podoby vhodné pro naše potřeby. Tedy zredukuje hloubku na 8bitů a uloží do textové podoby. V plné obecnosti to není úplně triviální záležitost. V prvé řadě musíte ze souboru přečíst jaká je vzorkovací frekvence. Pokud je příliš vysoká, musíte data "převzorkovat". Což není nic jiné než že použijete například každý druhý nebo každý čtvrtý vzorek, podle toho jaké vzorkovací frekvence chcete dosáhnout. Dále se musíte vypořádat se stereofoním signálem, protože našel aplikace může vytvářet pouze monofonní zvuk. Stereo by bylo v tomto případě příliš velký luxus. Atmel by na to svým výpočetním výkonem stačil, ale paměťové nároky by vzrostly na dvojnásobek a kvalitu už tak mizerného zvuku by to jistě o moc dál neposunulo. Program také musí upravit rozlišení na 8bitové a přirozeně může do zvuku i nějak obecně zasahovat (v mém případě zvuk třeba zesílit). Vždy je lepší skladovat v paměti zvuk v nejhlasitější možné podobě a tlumit ho až v analogové části (tedy za DA převodníkem) a s 8bitovou reprezentací to platí obzvlášť. Důvod vám zkusím ilustrovat na jednoduchém případě. Neutrální signál bez zvuku má hodnotu 128 (střed mezi 0 a 255). Tiché zvuky se od této hladiny odchylují jen drobně (dejme tomu v rozsahu 110-140). Z toho ale plyne že "tichý" zvuk má ke své reprezentaci pouze 30 vertikálních hladin a to je žalostně málo a signál se stává "schodovitý". Mnohem lepší je zvuk číslicově zesílit ještě během přípravy *.h souboru a roztáhnout jeho rozsah na plných 256 hladin. Za DA převodníkem je už ale signál zrekonstruovaný a snížení jeho amplitudy například děličem nebude mít vliv na tvar. Dalším problémem se kterým se program musí vypořádat je délka signálu (tedy celkový počet vzorků). Na našem Atmelu máme k dispozici 32kB paměti z níž část je zabraná programem. S rezervnou máte tedy přibližně 31kB a program musí delší wav soubory zkrátit (tedy část zvuku useknout). Případně se s tímto problémem vypořádat jinak.

Pro testovací účely jsem v Octave připravil ne příliš dokonalý program, kterým lze takový soubor připravit. Napsal jsem jej tak aby v případě kratších WAV souborů doplnil paměť atmelu neutrálním signálem. Což neznamená nic jiného než že doplnil za signál kus "ticha". To je očividné plýtvání pamětí a je zde jen z toho důvodu abych udržel program v Atmelu co nejjednoduší. Mnohem vhodnějším řešením by bylo tuto tichou mezeru realizovat prostě tak že v Atmelu celé generování zastavíme a obnovíme ho až budeme chtít přehrát zvuk znovu. To je ale aplikace, kterou bych rád nechal na vás. Zdrojový kód programu pro octave je zde. Proměnnou max_len můžete specifikovat jakou část paměti chcete pro zvuk alokovat. Pomocí proměnné gain specifikujete míru zesílení (o němž už byla řeč). V argumentu příkazu wavread pak volíte soubor, který se má převádět. Výstupem programu by měl být soubor sample.h,který slouží ke vložení do zdrojového kódu programu pro Atmel.


Zdrojový kód main.c a vygenerovaný hlavičkový soubor se zvukovou stopou sample.h