Generování zvuku

Lukáš Motyčka, Miroslav Procházka
xmotyc03stud.feec.vutbr.cz, xproch41stud.feec.vutbr.cz 

Obsah:

  1. Úvod
  2. Realizace
  3. Závěr
  4. Soubory
  5. Literatura

Úvod

Cílem toho projektu je vytvořit zařízení generující lidský hlas pomocí mikrokontroléru a jednoduchého reproduktoru. Zařízení bude generovat čísla v rozsahu <1,99>, bude obsahovat vstupní interface pro zadání požadovaného čísla a jednoduchý displej, který bude informovat obsluhu o zadávaném čísle.

Realizace

Princip generování zvuku
Jako způsob generování zvuku byla zvolena pulzně šířková modulace (PWM). Je to jedna z nejjednodušších možností, jež se dá realizovat na velkém množství v dnešní době dostupných mikrokontrolérů.
       PWM modulace je v podstatě speciální případ využití čítače/časovače. V porovnávacím registru OCR se nastaví požadovaná referenční úroveň, jež je úměrná okamžité amplitudě vzorku. Tento signál je pak přiveden na reproduktor, který díky vlastní indukčnosti zajistí filtraci a přeměnu elektrického signálu na signál akustický.

Pouzdro

Obr.1: Blokové schéma zařízení.

Podrobnější popis zařízení
O funkci celého zařízení se stará mikrokontrolér ATmega8A. Mikrokontrolér je taktován externím krystalem o frekvenci 8 MHz a je napájen napětím 3,3 V. Tato velikost napájecího napětí je použita především kvůli kompatibilitě s pamětí Flash EEPROM, která vyžaduje právě napájecí napětí 3,3 V. S ohledem na napěťové úrovně používaného logického standardu a využití alfanumerického displeje pouze pro zápis (ze strany mikrokontroléru) je napájecí napětí displeje 5 V.
       Jako vstupní rozhraní slouží maticová klávesnice. Je možné zadat číslo v rozmezí od 1 do 99 a jeho potvrzení se provede znakem # nebo *. O hodnotě zadaného čísla informuje uživatele alfanumerický displej.
       Pro hlasové vzorky byla zvolena vzorkovací frekvence fVZ = 16 kHz, s kvantováním 8 bitů. Při těchto parametrech odpovídá jedna vteřina záznamu 16 kB paměťového prostoru. Potřebná velikost paměťového prostoru pro uložení všech vzorků tedy je 27*16 = 432 kB. Sériové Flash EEPROM paměti, vybavené sběrnicí I2C, nejsou v této kapacitě snadno dostupné a pro naše účely by byly pravděpodobně pomalé. Paralelní Flash EEPROM paměti jsou dostatečně rychlé i vyhovující z hlediska dostupné kapacity. Vyžadují však velký počet vodičů, kterými je realizováno připojení k mikrokontroléru.
       Jako paměť byla proto vybrána sériová Flash paměť AT45DB081D, disponující rozhraním SPI. Tato paměť poskytuje dostatečný prostor (8 Mb) pro uložení dat, dostatečnou rychlost (až 66 MHz) a není třeba takového počtu vodičů jako u paralelní paměti. Výhodou této paměti je nízká pořizovací cena a již zmiňovaná vysoká rychlost, jež zajišťuje bezproblémové přehrávání vzorků.
       Vzorky byly nejdříve pomocí mikrofonu nahrány do PC a jednoduše upraveny pomocí programu GoldWave, kde došlo k odstranění šumu na pozadí a zesílení signálu. Takto upravená data byla převedena do binárního souboru „*.bin“. Nahrání takto velkého souboru (cca 500 kB) by bylo pomocí mikrokontroléru složité a zdlouhavé. Proto byla data do paměti nahrána pomocí univerzálního programátoru BeeProg+, který zajišťuje snadné naprogramování paměti takřka během okamžiku.
       Na obr. 2 je pak zobrazeno schéma zapojení zařízení včetně hodnot a označení součástek použitých při realizaci. Funkčnost zařízení byla otestována na nepájivém poli viz obr. 3.

Schéma

Obr. 2: Schéma zapojení zařízení

Redukce

Obr. 3: Realizace zařízení na nepájivém poli.

Popis programu mikrokontroléru
Po úvodních deklaracích proměnných a inicializacích používaných periférií se program neustále pohybuje v nekonečné smyčce. V rámci této smyčky je kontrolován stisk tlačítek maticové klávesnice, což zajišťuje obsluha přerušení při přetečení čítače/časovače 0. Stisk klávesy je doprovázen výpisem dané číslice na alfanumerický displej.
       Pokud dojde k potvrzení zadaného čísla stiskem klávesy # nebo *, bezprostředně poté následuje jeho přehrání pomocí čítače/časovače 2, pracujícího v 8-bitovém Fast PWM módu, a reproduktoru.

Zdrojový kód programu(hlavní soubor):


/*
** ===================================================================
** Název souboru   : MMIA.c
** Mikrokontrolér  : ATmega8A,...
** Popis           : Hlavní soubor projektu "Generování zvuku pomocí MCU".
**
** Autor           : Lukáš Motyčka (motylu@seznam.cz)
**		   : Miroslav Procházka (xproch41@stud.feec.vutbr.cz)
** Poslední změna  : 24.04.12 22:59
** ===================================================================
*/


/*
** Hlavičkové soubory
*/

#include <avr/io.h>			// hlavičkový soubor pro mikrokontrolér
#include <avr/interrupt.h>		// hlavičkový soubor pro práci s přerušením
#include <avr/pgmspace.h>		// hlavičkový soubor pro práci s pamětí FLASH MCU
#ifndef F_CPU
#define F_CPU 8000000UL 		// frekvence krystalu (nutné pro delay.h)
#endif
#include <util\delay.h>  	 	// hlavičkový soubor pro zpoždění
#include "stdlib.h"			// hlavičkový soubor pro práci s LCD
#include "flash_eeprom.h"		// hlavičkový soubor pro práci s externí FLASH EEPROM
#include "lcd_h.h"			// hlavičkový soubor pro práci s LCD



/*
** Globální proměnné a konstanty v programové paměti
*/

prog_uint16_t sound_page[27] = {0, 36, 82, 126, 181, 223, 270, 314, 363, 408, 461, 517,
				565, 625, 689, 748, 817, 886, 944, 1005, 1056, 1110, 1169, 1225, 1287, 1356, 1427};
prog_uint16_t sound_byte[27] = {0, 225, 113, 17, 89, 9, 145, 49, 169, 64, 153, 217, 89,
				121, 121, 161, 121, 81, 129, 153, 1, 81, 121, 185, 201, 161, 105};
prog_uint16_t sound_length[27] = {9728, 12031, 11519, 14591, 11007, 12543, 11519, 13055, 11775, 14080, 14847, 12543, 15871, 16895, 
				  15615, 18175, 18175, 15359, 16127, 13311, 14335, 15615, 14847, 16383, 18175, 18687, 16895};

volatile uint8_t key = 0;		// proměnná - hodnota zmáčknuté klávesy
volatile uint8_t key_lock = 0;		// pro "zamknutí" klávesnice po stisku tlačítka



/*
** Obsluha přerušení
*/

