1 | /* ********************************************************************
|
2 | * Melodiegenerator fuer ATtiny85
|
3 | * ==============================
|
4 | * Uwe Berger; 2009
|
5 | *
|
6 | * Software:
|
7 | * ---------
|
8 | * Inputformat der Melodien ist Nokias RTTTL-Format.
|
9 | * Der Rest ist aus dem Quelltext ersichtlich!
|
10 | *
|
11 | * Idee: http://hackvalue.de/hv_atmel_rtttl
|
12 | *
|
13 | * Wobei im vorliegenden Programm aber der RTTTL-Parser gleich mit drin
|
14 | * ist und somit ohne externen Konverter auskommt.
|
15 | *
|
16 | * Vorteil: man kann RTTTL-Klingeltoene ohne Anpassung an der
|
17 | * entsprechenden Stelle im Programm eingefuegt werden
|
18 | *
|
19 | * Nachteil: es wird etwas mehr Speicherplatz fuer das Programm verbraucht
|
20 | *
|
21 | *
|
22 | * Hardware:
|
23 | * ---------
|
24 | * Ein ATtiny85 in Standardbeschaltung, also ohne externen Quarz, reicht
|
25 | * aus. Folgende Peripherie:
|
26 | * - an PB1 ist ein Lautsprecher angeschlossen. Ich habe dazu eine dieser
|
27 | * laermenden Geburtstagskarten gepluendert. Dieser Lautsprecher wurde
|
28 | * direkt angeschlossen, eventuell sollten aber bei anderen Modellen
|
29 | * Strombegrenzungswiderstaende vorgesehen werden.
|
30 | * - an PB2 ist ein Taster angeschlossen, der den Tiny aus dem Schlaf
|
31 | * reisst und ihn die naechste Melodie dudeln laesst. Der geschlossene
|
32 | * Taster legt GND an den Pin, es sollte ein PullUp-Widerstand von
|
33 | * 10kOhm vorgesehen werden.
|
34 | *
|
35 | * --------------------------------------------------------------------
|
36 | *
|
37 | * RTTTL-Formatbeschreibung z.B.:
|
38 | * http://de.wikipedia.org/wiki/Ringing_Tones_Text_Transfer_Language
|
39 | *
|
40 | * freie Nokia-Klingeltoene (RTTTL-Format):
|
41 | * http://www.2thumbswap.com/members/tones/nokia/tones_nokia_main.html
|
42 | *
|
43 | *********************************************************************/
|
44 | #ifndef F_CPU
|
45 | #define F_CPU 1000000UL
|
46 | #endif
|
47 |
|
48 | #include <avr/io.h>
|
49 | #include <util/delay.h>
|
50 | #include <avr/interrupt.h>
|
51 | #include <avr/pgmspace.h>
|
52 | #include <avr/sleep.h>
|
53 |
|
54 |
|
55 | // Lautsprecherport
|
56 | #define SPEAKER_DDR DDRB
|
57 | #define SPEAKER_PORT PORTB
|
58 | #define SPEAKER PB1
|
59 |
|
60 | // Taster
|
61 | #define TASTER_DDR DDRB
|
62 | #define TASTER_PORT PORTB
|
63 | #define TASTER_PIN PB2
|
64 | #define TASTER_INT PCINT2
|
65 |
|
66 | // Vorteiler fuer Timer0 (in ctc_on() TCCR0B entsprechend mit anpassen!)
|
67 | #define PRESCALE 8
|
68 |
|
69 | // Tonfrequenzen
|
70 | const uint16_t freq[] PROGMEM = {
|
71 | // C, C#, D, D#, E, F, F#, G, G#, A, A#, B
|
72 | 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, //Oktave 5
|
73 | 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, //Oktave 6
|
74 | 1046, 1109, 1175, 1244, 1328, 1397, 1480, 1568, 1661, 1760, 1865, 1975, //Oktave 7
|
75 | 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951 //Oktave 8
|
76 | };
|
77 |
|
78 |
|
79 | const char m1[] PROGMEM = "99luv:d=4,o=5,b=180:d6,8e6,c6,e.6,d6,8c6,a.,8p,8a,8f6,f6,f6,f.6,8f6,e6,d6,c.6,d6,8e6,c6,e.6,d6,8c6,a.,8p,8a,f6,f6,f6,f6,8f6,e6,d.6,8c6,8d6,e6,c6,e.6,d6,8c6,a.,8p,8a,c6,8a,c6,f6,f6,e6,d.6,8c6,d6,e6,c6,e.6,d6,8c6,a.,8p,8f6,f6,8f6,f6,f6,f6,e6,2d6";
|
80 | const char m2[] PROGMEM = "letitbe:d=4,o=5,b=112:16g.,8g,g,8g,a,8e,8g,g.,8c6,d.6,e6,8e6,e6,8d6,8d6,8c6,2c6,e6,8e6,f6,8e6,8e6,d.6,8e6,8d6,2c6";
|
81 | const char m3[] PROGMEM = "Peter Schilling - Major Tom:d=4,o=6,b=200:c.,8p,c,c,c.,8p,c.,8p,2a5,2c,c,8a#5,8a5,g.5,8p,2a#5,2d,2d,c.,8p,2a5,2c,c,8a#5,8a5,g.5,8p,2a#5,2d,2d,e.,8p,1f,2e,d,c,2d.,c,2d,e.,8p,1f,2e,d,c,2d.,c,2d,2";
|
82 | const char m4[] PROGMEM = "yday:d=4,o=5,b=125:g,8f,f,p,8a,8b,8c#6,8d6,e6,8p,8f6,e6,8d6,d6,p,8d6,8d6,8c6,8a#,8a,8g,8a#,8a,8a,a,8p,g,f,8a,8g,g,8p,8d,f,8p,8a,a";
|
83 |
|
84 | const char *ma[] PROGMEM = {m4, m1, m2, m3};
|
85 |
|
86 |
|
87 | //*********************************************************************
|
88 | ISR(PCINT0_vect){
|
89 | // vorhanden, um MCU definiert aus dem Sleep-Mode zu holen
|
90 | }
|
91 |
|
92 |
|
93 | //*********************************************************************
|
94 | static void ctc_on(void) {
|
95 |
|
96 | // CTC mode mit toggeln von OC0A/OC0B (Tiny85)
|
97 | TCCR0A |= (1<<COM0A0 | 1<<COM0B0 | 1<<WGM01);
|
98 |
|
99 | // Prescaler 8
|
100 | TCCR0B = 1<<CS01;
|
101 |
|
102 | }
|
103 |
|
104 | //*********************************************************************
|
105 | static void ctc_off(void) {
|
106 | TCCR0A = 0;
|
107 | TCCR0B = 0;
|
108 | }
|
109 |
|
110 |
|
111 | //*********************************************************************
|
112 | int f(int n, int o) {
|
113 | // lustiger Nebeneffekt des Zugriffs auf den Flash-RAM: die Zeit
|
114 | // die dazu notwendig ist, fuegt eine kleine Pause zwischen den
|
115 | // Toenen ein, die auch ganz sinnvoll ist...
|
116 | //
|
117 | // lt. Datenblatt fuer CTC-Mode ist
|
118 | // Focnx = Fclk / (2 * Prescale * (1 + OCRnx)
|
119 | // umgestellt nach OCRnx:
|
120 | // OCRnx = (Fclk / Focnx / 2 / Prescale) - 1
|
121 | return (F_CPU/pgm_read_word(&freq[n+(o-5)*12])/2/PRESCALE)-1;
|
122 | }
|
123 |
|
124 |
|
125 | //**********************************************************************
|
126 | char rf(const char *p) {
|
127 | return pgm_read_byte(p);
|
128 | }
|
129 |
|
130 | //**********************************************************************
|
131 | void my_delay_ms(uint16_t ms) {
|
132 | uint16_t i;
|
133 | // es ist mir nicht gelungen, die Pause via Timer zu
|
134 | // realsieren: es gab merkwuerdige Seiteneffekte, denen ich
|
135 | // nicht auf die Spur gekommen bin...
|
136 | //
|
137 | // _delay_ms generiert nur dann wenig Maschinencode, wenn
|
138 | // es mit konstanten Werten gefuettert wird, also deshalb dieser
|
139 | // Konstrukt....
|
140 | for (i=0; i<ms; i++) _delay_ms(1);
|
141 | }
|
142 |
|
143 | //**********************************************************************
|
144 | void play(uint8_t i) {
|
145 | const char *p;
|
146 |
|
147 | uint16_t def_noten_laenge = 4;
|
148 | uint8_t def_oktave = 6;
|
149 | uint16_t def_bpm = 63;
|
150 |
|
151 | // Anfangspointer setzen
|
152 | p = (const char*)(pgm_read_word(&(ma[i])));
|
153 |
|
154 | // Titel ueberspringen
|
155 | while (rf(p) && rf(p) != ':') p++;
|
156 | if (!rf(p)) return;
|
157 | p++;
|
158 |
|
159 | // als naechstes kommen die Song-Defaults
|
160 | while (rf(p)) {
|
161 | uint8_t param;
|
162 | uint16_t value;
|
163 |
|
164 | // fuehrende Leerzeichen ueberspringen
|
165 | while (rf(p) == ' ') p++;
|
166 | if (!rf(p)) return;
|
167 |
|
168 | // hier ist das Ende des Default-Bereiches erreicht
|
169 | if (rf(p) == ':') break;
|
170 |
|
171 | param = rf(p);
|
172 | p++;
|
173 | if (rf(p) != '=') return;
|
174 |
|
175 | p++;
|
176 | value = 0;
|
177 | while (rf(p) >= '0' && rf(p) <= '9') {
|
178 | value = value * 10 + (rf(p) - '0');
|
179 | p++;
|
180 | }
|
181 | switch (param) {
|
182 | //case 'd': def_noten_laenge = 32 / value; break;
|
183 | case 'd': def_noten_laenge = value; break;
|
184 | case 'o': def_oktave = value; break;
|
185 | case 'b': def_bpm = value; break;
|
186 | }
|
187 | // folgende Leerzeichen ueberspringen
|
188 | while (rf(p) == ' ') p++;
|
189 | if (rf(p) == ',') p++;
|
190 | }
|
191 | p++;
|
192 |
|
193 | // Noten selbst analysieren
|
194 | while (rf(p)) {
|
195 | uint8_t note = 63;
|
196 | uint16_t oktave = def_oktave;
|
197 | uint16_t noten_laenge = def_noten_laenge;
|
198 | uint16_t dauer_ms;
|
199 | uint8_t punkt_note = 0;
|
200 |
|
201 | // Leerzeichen ueberspringen
|
202 | while (rf(p) == ' ') p++;
|
203 | if (!rf(p)) return;
|
204 |
|
205 | // Notenlaenge bestimmen
|
206 | if (rf(p) >= '0' && rf(p) <= '9') {
|
207 | uint16_t value = 0;
|
208 | while (rf(p) >= '0' && rf(p) <= '9') {
|
209 | value = value * 10 + (rf(p) - '0');
|
210 | p++;
|
211 | }
|
212 | noten_laenge = value;
|
213 | }
|
214 |
|
215 | // Note bestimmen
|
216 | switch (rf(p)) {
|
217 | case 0: return;
|
218 | case 'C': case 'c': note = 0; break;
|
219 | case 'D': case 'd': note = 2; break;
|
220 | case 'E': case 'e': note = 4; break;
|
221 | case 'F': case 'f': note = 5; break;
|
222 | case 'G': case 'g': note = 7; break;
|
223 | case 'A': case 'a': note = 9; break;
|
224 | case 'H': case 'h': note = 11; break;
|
225 | case 'B': case 'b': note = 11; break;
|
226 | case 'P': case 'p': note = 63; break;
|
227 | }
|
228 | p++;
|
229 |
|
230 | // Halbnote hoeher
|
231 | if (rf(p) == '#') {
|
232 | note++;
|
233 | p++;
|
234 | }
|
235 |
|
236 | // Halbnote tiefer(?)
|
237 | if (rf(p) == 'b') {
|
238 | note--;
|
239 | p++;
|
240 | }
|
241 |
|
242 | // Punktnote=
|
243 | if (rf(p) == '.') {
|
244 | //noten_laenge += noten_laenge / 2;
|
245 | punkt_note = 1;
|
246 | p++;
|
247 | }
|
248 |
|
249 | // Oktave ermitteln
|
250 | if (rf(p) >= '0' && rf(p) <= '9') {
|
251 | oktave = (rf(p) - '0');
|
252 | p++;
|
253 | }
|
254 |
|
255 | // hier koennte auch ein Punkt sein...
|
256 | if (rf(p) == '.') {
|
257 | //noten_laenge += noten_laenge / 2;
|
258 | punkt_note = 1;
|
259 | p++;
|
260 | }
|
261 |
|
262 | // nachfolgende Leerzeichen ueberspringen
|
263 | while (rf(p) == ' ') p++;
|
264 |
|
265 | // Komma ist Notentrenner
|
266 | if (rf(p) == ',') p++;
|
267 |
|
268 | // Tondauer berechnen (60s = 60000ms)
|
269 | dauer_ms = (((60000 / def_bpm) * def_noten_laenge) / noten_laenge);
|
270 | // war eine Punktnote dabei?
|
271 | if (punkt_note) dauer_ms += dauer_ms/2;
|
272 |
|
273 | if (note != 63) {
|
274 | // Tonfrequenz einstellen
|
275 | OCR0A = f(note, oktave);
|
276 | OCR0B = f(note, oktave);
|
277 | ctc_on(); // Ton einschalten
|
278 | my_delay_ms(dauer_ms); // Ton fuer berechnete Dauer halten
|
279 | ctc_off(); // Ton ausschalten
|
280 | } else {
|
281 | // Pause fuer berechnete Dauer
|
282 | ctc_off(); // Ton ausschalten
|
283 | my_delay_ms(dauer_ms);
|
284 | }
|
285 | }
|
286 | }
|
287 |
|
288 |
|
289 | int main(void) {
|
290 |
|
291 | uint8_t i = 0;
|
292 |
|
293 | // Taster
|
294 | TASTER_PORT &= ~(1 << TASTER_PIN); // Taster-Pin als Eingang
|
295 |
|
296 | // PCINT0 initialisieren
|
297 | PCMSK |= (1 << TASTER_INT); // Taster-Pin maskieren
|
298 | GIMSK |= (1 << PCIE); // Port Change Interrupt frei
|
299 |
|
300 | sei(); // Interrupts einschalten
|
301 |
|
302 | // Lautsprecher als Ausgang
|
303 | SPEAKER_DDR |= (1<<SPEAKER);
|
304 |
|
305 | while(1) {
|
306 |
|
307 | if (i >= (sizeof(ma)/sizeof(ma[0]))) i = 0;
|
308 | play(i);
|
309 | // Sleep-Mode
|
310 | set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
311 | sleep_mode();
|
312 | i++;
|
313 | }
|
314 |
|
315 | return 0;
|
316 | }
|