Hallo, ich baue mir einen Signalgenerator, ich bin eigentlich schon fertig damit. Ein Atmega8 (16Mhz) hängt mit 8 Pins an einem 8Bit DAC. Es sind verschiedene Signalformen wählbar (Sinus, Rechteck, Dreieck, Sägezahn). Bei jeder Signalform werden in einer vollen Periode 256 Werte abgetastet, egal bei welcher Frequenz. Damit muss die Interruptfrequenz 256 mal höher liegen, als die gewählte Ausgangsfrequenz. Problem nun: ab 96Hz gewählter Frequenz ist der Atmega an der Grenze (24576Hz Interruptfrequenz) und es lassen sich z.B. keine Tastendrücke mehr einlesen. Ist mein Programm so ineffizient oder muss ich mit geringerer Auflösung leben? Was kann man da machen? 96Hz sind mir zumindest ein bisschen zu wenig. Natürlich kann ich beim Rechtecksignal direkt den Port schalten, bei den anderen Signalformen geht das aber nicht. Anbei der Code.. Er ist im Moment sicher nicht sehr hübsch, ich habe ihn noch in keiner Weise optimiert.. Die "freqErrorPercent" Funktion errechnet übrigens die Abweichung der ausgegebenen von der eingestellten Frequenz durch die Nichtstetigkeit des OCR1A Zählregisters. Danke im Voraus!
Gast
#3113002
Wenn man den Generator unbedingt in Software mit einem µC machen will, dann schon eher als DDS Generator - also mit konstanter Abtastrate für den DAC und dann variablen Daten. Spezielle Chips (z.B. AD9851) sind da aber einiges Leistungsfähiger. Vermutlich kann dem den Code noch um einiges beschleunigen, aber es bleibt eine suboptimale Lösung. Schon der Code um den DAC zu setzen ist ggf. noch schön universell, aber auch extrem ineffizient.
Gast
#3113004
Hi Das geht schon schneller. Nur nicht, so wie du dir das gedacht hast: http://www.elektronik-labor.de/AVR/DDSGenerator.htm MfG Spess
Gast
#3113006
>double posTimesTen;
in der Tat, etwas Optimierung waere faellig. Aber double ist etwas
abgehoben.. Was soll double ueberhaupt ? Ich sah's an verschiedenen
Orten.
So etwas macht man mit dem DDS-Prinzip, nicht über viele int's. google deshalb nach: jesper, poor man's dds. Eine solche Aufgabe muss sehr zeit-sparsam ausgeführt werden, deshalb ist sie ein Musterfall für Ausführung in Assembler. Auch hier im forum gibt es mehrere threads zu diesem Thema. Bei 16 MHz Taktfrequenz lässt sich in Assembler mit einem atmega8 selbst 20 kHz als recht sauberer Sinus erzeugen. Wegen der einfachen Schleife kann man auch gut übertakten, mit externem Oszillator hab ich den atmega schon mit 30MHz gequält. Sinnvoller wäre natürlich der Einsatz eines DDS-IC. Diese sind inzwischen nicht so teuer wie zu der Zeit als Jesper seine Lösung vorstellte.
> Schon der Code um den DAC zu setzen ist > ggf. noch schön universell, aber auch extrem ineffizient. Ja allerdings! dummerweise habe ich ihn nicht an einen einzigen port angeschlossen, vermutlich ändere ich das noch. > Was soll double ueberhaupt ? Die Frequenz wird als double gespeichert.. das mag übertrieben sein, der Wert wird aber beim eigentlichen Generieren der Ausgangsspannung nicht mehr benutzt. All die großen Funktionen rechnen ja nur aus der gewählten Taktrate die nötigen Interrupt-Einstellungen aus. Das von dir zitierte Beispiel ist aber was anderes, das ist wirklich unnötig ;) > So etwas macht man mit dem DDS-Prinzip, nicht über viele int's. Ich benutze ja "fast" nur einen interrupt (der für die taster arbeitet nur bei ca. 60 Hz) ;) Das DDS Prinzip war mir neu! Man opfert also also mit steigender Ausgabefrequenz zeitliche Auflösung, bis man mit der Samplingfrequenz gerade noch mehr als die doppelte auszugebende Frequenz erreicht hat? Zu meinem Verständnis: Es gibt einen Zähler, der mit maximaler Rate hochgezählt wird, bis er überläuft und zurückgesetzt wird. Je nach gewählter Frequenz wird alle "x" Zählschritte ein Wert aus der Wertetabelle abgetastet (also der Nächstgelegene) und an den DAC übertragen, je höher die Zahl der Zählschritte, desto kleiner die Frequenz. Ist ein Interrupt dafür denn schlecht geeignet? Immerhin kann ich damit sicher sein, wie schnell der Zähler läuft. Ich brauche keine hohen Frequenzen, ich bin auch mit 10kHz (bei mir momentan dann 2,56Mhz Interrupt Frequenz) zufrieden. Takte zählen kann man ja tatsächlich nur im (mir ziemlich unangenehmen) Assembler.. Ein Sinus mit knapp über zwei Abtastwerten pro Periode sieht doch nun wirklich nicht mehr hübsch aus..
Gast
#3113175
Hi >ich bin auch mit 10kHz (bei mir momentan dann 2,56Mhz >Interrupt Frequenz) zufrieden. Bei deinen 16MHz Takt bleiben dann 6,25 Takte für einen Interrupt. Ein Interrupt braucht schon allein ein paar Takte um zum Interrupt zu springen und für das abschließende RETI. Und du rufst vom Interrupt noch so eine 'Warteschleife' >void setDacValue(uint8_t value) { > for(int i=0; i<=7; i++) { > if((value>>i)&1 == 1) {*(dacPorts[i]) |= (1<<dacPins[i]);} > else {*(dacPorts[i]) &= ~(1<<dacPins[i]);} > } >} auf. Deine angestrebten 10 kHz sind absolut utopisch. MfG Spess
Im Hauptprogramm, in Assembler vielleicht so in der Art:
neu:
lade aus der Tabelle
ab auf den Port
goto neu
Tastendrücke dann per Interrupt
Gast
#3113201
Adrian Figueroa schrieb: > Ist ein Interrupt dafür denn schlecht geeignet? Ja. Wenn es Ziel ist, die Daten aus der Tabelle mit maximaler Frequenz auszugeben, ist es von der Programmausführung am schnellsten, das Programm genau darauf zu beschränken. Wenn dort mehr als ein Pointer mit festgelegter Schrittweite kreist und das gelesene Byte auf einen Port ausgegeben wird, kostet das zusätzlich Zeit, was unweigerlich die maximale Frequenz verringert. Die DDS-Kernschleife muss natürlich so aufgebaut sein, dass sich eine konstante Laufzeit ergibt. Tastaturbedienung über Interrupts kostet natürlich Zeit, die zu Phasensprüngen oder Aussetzern im Signal führt. Ein korrigierender Eingriff in den Phasenzähler bringt allenfalls bei niedrigen Frequenzen etwas.
> Das DDS Prinzip war mir neu! Man opfert also also mit > steigender Ausgabefrequenz zeitliche Auflösung, bis man mit > der Samplingfrequenz gerade noch mehr als die doppelte > auszugebende Frequenz erreicht hat? Ja. Irgendeinen Tod musst du sterben. Man kann nicht alles haben, wenn die verfügbaren Resourcen nicht ausreichen.
Ich habe mal vier Wertetabellen in meinem Programm abgelegt. Das sind 1024Byte und die passen (wenn die anderen Variablen noch dazukommen) nicht mehr in den 1kb Datenspeicher.. Wie hat der Mensch das mit dem Attiny2313 angestellt? Was habe ich da übersehen? Es scheint mir unvorstellbar, auch in Assembler, das Programm UND die Daten auf 2kB zu bekommen.
Adrian Figueroa schrieb: > Ich habe mal vier Wertetabellen in meinem Programm abgelegt. Das sind > 1024Byte und die passen (wenn die anderen Variablen noch dazukommen) > nicht mehr in den 1kb Datenspeicher.. > Bei jeder Signalform werden in einer vollen Periode 256 Werte Für einen kompletten Sinus musst du zb nicht die komplette Signalform ablegen. Einen kompletten Sinus kann man aus einer 1/4 Kurve und entsprechende Spiegelungen erzeugen. Weiters: Niemand sagt, dass alle Kurventabellen im SRAM liegen müssen. Eine reicht. Und wenn der Benutzer die Kurvenform wechselt, wird diese Tabelle aus dem Flash nachgeladen. Flash hat man mehr als SRAM > Es scheint mir unvorstellbar, auch in Assembler, das > Programm UND die Daten auf 2kB zu bekommen. 1Kb Flashspeicher für das Programm ist schon mächtig viel Holz. Da geht so einiges an Funktionalität rein.
Gast
#3113542
Adrian Figueroa schrieb: > Es scheint mir unvorstellbar, auch in Assembler, das Programm > UND die Daten auf 2kB zu bekommen. Guck dir einfach den Code an. Im Source Code steht es schwarz auf weiss. Die Tabellen sind z.B. nur 256 Byte lang, d.h. bei vier Kurvenformen ist die Hälfte des Speichers belegt. http://www.myplace.nu/avr/minidds/minidds.asm
Gast
#3113546
Hi >Es scheint mir unvorstellbar, auch in Assembler, das Programm >UND die Daten auf 2kB zu bekommen. Wieso nicht? In dem restlichen 1k Programmspeicher lassen sich ca. 350...400 Assemblerbefehle unterbringen. Die eigentliche Ausgabe der Tabellenwerte in der richtigen Frequenz beansprucht gerade mal 8 Assemblerbefehle. Der ganze Rest ist für die Bedienung übrig. Und da packt man eben rein, was rein passt. MfG Spess
Das Problem war eher, dass die Daten im Datenspeicher standen und nicht im Programmspeicher.. Ich habe jetzt "PROGMEM" entdeckt, wohl das Äquivalent zur Auswahl des Speichersegments in Assembler. Mein Code, speziell für das Display, ist sehr allgemein und riesig groß, ich habe aber immerhin 8kB Platz.
So soll es dann erstmal aussehen..
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
Gast
#3114117
Ach Leute, warum sowas immer wieder und wieder... Warum bloß habt ihr es nicht drauf, wenigstens einen AD9833 zu kaufen und diesen IC die Arbeit machen zu lassen? 25 MHz DDS-Takt mit 10 Bit analoger Auflösung schafft ihr mit keinem Atmel und der AD9833 ist auch ein recht billiger Chip, der Sinus+Rechteck+Sägezahn liefern kann. Warum also bloß immer wieder diese Verrenkungen, ein DDS per Software auf eher ungeeignetem uC zu bauen? Ist das ein spezieller Atmel-Fetischismus? W.S.
W.S. schrieb: > Sägezahn Nö kann er nicht, der kann Dreieck und den Rest. Zudem ist ein AD9833 jetzt nicht nen IC den man am liebsten einlötet. MSOP10, 3x3mm groß und 10 Pins.
Gast
#3114145
Adrian Figueroa schrieb: > So soll es dann erstmal aussehen.. Da wirst du noch nicht glücklich mit werden, weil die Frequenz nicht einstellbar ist. Es fehlt der Phasenzähler für die Bruchteile und durch den if-Block hast du eine variable Schleifenlaufzeit. Den if-Block spart man sich, indem man den Variablentyp vom Phasenzähler passend zur Länge der Wellenformdaten wählt. Außerdem sollten xpos und counter besser gekoppelt sein ;-)
Gast
#3114159
Martin Wende schrieb: > Zudem ist ein AD9833 jetzt nicht nen IC den man am liebsten einlötet. > MSOP10, 3x3mm groß und 10 Pins. Mit ein bisschen Flussmittel sind die 0,5mm doch wirklich kein Problem http://www.youtube.com/watch?v=erb6-i54tbo
Gast
#3114168
Dann kauf Dir ein AD9850-Board in China mit 125 MHz-Oszillator für 4,30 Euro inkl. Versand. Jespers DDS hat noch Optimierungspotential: Beitrag "DDS normal ?" Adrian, Du musst noch ein bisschen über das DDS-Prinzip meditieren. Und wenn Du den Chip ausreizen willst, kommst Du um Asm nicht herum.
Sag ja nich, dasses unmöglich is, aber macht einfach kein Spaß ;)
Martin Wende schrieb: > Sag ja nich, dasses unmöglich is, aber macht einfach kein Spaß ;) Na ja, mit Deiner Leiterplattenqualität und ohne Lötstopplack hast Du ja auch erschwerte Bedingungen. Mit vernünftigen Boards ist das kein Problem. fchk
W.S. schrieb: > Warum also bloß immer wieder diese Verrenkungen, ein DDS per Software > auf eher ungeeignetem uC zu bauen? Ist das ein spezieller > Atmel-Fetischismus? Ich frage mich das auch immer. Ihm wäre ja auch schon mit einem dsPIC33F256MC802 geholfen. Gleiches Gehäuse wie ein Mega8, aber mindestens dreifache Leistung und zwei eingebaute 14 Bit DACs, die bis 100 kHz gehen und per DMA gefüttert werden können. Die Idioten sterben halt nicht aus. fchk
@ W.S. (Gast) >Warum also bloß immer wieder diese Verrenkungen, ein DDS per Software >auf eher ungeeignetem uC zu bauen? Keine Verrenkung sondern sportliche Herausforderung! > Ist das ein spezieller Atmel-Fetischismus? AVRs können's halt. Und für NF bis vielleicht 100kHz voll OK. Clever und leistungsstark. Sogar ein Videosignal incl. Terminal kann man mit so einem kleinen Käfer machen. Klar, man kann auch spezielle Hardware nutzen, aber das ist ja keine Kunst ;-)
Hier ist ja einiges passiert :D Danke für die (meisten) Antworten. Ich hatte bisher keine Ahnung von der Existens eigens für diesen Zweck gemachter DDS Chips, allerdings brauche ich diesen Signalgenerator auch kaum für seine eigentliche Funktion, er ist ein reines Lernobjekt. Es handelt sich bei ihm um mein erstes AVR-Projekt, das über einfache blinkende Lampen und kleine Uhren hinausgeht und ich wollte das Problem erstmal ohne fertige Lösungen angehen. Bis 96Hz bin ich gekommen, jetzt befasse ich mich wohl näher mit DDS.
Gast
#3114484
Hi
>Bis 96Hz bin ich gekommen, jetzt befasse ich mich wohl näher mit DDS.
Dann fang auch gleich noch mit Assembler an. Mit C kommst du da auch
nicht viel weiter.
MfG Spess
Dann bau doch Dein Projekt in zwei Teile auf: Ein atmega8 als DDS-IC. Die DDS-Schleife als Assembler-inline-Teil, das sind nur etwa 10 Befehle. Zusätzlich, als int-Teil, die Verstellung per I2C, in C geschrieben. Dann verhält sich dieser Atmega wie ein I2C-Baustein. Ein zweiter atmega8 als Bedienzentrale. Gibt Sollwerte an I2c ab, bedient Tastatur,Scrollrad,Display,Speicher, RS232 zum PC usw. Der kann voll in C programmiert werden. Letztendlich sinnvoller wäre: den analogen Teil als DDS-IC und die Steuerung als Kontroller aufzuteilen.
Adrian Figueroa schrieb: > Ein Sinus mit knapp über zwei Abtastwerten pro Periode sieht doch nun > wirklich nicht mehr hübsch aus.. Da fehlt ja auch noch ein Tiefpass am Ausgang. Und das macht dann wieder einen schoenen Sinus draus.
> den analogen Teil als DDS-IC Das werde ich wohl anstreben! > Da fehlt ja auch noch ein Tiefpass am Ausgang. Und das macht dann wieder > einen schoenen Sinus draus. Ich habe einen 50kHz Tiefpass eingeplant.
Adrian Figueroa schrieb: > Ich habe einen 50kHz Tiefpass eingeplant. Dein Tiefpass muss eine Grenzfrequenz < fs/2 haben. fs = Samplefrequenz fuer deine Abtastungen. In deinem Beispiel von 50kHz muss die Sample (Ausgabefrequenz) bei mehr als 100 kHz liegen. Damit das Filter nicht unendlich steil sein muss eher bei 150kHz. Die Daempfung des Filters muss groesser sein als die Aufloesung deines DACs. Mit einem einfachen RC Glied funktioniert das nur recht selten.
> Dein Tiefpass muss eine Grenzfrequenz < fs/2 haben.
warum ist das so? Mir ist klar, dass der Tiefpass bei seiner
Grenzfrequenz kaum filtert, aber wie kommt man auf fs/2?
Mein Generator gibt ja eigentlich immer Rechtecksignale aus, ich habe
mal vereinfacht angenommen, dass nur die 5. Oberwelle noch relevant ist.
Die Samplingfrequenz liegt ja bei 10 Assemblerbefehlen bei (angenommen)
einem Takt pro Befehl bei 1,6Mhz. Bei einer 10kHz-Sinusschwingung werden
dann 160 Werte abgetastet, zurückgerechnet komme ich natürlich wieder
auf 1,6Mhz Samplingfrequenz.. Es entsteht also ein "eckiges" 1,6Mhz
Signal, die relevanten Oberwellen gehen nach obiger Vereinfachung bis
8Mhz.
Muss der Tiefpass dann nicht auch in der Region arbeiten? Ich tu mich
schwer mit der Dimensionierung...
Wir sprechen hier vom DDS Prinzip. Da wird auf einen Summenakku immer in abhaengigkeit der eingestellten Frequenz ein bestimmter Phasenwinkel addiert. Dieses addieren erfolgt in der festen Samplingfrequenz fs. Nun ist es ja so das dieses abtasten eine Multiplikation ist. Da durch erscheint am Ausgang des DAC im Spektrum jetzt neben deiner gewuenschten Ausgangsfrequenz auch die Mischung der Samplingfrequenz mit dem Ausgangssignal und noch weitere Komponenten. Diese unerwunschten Signale gilt es nun wegzubekommen. z.B. Ausgangsfrequenz fa: 10kHz Samplingfrequenz fs: 30kHz Dann hast du im Signal Spektralanteile von: fa = 10kHz, fs-fa=20kHz,fs=30kHz,fs+fa=40kHz usw. Das heist also alles was oberhalb von 10kHz ist muss weg. Das geht in diesem Fall problemlos da zwischen 10kHz (die nicht abgeschwaecht werden sollen) und dem kleinsten Stoersignal von 20kHz viel Platz ist fuer das Filter abzufallen. Wenn man jetzt die Ausgangsfrequenz weiter erhoehen wuerde, wuerde sich folgendes Bild einstellen. Ausgangsfrequenz fa: 15kHz Samplingfrequenz fs: 30kHz Dann hast du im Signal Spektralanteile von: fa = 15kHz, fs-fa=15kHz,fs=30kHz,fs+fa=45kHz usw. Du siehst dein erstes Stoersignal ist identisch mit der Ausgangsfrequenz. Das Filter muesste in dem Fall unendlich Steil sein. Was aber nicht geht. Macht man die Ausgangsfrequenz jetzt noch groesser kann man ueberhaupt nicht mehr trennen. Mit DDS kann man zwar theoretisch Frequenzen bis zu fs/2 erzeugen praktisch scheitert das aber am Filter. Deshalb nimmt man meistens fs/3 als hoechste Ausgangsfrequenz. Zur Filterdaempfung: Du siehst im ersten Fall das wir einen Bereich von 10kHz .. 20Khz zum Abfall des Tiefpasses haben. Bei 20kHz muss der Tiefpass dann theoretisch eine Daempfung groesser als die Aufloesung des DACs haben. Im Fall eines 8Bit DAC dann 48dB. Da 10kHz .. 20kHz eine Oktave->verdoppelung ist und ein einfacher Tiefpass 6db/ Oktave macht muesste man hier einen Tiefpass 8. Ordnung einsetzen. Praktisch kaemme man auch mit 6. Ordnung aus wenn man an der Signalqualitaet etwas Abstriche macht.
> Deshalb nimmt man meistens fs/3 als hoechste Ausgangsfrequenz. Alles klar! Bei 1,6Mhz Abtastfrequenz bin ich ja gerade mal bei fs/160 für 10kHz Ausgangsfrequenz.. Damit muss mein Filter doch nur ab 10kHz bis 1,6Mhz-10kHz (~ 1,6Mhz) abfallen.. > theoretisch eine Daempfung groesser als die Aufloesung des DACs haben. Bei 8Bit 48dB? Heißt das, der niedrigste ausgebbare Wert bei einem 8bit DAC liegt 48dB unter dem höchsten? Kommt man darauf so?: 8Bit DAC -> niedrigster Wert ist um Faktor 2^8-1 kleiner als der höchste, damit um 20*log(255/1) = ca. 48dB.. Nun habe ich von 10kHz bis 1,6Mhz ganze 160 Oktaven "Platz", es reicht eine Steilheit von 48/160 dB/Oct = 0,3dB/Oct. Ich habe einen aktiven Filter zweiter Ordnung mit einem OPAmp gebaut (12dB/oct), damit bin ich bei 800kHz(?) bei -48dB.
Adrian Figueroa schrieb: > Kommt man darauf so?: > 8Bit DAC -> niedrigster Wert ist um Faktor 2^8-1 kleiner als der > höchste, damit um 20*log(255/1) = ca. 48dB.. Richtig. Adrian Figueroa schrieb: > Alles klar! Bei 1,6Mhz Abtastfrequenz bin ich ja gerade mal bei fs/160 > für 10kHz Ausgangsfrequenz.. Damit muss mein Filter doch nur ab 10kHz > bis 1,6Mhz-10kHz (~ 1,6Mhz) abfallen.. Warum tastes du mit 1.6Mhz ab? Dann macht dein uC nix anderes mehr falls er die ueberhaupt schafft. Es reichen fuer 10KHz locker 30Khz Samplefrequenz.
> Dann macht dein uC nix anderes mehr falls er die ueberhaupt schafft.
Muss er das denn? Außer wenn eine Eingabe erfolgt tastet er ja nur ab.
Ob er die Rate schafft, weiß ich nicht. Weiter oben hat aber jemand von
10 Takten gesprochen, die für die Abtastung in Assembler nötig wären.
Eine andere Frage:
Den Phasenzähler (32bit) muss ich ja umrechnen auf einen Wert zwischen 0
und 255 für meine Tabelle. Ich schiebe ihn also 24 Stellen nach rechts?
1 | |
2 | |
3 | |
4 | |
5 | |
wie schon oben vorgeschlagen, schreibe ich dazu vielleicht ein bisschen inline asm code.
Man nimmt die hoechsten Bits des Counter zur addressierung der Sinustabelle. buffer[x] ist deine Sinustabelle? Adrian Figueroa schrieb: > Muss er das denn? Das weist nur du ob der noch was anders machen soll und ob dir 10kHz max. Ausgabefrequenz genuegt. Bei 1.6MHz koenntes du bis rund 500kHz kommen. Hinter dem DAC ein Cauerfilter 7. Ordnung (3 Spulen,7 Kondensatoren) und das wars. Ops wuerde ich da nicht mehr nehmen. Die Ops muessen in dem Bereich bis 1.6Mhz und drueber noch vernueftig Verstaerkung haben damit das Aktiv Filter funktionieren kann.
> Man nimmt die hoechsten Bits des Counter das kommt ja dem schieben gleich..? Ja, der buffer enthält die Signalform. DDRD = buffer[counter&FF000000]: Einfaches maskieren reicht doch nicht, die Werte sind ja viel zu groß > Cauerfilter Mal sehen was das wieder ist :)
Adrian Figueroa schrieb: > Einfaches maskieren reicht doch nicht, die Werte sind ja viel zu groß So isses. Adrian Figueroa schrieb: >> Cauerfilter > Mal sehen was das wieder ist :) Sit eine Filteruebertragunsgfunktion wie Bessel,Butterworth,Tschibyscheff nur steiler. von Wilhelm Cauer in den 30 u. 40 Jahren erfunden. http://de.wikipedia.org/wiki/Wilhelm_Cauer Schaltungstechnisch sehen die alle gleich aus: DAC ---+---L1 ----+-----L2----+----L3-----+-----Ausgang | | | | +---C01----+----C02----+----C03----+ | | | | C1 C2 C3 C4 | | | | GND GND GND GND Wird meistens als Ausgangsfilter in DDS Schaltungen verwendet.
Die Steilheit erkauft man sich allerdings durch ordentliche Überschwinger und die Gruppenlaufzeit is auch eher Banane ;)
Martin Wende schrieb: > Die Steilheit erkauft man sich allerdings durch ordentliche > Überschwinger und die Gruppenlaufzeit is auch eher Banane ;) Macht aber in dem Fall nix.
@ Martin Wende (Firma: fritzler-avr.de) (fritzler) >Die Steilheit erkauft man sich allerdings durch ordentliche >Überschwinger und die Gruppenlaufzeit is auch eher Banane ;) Was bei einem SINUS wohl kaum eine Rolle spielt. Anders sieht es bei Rechteck, Dreieck und Sägezahn aus, hier sollte man besser einen Besselfilter nehmen, auch wenn der weniger steil ist.
Gast
#3115387
Eventuell kannst Du ja hiermit was anfangen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
Das Cauerfilter bei Sinusausgabe hat aber noch einen Vorteil. Im Sperrbereich hat es Stellen wo die Daemfung gegen unendlich geht. Man kann durch geschickte Dimensonierung die 1. Stelle jetzt so legen das sie genau auf die Samplefrequenz faellt. Schon ist diese maximal unterdrueckt.
Im Eingangspost steht nix von einer Signalform, also kann ja auch was anderes als nen Sinus erwünscht sein.
Gast
#3115865
@Autor: Egal (Gast) Dein Code ist schon mal ein guter Ansatz fürs Verständnis. Aber: der counter sollte 24 Bit haben, damit die Frequenz feiner eingestellt werden kann. 24 Bit geht nur in ASM. Zur Berechnung des offset: Wenn die Frequenz häufig geändert wird, kann die Berechnung des offset auf eine Multiplikation mit der Konstanten TIMERRELOAD * AUFLOESUNG / F_CPU reduziert werden. Die Rundung erfolgt -falls überhaupt nötig- über die Addition des nächstniedrigeren Bits (bei 24Bit FTW und 8 Bit Tabelle: Bit15) Die eigentlich DDS-Schleife soll nicht in einer ISR, sondern in der Main (mit voller Geschwindigkeit) laufen.
Gast
#3115873
Und wenn die Tabelle statt im Flash im RAM steht, spart man einen Cycle. So wie ich das sehe, willst Du nur kleine Frequenzen ausgeben, da reicht Deine PWM-Ausgabe. Sonst sollte es schon ein 8 - 10-Bit-DAC sein, zur Not R2R an einem Port.
> volatile union {uint16_t u16; uint8_t u8[2];} counter; Ah das ist ja geschickt :D da spart man sich das ganze schieben.. Für 32bit Zähler dann "union {uint32_t u32; uint8_t u8[4];} counter;"? Greife ich dann mit counter.u8[0] auf die 8 höchsten Stellen zu? Ich weiß nicht so ganz, wie C die Werte im Speicher ablegt. Martin Wende schrieb: > Im Eingangspost steht nix von einer Signalform, also kann ja auch was > anderes als nen Sinus erwünscht sein. Doch, da steht doch was von Dreieck, Rechteck, Sägezahn und Sinus? ;) eProfi schrieb: > So wie ich das sehe, willst Du nur kleine Frequenzen ausgeben, da reicht > Deine PWM-Ausgabe. Sonst sollte es schon ein 8 - 10-Bit-DAC sein, zur > Not R2R an einem Port. Ja, ich möchte einen 8bit Dac benutzen. Die Schaltung habe ich schon gebaut, das Programm ist bekannterweise noch zu optimieren. Ein 8bit dac hängt an einem Port, an ihm ein paar OPAmps zur Wandlung von Strom zu Spannung, Offsetkorrektur, Verstärkung... eProfi schrieb: > Und wenn die Tabelle statt im Flash im RAM steht, spart man einen > Cycle. Wie adressiert man den Ram-Bereich? ich habe da nichts gefunden.. Oder muss man mit assembler manuell die Speicheradresse angeben?
Adrian Figueroa schrieb: > Ah das ist ja geschickt :D da spart man sich das ganze schieben.. Wenn der Compiler gescheit ist sollte er das auch so machen. Adrian Figueroa schrieb: > Greife ich dann mit counter.u8[0] auf die 8 höchsten Stellen zu? Ich > weiß nicht so ganz, wie C die Werte im Speicher ablegt. Yepp, das ist hier die Frage. Das weiss nur der Compiler und haengt ab von der Endiness der Maschine. Adrian Figueroa schrieb: > Wie adressiert man den Ram-Bereich? ich habe da nichts gefunden.. Oder > muss man mit assembler manuell die Speicheradresse angeben? Die meisten Speicherzugriffe beim AVR gehen auf das RAM. Nur bei Konstanten im Flash bedarf es eines gesonderten Befehles. Also ein Array anlegen in C un los gehts.
Ok, dann probiere ich das einfach aus, von wo der Compiler zu zählen anfängt. buffer enthält die aktuelle Signalform. "intervallSetzen" setzt die Schrittweite. schleifenFreq muss ich noch herausfinden, indem ich die Takte der Hauptschleife zähle, freq ist die Vorgabefrequenz. 256/schleifenFreq kann ich ja vorher schon ausrechnen.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
Die Schleife wird laut Atmel Studio zu 36 Clock Cycles (2,25 us; ca. 444kHz max. Samplingrate). Nicht ganz die 10 Takte, die man mit Assembler schafft :D Ohne Union mit dem Shift um 24 Stellen braucht man 40 Takte (2,5 us; ca 400kHz).
Ich habe noch ein Problem. Der Atmega 8 kann keine Pinchange interrupts auf beliebigen Pins, nur auf Port D (int0 und int1) den ich für den dac komplett brauche. Es bleibt nur ein niederfrequenter timer interrupt (vielleicht 40 Hz..), der die Pins abfragt, z.B.:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
Das Teil braucht inklusive hin- und Rücksprung 10 Takte, wenn die Tasten nicht gedrückt werden. Gibt es einen anderen Weg? Ich wollte erst über den Interrupt an einem Knopf die Eingabe starten (und damit den Timer), das geht aber nicht, weil ich den Port D brauche (kein anderer bietet mir 8 Pins).
Hallo! Ich hatte damals die Bedienerschnittstelle mit einem zweiten µC gelöst. Der macht Tasten, Drehencoder, LCD, Umrechnung Frequenz->Phasenakku, Umschaltung Filter und Ansteuerung des dB-Teilers am Ausgang. Mit dem DDS-AVR wird wie bei Jesper seriell kommuniziert, per RX-Interrupt. War aber in Assembler auf 'nem Atmel 89c2051.
Adrian Figueroa schrieb: > Ich habe noch ein Problem. Der Atmega 8 kann keine Pinchange interrupts > auf beliebigen Pins, nur auf Port D (int0 und int1) den ich für den dac > komplett brauche. Wenn du noch einen externen Interrupt haben willst kannst du einen der ICP Eingaenge nehmen. ICP1/PB0. Du brauchst den Caputrewert ja nicht fuer was zu verwenden. Aber du hast deinen Interrupt von aussen.
Gast
#3116957
Falk Brunner schrieb: > AVRs können's halt. Und für NF bis vielleicht 100kHz voll OK. Clever und > leistungsstark. Sportliche Herausforderung.. Wer fordert dich denn da heraus? AVRs könnens halt.. Was können die denn? DDS-Takt bis vielleicht 400 kHz? Bis 100 kHz.. jahaha.. bis 100 kHz DDS-Takt sicherlich, aber ein wirklich brauchbares Ausgangssignal für NF-Zwecke wohl eher nicht. Clever.. ähem, bitte? Also, das Ganze ist offensichtlich eine zweckfreie Betätigung, beí der nicht beabsichtigt ist, zum Schluß irgend etwas tatsächlich benutzbares zu erzeugen. Nun, wenn man das genau so in aller Klarheit am Beginn sieht und trotzdem Lust drauf hat, dann nur zu. Ich hab ja auch den halben Bastelkeller voll mit Bauteilen, die ich mal mit 'da könnte man ja mal...' angeschafft hatte und die seitdem ihrer Verschrottung entgegensehen. Aber wer tatsächlich was BRAUCHBARES erbasteln will, sollte den AVR oder sonstigen uC Favoriten mit nem LCD und ein paar Tasten nebst Drehgeber versehen und zum eigentlichen Signalerzeugen den o.g. AD9833 benutzen. So herum kommt Vernunft in die Sache und die betreffenden Chips kriegen die ihnen angemessene Aufgabe. W.S.
Gast
#3116990
W.S. schrieb: > Aber wer tatsächlich was BRAUCHBARES erbasteln will, sollte den AVR oder > sonstigen uC Favoriten mit nem LCD und ein paar Tasten nebst Drehgeber > versehen und zum eigentlichen Signalerzeugen den o.g. AD9833 benutzen. > So herum kommt Vernunft in die Sache und die betreffenden Chips kriegen > die ihnen angemessene Aufgabe. Nach meiner Schätzung reichen 9 Takte für DDS. Einen weiteren Takt nutzt man für die Ein- und Ausgabe, indem man einen einfachen Softcore programmiert. Taktfrequenz wahrscheinlich Faktor ~50 langsamer, aber für das Interface reicht das locker. Am Ende hat man 2 MHz DDS-Takt @20 Mhz Taktfrequenz. Und das weniger als einen Euro. Dein IC kostet das 10fache.
Helmut Lenzen schrieb: > Aber du hast > deinen Interrupt von aussen. Danke, sehr praktisch! W.S. schrieb: > Aber wer tatsächlich was BRAUCHBARES erbasteln will, sollte den AVR oder > sonstigen uC Favoriten mit nem LCD und ein paar Tasten nebst Drehgeber > versehen und zum eigentlichen Signalerzeugen den o.g. AD9833 benutzen. > So herum kommt Vernunft in die Sache und die betreffenden Chips kriegen > die ihnen angemessene Aufgabe. Das kann ich ja später noch erweitern, wenn ich weiß, wozu ich das Teil überhaupt benutzen will. Fallobst schrieb: > Nach meiner Schätzung reichen 9 Takte für DDS. Wenn ich das so lese, bin ich mit 36 Takten nicht mehr so ganz zufrieden ;) Fallobst schrieb: > indem man einen einfachen Softcore > programmiert. Was ist denn das?
Interessantes Thema (DDS) finde ich. Habe gerade mal überlegt, wie das ganze wohl minimal in Assembler funktionieren könnte und komme auf 7 Takte für den main loop (siehe Anhang). Kann das so funktionieren? Habe länger nichts mehr mit uCs gemacht. Der Trick ist, die Wellenformdaten auf ganze 256-Byte-Blöcke im Ram zu legen und das höchste Byte des Counters als unteres X-Pointer-Byte zu verwenden.
Gast
#3117238
Mit den Daten im RAM gewinnt man noch 1 Zyklus. Allerdings macht es für mehr Qualität schon Sinn die Tabelle auf 1024 Einträge zu verlängern, auch wenn das 2 oder 3 Zyklen extra kostet, das kommt der Qualität zu Gute, vor allem bei sehr niedrigen Frequenzen. Dazu gab es hier auch schon mal einen Thread. Trotzdem bringt die Lösung mit dem AVR und DA Wandler eine eher geringe Qualität. Für den NF Bereich reicht es aber immerhin, etwa auf dem Niveau des XR2206 oder ICL8038, wenn auch mit anderen Schwächen. Als eine Programmierübung (in ASM) bzw. zum lernen ist das auch kein so schlechtes Projekt. So ein IC wie AD9833 oder AD9832 oder auch das oben schon erwähnte fertige Modul mit dem AD9850 (auch wenn die Qualität nicht optimal ist) ist auch nicht mehr so teurer, dafür aber deutlich besser (2 Bit mehr Auflösung beim DA und eine etwa 12-fache maximale Abtastrate (beim AD983x). Die Idee mit dem Softcore klingt interessant, ist aber doch schon recht aufwendig zu Programmieren. Die DDS Schleife wäre entrollt, würde also je Durchgang mehr als 1 Sample ausgeben, also etwa 4 DDS Samples und dann 2-4 Befehle für die 2. parallel auszuführende Aufgabe. Im Prinzip eine Arte Multitasking, nur direkt durch sorgfältiges mischen des Codes der beiden Aufgaben. Ein Softcore ist halt die Simulation einer einfachen CPU, die dann das eigentliche nicht zeitkritische Programm ausführen kann. Wenn man einmal den Softcore fertig hat, wäre man beim Rest flexibel. Die Frage wäre ob der Softcore einfacher ist als die direkte Bedienung von LCD und Tasten usw., möglich wäre das schon. Zumindest den Code für ein Kommunikation mit einem 2. µC könnte man aber auch direkt mit dem DDS Code mischen.
Gast
#3118038
Einen richtigen Softcore zu schreiben, wäre in der Tat aufwändig. Ich dachte dabei eher an eine Mischung aus Softcore und kooperativen Multitasking: Man nutzt einen Takt für den "Softcore" pro DDS-Takt. In diesem wird schon angefangen, den nächsten DDS-Wert zu berechnen. Das macht man solange bis man genügend Takte frei hat. An dieser Stelle könnte man in externen Programmcode springen, der natürlich rechtzeitig und vor allem pünktlich zurückspringen muss. Bsp.:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
Dieser "Softcore" würde 12 Takte pro Softzyklus benötigen. Die DDS-Schleife müsste man also 12+2 (rjmp für die Schleife) = 14 fach aufrollen. Bei einem DDS-Zyklus von 10 Takten und einem Softcore, der immer 4 Takte in einem Block ausführt, läge dessen Taktfrequenz bei 20 Mhz 10 14 * 4 = 429 kHz.
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.
