Forum: Compiler & IDEs AUdioprobleme: Sägezahnwelle beim Atmega8


von Johannes (Gast)


Lesenswert?

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
int main(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
    float freq=220.0f;
27
28
    uint16_t voutFull=0; //mantissa is the right half of the integer
29
  
30
  uint16_t maxValueOf16Bit=0;
31
  maxValueOf16Bit-=1;
32
33
    uint16_t deltaM=(uint16_t)((SAMPLING_TIME*freq)*maxValueOf16Bit/F_CPU);
34
  
35
  unsigned char pinb_old=PINB&(1<<0);
36
37
    while (1) {
38
    /*
39
    ++voutFull;
40
    PORTD=voutFull>>8;
41
    */
42
    if ((PINB&(1<<0))!=pinb_old) {
43
      if (pinb_old==(1<<0))
44
        freq=440.0f;
45
      else
46
        freq=220.0f;
47
      deltaM=(uint16_t)((SAMPLING_TIME*freq)*maxValueOf16Bit/F_CPU);
48
      pinb_old=PINB&(1<<0);
49
    }      
50
    
51
    if (TIFR & (1<<OCF1A)) { //if the interrupt flag is set
52
      voutFull+=deltaM;
53
54
            PORTD=(voutFull>>8);
55
      
56
      TIFR|=(1<<OCF1A); //Reset the flag
57
      
58
    }
59
          
60
    }    
61
}

von Karl H. (kbuchegg)


Lesenswert?

>   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.

von Johannes (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Johannes (Gast)


Lesenswert?

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.

von Johannes (Gast)


Lesenswert?

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!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.