Warum genau 4096 Hz? Wenn du nur den RC-Oszillator hast, wird die
Genauigkeit sowieso nicht so groß sein.
Wenn du den Summer an einem OC-Ausgang eines Timers hast, ist die
Sache recht einfach. Du programmierst den Timer so, dass er mit
jedem Erreichen des OCRx-Registers den Ausgang umschaltet und das
OCRx-Register für die entsprechende Frequenz.
Das folgende Beispiel geht von 1 MHz Standard-RC-Oszillatorfrequenz
aus und davon, dass die Pizeo-Piepse an OC2 (PB3) hängt. Das ist
auch zugleich MOSI. Falls dein ISP-Programmer die kapazitive Last
dort nicht verträgt, musst du entweder noch ein Gatter als
Verstärker davor hängen. Wenn du ein NAND-Gatter nimmst und den
zweiten Eingang mit /RESET verbindest, dann piepst es während der
Programmierung nicht. ;-) Oder aber du nimmst den Timer 1 und dessen
OC1A- oder OC1B-Ausgänge, aber meistens hat man für den 16-bit-Timer
1 bessere Aufgaben als sowas.
1 | #define F_CPU 1000000UL
|
2 | #define F_AUDIO 4096
|
3 |
|
4 | #if (F_CPU / (2 * F_AUDIO)) > 255
|
5 | # define PRESCALER 8
|
6 | # define OCR2VAL ((F_CPU / (2 * 8UL * F_AUDIO)) - 1)
|
7 | #else
|
8 | # define PRESCALER 1
|
9 | # define OCR2VAL ((F_CPU / (2UL * F_AUDIO)) - 1)
|
10 | #endif
|
11 |
|
12 | static void
|
13 | timer2start(void)
|
14 | {
|
15 | #if PRESCALER == 8
|
16 | TCCR2 = _BV(WGM21) /* CTC mode */ |
|
17 | _BV(COM20) /* toggle OC2 on compare match */ |
|
18 | _BV(CS21);
|
19 | #else
|
20 | TCCR2 = _BV(WGM21) /* CTC mode */ |
|
21 | _BV(COM20) /* toggle OC2 on compare match */ |
|
22 | _BV(CS20);
|
23 | #endif
|
24 | OCR2 = OCR2VAL;
|
25 | DDRB |= _BV(3); /* activate OC2 as output */
|
26 | }
|
27 |
|
28 | static void
|
29 | timer2stop(void)
|
30 | {
|
31 | TCCR2 = 0;
|
32 | DDRB &= ~_BV(3); /* deactivate OC2 */
|
33 | }
|