Generování zvuku pomocí mikrokontroléru

Projekt v rámci předmětu MMIA
Matej Gálus, xgalus02@stud.feec.vutbr.cz
Lenka Zelinová, xzelin13@stud.feec.vutbr.cz
projekt zveřejněn také na www.pandatron.cz

Obsah:

  1. Úvod
  2. Popis možností generování zvuku mikrokontrolérem
  3. Realizace
  4. Závěr
  5. Použitá literatura

Úvod

Mikrokontroléry nalézají uplatnění ve velkém rozsahu aplikací. Každý mikrokontrolér však musí nějak komunikovat s okolím. Pokud je mikrokontrolér použit jako součást komplexnějšího číslicového systému, tak se pro komunikaci používají různé druhy číslicových zběrnic a protokolů - Pro připojení k jinému mikrokontroléru resp. k číslicové periferii se používají sběrnice. IIC, SPI apod. Pro připojení k PC dominují zběrnice USART a nověji také USB. Naproti tomu při komunikaci s reálným světem - člověkem musí být signály z číslicového mikrokontroléru převedeny do analogové formy, vnímatelné člověkem. K tomu se velmi často používají LED resp. LCD displeje, LED diody - tedy prvky převádějící digitální signál z mikrokontroléru na signál optický. Tato metoda je velmi rozšířená i u jednoduchých aplikací s mikrokontroléry. Někdy je ovšem potřeba signál z mikrokontroléru převést na signál akustický. Příkladem mohou být různé automatické telefonní služby, např. odkazová služba provozována telefonním operátorem. Zde je jedinou možností sdělovat informace pomocí akustického - řečového signálu.

Popis možností generování zvuku mikrokontrolérem

Pro generování zvuku mikrokontrolérem existují v podstatě tři základní možnosti. První je použít specializovaný Č/A převodník. Tento může být připojen k mikrokontroléru např. pomocí IIC anebo SPI sběrnice. Digitální data jsou mikrokontrolérem přenášena do převodníku, který je převádí na akustický signál. Tato varianta je vhodná v případě, že požadujeme akustický výstup o vyšší kvalitě, např. pro jakostní poslech hudby. Používá se např. u zvukových karet PC.

Další možností je využít pro generování zvuku přímo několik výstupních pinů mikrokontroléru. Po doplnění rezistorovou sítí R-2R získáme jednoduchý paralelní Č/A převodník. Na příslušné výstupní piny mikrokontroléru program posílá přímo jednotlivé bity postupně každého přehrávaného vzorku. Odporová síť je zapojena jako binární dělič napětí. Většinou se používá síť složena z 8 párů rezistorů, poskytující rozlišení 8 bitů, což pro jednoduchou akustickou signalizaci většinou postačuje. Metoda je poměrně rozšířená, nicméně vyžaduje síť 16-ti rezistorů o potřebné přesnosti. Příklad takového zapojení je na následujícím obrázku.

Nejjednodušší možností je využít možnost pulsně-šířkové modulace. Moderní mikrokontroléry často disponují několika PWM výstupy, jejichž střídu lze řídit softwarově pomocí příslušných registrů. Střída je pak zachována až do následující změny registru. Jedná se v podstatě o speciální případ využití čítače/časovače, kdy tento běží na hodinovém kmitočtu vyšším, než je maximální frekvence v požadovaném audiosignálu. Střída každého pulsu je úměrná okamžité amplitudě vzorku. V nejjednodušším případě je možné příslušný výstupní pin čítače/časovače připojit (přes rezistor) na malý reproduktor. Díky jeho indukčnosti a setrvačnosti membrány je převod na akustický signál zajištěn přímo reprodutorem bez nutnosti další filtrace. Tato metoda, oproti výše zmíněným, poskytuje vzhledem k principu činnosti akustický signál o nejmenší jakosti. Nicméně i tato jakost, při použití 8-bitového čítače/časovače je pro většinu aplikací dostačující.

Realizace

Hardwarová část

Vzhledem k výše popsanému, byla pro tento projekt vybrána varianta s využitím pulsně-šířkové modulace. Jako zdroj dat pro akustický signál byla zvolena možnost použití SD paměťové karty a čtení souborů v RAW formátu. Tento je prakticky nejjednodušším audioformátem. Oproti např. WAV formátu neobsahuje hlavičku specifikující atributy datového toku - vzorkovací frekvenci, bitovou hloubku, počet kanálů apod. Proto je při použití RAW formátu pro jeho přehrání nutné tyto parametry znát. Pro jednoduché aplikace, kdy jsou použity vzorky (RAW soubory) o stejných parametrech, to nepředstavuje problém. Pro tento projekt byly zvoleny monofonní soubory s hloubkou 8 bitů a vzorkovací frekvenci 8 kHz (umožňující maximální frekvenci audiosignálu 4 kHz, což je pro telefonní služby s horním mezním kmitočtem asi 3400 Hz postačující).

Pro čtení z SD karty byl vzhledem k rozsáhlosti programu použit hotový projekt [1]. Jedná se o ukázku přístupu na SD/SDHC kartu se souborovým systémem FAT32 pomocí mikrokontroléru ATMega32 anebo ATMega16. Karta je k mikrokontroléru připojena sběrnicí SPI. Uživatelská komunikace je přes PC prostřednictvím rozhraní USART (RS232) a aplikací Hyperterminál, která je standardně součástí MS Windows. Mikrokontrolér poskytuje funkce jako vytvoření a mazání souboru, čtení souboru (výpis jeho obsahu do okna terminálu), mazání souboru. V případě nenaformátované SD karty (bez FAT32) je možnost číst a zapisovat data na kartě jako surové bloky. Schéma zapojení dle [1] je na následujícím obrázku.



Projekt [1] však neuvažuje s možností generování zvukového signálu pomocí souboru na SD kartě, omezuje se pouze na přenos přes rozhraní USART. Pro náš účel bylo tedy nutné v zdrojovém kódu a v hardwarovém zapojení provést několik změn a doplnění. Hardwarově bylo zapojení především doplněno o zesilující stupeň s tranzistorem T1 a rekonstrukčním filtrem typu dolní propust, složeným z R10 a C10. Mezní frekvence filtru je (při zanedbání vlivu impedance reproduktoru) zhruba 7,2 kHz, což je dostatečně nízko pod frekvenci PWM signálu, která, jak se zhruba 33 kHz. Zároveň je mezní frekvence dostatečně vysoko, aby neovlivňovala přenos v akustickém pásmu do 4 kHz. Je nutno poznamenat, že tento jednoduchý filtr je pouze 1. řádu a tedy filtrace vzorkovacího kmitočtu nebude zdaleka dokonalá. Nicméně, na jakosti zvuku se to prakticky neprojeví. Filtr je zařazen prakticky pouze pro zjednodušení sledování výstupu osciloskopem.
Zapojení bylo doplněno rovněž o další LED diodu pro možnost nezávislé indikace aktivity rozhraní USART a aktivity SD karty. Další úpravy (tlačítka, analogové vstupy, obvod RTC) byly doplněny s výhledem na možné budoucí experimentální využití desky.

Schéma zapojení a obrazec pro výrobu plošného spoje:


Osazovací plány a fotografie osazené desky:

 

Softwarová část

Po připojení kabelem RS232 a zapnutí přípravku se tedy v okně hyperterminálu objeví menu s možností výběru akce. Aby bylo možné přes hyperterminál ovládat přehrávání souboru, bylo menu programu doplněno o další možnost: Stisknutím klávesy "p" je uživatel dotázán na jméno jednoho souboru, který má být přehrán. Volbou "c" program vejde do smyčky, kdy každá přijatá číslice vyvolá přehrání příslušného souboru .raw.

Strukturu menu popisuje soubor SD_main.c.

Při zvolení možnosti "c" program vejde do cyklu, kdy každý přijatý znak doplní o příponu ".raw" a soubor s tímto názvem přehraje. Po přehrání pak čeká na další znak. Návrat do menu je možný klávesou ESC.

case 'c': TX_NEWLINE;
		    TX_NEWLINE;
		    transmitString_F(PSTR("Pro navrat do menu slouzi klavesa Backspace."));
		    TX_NEWLINE;
          transmitString_F(PSTR("Zadavej cislice: "));
          
		    for(i=0; i<13; i++)
			  fileName[i] = 0x00;   //smazani pripadneho predchoziho nazvu
            i=0;

		    InitPWM();			//inicializace citacu/casovacu
          while(1)
          {
            data = receiveByte(); // cekej na prijeti znaku
			
			  if(data == 0x08)		// zmacknuto Backspace?
	 		  break;				// ukonceni while cyklu
			
			   if(data <0x30 || data > 0x39) // pokud byl zadan jiny znak nez cislo 0-9
			
			   {	
					TX_NEWLINE;
					transmitString_F(PSTR("Byl zadan jiny znak nez je povoleno!")); //chybove hlaseni
					continue;  // pokud znak neni cislo tak skonci
			   };
			
			  transmitByte(data); // vypis zpatky do terminalu
			
			  // vytvoreni jmena souboru:
         	  fileName[0] = data; // cislo
            fileName[1] = 0x2e; // .
            fileName[2] = 0x72; // r
			  fileName[3] = 0x61; // a
			  fileName[4] = 0x77; // w

			  readFile( PLAY, fileName);	//volani funkce pro prehrani souboru
          }
        	
          break; 	//navrat do menu (po zmacknuti Backspace)

Kód pro volbu "p" je jednodušší, v podstatě jenom inicializuje PWM a volá funkci pro přehrání.

if(option == 'p') 
		  	{  
		  	InitPWM();					//inicializace citacu/casovacu
		  	readFile( PLAY, fileName);	//volani funkce pro prehravani souboru
			}

Funkce InitPWM() inicializuje příslušné čítače/časovače:

void InitPWM(void)
{

//TIMER/COUNTER0 ridi primo PWM vystup
	TCCR0|=(1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00); // bez preddelicky, rezim Fast PWM, neinvertovany

//TIMER/COUNTER2 ridi vzorkovaci periodu (rychlost toku vzorku na PWM)
	TCCR2|=(1<<WGM21)|(1<<CS21);	// preddelicka fclkio/8, rezim CTC
	OCR2=125;						// vzorkovaci perioda pro 8 MHz krystal a 8 kHz fvz

}

Funkce readFile je v původním programu použita pro čtení sektorů z SD karty a posílání jednotlivých bitů na rozhraní USART. Pro přehrávání jsme funkci upravili - bity nejsou posílány na USART ale volá se funkce playByte:

if(flag == PLAY) {

	TX_NEWLINE;
	TX_NEWLINE;
	transmitString_F(PSTR("Prehravam...")); 	//posli informaci na USART

	while(1)		// "nekonecna" smycka
	{
	  firstSector = getFirstSector (cluster);	// volani funkce pro zjisteni prvniho sektoru v clusteru

	  for(j=0; j<sectorPerCluster; j++)			// pro kazdy sektor v clusteru
	  {
	    SD_readSingleBlock(firstSector + j);	// volani funkce pro precteni jednoho bloku
    
		for(k=0; k<512; k++)					// pro kazdy byte v bloku
	    {
	      playByte(buffer[k]);					// volani funkce pro prehravani
	      if ((byteCounter++) >= fileSize ) return 0;	// pokud jsme dosahli konec souboru, skoncit
	    }
	  }
	  cluster = getSetNextCluster (cluster, GET, 0);	// zjisteni dalsiho clusteru
	  if(cluster == 0) {transmitString_F(PSTR("Error in getting cluster")); return 0;}	//hlaseni pri chybe
	}
	return 0;
}

Samotná funkce playByte posílá jednotlivé bity na PWM výstup se správnou rychlostí - vždy při shodě čítače/časovače2 s hodnotou nastavenou v OCR2:

void playByte( unsigned char data )
{
	while (bit_is_clear(TIFR,OCF2));	// cekej na signal z citace/casovace2
	{
		OCR0=data;				// posli hodnotu vzorku na PWM vystup (do citace/casovace0)
		TIFR|=(0<<OCF2);		// vynuluj flag shody TCNT2, abychom poznali dalsi signal z citace/casovace2	
	return;
	}
}

Závěr

Výšeuvedené softwarové řešení zajistí přehrávání vzorků souboru z SD karty s téměř správnou rychlostí. Je zde totiž jeden malý problém: Jakmile se při přehrávání dosáhle konec bloku a zásobník (buffer[k]) se vyprázdní, funkci SD_readSingleBlock trvá určitý čas, než načte další blok dat do bufferu. Čtení z SD karty totiž funguje tak, že se na kartu pošle adresa požadovaného bloku a karta pošle celý blok o 512 bytech. Ukázalo se, že tato operace trvá v našem případě přibližně 3 ms. To při vzorkovací frekvenci 8 kHz (vzorkovací perioda 125 us) způsobí, že přehrávání se pozastaví na 24-násobek vzorkovací periody. Každých 512 bytů se tedy pozastaví přehrávání "na 24 bytů". Subjektivně to způsobuje, že zvuk je přehráván se správnou výškou tónu, ale je v něm slyšitelné pravidelné "lupání" každých přibližně 60 ms. Přehrávání se tedy zpomalí na 0,955-násobek správné rychlosti. Tento jev dobře ilustruje následující oscilogram. Žlutý průběh je napětí na reproduktoru (PWM signál po nedokonalé filtraci) a zelený průběh je signál na kontrolním pinu resp. na LED, která se rozsvítí při práci s SD kartou:

Z oscilogramu je vidět, že po dobu práce s SD kartou je PWM výstup neměnný (mírná setrvačnost je způsobena výstupním filtrem).
Teoreticky by tento problém bylo možné vyřešit použitím delšího zásobníku, resp. dvou zásobníků místo jednoho a přehrávání vzorků řešit pomocí přerušení čítače/časovače2. Nicméně, při zvyšování velikosti zásobníku jsme limitováni velikostí SRAM paměti mikrokontroléru, která u ATmega16 činí 1 kB. V současnosti celý program využívá 593 B SRAM. Pro vyřešení tohoto problému by bylo nutné použít jiný typ mikrokontroléru, např. ATMega128, který disponuje 4 kB SRAM.

Zdrojový kód projektu je ve složce zdrojovy_kod.

Použitá literatura

  1. CC Dharmani: SD/SDHC Card Interfacing with ATmega8 /32 (FAT32 implementation). <http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html>
  2. Atmel: ATMega16 datasheet. <http://www.atmel.com/dyn/products/product_card.asp?part_id=2010>
  3. FEDRA, Z. Mikropočítače pro přístrojové aplikace. Přednášky. FEKT VUT Brno 2010. <https://krel.feec.vutbr.cz/VYUKA/M_EST/MMIA/Texty/>