ISR (TIMER0_OVF_vect)		// skenování klávesnice každých cca 33 ms
{
	if (key_lock == 0)  	// pokud je klávesnice "odemčená", proveď skenování
	{
		PORTD = 0b00001110;		// 1. sloupec
		if (bit_is_clear(PIND,4)) {key = 3; key_lock = 1;}
		if (bit_is_clear(PIND,5)) {key = 4; key_lock = 1;}
		if (bit_is_clear(PIND,6)) {key = 7; key_lock = 1;}
		if (bit_is_clear(PIND,7)) {key = 10; key_lock = 1;}

		PORTD = 0b00001101;		// 2. sloupec
		if (bit_is_clear(PIND,4)) {key = 1; key_lock = 1;}
		if (bit_is_clear(PIND,5)) {key = 5; key_lock = 1;}
		if (bit_is_clear(PIND,6)) {key = 8; key_lock = 1;}
		if (bit_is_clear(PIND,7)) {key = 0; key_lock = 1;}

		PORTD = 0b00001011;		// 3. sloupec
		if (bit_is_clear(PIND,4)) {key = 2; key_lock = 1;}
		if (bit_is_clear(PIND,5)) {key = 6; key_lock = 1;}
		if (bit_is_clear(PIND,6)) {key = 9; key_lock = 1;}
		if (bit_is_clear(PIND,7)) {key = 10; key_lock = 1;}
	}	
}


/*
** Hlavní program
*/

int main (void)
{
	DDRD = 0x0F;
	PORTD = 0x0F;	// nastavení pinů pro maticovou klávesnici
	
	TCCR0 |= 0b00000101;
	TIMSK |= _BV(TOIE0);	// č/č 0: normální mód, předdělička fclk/1024, přerušení 
				// při přetečení (skenování klávesnice každých cca 33 ms)
	flash_init();
	lcd_init();

	sei();	// globální povolení přerušení

	lcd_puts_mov(" Zadejte číslo§1-99 a potvrďte§   * nebo #.");
	lcd_puts("Zadáno: ");


	uint8_t number = 0, count = 0;	// pomocné proměnné

	while (1)	// nekonečná smyčka programu
	{
		while (!key_lock);	// čekání na "zamčení" klávesnice = stisk tlačítka

		cli();	// globální zákaz přerušení

		if ((key != 10) && (count < 2))		// pokud dosud nebyly zadány 2 číslice
		{					// a zároveň nebylo potvrzeno zadání

			if ((count != 0) || (key != 0))
			{						// pokud není jako první číslice
				lcd_putc(key+48);			// stisknuta 0
				count++;					
			}

			switch (count)
			{
				case 1: number += key;
						break;
				case 2: number = number*10 + key;
						break;
				default: break;		// zápis zadávaných čísel do pomocné proměnné
			}
		}
		else if ((count > 0) && (key == 10))	// pokud byla stisknuta alespoň jedna
		{					// platná číslice + potvrzení zadání

			if (number < 21)	// přehrání číslic 1 až 20
				flash_read(pgm_read_word(&sound_page[number-1]),pgm_read_word(&sound_byte[number-1]),pgm_read_word(&sound_length[number-1]));
			
			else	// přehrání číslic 21 až 99
			{
				flash_read(pgm_read_word(&sound_page[(number/10)+17]),pgm_read_word(&sound_byte[(number/10)+17]),pgm_read_word(&sound_length[(number/10)+17]));

				number -= (number/10)*10;

				if (number > 0)
					flash_read(pgm_read_word(&sound_page[number-1]),pgm_read_word(&sound_byte[number-1]),pgm_read_word(&sound_length[number-1]));
			}

			number = 0;
			count = 0;		// vynulování pomocných proměnných

			lcd_clrscr();
			lcd_puts("Zadáno: ");

		}

		_delay_ms(300);		// zpoždění kvůli zákmitům a prodlevě stisku tlačítka

		key_lock = 0;	// opětovné "odemčení" klávesnice

		sei();	// opětovné povolení přerušení
	}

	return 1;
}

