Zaprojektujemy teraz prostą płytkę aplikacyjną
APP005, za pomocą której będziemy mogli pobawić się tymi nowymi
funkcjami licznika w ATTiny13. Płytka będzie zawierała
wzmacniacz na jednym tranzystorze BC547, mały głośniczek oraz
cztery przyciski. Podobny wzmacniacz projektowaliśmy dla
generatora przerywanego. Wykorzystamy go również tutaj.
Schemat jest następujący:
|
|
Spis elementów
Element |
Ilość |
Opis |
opornik 330Ω/0,125W |
1 |
–(
)– |
opornik 390Ω/0,125W |
1 |
–(
)– |
głośnik 8Ω/0,25W |
1 |
|
tranzystor BC547 |
1 |
|
głośnik 8Ω/0,25W |
1 |
|
tranzystor BC547 |
1 |
|
Złącze kątowe męskie 2 x 4 Goldpin |
1 |
|
przycisk Omron |
4 |
do "gry?" |
|
Przyciski wykorzystamy do "grania" na naszym instrumencie lub
do innych funkcji.
Uruchom program Eagle, utwórz nowy projekt APP005, a w nim
nowy schemat. Do schematu wstaw elementy:
frames A5L-LOC
(ramka)supply1 GND x 4
(masa)
pinhead PINHD-2X4 PINHD-2X4/90 x 1
(goldpiny kątowe męskie do połączenia z płytką bazową,
element obróć o 180º i odbij lustrzanie)
switch-omron 10-XX x 4
(przycisk W)
rlc R-EU R-EU_0204/7 x 2
(oporniki)
transistor *-NPN- BC547-NPN-TO92-CBE x 1
(tranzystor wzmacniacza)
wirepad 1,6/0,8 x 2 (pole
lutownicze do głośnika)
Elementy ułóż na schemacie i połącz przewodami narzędziem
Net:
Kształt i wielkość płytki PCB będą uzależnione od posiadanego
głośniczka. Mi udało się zakupić głośniczki w obudowach
plastikowych (po 4zł sztuka) z
wyprowadzonymi nóżkami, które bardzo łatwo przylutuję do płytki
i osadzę na kropelce kleju.
Dla innych głośniczków należy odpowiednio rozmieścić na
płytce pola lutownicze SPK1/SPK2 oraz pozostawić wolne miejsce
na sam głośniczek. Utwórz w edytorze PCB płytkę o poniższym
układzie ścieżek:
Pierwszy program generuje wybrane dźwięki gamy po naciśnięciu
przycisku na płytce. Ponieważ przycisków tych jest tylko 4, nasz
"instrument" potrafi odegrać jedynie cztery różne dźwięki. Do
wkurzania otoczenia to zupełnie wystarczy.
/*
* main.c
*
* Created on: 11 gru 2015
* Author: Jerzy
* Opis:
* Program wygrywa różne dźwięki gamy
* za pomocą przycisków na płytce APP005
*/
#include <avr/io.h>
uint8_t ocr[] = {238,189,158,125};
// C E G H'
int main(void)
{
uint8_t i,pbi;
DDRB = (1<<PB0); // Ustawiamy PB0 jako wyjście, reszta jako wejścia
PORTB = 0b11110; // Oporniki podciągające do PB1...PB4
while (1)
{
pbi = (~PINB & 0b11110) >> 1; // Stan wejść
if(pbi)
{
for(i = 0; i < 4; i++)
{
if(pbi & 1) break;
pbi >>= 1;
}
OCR0A = ocr[i];
TCCR0A = (1<<COM0A0)|(1<<WGM01); // Tryb CTC ze zmianą PB0 na przeciwną
TCCR0B = (1<<CS01); // Preskaler: podział przez 8
}
else
{
TCCR0B = 0; // Licznik stop
TCCR0A = 0; // Linia PB0 w stan normalny
PORTB = 0b11110; // PB0 w stan 0, aby odciążyć tranzystor na płytce
}
}
}
|
Drobnego wyjaśnienia wymaga końcówka programu. Po zakończeniu
generacji dźwięku linia PB0 jest ustawiana w stan niski 0. Linia
ta steruje bazą tranzystora. W stanie 1 przez tranzystor
przepływa dosyć duży prąd, który powoduje jego nagrzewanie. Aby
zatem oszczędzić tranzystor, linię PB0 ustawiamy w stan 0, co
spowoduje zablokowanie tranzystora. Przez zablokowany tranzystor
nie płynie prawie żaden prąd i tranzystor się nie będzie
niepotrzebnie nagrzewał. Następny program generuje 4 różne
efekty dźwiękowe wyzwalane naciśnięciem jednego z czterech
przycisków na płytce APP005. Przytrzymanie przycisku powoduje
powtarzanie wybranego efektu.
/*
* main.c
*
* Created on: 12 gru 2015
* Author: Jerzy
* Opis:
* Program wygrywa różne efekty dźwiękowe
* za pomocą przycisków na płytce APP005
*/
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
uint8_t i,j,pbi;
uint16_t x = 0; // Ziarno generatora pseudolosowego
DDRB = (1<<PB0); // Ustawiamy PB0 jako wyjście, reszta jako wejścia
PORTB = 0b11110; // Oporniki podciągające do PB1...PB4
while (1)
{
pbi = (~PINB & 0b11110) >> 1; // Stan wejść
if(pbi)
{
for(i = 0; i < 4; i++)
{
if(pbi & 1) break;
pbi >>= 1;
}
TCCR0A = (1<<COM0A0)|(1<<WGM01); // Tryb CTC ze zmianą PB0 na przeciwną
TCCR0B = (1<<CS01); // Preskaler: podział przez 8
switch(i)
{
case 0: for(j = 254; j > 64; j--)
{
OCR0A = j;
_delay_ms(3);
}
break;
case 1: for(j = 32; j < 128; j++)
{
OCR0A = j;
_delay_ms(3);
}
break;
case 2: for(j = 2; j < 128; j <<= 1)
{
OCR0A = j;
_delay_ms(50);
}
break;
case 3: for(j = 0; j < 10; j++)
{
x = (251 * x + 101) % 250; // Następna liczba pseudolosowa
OCR0A += x;
_delay_ms(20);
}
break;
}
}
else
{
TCCR0B = 0; // Licznik stop
TCCR0A = 0; // Linia PB0 e stan normalny
PORTB = 0b11110; // PB0 w stan 0, aby odciążyć tranzystor
}
}
}
|
W programie wykorzystano prosty generator pseudolosowy, który
daje liczby z przedziału od 0 do 249. Więcej na temat takich
generatorów dowiesz się z tego
materiału. Teraz pobawimy się odtwarzaniem nut. Każdy
dźwięk gamy charakteryzuje określona częstotliwość.
Częstotliwości te można uzyskać w ATTINY13 z pewnym
przybliżeniem. Nie zagłębiając się zbytnio w teorie muzyczne,
możemy powiedzieć, że gama składa się oktaw po 12 dźwięków,
które oznaczamy literowo:
C Cis D Dis E F Fis G Gis A Ais H Kolejny, trzynasty
dźwięk, jest dźwiękiem C następnej oktawy. W następnych oktawach
częstotliwości dźwięków są dwa razy wyższe. Np. jeśli C0 ma
częstotliwość 32,705 Hz, to C1 w kolejnej oktawie ma
częstotliwość
65,41 Hz = 2 x 32,705. Częstotliwości dźwięków
wewnątrz oktawy otrzymamy mnożąc częstotliwość poprzedzającego
dźwięku przez pierwiastek dwunastego stopnia z dwóch, czyli
przez 1,05946... Otrzymamy wtedy tzw. gamę temperowaną. Kolejne
dźwięki tej gamy są oddalone od siebie o równe półtony:
|
Oktawa 0 |
Oktawa 1 |
Oktawa 2 |
Oktawa 3 |
Oktawa 4 |
Oktawa 5 |
Oktawa 6 |
Oktawa 7 |
Oktawa 8 |
C |
32,70 Hz |
65,41 Hz |
130,81 Hz |
261,63 Hz |
523,25 Hz |
1046,50 Hz |
2093,00 Hz |
4186,01 Hz |
8372,02 Hz |
Cis |
34,65 Hz |
69,30 Hz |
138,59 Hz |
277,18 Hz |
554,37 Hz |
1108,73 Hz |
2217,46 Hz |
4434,92 Hz |
8869,84 Hz |
D |
36,71 Hz |
73,42 Hz |
146,83 Hz |
293,66 Hz |
587,33 Hz |
1174,66 Hz |
2349,32 Hz |
4698,64 Hz |
9397,27 Hz |
Dis |
38,89 Hz |
77,78 Hz |
155,56 Hz |
311,13 Hz |
622,25 Hz |
1244,51 Hz |
2489,02 Hz |
4978,03 Hz |
9956,06 Hz |
E |
41,20 Hz |
82,41 Hz |
164,81 Hz |
329,63 Hz |
659,26 Hz |
1318,51 Hz |
2637,02 Hz |
5274,04 Hz |
10548,08 Hz |
F |
43,65 Hz |
87,31 Hz |
174,61 Hz |
349,23 Hz |
698,46 Hz |
1396,91 Hz |
2793,83 Hz |
5587,65 Hz |
11175,30 Hz |
Fis |
46,25 Hz |
92,50 Hz |
185,00 Hz |
369,99 Hz |
739,99 Hz |
1479,98 Hz |
2959,95 Hz |
5919,91 Hz |
11839,82 Hz |
G |
49,00 Hz |
98,00 Hz |
196,00 Hz |
392,00 Hz |
783,99 Hz |
1567,98 Hz |
3135,96 Hz |
6271,93 Hz |
12543,85 Hz |
Gis |
51,91 Hz |
103,83 Hz |
207,65 Hz |
415,30 Hz |
830,61 Hz |
1661,22 Hz |
3322,44 Hz |
6644,88 Hz |
13289,75 Hz |
A |
55,00 Hz |
110,00 Hz |
220,00 Hz |
440,00 Hz |
880,00 Hz |
1760,00 Hz |
3520,00 Hz |
7040,00 Hz |
14080,00 Hz |
Ais |
58,27 Hz |
116,54 Hz |
233,08 Hz |
466,16 Hz |
932,33 Hz |
1864,66 Hz |
3729,31 Hz |
7458,62 Hz |
14917,24 Hz |
H |
61,74 Hz |
123,47 Hz |
246,94 Hz |
493,88 Hz |
987,77 Hz |
1975,53 Hz |
3951,07 Hz |
7902,13 Hz |
15804,27 Hz |
Do generacji dźwięku potrzebujemy dwóch ustawień: stanu
rejestrów OCR0A oraz TCCR0B. W rejestrze OCR0A określamy stan
licznika, przy którym nastąpi zmiana stanu wyjścia PB0 na
przeciwny. W trybie CTC licznik jest zerowany po osiągnięciu
tego stanu i zaczyna zliczać od początku. Dzięki temu na wyjściu
PB0 otrzymujemy sygnał prostokątny o częstotliwości zależnej od
zawartości rejestru porównawczego OCR0A. W rejestrze TCCR0B
ustalamy dzielnik preskalera, przez który jest dzielona
częstotliwość taktowania procesora - normalnie 1000000 Hz,
będąca źródłem impulsów, które licznik zlicza. Wartości te
wyliczymy ze wzoru:
|
OCR0A |
– |
rejestr porównawczy |
fCLK |
– |
częstotliwość zegara mikrokontrolera |
N |
– |
dzielnik preskalera
(1,8,64,256 lub 1024) |
f |
– |
częstotliwość dźwięku gammy |
Wartość N musi być tak dobrana, aby OCR0A mieściło się w
zakresie do 255 (1 bajt). Obliczenia po
zaokrągleniu do najbliższej wartości całkowitej dają poniższe
wyniki:
|
Oktawa 0 |
OCR0A |
N |
Oktawa 1 |
OCR0A |
N |
Oktawa 2 |
OCR0A |
N |
C |
32,70 Hz |
238 |
64 |
65,41 Hz |
118 |
64 |
130,81 Hz |
59 |
64 |
Cis |
34,65 Hz |
224 |
64 |
69,30 Hz |
112 |
64 |
138,59 Hz |
55 |
64 |
D |
36,71 Hz |
212 |
64 |
73,42 Hz |
105 |
64 |
146,83 Hz |
52 |
64 |
Dis |
38,89 Hz |
200 |
64 |
77,78 Hz |
99 |
64 |
155,56 Hz |
49 |
64 |
E |
41,20 Hz |
189 |
64 |
82,41 Hz |
94 |
64 |
164,81 Hz |
46 |
64 |
F |
43,65 Hz |
178 |
64 |
87,31 Hz |
88 |
64 |
174,61 Hz |
44 |
64 |
Fis |
46,25 Hz |
168 |
64 |
92,50 Hz |
83 |
64 |
185,00 Hz |
41 |
64 |
G |
49,00 Hz |
158 |
64 |
98,00 Hz |
79 |
64 |
196,00 Hz |
39 |
64 |
Gis |
51,91 Hz |
149 |
64 |
103,83 Hz |
74 |
64 |
207,65 Hz |
37 |
64 |
A |
55,00 Hz |
141 |
64 |
110,00 Hz |
70 |
64 |
220,00 Hz |
35 |
64 |
Ais |
58,27 Hz |
133 |
64 |
116,54 Hz |
66 |
64 |
233,08 Hz |
33 |
64 |
H |
61,74 Hz |
126 |
64 |
123,47 Hz |
62 |
64 |
246,94 Hz |
31 |
64 |
|
Oktawa 3 |
OCR0A |
N |
Oktawa 4 |
OCR0A |
N |
Oktawa 5 |
OCR0A |
N |
C |
261,63 Hz |
238 |
8 |
523,25 Hz |
118 |
8 |
1046,50 Hz |
59 |
8 |
Cis |
277,18 Hz |
224 |
8 |
554,37 Hz |
112 |
8 |
1108,73 Hz |
55 |
8 |
D |
293,66 Hz |
212 |
8 |
587,33 Hz |
105 |
8 |
1174,66 Hz |
52 |
8 |
Dis |
311,13 Hz |
200 |
8 |
622,25 Hz |
99 |
8 |
1244,51 Hz |
49 |
8 |
E |
329,63 Hz |
189 |
8 |
659,26 Hz |
94 |
8 |
1318,51 Hz |
46 |
8 |
F |
349,23 Hz |
178 |
8 |
698,46 Hz |
88 |
8 |
1396,91 Hz |
44 |
8 |
Fis |
369,99 Hz |
168 |
8 |
739,99 Hz |
83 |
8 |
1479,98 Hz |
41 |
8 |
G |
392,00 Hz |
158 |
8 |
783,99 Hz |
79 |
8 |
1567,98 Hz |
39 |
8 |
Gis |
415,30 Hz |
149 |
8 |
830,61 Hz |
74 |
8 |
1661,22 Hz |
37 |
8 |
A |
440,00 Hz |
141 |
8 |
880,00 Hz |
70 |
8 |
1760,00 Hz |
35 |
8 |
Ais |
466,16 Hz |
133 |
8 |
932,33 Hz |
66 |
8 |
1864,66 Hz |
33 |
8 |
H |
493,88 Hz |
126 |
8 |
987,77 Hz |
62 |
8 |
1975,53 Hz |
31 |
8 |
|
Oktawa 6 |
OCR0A |
N |
Oktawa 7 |
OCR0A |
N |
Oktawa 8 |
OCR0A |
N |
C |
2093,00 Hz |
238 |
1 |
4186,01 Hz |
118 |
1 |
8372,02 Hz |
59 |
1 |
Cis |
2217,46 Hz |
224 |
1 |
4434,92 Hz |
112 |
1 |
8869,84 Hz |
55 |
1 |
D |
2349,32 Hz |
212 |
1 |
4698,64 Hz |
105 |
1 |
9397,27 Hz |
52 |
1 |
Dis |
2489,02 Hz |
200 |
1 |
4978,03 Hz |
99 |
1 |
9956,06 Hz |
49 |
1 |
E |
2637,02 Hz |
189 |
1 |
5274,04 Hz |
94 |
1 |
10548,08 Hz |
46 |
1 |
F |
2793,83 Hz |
178 |
1 |
5587,65 Hz |
88 |
1 |
11175,30 Hz |
44 |
1 |
Fis |
2959,95 Hz |
168 |
1 |
5919,91 Hz |
83 |
1 |
11839,82 Hz |
41 |
1 |
G |
3135,96 Hz |
158 |
1 |
6271,93 Hz |
79 |
1 |
12543,85 Hz |
39 |
1 |
Gis |
3322,44 Hz |
149 |
1 |
6644,88 Hz |
74 |
1 |
13289,75 Hz |
37 |
1 |
A |
3520,00 Hz |
141 |
1 |
7040,00 Hz |
70 |
1 |
14080,00 Hz |
35 |
1 |
Ais |
3729,31 Hz |
133 |
1 |
7458,62 Hz |
66 |
1 |
14917,24 Hz |
33 |
1 |
H |
3951,07 Hz |
126 |
1 |
7902,13 Hz |
62 |
1 |
15804,27 Hz |
31 |
1 |
Największe niedokładności częstotliwości dźwięku występują
tam, gdzie rejestr OCR0A przyjmuje małą wartość. Są to gamy 2, 5
i 8. Staraj się unikać dźwięków z tych oktaw, jeśli będzie to
możliwe, ponieważ tony są nieco zafałszowane. Nic na to nie
poradzimy z licznikiem 8-bitowym. Przypomnijmy, podziałem
częstotliwości zegarowej sterujemy poprzez rejestr:
bit |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
nazwa |
FOC0A |
FOC0B |
– |
– |
WGM02 |
CS02 |
CS01 |
CS00 |
R/W |
W |
W |
R |
R |
R/W |
R/W |
R/W |
R/W |
stan |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Rejestr sterowania licznikiem TCCR0B
CS02 |
CS01 |
CS00 |
Opis |
0 |
0 |
0 |
Brak impulsów zegarowych, licznik jest
zatrzymany. |
0 |
0 |
1 |
Licznik zlicza impulsy o częstotliwości FCPU
(1000000 Hz). |
0 |
1 |
0 |
Licznik zlicza impulsy o częstotliwości FCPU/8
(125000 Hz) |
0 |
1 |
1 |
Licznik zlicza impulsy o częstotliwości FCPU/64
(15625 Hz) |
1 |
0 |
0 |
Licznik zlicza impulsy o częstotliwości FCPU/256
(3906,25 Hz) |
1 |
0 |
1 |
Licznik zlicza impulsy o częstotliwości
FCPU/1024
(976,5625 Hz) |
1 |
1 |
0 |
Licznik zlicza impulsy z linii PB2 pracującej
jako wejście T0. Zliczenie następuje przy opadającym
zboczu impulsu. |
1 |
1 |
1 |
Licznik zlicza impulsy z linii PB2 pracującej
jako wejście T0. Zliczenie następuje przy
narastającym zboczu impulsu. |
W naszym przypadku do TCCR0B będziemy wprowadzać wartości: 3
(0b011 – podział przez 64), 2
(0b010 – podział przez 8) i 1
(ob001 – podział przez 1, czyli pełna
częstotliwość taktowania). Wyłączenie zliczania następuje
po wprowadzeniu do TCCR0B wartości 0. Wypróbujmy tę wiedzę w
kolejnym programie, który będzie odgrywał dźwięki poszczególnych
gam.
/*
* main.c
*
* Created on: 12 gru 2015
* Author: Jerzy
* Opis:
* Program wygrywa kolejne dźwięki gam 0...5
*/
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
const uint8_t nt[36] PROGMEM = {238,224,212,200,189,178,168,158,149,141,133,126,
118,112,105, 99, 94, 88, 83, 79, 74, 70, 66, 62,
59, 55, 52, 49, 46, 44, 41, 39, 37, 35, 33, 31};
int main(void)
{
uint8_t i,s,n;
DDRB = (1<<PB0); // Ustawiamy PB0 jako wyjście, reszta jako wejścia
TCCR0A = (1<<COM0A0)|(1<<WGM01); // Tryb CTC ze zmianą PB0 na przeciwną
while (1)
{
s = 0; // stan OCR0A dla dźwięku
n = 3; // stan TCCR0B, preskaler, podział przez 64
for(i = 0; i < 108; i++)
{
OCR0A = pgm_read_byte(&nt[s++]);
TCCR0B = n;
if(s == 36) // po odegraniu wszystkich dźwięków z tablicy
{
s = 0; // rozpoczynamy następny zestaw
n--; // z innym podziałem przez preskaler (8, 1)
}
_delay_ms(100);
}
}
}
|
W programie zastosowaliśmy tablicę nt[], która jest
umieszczona w pamięci FLASH, nie w RAM. Pamięć FLASH posiada
większą pojemność (1024 B) w porównaniu
z pamięcią RAM (64 B). Dlatego dane,
które się nie zmieniają, umieszcza się właśnie w pamięci FLASH.
Aby to zrobić, należy dołączyć do programu plik nagłówkowy
avr/pgmspace.h. Tablica musi być zdefiniowana jako:
const typ nazwa[rozmiar] PROGMEM = {zawartość};
Dostęp do poszczególnych komórek tej tablicy
(tylko odczyt) uzyskuje się za pomocą
makra:
pgm_read_byte(&nazwa[indeks]);
Efektu działania tego programu nie daje się zbyt
długo słuchać, dlatego napiszemy kolejny program, w którym nasz
maluch będzie wygrywał melodyjkę. Program nie będzie korzystał z
przycisków, melodia będzie odgrywana w kółko
(możesz dopisać sobie kod, który czeka na naciśnięcie przycisku).
Kolejne nuty będą zapisane w tablicy, jednakże w celu
oszczędzenia miejsca zastosujemy specjalny sposób kodowania.
Zapis muzyki będzie swojego rodzaju "programem muzycznym"
złożonym z poleceń. Polecenia te będą kolejno odczytywane przez
procesor i interpretowane. Każde polecenie będzie miało długość
1 bajtu. Bajt polecenia będzie się składał z dwóch pól bitowych
(grupa sąsiadujących ze sobą bitów).
Cztery najmłodsze bity określą rodzaj polecenia
(daje to 16 rozkazów). 4 najstarsze
bity określą argument. Polecenia będą następujące:
Polecenie |
Mnemonik |
Opis |
0bnnnn0000 |
C |
odgrywa nutę C przez 0bnnnn jednostek czasu |
0bnnnn0001 |
Cis |
odgrywa nutę Cis przez 0bnnnn jednostek czasu |
0bnnnn0010 |
D |
odgrywa nutę D przez 0bnnnn jednostek czasu |
0bnnnn0011 |
Dis |
odgrywa nutę Dis przez 0bnnnn jednostek czasu |
0bnnnn0100 |
E |
odgrywa nutę E przez 0bnnnn jednostek czasu |
0bnnnn0101 |
F |
odgrywa nutę F przez 0bnnnn jednostek czasu |
0bnnnn0110 |
Fis |
odgrywa nutę Fis przez 0bnnnn jednostek czasu |
0bnnnn0111 |
G |
odgrywa nutę G przez 0bnnnn jednostek czasu |
0bnnnn1000 |
Gis |
odgrywa nutę Gis przez 0bnnnn jednostek czasu |
0bnnnn1001 |
A |
odgrywa nutę A przez 0bnnnn jednostek czasu |
0bnnnn1010 |
Ais |
odgrywa nutę Ais przez 0bnnnn jednostek czasu |
0bnnnn1011 |
H |
odgrywa nutę H przez 0bnnnn jednostek czasu |
0bnnnn1100 |
P |
pauza przez 0bnnnn jednostek czasu |
0bxxxx1101 |
END |
koniec, powrót na początek melodii |
0bnnnn1110 |
O0...O8 |
ustawia oktawę od 0 do 8 |
0bxxxx1111 |
brak |
nieużywane |
Przez jednostkę czasu rozumiemy czas odgrywania najkrótszej
nuty. Na przykład, jeśli pole nnnn ma stan 0000, to będzie
odegrana nuta szesnastka. Dla stanu 0010 będą to trzy szesnastki
(w muzyce taką nutę oznacza się z kropką).
Pole nnnn zawiera wartość binarną czasu minus 1.
Pierwszym poleceniem każdej melodii powinno być Gn, które
ustawi właściwą gamę oraz preskaler.
/*
* main.c
*
* Created on: 13 gru 2015
* Author: Jerzy
* Opis:
* Program wygrywa zaprogramowaną melodię
*/
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
// Stale określają wartości poleceń
// Dźwięki gam
#define C 0b0000
#define Cis 0b0001
#define D 0b0010
#define Dis 0b0011
#define E 0b0100
#define F 0b0101
#define Fis 0b0110
#define G 0b0111
#define Gis 0b1000
#define A 0b1001
#define Ais 0b1010
#define H 0b1011
#define P 0b1100
// Koniec melodii
#define END 0b1101
// Oktawy
#define O0 0b00001110
#define O1 0b00011110
#define O2 0b00101110
#define O3 0b00111110
#define O4 0b01001110
#define O5 0b01011110
#define O6 0b01101110
#define O7 0b01111110
#define O8 0b10001110
// argumenty
#define N1 0b00000000
#define N2 0b00010000
#define N3 0b00100000
#define N4 0b00110000
#define N5 0b01000000
#define N6 0b01010000
#define N7 0b01100000
#define N8 0b01110000
#define N9 0b10000000
#define N10 0b10010000
#define N11 0b10100000
#define N12 0b10110000
#define N13 0b11000000
#define N14 0b11010000
#define N15 0b11100000
#define N16 0b11110000
// Tablica przechowuje wartości OCCR0A dla poszczególnych nut w 3 oktawach
const uint8_t nt[] PROGMEM = {238,224,212,200,189,178,168,158,149,141,133,126,
118,112,105, 99, 94, 88, 83, 79, 74, 70, 66, 62,
59, 55, 52, 49, 46, 44, 41, 39, 37, 35, 33, 31,};
// Tablica przechowuje polecenia melodii
// Dla Elizy, L. Van Beethoven
const uint8_t m[] PROGMEM = {
O4,E|N1,Dis|N1, // Takt 1
E|N1,Dis|N1,E|N1,O3,H|N1,O4,D|N1,C|N1, // Takt 2
O3,A|N3,C|N1,E|N1,A|N1, // Takt 3
H|N3,E|N1,Gis|N1,H|N1, // Takt 4
O4,C|N3,O3,E|N1,O4,E|N1,Dis|N1, // Takt 5
E|N1,Dis|N1,E|N1,O3,H|N1,O4,D|N1,C|N1, // Takt 6
O3,A|N3,C|N1,E|N1,A|N1, // Takt 7
H|N3,E|N1,O4,C|N1,O3,H|N1, // Takt 8
A|N2,P|N2,O4, E|N1,Dis|N1, // Takt 9
E|N1,Dis|N1,E|N1,O3,H|N1,O4,D|N1,C|N1, // Takt 10
O3,A|N3,C|N1,E|N1,A|N1, // Takt 11
H|N3,E|N1,Gis|N1,H|N1, // Takt 12
O4,C|N3,O3,E|N1,O4,E|N1,Dis|N1, // Takt 13
E|N1,Dis|N1,E|N1,O3,H|N1,O4,D|N1,C|N1, // Takt 14
O3,A|N3,C|N1,E|N1,A|N1, // Takt 15
H|N3,E|N1,O4,C|N1,O3,H|N1, // Takt 16
A|N3,H|N1,O4,C|N1,D|N1, // Takt 17
E|N3,O3,G|N1,O4,F|N1,E|N1, // Takt 18
D|N3,O3,F|N1,O4,E|N1,D|N1, // Takt 19
C|N3,O3,E|N1,O4,D|N1,C|N1, // Takt 20
O3,H|N3,E|N1,O4,E|N1,O3,E|N1, // Takt 21
O4,E|N3,O3,E|N1,O4,E|N1,O3,E|N1, // Takt 22
O4,E|N1,Dis|N1,E|N1,Dis|N1,E|N1,Dis|N1, // Takt 23
E|N1,Dis|N1,E|N1,O3,H|N1,O4,D|N1,C|N1, // Takt 24
O3,A|N3,C|N1,E|N1,A|N1, // Takt 25
H|N3,E|N1,Gis|N1,H|N1, // Takt 26
O4,C|N3,O3,E|N1,O4,E|N1,Dis|N1, // Takt 27
E|N1,Dis|N1,E|N1,O3,H|N1,O4,D|N1,C|N1, // Takt 28
O3,A|N3,C|N1,E|N1,A|N1, // Takt 29
H|N3,E|N1,O4,C|N1,O3,H|N1, // Takt 30
A|N3,H|N1,O4,C|N1,D|N1, // Takt 31
E|N3,O3,G|N1,O4,F|N1,E|N1, // Takt 32
D|N3,O3,F|N1,O4,E|N1,D|N1, // Takt 33
C|N3,O3,E|N1,O4,D|N1,C|N1, // Takt 34
O3,H|N3,E|N1,O4,E|N1,O3,E|N1, // Takt 35
O4,E|N3,O3,E|N1,O4,E|N1,O3,E|N1, // Takt 36
O4,E|N1,Dis|N1,E|N1,Dis|N1,E|N1,Dis|N1, // Takt 37
E|N1,Dis|N1,E|N1,O3,H|N1,O4,D|N1,C|N1, // Takt 38
O3,A|N3,C|N1,E|N1,A|N1, // Takt 39
H|N3,E|N1,Gis|N1,H|N1, // Takt 40
O4,C|N3,O3,E|N1,O4,E|N1,Dis|N1, // Takt 41
E|N1,Dis|N1,E|N1,O3,H|N1,O4,D|N1,C|N1, // Takt 42
O3,A|N3,C|N1,E|N1,A|N1, // Takt 43
H|N3,E|N1,O4,C|N1,O3,H|N1, // Takt 44
A|N10, // Takt 45/46 - koniec
P|N14,P|N12|P|N12,END
};
// Procedura wprowadza zadane opóźnienie
void wait(uint8_t x)
{
uint8_t i;
for(i = 0; i < x; i++) _delay_ms(180); // określa szybkość odtwarzania melodii
}
int main(void)
{
uint8_t s,n,t,b;
uint16_t i; // indeks m[] musi być 16-bitowy!!!
DDRB = (1<<DDB0); // Ustawiamy PB0 jako wyjście, reszta jako wejścia
while (1)
{
i = 0; // Ustawiamy indeks pierwszego polecenia
do
{
b = pgm_read_byte(&m[i++]); // odczytujemy polecenie
t = b >> 4; // w t argument
b &= 0b1111; // w b kod polecenia
if(b == O0) // gama
{
s = 12 * (t % 3); // indeks bazowy w nt[]
n = 3 - t / 3; // wartość dla OCCR0B
}
else if(b == P) // pauza
{
TCCR0B = 0; // zatrzymujemy licznik
TCCR0A = 0; // przywracamy tryb normalny
PORTB = 0b11110; // linia PB0 w stan niski
wait(t+1); // czekamy odpowiedni czas
}
else if(b != END) // nuta
{
TCCR0A = (1<<COM0A0)|(1<<WGM01); // Tryb CTC ze zmianą PB0 na przeciwną
OCR0A = pgm_read_byte(&nt[s + b]);
TCCR0B = n; // włączamy generację dźwięku
wait(t+1); // czekamy odpowiedni czas
}
} while(b != END);
}
}
|
|