sigma delta dac in c

Gast #3127896
Lesenswert?

Im Moment beschäftige ich mich gerade mit den Prinzipien der Sigma-Delta 
AD-Wandlung.

Um das mal praktisch auszuprobieren, habe ich einen Sinus Genrator 
programmiert:

http://hobby-roboter.de/forum/viewtopic.php?f=5&t=141

Man muss nur eine RC-Tiepass an den LED-Ausgang des Arduino anschließen 
und dann das Signal auf einen Lautsprecher geben.
Gast #3128867
Lesenswert?

Erstaunlich fand ich, wie wenig Codezeilen für eine Sigma Delta Dac 
benötigt werden.
1
// sigma delta DAC, hold the DAC value for n-steps constant
2
for(n=0;n<50;n++)
3
{
4
   integrator+=sollwert-oldValue;
5
   if(integrator>0)
6
   {
7
      oldValue=MAXVALUE;
8
      digitalWrite(13, HIGH);   // set the LED on
9
   }
10
   else
11
   {
12
      oldValue=0;
13
      digitalWrite(13, LOW);    // set the LED off
14
   }
15
}

Durch die Arduino digitalWrite Funktionen wird die Hauptschleife 
allerdings etwas langsam. Der DAC läuft in etwa mit 100kHz, sodass eine 
Schleife mit 50 Durchläufen in meinem Beipiel mit dem Sinus eine 
effektive Abtastfrequenz von 100kHz/50=2kHz ergibt. Bin gepsannt, wie 
schnell das Ganze wird, wenn die Funktionen durch direkte Portzugriffe 
ersetzt werden.
Moderator #3128913
Lesenswert?

Mit GCC 4.2.4 oder 4.3.6 dauert ein Schleifendurchlauf 14 Taktzyklen.
Bei 20 MHz Taktfrequenz entspricht das 1,43 MHz, die Abtastrate ist 28,6
kHz. Neuere GCCs sind aber teilweise mehr als Faktor 2 langsamer. Ein
schon etwas älterer Snapshot von 4.8.0 braucht für jeden Schleifendurch-
lauf 36,5 Taktzyklen. Möglicherweise ist die Release-Version besser, ich
habe sie allerdings noch nicht getestet.
Moderator #3129589
Lesenswert?

chris schrieb:
> Stellt sich nur die Frage, mit welcher Optimierung komiliert?

Ach so, das habe ich ganz vergessen zuschreiben: Die angegebenen
Ergebnisse gelten für die Optimierung mit -O2. Bei -Os wird der Code
etwas langsamer und etwas kleiner. Die Unterschiede liegen in der
Größenordnung von 10%, sind also nicht so riesig.

Was tut eigentlich diese digitalWrite-Funktion alles, dass sie so extrem
langsam wird? Klar, sie muss die Pin-Nummer in ein I/O-Register und eine
Bitmaske umsetzen, dazu sind sicher ein paar Lookups und/oder Shifts
erforderlich, aber dass das Ganze gleich sooo lange dauert?
Gast #3129645
Lesenswert?

>Was tut eigentlich diese digitalWrite-Funktion alles, dass sie so extrem
>langsam wird?
Das weis ich leider auch nicht. Die Routine ist wohl in C++ geschrieben.

Ich habe noch mal ein wenig experimentiert. Mit dem direkten Zugriff auf 
PORTD komme ich auf 500kHz Abtastfrequenz.
Es war auch eine hochfrequente Störung auf dem Signal. Diese verschwand 
interessanterweise, als ich die Variablendefinitionen in die loop 
gesetzt habe ( vorher waren sie global )
1
void loop() {
2
  uint16_t phaseDelta=1070;
3
  int16_t integrator=0;
4
  uint8_t sollwert=64;
5
  uint8_t oldValue=0;
6
  uint16_t phase=0;
7
  cli();
8
  while(1)
9
  {
10
    // sin wave DDS ( direct digital synthesis )
11
    sollwert=128+sintab[phase>>8];
12
    phase+=phaseDelta;
13
    //phaseDelta++;
14
  
15
    // sigma delta DAC, hold the DAC value for n-steps constant
16
    DDRD|=(1<<2); // set pin as output
17
    for(n=0;n<25;n++)
18
    {
19
      integrator+=sollwert-oldValue;
20
      if(integrator>0) 
21
      {
22
        oldValue=MAXVALUE;
23
        PORTD|=(1<<2); // set pin high
24
      }
25
      else 
26
      {
27
        oldValue=0;
28
        PORTD&=~(1<<2); // set pin low
29
      }
30
    }
31
    DDRD&=~(1<<2); // set pin into high impedance state
32
    PORTD&=~(1<<2); // turn off pull up
33
  }
34
}
Den Sinus habe ich mit dem Mikrofon aufgenommen. Deshalb die 
tieffrequenten Schwingungen ( die man aber nicht hört )
Angehängte Dateien:
Moderator #3129805
Lesenswert?

chris schrieb:
> Diese verschwand
> interessanterweise, als ich die Variablendefinitionen in die loop
> gesetzt habe ( vorher waren sie global )

Die alten GCCs (4.2.4 und 4.3.6) haben netterweise selber die Inhalte
der globalen in lokale Variablen (d.h. Register) kopiert, weswegen die
innere Schleife auch so schnell lief. Bei den neueren Versionen muss
der Programmierer etwas nachhelfen, so wie du es getan hast.

>     DDRD&=~(1<<2); // set pin into high impedance state
>     PORTD&=~(1<<2); // turn off pull up

Warum denn das? Das gibt doch nur zusätzliches Rauschen und kostet
Zyklen?
Gast #3129981
Lesenswert?

>Warum denn das? Das gibt doch nur zusätzliches Rauschen und kostet
>Zyklen?

Du hast recht, es kostet Zyklen. Allerdings gibt es am Ende der 
For-Schleife eine zeitliche Verzögerung. In der Zeit, in der dann der 
Sinuswert aktualisiert wird, bleibt der Ausgang auf dem letzten Wert. 
Ist dieser Wert High, wird der Kondensator des RC-Tiefpass am Ausgang ( 
bie mir 470Ohm, 100nF ) zu stark aufgeladen. Ist der Wert Low wird der 
Kondensator zu stark entladen. Wenn man den Ausgan hochohmig schaltet, 
hält der Kondensator den Wert ( zumindest wenn die Senke hochohmig genug 
ist ).

Mein Eindruck war, dass der Sinus mit dem "hochohmig Schalten" sauberer 
klingt.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren