generování různé frekvence pro účely generování stupnice

Dobrý den, chtěl bych se zeptat, jak mám nastavit frekvenci na výstupu? Myslím si, že to půjde přes PWM, ale s tím nemám zkušenost. Proto prosím o radu. :slight_smile: Děkuji

Ahoj.
Princip generovani zvuku je jednoduchy.
Komorni A ma frekvenci 440 Hz.
Je tedy nutno menit vystup 440x za sekundu.
Otevreme si tedy tento clanek
avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106&sid=20d788f89772f7a108c5d6a8f8655654
a ridime se odstavcem pure hardware CTC
zkopirujeme zdrojak do AVR Studia

[code]#include <avr/io.h>

int main (void)
{
DDRD |= (1 << 5); // Set LED as output
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode
OCR1A = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64
TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64
for (;:wink:
{

}
}[/code]
Nyni je nutno vypocitat OCR1A aby to generovalo 440HZ (pokud pouzijeme logiku tak by ta hodnota mela byt 440x mensi)
Z toho sameho clanku zjistime ze

Target Timer Count = (Input Frequency / Prescale) / Target Frequency - 1

Vyjde nam tedy (1000000/64/440)-1=34.5.
Problem je ze 34.5 neni cele cislo, bylo by lepsi kdyby jsme cele cislo nasli. Nicmene s frekvenci 1MHz toho moc neudelame. Nejpresnejsi frekvenci ziskame kdyz pouzijeme prescaler 8
(1000000/8/440)-1 = 283.1
Nas program tedy bude vypadat takto

[code]#include <avr/io.h>

int main (void)
{
DDRD |= (1 << 5); // Set LED as output
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode
OCR1A = 283; // Set CTC compare value to 440Hz at 1MHz AVR clock, with a prescaler of 8
TCCR1B |= (1 << CS11); // Prescaler 8

for (;:wink:
{

}
}[/code]

Doufam, ze jsem nikde neudelal chybu.

možná jsem špatně napsal, co chci udělat. Mám 8 tlačítek a chtěl bych, aby každé generovalo nějaký tón. Našel jsem si frekvence stupnice (C-1000Hz, D-1125Hz, E-1250 Hz…)

a nevím, co mám napsat do nekonečné smyčky. Z jakého výstupu mi bude vycházet generovaná frekvence… Omlouvám se za své stupidní dotazy, ale jsem začátečník…

Ahoj.
Precti si ten tutorial, tam je vse popsane.
Hodilo by se tedy poslat schema jak mas zapojene ty tlacitka.
Ja si v programu definuji tyto makra

#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) // nastaví bit #define CLRBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) // nuluje bit #define NEGBIT(ADDRESS,BIT) (ADDRESS ^= (1<<BIT)) // neguje bit #define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT)) // testuje bit
Do nekonecne smycky si das pouze obsluhu tlacitek.
Neco ve stylu

Je nutno take vypocitat cislo OCR1A pro jednotlive frekvence.
V tomto pripade bude pro C OCR1A mit hodnotu

if ((CHECKBIT(PORTA,0)==1)) OCR1A=125; // C = 1000Hz 1000000/8/1000 = 125
if ((CHECKBIT(PORTA,1)==1)) OCR1A=111; // D = 1000Hz 1000000/8/1125 = 111

Bude to znit falesne, aby to nebylo falesne je to treba udelat pres interrupt citac, ktery bude pocitat pocet pruchodu a nastavovat manualne prislusny pin, ale nad tim se mi nechce ted premyslet…

Pro inspiraci jeden příklad přehrávání melodie: duzozua.yoyo.pl/raf/doku.php … und_player

Mno ja jsem mu schvalne nenapsal cely program, protoze studium datasheetu a vlastni pokusy jsou to nejcenejsi, co clovek muze ziskat. Tvuj program je chytre delany, ale pochybuji ze se z nej neco nauci.
Prosazuji TVOJI metodu (a moji taky) nejprve blikani led a potom dalsi zabava :slight_smile:

Máš pravdu, zredukoval jsem to. Pokud se člověk ptá a nemá vůbec představu jak by to mělo fungovat, tak na to jde ze špatné strany a začíná od konce.

Jenom malý detail - 440x za sekundu se musíš dostat na začátek periody - tedy 0->1->0 (a jsi znova na začátku). Takže pro 440Hz musíš měnit výstup 880x za sekundu …

Další fígl je řízení hlasitosti. Ano - i při log. stavech 1/0 lze hlasitost zvuku měnit - a to poměrem 1/0 (něco na způsob PWM). Je tam jenom jediný rozdíl. Při PWM je maximální výkon při poměru 100%*log.1 a 0%*log.0 (za předpokladu, že log. 1 = zapnuto), u řízení hlasitosti platí, že nějvyšší hlasitost je při poměru 1/0 = 50%.

V obrázku je příklad maximální a snížené hlasitosti komorního A.
Hlasitost_digitalniho_audio_signalu.GIF

TOHLE JSEM VYMYSLEL JE TO ŠPATNĚ, ALE NEVÍM JAK DÁL…
#include <avr/io.h>
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT)) // testuje bit
int main (void)
{
DDRD |= (1 << 5); // Set LED as output
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode
OCR1A = 283; // Set CTC compare value to 440Hz at 1MHz AVR clock, with a prescaler of 8
TCCR1B |= (1 << CS11); // Prescaler 8

while (1)
{
if ((CHECKBIT(PORTA,0)==1))
{OCR1A=125;
PORTD= 0b00100000;
}

if ((CHECKBIT(PORTA,1)==1))
{OCR1A=111;
PORTD= 0b00100000;
}
}
}

prosím, potřebuji opravdu poradit. Zajímalo by mě, jak to bude. PS. blikání diod mám za sebou, ovládání displeje také, ale s tímto si nevím rady…

takže, jak je tam ta podmínka: if ((CHECKBIT(PORTA,0)==1))
OCR1A=125;
tak tam dám přerušení od čítače1: sei()
ISR(TIMER1_OVF_vect)
{
PORTB = ~PORTB;
}

opravdu prosím o radu, protože to PWM moc nechápu, jak to z toho dostat…

PWM potřebuješ jen tehdy, jestli potřebuješ generovat např. sinusovku nebo regulovat hlasitost. Ale jestli jsem správně pochopil, Tobě jde jen o jednoduché generování tónu, obdélník se střídou 1:1? Pak PWM nepotřebuješ, to by bylo už mnohem složitější.

Jedna z chyb co tam máš - funkce CHECKBIT nevrací hodnotu 0 nebo 1, ale 0 nebo (1<<BIT). Takže ji netestuj na ==1, ale na !=0.

Dále - v módu čítače s komparátorem A budeš obsluhovat spíš přerušení TIMER1_COMPA_vect než TIMER1_OVF_vect.

ano jde mi o generování stupnice, ale jak mám nastavit tu frekvenci? jenom pomocí přerušení od čítače?

Možnosti, možná by bylo dobré je “tréninkově” postupně vyzkoušet všechny, každá objasní něco pro použití další metody:

  1. Nepoužívat ke generování tónu přerušení, ale čekací smyčku _delay_us. Prodleva se spočítá jako us=500000/frekvence(Hz). Pro tón 440 Hz by to bylo 1136 us. Funkce má rozsah jen do pár desítek us, proto se to musí složit z více čekání - např. jednou _delay_ms(1) a 13x (přes for()) _delay_us(10). Je to metoda jednoduchá, ale ne moc přesná.

  2. Použít přerušení časovače s přetečením (vektor TIMER1_OVF_vect) - lze použít jen pár frekvencí, které lze dosáhnout dělením časovače (prescaler).

  3. Přerušení časovače s přetečením, jako v předešlém případě, ale navíc s modifikací čítače. Nepoužije se plný rozsah čítače, např. 256, ale hned na začátku přerušení se nastaví nová výchozí hodnota čítače a tím se zkrátí cyklus a zvýší se frekvence. Např. nastavením na 128 se zvýší frekvence na dvojnásobek (záleží ještě na tom, zda čítač čítá nahoru nebo dolů). Lze tak dosáhnout celkem libovolné frekvence, podmínkou je nastavení nové hodnoty čítače hned na začátku přerušení, dříve než by se čítač posunul sám.

  4. Přerušení s komparací, čítač běží v módu CTC s komparátorem, po dosažení hodnoty komparátoru se čítač vynuluje (a vyvolá přerušení). Na programování je to nejnáročnější, ale výsledek je nejlepší (registry není třeba modifikovat programově při každém přerušení). Vektor přerušení je TIMER1_COMPA_vect.

nevsimol som si, ze by to tu niekto sponemul, ale myslim si ze na generovanie nejakeho periodickeho signalu je najjednoduchsie pouzit CTC s togglovanim vystupu. Dokonca aj v datasheete je napisane, ze je to najpresnejsie.

  • Treba nastavit pin, ktory bude generovat signal. Nastavuje sa bitmi COM* (treba pozret v datasheete, hladat Toggle on Compare Match), a na tento ucel byva vyhradeny pin/piny (v datasheete oznaceny ako OC*).
  • Dalej treba nastavit WGM bity. V tabulke treba potom hladat , ktorymi bitmi sa nastavuje CTC.
  • No a potom uz len predelicku a vrchol pocitadla znaceny ako OCR*.
    Ak sa to takto nastavi, timer bude pocitat do hodnoty nastavenej v OCR. Ked dosiahne tuto hodnotu, pocitadlo sa vynuluje, a zaroven sa neguje vystupny pin. Treba ale pamatat ze pin sa ma negovat kazdu polperiodu. Toto nastavenie funguje len pre urcite piny.

Ked si vezmeme za priklad napr mega8a, potom v datasheete na strane 87 je popis tohto nastavenia (Clear Timer on Compare Match (CTC) Mode), hned na zaciatku str.88 je priebeh, strana 96 - table 36 je nastavenie COM (druhy riadok je CTC), na strane 97 - table 39 je popis WGM (mode 4, teda piaty riadok je opisanz CTC mod). No a predelicku uz podla f hodin.

Snad som nikoho nedomotal a nenapisal nejaku blbos, pripadne nieco, co tu uz spomenute bolo…

Nebylo spomenuté a je to jistě nejvhodnější metoda, jen nevím zda to tazatel dá dohromady bez funkčního příkladu.

keby napise procesor a frekvenciu tak mu napisem aj konkretny prikladik… bez tychto udajov sa mi do toho nechce :smiley:

EDIT: ok tak som sa dokopal ku kodu, a ked to tazatel bude lustit, mozno sa i daco nauci.

[code]//mega8a, f_cpu = 8MHz
#include <avr/io.h>

int main(void)
{
//s tymto pinom budem kmitat s periodou 0,5Hz,
DDRB = 0x02;
//nastavenie CTC modu na pin PB1 znaceny ako OC1A
TCCR1A |= (1<<COM1A0);
//nastavenie CTC s porovnavanim OCR1A
TCCR1B |= (1<<WGM12);
//nastavenie predelicky f/1024
TCCR1B |= (1<<CS10) | (1<<CS12);
//nastavenie vrcholu, negovat ma kazdu sekundu, lebo 0,5Hz
//preto 8000 000/ 1024 = 7812.5, zaokruhlene na 7812
OCR1A = 7812;

while(1)
{
}

}[/code]

netreba sa starat o ziadne prerusenie a je to superjednoduche :slight_smile:

EDIT: no a po stlaceni tlacitka staci pre zmenu frekvencie len zmenit hodnotu OCR1A a nastavenie predelicky. Pre vypnutie generatora treba nastavit COM1A0 na nulu, teda odpojit tento vystup, alebo zastavit timer.

to bys mi moc pomohl, procesor mám ATmega32 s externím krystalem 16MHz

díky moc jdu to vyzkoušet :wink: