Hallo,
ich versuche grade mit meinem Atmega8 einen Tonerzeuger zu bauen, dabei
habe ich mir für's Erste eine Sägezahnwelle als Schwingungsform
ausgesucht, da sich diese recht einfach über Variablenüberläufe
simulieren lässt. Nun habe ich folgendes Problem: Ich habe einen
Schalter an Pin 14 angeschlossen, diesen frag ich in jedem
Hauptschleifendurchlauf ab, ob er sich geändert hat. Wenn er sich
geändert hat, wird je nachdem ob er nun gedrückt ist oder nicht die
Frequenz von a (220 Hz) auf a' (440 Hz) und umgekehrt geändert. Was sich
nun aber eigentlich wie eine Oktave anhören sollte, hört sich eher an
wie eine Quinte, ich finde leider nicht die Ursache, und hier bitte ich
um Hilfe :)
Info zur Schaltung:
An PORTD liegt ein 8bit R2R DAC, aufgebaut wie in diesem Video:
http://youtu.be/b-vUg7h0lpE
An den DAC ist ein Piezo Lautsprecher angeschlossen, ansonsten gibt es
nur noch den schon beschriebenen Schalter zum Frequenzwechsel an Pin 14
(also PINB:0)
Hier ist mein Code:
1
#include<stdint.h>
2
#include<avr/io.h>
3
#include<avr/wdt.h>
4
#include<avr/interrupt.h>
5
#include<math.h>
6
7
#define F_CPU 1000000
8
#define SAMPLING_TIME 16
9
10
intmain(void)
11
{
12
wdt_disable();
13
14
DDRD=0xFF;//set all pins to 'out'
15
DDRB&=~(1<<0);//set switch pin to 'in'
16
17
TCCR1B|=(1<<CS10);// prescaler is set to 1
18
TCCR1B|=(1<<WGM12);//Timer Compare Match mode on
19
20
TCNT1=0x0;//Initialise timer 1 with 0.
21
22
OCR1A=SAMPLING_TIME;//Timer Compare Value set to x Ticks.
23
24
PORTD=0;
25
26
floatfreq=220.0f;
27
28
uint16_tvoutFull=0;//mantissa is the right half of the integer
> uint16_t deltaM=(uint16_t)((SAMPLING_TIME*freq)*maxValueOf16Bit/F_CPU);
Wenn ich die Formel zugrunde lege, dann müsste SAMPLING_TIME aber 256
sein. Denn immerhin setzt du auf den Überlauf einer uint16_t Variable um
daraus eine Schwingung zu generieren. Da du von der nur die obersten 8
Bit zur Ausgabe benutzt, hast du daher 256 Samples, von denen du eine
Auswahl auf den Port gibst.
Naja, eine Frequenz von f bedeutet ja, dass f Perioden pro Sekunde
durchlaufen werden. Pro Periode müssen die Werte 0 bis 65535 durchlaufen
werden, damit ich einen vollen "Sägezahn" in 16bit bekomme, 16bit
deswegen, damit mir aufgrund der geringen Auflösung bei 8bit nicht
zuviele Nachkommastellen verloren gehen. Ich muss also uint16_t voutFull
jede Sekunde von 0 auf f*65535 bringen, damit f Variablenüberläufe
passieren. Wenn ich jetzt wissen möchte, was ich jeden Takt addieren
muss, dann muss ich diesen Wert durch F_CPU teilen. Und da ich nicht
jeden Takt, sondern nur alle SAMPLING_TIME Takte den Wert aktualisiere,
muss ich natürlich noch mit SAMPLING_TIME multiplizieren:
deltaM=f*maxValueOf16Bit*SAMPLING_TIME/F_CPU
Ist im Code nur leider etwas verdreht, sorry.
Johannes schrieb:> Naja, eine Frequenz von f bedeutet ja, dass f Perioden pro Sekunde> durchlaufen werden. Pro Periode müssen die Werte 0 bis 65535 durchlaufen> werden, damit ich einen vollen "Sägezahn" in 16bit bekomme,
Du erzeugst aber keinen Sägezahn in 16 Bit. Du erzeugst einen Sägezahn
in 8 Bit. Die restlichen 8 Bit dienen als "Nachkommastellen".
D.h. du hast 256 Samples. Ergo muss SAMPLING_TIME auch 256 sein.
Ich hab den Wert grade mal getestet, mit SAMPLING_TIME=256 fehlen immer
noch zwei bis drei Halbtöne. Eigentlich sollte SAMPLING_TIME auch keinen
Einfluss auf die Frequenz haben, sondern
SAMPLING_TIME ist in meinem Code die Anzahl an Takten zwischen zwei
Samples. Die Überläufe der 16bit Variable werden geregelt durch die
Variable maxValueOf16bit, mit der das Ganze multipliziert wird, schätze
mal eine Konstante wäre da angebrachter gewesen. Habe das jetzt
geändert.
Mein Code sollte eigentlich einen Sägezahn auf 16bit in der Frequenz f
erzeugen, wenn man da die vorderen 8 Bits nimmt, hat man einen Sägezahn
in 8bit, in der selben Frequenz.
Hey,
entschuldigt bitte den Doppelpost, aber ich habe jetzt herausgefunden,
woran's lag:
Die SAMPLING_TIME=16 war zu niedrig, ich hab sie jetzt auf 32
hochgesetzt und es funktioniert :) Der Erfolg des Programms hängt im
Prinzip davon ab, dass SAMPLING_TIME größer ist als ein kompletter
Durchlauf des Mainloops, da sonst mehrere gefakete Interrupt Routinen
(die ich mit der Interrupt-Flag in der zweiten if-Anweisung prüfe) pro
Loop ausgeführt werden müssten, dafür ist das Programm aber natürlich
nicht ausgelegt...
Anfängerfehler, würde ich mal sagen... Danke jedenfalls!