Pro samotnou komunikaci s externí Flash EEPROM pamětí byla napsána vlastní knihovna funkcí pro čtení a zápis z/do paměťového prostoru. V našem případě využijeme pouze funkci pro čtení, jejímiž vstupními parametry jsou adresa stránky a adresa bytu v paměti a požadovaný počet vyčítaných hodnot, tj. délka hlasového vzorku.
       Jak již bylo řečeno výše, pro čtení dat se s výhodou využívá tzv. mód kontinuálního čtení přímo z paměťového prostoru. Pro čtení vzorků z paměti pomocí této metody je zaslán operačního kódu 0x03, následovaný adresou stránky a bytu. S každou další hranou hodinového signálu SPI sběrnice je do mikrokontroléru zaslán 1 bit, počínaje pozicí paměti udanou adresou stránky a bytu. Přijatá data slouží přímo jako referenční hodnota pro výstupní PWM signál. V ideálním případě (při zanedbání časových zpoždění při čtení a zápisech do registrů,…) je frekvence generování vzorků 15625 Hz viz obr. 1. Tato frekvence se mírně liší od zvolené vzorkovací frekvence. Pro nedokonalé lidské ucho je však tento nepatrný rozdíl zcela nezaznamenatelný.
       Po vyčtení požadovaného počtu vzorků je funkce ukončena, dojde k nulování zadaného čísla a zařízení je připraveno na další podnět z vnějšího okolí.

Zdrojový kód funkce pro čtení:

/*
** ===================================================================
**     Název funkce  : flash_read
**
**     Popis         : Přímé kontinuální čtení dat z externí paměti (vynecháním SRAM
**			bufferů) po rozhraní SPI do registru OCR1A č/č 1.
**
**     Parametry     : Adresa stránky (page_adr = 0-4095) a bytu (byte_adr = 0-263)
**			paměti pro čtení a chtěný počet hodnot.
**
**     Výst. hodnota : Žádná.
** ===================================================================
*/

void flash_read (uint16_t page_adr, uint16_t byte_adr, uint16_t no_data)
{
	uint16_t i;

	if (page_adr>4095)
		page_adr = 4095;

	if (byte_adr>263)
		byte_adr = 263;		// ošetření maximálně možných hodnot adres

	SS_LOW;

    spi_rw(0x03);	// 0x03 = opcode pro přímé kontinuální čtení dat

	spi_rw((uint8_t)(page_adr>>7));
	spi_rw(((uint8_t)(page_adr<<1))|((uint8_t)(byte_adr>>8)));
	spi_rw((uint8_t)(byte_adr));		// zaslání adresy stránky a bytu

	for (i=0;i< no_data;i++)
		OCR1A = spi_rw(0x00);

	SS_HIGH;
}

Závěr

Tento projekt se zabýval generováním zvuku pomocí mikrokontroléru. Projekt se podařilo úspěšně realizovat a otestovat jeho funkčnost.
       Při vyčítání hlasových vzorků není použit vyrovnávací buffer, ale využívá se módu kontinuálního čtení dat přímo z paměťového prostoru externí Flash EEPROM paměti. Výhodou tohoto řešení je úspora datové paměti mikrokontroléru a navíc při přehrávání vzorků nevznikají žádné pomlky, „lupání“ v reproduktoru ani jiné rušivé vlivy znehodnocující přehrávaný zvuk. Akustický výstup reproduktoru má dostatečnou hlasitost, nicméně při použití v hlučnějším prostředí je bezpodmínečně nutné generovaný signál zesílit a použít kvalitnější reproduktor.
       Realizované zařízení může sloužit pro hlasové potvrzení volby např. ve výtahu apod.


Soubory

Zdrojový kód projektu
Schéma zapojení


Literatura

[1] FEDRA, Z. Mikrokontroléry pro přístrojové aplikace. Přednášky. FEKT VUT Brno 2012.
[2] Atmel Corporation. Datasheet ATmega8. [Online] citováno 2012-4-26. Dostupné na:
[3] Atmel Corporation. Sériová flash paměť AT45DB081D-SU. [Online] citováno 2012-4-26. Dostupné na WWW: