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.
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.
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.
>Bei 20 MHz Taktfrequenz entspricht das 1,43 MHz
Das klingt gut. Auf dem Arduino mit 16MHz und digitalWrite waren es nur
so um die 100kHz. Faktor 10 bringt da qualitativ viel. Stellt sich nur
die Frage, mit welcher Optimierung komiliert?
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?
>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 )
Man sieht die für einen Sigma-Delta Wandler typische Verlagerung des Rauschens zu hohen Freunzen. Die harmonischen im unteren Frequenzbereich hätte ich in dieser Stärke nicht erwartet. Das könnte aber mit der geringen Auflösung des Sinus ( 8 Bit ) zusammenhängen.
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?
>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.
>Ein Sigma-Delta ADC mit weniger als 50dB SNR - Genial.
Das SNR des DAC hängt vom Glättungsfilter ab. Dieser besteht momentan
aus einem RC-Glied ( 470 Ohm, 100nF ). Durch einen zweiten
nachgeschaltetn RC-Tiefpass ( 4700Ohm, 10nF ) sollte sich das SNR
deutlich verbessern.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.