Hi, zunächst einige Daten zu dem Projekt: 1)Verwendet wird ein Arduino Uno mit einem Atmega 328p 2)Ziel Sinus-PWM generieren mit anschließendem Tiefpass zum abschneiden der Harmonischen. 3)Ich habe hier 2 Look Up -Tables in Calc vorgeschrieben welche einmal einen Sinus mit 50 Hz und 200 Hz realisieren sollen bei denen ich später hin und her schalten möchte.(Ich weiß ich habe hier viel zu viele Werte eingespeichert das kann man sicherlich durch Spiegelung etc. eleganter lösen ich wollte hier trotzdem erst einmal das Grundprinzip verstehen das geht ja auch wenn ich die Werte von 0-2pi plotte). Probleme: 1)Der Sinus der ausgegeben wird siehe Oszi (0.2ms/Div) hat nicht die gewünschten 50Hz sondern hat ca 1.67kHz warum? 2)Sobald ich den 2ten Sinus durchlaufen lasse ist dieser zwar schneller aber die Frequenz passt immer noch nicht mit den gewünschten 200Mhz überein 3)Wie erzeuge ich mir also eine richtige Lookup-Table für 0-2pi sodass hinten die richtige Frequenz raus kommt (ich weiß man bräuchte eigentlich nur 0-pi/2 aber um es zu vereinfachen langt mir erstmal von 0-2pi). Ich bin leider etwas neu im Programmieren mit Registern deshalb sorry, falls etwas nicht schlüssig sein sollte. Mfg Peter Code: //lookup1 -> sinus 50 HZ //lookup2 -> sinus 200 HZ int lookUp1[]= {128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 127, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128, 108, 88, 70, 53, 37, 24, 14, 6, 2, 0, 2, 6, 14, 24, 37, 53, 70, 88, 108, 128, 147,}; int lookUp2[]= {128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 127, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 127, 53, 6, 6, 53, 127, 202, 249, 249, 202, 127, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202, 249, 249, 202, 128, 53, 6, 6, 53, 128, 202,}; const int ledPin = 3; //digitalerPin11 int i = 0; void setup() { DDRB |=(1<<ledPin); //setze LEDPin als Ausgang TCCR2A =(1<<WGM21)|(1<<WGM20) //Lege Timer fest mit Vergleich am OCRA Register und Fast PWM |(1<<COM2A1); //Lege VergleichsRegister an OC2A BOTTOM set TCCR2B = (1<<CS20); //Prescaler hier ohne Prescaler OCR2A = lookUp1[i]; //->AusgangsSpannung TIMSK2 = (1<<OCIE2A); //Enable den Overflow ISR sei(); //globale Interrupts alle on } //loop gehen wir nicht mehr void loop() { } //Timer Interrupt ISR(TIMER2_COMPA_vect){ if(i<sizeof(lookUp1)/sizeof(int)){ i++; OCR2A = lookUp1[i]; //->AusgangsSpannung }else{ i=0; } }
> aber die Frequenz passt immer noch nicht mit den gewünschten 200Mhz > überein Sollte 200 Hz heißen
Nun ja, der Uno läuft, glaube ich, mit 16 MHz, der Timer2 über seine vollen 8 bit: 16 MHz / 2^8 = 62500 Hz. Dies geteilt durch die Anzahl der Werte pro Wellenzug ergibt die resultierende Frequenz. Die Tabelle abzählen erspare ich mir jetzt.
Peter schrieb: > 1)Der Sinus der ausgegeben wird siehe Oszi (0.2ms/Div) hat nicht die > gewünschten 50Hz sondern hat ca 1.67kHz warum? Ja warum wohl? Offensichtlich gibst du die Werte aus der Tabelle zu schnell aus. Um mit deiner Methode auf die korrekte Frequenz zu kommen, bräuchtest du eine sehr viel größere Sinustabelle. Deswegen benutzt man üblicherweise ein Methode, die sich DDS nennt, um den Fortschritt in der Sinustabelle so zu steuern, dass sich die gewünschte Ausgabefrequenz ergibt. Das hat außerdem noch den Vorteil, dass man nur eine einzige Sinustabelle benötigt, um unterschiedliche Frequenzen erzeugen zu können. Als "Controllator" für den Tabellenfortschritt benutzt man typischerweise Festkomma-Arithmetik, die so gestaltet ist, dass der ganzzahlige Teil den Tabellenindex darstellt. Bei jedem Interrupt addierst du dann einfach eine Konstante in diesem FP-Format zu einer Laufvariable im gleichen Format. Den ganzzahligen Teil der Laufvariable trennst du dann ab und benutzt ihn als Tabellenindex, um an den Ausgabewert zu kommen. Aus Gründen der Laufzeiteffizient ist es übrigens sinnvoll, der Sinustabelle die Größe einer Zweierpotenz zu geben, also z.B. 256, 512 oder 1024 Samples, dann kann man die FP-Arithmetik auch bezüglich des Überlaufproblems am Tabellenende sehr schön einfach halten. > //Timer Interrupt > ISR(TIMER2_COMPA_vect){ > > if(i<sizeof(lookUp1)/sizeof(int)){ > i++; > OCR2A = lookUp1[i]; //->AusgangsSpannung > }else{ > i=0; > } > } Das war übrigens selbst für deinen Ansatz falsch. Du mußt immer einen Wert ausgeben, auch bei Tabellenende. Die Ausgabe gehört also hinter das if-else-Konstrukt, nicht in einen der beiden Zweige desselben.
Ich habe jetzt auch nicht die Tabelle abgezählt, um herauszufinden, wieviel Steps ein voller Wellenzug beinhaltet. Sinnvollerweise betreibst du den Timer im CTC Modus, um die Abtastgeschwindigkeit der Tabelle einzustellen.
an Peter:
> Anzahl der Werte pro Wellenzug
'Wellenzug', wohlgemerkt, davon stehen offenbar in der Tabelle mehrere.
> Sinnvollerweise betreibst du den Timer im CTC Modus, um die > Abtastgeschwindigkeit der Tabelle einzustellen. Also wie gesagt in der Uni haben wir das nie gelernt wie man sowas wirklich in die Praxis umsetzt haha, aber ich hab gedacht die Schaltfrequenz von meinem Sägezahn-Signal muss immer konstant sein und das ist es ja grade nicht beim CTC Modus oder?
S. Landolt schrieb: > an Peter: > >> Anzahl der Werte pro Wellenzug > 'Wellenzug', wohlgemerkt, davon stehen offenbar in der Tabelle mehrere. Ja ich weiß hab halt mal ein paar mehr gemacht
Mal den Mode 5 näher anschauen: da lässt sich mit OCRA der TOP-Wert und damit die Frequenz vorgeben, mit OCRB die Amplitude (d.h. die Tabellenwerte kommen bei diesem Modus in OCRB). Allerdings wird damit die Frequenz erstmal nur höher; für eine niedrigere Frequenz benötigt man mehr Werte pro Wellenzug oder man muss die paar vorhandenen mehrfach hintereinander zuweisen.
"Uni"? - Dann sollte doch der Wikipedia-Artikel ausreichen: https://de.wikipedia.org/wiki/Direct_Digital_Synthesis
PS: Vielleicht findet sich auch etwas hier in der Artikelsammlung des Forums, wäre dann sicher praxisnäher.
S. Landolt schrieb: > "Uni"? - Dann sollte doch der Wikipedia-Artikel ausreichen: > https://de.wikipedia.org/wiki/Direct_Digital_Synthesis Erstmal danke für die ganzen Antworten ja da muss ich mich jetzt erstmal rein arbeiten :). Ja Uni deswegen kommt man aber lange nicht weiter nur weil man einen Wiki Artikel gelesen hat der gibt mir max. einen groben Überblick haha. Außerdem studiere ich Elektrotechnik und keine Info. also gehört so ein Projekt grade nicht zu meinem Tagesgeschäft mache das alles neben dem ganzen Lern-Stress in meiner Freizeit also alles mit der Ruhe haha.
Okay - hier gibt es viele, die gerne weiterhelfen, auch bei den Grundlagen. Dann also erstmal einen Abend in Ruhe.
Peter schrieb: > Sägezahn-Signal muss immer konstant sein und das ist es ja grade nicht > beim CTC Modus oder? Der CTC Modus ist das gleiche wie ein OVF, nur das der OVF immer beim Überlauf auftaucht und im CTC Modus beim Vergleichswert, sozusagen ein einstellbarer Überlauf. Während der Timer im Normalmodus also immer beim Überlauf von 0xFF nach 0 die ISR auslöst, tut er das im CTC Modus also früher. Damit kann man die Abtastfrequenz erhöhen. Um die Abtastfrequenz zu verlangsamen, kann man in der ISR eine Variable X hochzählen und das PWM Register erst laden, wenn diese einen bestimmten Wert erreicht hat. Wenn die Tabelle fix ist, kann man auch eine Kombination beider Methoden verwenden, um möglichst viele verschiedene Frequenzen zu erzeugen. Dabei wird eine Verkleinerung des CTC Vergleichswertes die Frequenz erhöhen und eine Vergrösserung von X die Frequenz senken.
Matthias S. schrieb: > Damit kann man die Abtastfrequenz erhöhen. Um die Abtastfrequenz zu > verlangsamen, kann man in der ISR eine Variable X hochzählen und das PWM > Register erst laden, wenn diese einen bestimmten Wert erreicht hat. > > Wenn die Tabelle fix ist, kann man auch eine Kombination beider Methoden > verwenden, um möglichst viele verschiedene Frequenzen zu erzeugen. Dabei > wird eine Verkleinerung des CTC Vergleichswertes die Frequenz erhöhen > und eine Vergrösserung von X die Frequenz senken. Ah okay das hört sich nach einer Idee an danke : also das heißt den CTC Wert verkleinern erhöht mir die Abtastfrequenz je schneller die Frequenz meines Sinus desto schneller muss ich natürlich abtasten nach Nyquist ja min. 2*fmax = fabtast. Und mit der Zähl-Variable steuer ich mir quasi welche Frequenz hinten raus kommen soll. Jetzt dazu einige Fragen: 1) Die Abtast Frequenz ist ja dementsprechend nicht mehr constant wir hatten gelernt diese Immer constant zu lassen und deshalb eine sehr schnelle Frequenz zu wählen das wir immer genug Samples haben, deshalb habe ich auch in meinem Code keinen Prescaler verwendet. Nun habe ich gedacht das das Sägezahnssignal unabhängig von meinem auf moduliertem Signal nämlich dem Sinus ist solang der Sägezahn schneller ist als dieser (nur dann sind ja genügend Samples da). Deshalb verstehe ich nicht warum sich jetzt durch eine höhere Abtast-Frequenz irgendwas an der Frequenz meines Sinus ändern soll es ist doch immer noch der gleiche Sinus ? 2) Was ich auch immer noch nicht verstanden habe bei der ISR musste ich nie das Bit für den Ausgang an PORTB setzen sondern das wurde schon immer automatisch anscheinend Hardware Intern gemacht. Wie kann ich jetzt mit einer Laufvariable verhindern das meine ISR die ganze Zeit ausgeführt wird? nach Compare versteht sich. Soll ich einfach solange ich nicht will das geschaltet wird einfach OCR2A auf einem konstanten Wert lassen ? Oder wie soll ich das mit der Zählvariable machen? Code: //Timer Interrupt ISR(TIMER2_COMPA_vect){ if(i<sizeof(lookUp1)/sizeof(int)){ i++; }else{ i=0; } OCR2A = lookUp1[i]; //->AusgangsSpannung }
Peter schrieb: > desto schneller muss ich natürlich > abtasten nach Nyquist ja min. 2*fmax = fabtast Ich denke genau das ist dein Problem. 1. Da gehört kein = hin sondern ein < nach Nyquist ;) 2. Du weißt ja schon, dass du einen Sinus haben wirst und auch die Frequenz ist bekannt (entweder 50 Hz oder 200 Hz). Muss dann immer noch das Nyquist-Kriterium fabtast > 2*fmax erfüllt sein? Denk mal drüber nach...und denke auch dran, du willst ja den Sinus generieren, nicht messen ;) Ich hab mal für eine Resolver-Auswertung ein Sinus generieren lassen mit 144 (alternativ 57 und 28) Stützstellen im Sinus, die Frequenz des Referenzsignals lies sich zwischen 200 Hz und 2 kHz dabei einstellen und ich nutzte dabei den OVF-Interrupt um in das PWM-Register den nächsten, passenden Wert zu schreiben. Die Frequenz des Sinus ergab sich aus der Anzahl der Stützstellen (weniger Stellen, Array ist schneller durchlaufen, Frequenz des Sinus ist höher). Der Vorteil dieses Verfahrens war/ist dass ich immer einen vollständigen PWM-Zyklus hatte, der Nachteil ist halt, dass man nicht beliebige Frequenzen einstellen konnte (war bei mir auch nicht nötig, es wurden nur 400, 1kHz und 2kHz gefordert).
Peter schrieb: > Was ich auch immer noch nicht verstanden habe bei der ISR musste ich nie > das Bit für den Ausgang an PORTB setzen sondern das wurde schon immer > automatisch anscheinend Hardware Intern gemacht. Das ist wahr. Aber du hast so viele Timer im Mega328, das du einen als PWM Erzeuger und einen als Abtastfrequenzgenerator (Samplerate) benutzen könntest. Wenn dir 256 Stufen in der PWM reichen, nimmst du Timer 0 oder 2 als PWM Generator und Timer 1 als Samplerate, denn das ist ein 16-Bit Timer. Läuft Timer 1 über (entweder als CTC oder freilaufend), lädst du in seiner ISR das PWM Register von Timer 0 oder 2 nach.
:
Bearbeitet durch User
M. K. schrieb: > 1. Da gehört kein = hin sondern ein < nach Nyquist ;) > 2. Du weißt ja schon, dass du einen Sinus haben wirst und auch die > Frequenz ist bekannt (entweder 50 Hz oder 200 Hz). Muss dann immer noch > das Nyquist-Kriterium fabtast > 2*fmax erfüllt sein? Denk mal drüber > nach...und denke auch dran, du willst ja den Sinus generieren, nicht > messen ;) zu 1.) Ja deswegen habe ich ja auch min.=mindestens geschrieben :) zu 2.) Hmm ja ich denke schon oder? Wenn ich im Zeibereich meinen Sinus-abtaste(das ist ja grade die LookUp) dann dürfen sich die Frequenz-Spektren im Bild-Bereich ja nicht überlappen sonst bekomm ich bei der Rück-Transformation in den Zeitbereich Schrott raus (so viel zum messen).Wenn ich nun zu wenige Werte in der LookUp habe würde ich eben eine lineare Spline Interpolation dieser Punkte am Oszi sehen oder? Klär mich auf ich verstehs nicht haha
Peter schrieb: > zu 1.) Ja deswegen habe ich ja auch min.=mindestens geschrieben :) Das ist aber falsch, nicht "gleich" sondern "größer als", das ist ein Unterschied. Ansonsten einfach mal ein Sinus vergegenwärtigen: Wäre "gleich" OK und man hat "Glück" (Murphy und so ;)) dann erwischt man immer genau die Null-Durchgänge und meint, da ist ja nix ;) Peter schrieb: > zu 2.) Hmm ja ich denke schon oder? Nein, da du weißt, dass es ein Sinus ist und da dir auch die Frequenz bekannt ist brauchst du ausschließlich 2 Punkte deren Abstand kein ganzzahliges Vielfaches der halbe Periode entspricht. Damit lässt sich das Signal exakt rekonstruieren. Probiers einfach mal aus. ;)
M. K. schrieb: > ausschließlich 2 Punkte deren Abstand kein > ganzzahliges Vielfaches der halbe Periode entspricht Das verstehe ich nicht siehe Bild hätte ich nur 2 Punkte meines Sinus (die deinen Bedingungen entsprechen) so würde nach der Tiefpass-Filterung eben genau das grüne Signal wieder rauskommen oder?
Dein TP weiß ja auch nicht, dass er einen 50Hz Sinus filtern soll ;)
M. K. schrieb: > Dein TP weiß ja auch nicht, dass er einen 50Hz Sinus filtern soll > ;) Du sprichst in Rätseln. Keine Ahnung ich mach da meine Fourier Traffo und knall mit meinem Tiefpass alle Harmonischen weg damit ich meine Grundschwingung raus bekomme ich weiß nicht was du mir sagen willst und ich weiß auch immer noch nicht wie ich mir eine LookUp-Table generiere sodass bspw. mein Sinus mit 50Hz hinten raus kommt ich hab gedacht ich kann das mit dem Fast PWM Mode machen-> überlagere die Zägezahn Schwingung meinem Sinus und fertig ist der Lack ist aber anscheinend nicht so einfach ich versteh einfach nicht warum ich da die Geschwindigkeit regulieren muss mit der die Werte in das VergleichsRegister geladen werden ich möchte das doch gerade schnell machen damit ich dort auch einen Sinus mit hohen Frequenzen einlesen kann. Denn mein Zägezahnssignal muss ja im Endeffekt schneller sein als mein Sinus ansonsten könnte ich ja bspw 2 Sinus-Perioden in einer Zägezahn-Schwingung haben und somit wäre mein Signal verfälscht.
OK, ich versuchs mal einfach zu halten, am WE kann ich auch mal nen konkreten Code senden. Ich will aber erstmal das Ganze abstecken: Du hast einen Atmega328P und willst hier ein Sinussignal generierern, dass Wahlweise mit 50 Hz oder mit 200 Hz schwingt, ja? Einen Schaltplan hast du schon vorliegen oder brauchst du den auch noch?
M. K. schrieb: > OK, ich versuchs mal einfach zu halten, am WE kann ich auch mal nen > konkreten Code senden. Oh wow okay danke haha Ja das mit den 50 Hz bzw. 200 Hz hatte ich gemacht um es erstmal einfach zu halten, ich wollte aber eigentlich zum Schluss einen Sinus von (0-max)Hz generieren ich weiß nicht wo da die Obergrenze beim Atmega328P liegt wie gesagt ich kenne mich mit IC Programmierung noch nicht so wirklich aus taste mich da gerade erst ran. Danke gell für die Unterstützung :)
M. K. schrieb: > Einen Schaltplan hast du schon vorliegen oder > brauchst du den auch noch? Nein Schaltplan brauch ich nicht das bekomme ich schon selber hin mir geht es nur um die Programmierung :)
Ich hab ein Beispiel geschrieben, schau es dir mal an. Ich gehe davon aus, dass du C kannst. Ich benutze als PWM-Ausgang hierbei PB1 und Timer 1 als PWM-Generator. Zudem nutze ich hierbei dem Overflow-Interrupt des Timer 1 um nach jedem PWM-Zyklus den nächsten Wert einzuladen. Die Lookup-Table ist so aufgebaut, dass sich ein 50 Hz Sinus ergibt wenn der Atmega auf 16 MHz läuft und man jeden Wert der Tabelle nutzt. Die Frequenz der PWM ergibt sich dadurch zu 62.5 kHz, wenn du deinen Tiefpass also auf z.B. 1 kHz Grenzfrequenz auslegst solltest du da einen recht brauchbaren Sinus für 50 Hz und 200 Hz bekommen. Ich hoffe, dass dir das hilft.
Hi erstmal vielen vielen Dank für den Code ich hab deinen Code jetzt mal nachvollzogen also man geht quasi so vor: 1) Erzeuge eine Look-Up-Table mit möglichst vielen Werten des Sinus (200 Stützstellen sollten ausreichen) 2) Durchlaufe die Tabelle des Sinus einmal schneller einmal langsamer um die Frequenz dadurch zu skalieren das heißt aber im klar Text ich brauch viele Samples für eine hohe Frequenz wenn ich mit dieser Methode hohe Frequenzen ausgeben will berechne ich mir eben eine neue Look-Up Table für verschiedene Frequenz Bereiche oder? :) 3) Des Weiteren verstehe ich auch noch nicht warum man den TIMER1_OVF benutzt das heißt ja das wir immer nach einem Überlauf in dem Fall dem (Timer_1 8 bit Fast PWM) immer nach 255 Werten überlaufen jetzt vergleichen wir ja die ganze zeit mit dem OCR1A Register und lösen die Interrupt Methode immer bei einem Vergleich aus und nicht nach einem Überlauf deshalb verstehe ich nicht warum man da nicht den TIMER1 COMPA Fall nehmen muss? 4) Ich hab deinen Code mit LookUp 1:1 bei mir rein kopiert und bekomme zum schluss einen -|sin(w*t)| raus also eventuell ist dir da ein Fehler passiert beim berechnen der Werte oder ich hab einen Fehler gemacht?
Ich hab grad nochmal drüber geschaut und meine zwei Fehler entdeckt zu haben. 1. Meine Zählvariable i hab ich als 8-Bit-Wert definiert, soll aber bis 1250 laufen...finde den Fehler ;) 2. Ich hatte für 50Hz bei 16MHz Taktfrequenz 1250 Werte ausgerechnet...jetzt wo ich noch mal so drüber nachdenke...müssten eigentlich 625 Werte sein, die mein Beispiel für 50Hz braucht. Nun zu deinen Anmerkungen zu 2) Ja, das ist der Nachteil meiner Methode. Ich lasse den Timer immer konstant laufen und hole mir die Frequenz dadurch, dass ich eine recht große Wertetabelle in unterschiedlichen Schritten durchlaufen. Man könnte die Wertetabelle auch konstant lassen und alternativ den Timer schneller laufen lassen. zu 3) Vergleiche mein angehangenes Bild (hier wurden 32 Stützstellen für den Sinus verwendet damit man auch die PWM-Zyklen gut erkennen kann). Wir starten beim ersten Puls der PWM. Hier in diesem Beispiel ist das ein 0% Puls. Der nächste Puls muss etwas größer sein und nun die Frage: Wann muss der Wert für den nächsten Puls in das zugehörige PWM-Register geladen werden? Genau, wenn der aktuelle PWM-Zyklus durchgelaufen ist. Und das ist der Fall wenn der Timer sein Maximum erreicht hat und zurück zu 0 springt. Netter Nebeneffekt dabei: Das erzeugt auch eine Overflow-Event des Timers und genau den nutze ich um genau dann als erstes den neuen Wert ins PWM-Register zu schreiben ;) zu 4) Ich habs grad auch bei mir getestet (deshalb fielen mir die oben genannten beiden Fehler auf ;)) und da funktioniert der Code erwartungsgemäß. Ändere mal in Zeile 13 das "volatile uint8_t i=0;" in "volatile uint16_t i=0;" Dann solltest du einen 25 Hz Sinus (genauer bekommst du dann den Verlauf: Vcc/2*sin(t*pi/20ms)+(Vcc/2)) nach dem Tiefpass erhalten.
:
Bearbeitet durch User
M. K. schrieb: > Vergleiche mein angehangenes Bild Haha welches Bild? und ja optimal jetzt läufts bekomme allerdings einen sauberen 50 Hz Sinus raus für i=1 :) Daaankeee ! Top top okay zu 3) Habe ich jetzt verstanden danke! Dann noch einige Fragen bezüglich der maximal möglichen Frequenz ich kann ja mit meinem Arduino max. f=16Mhz einspeißen das heißt also bei einem 8 bit Timer und der obigen OVF-Methode bekomme ich eine maximale Sägezahn-Freq. von ca. 63Khz hin Formel für die 63kHz-> 1/((1/(16*10^6Hz))*255) Jetzt kann ich ja maximal einen Sinus ausgeben der langsamer als diese 63kHz sind oder? Ist auch irgendwie möglich in den MHz oder gar GHz Bereich zu kommen? Oder geht das nur mit einem externen Oszillator oder wie wird das üblicherweise gemacht weißt du das zufällig?
Peter schrieb: > Ist auch irgendwie möglich in den MHz oder gar GHz Bereich zu kommen? Das geht natürlich nicht. Der Atmega läuft maximal mit 16 MHz und selbst der kürzeste Befehl braucht 1 Zyklus, für ein periodisches Signal müsste man einen Befehl aber zwei mal aufrufen (einmal High und einmal Low setzen), da wären also schon mal nur noch 8 MHz drin. Und ich habs grad nicht auswendig im Kopf ob das Setzen der Portpins, bis das Signal am Pin auch wirklich steht, auch innerhalb dieses einen Zyklus anlegbar ist, ich fürchte aber dass das auch 1-2 Zyklen braucht. Peter schrieb: > Dann noch einige Fragen bezüglich der maximal möglichen Frequenz ich > kann ja mit meinem Arduino max. f=16Mhz einspeißen das heißt also bei > einem 8 bit Timer und der obigen OVF-Methode bekomme ich eine maximale > Sägezahn-Freq. von ca. 63Khz hin Formel für die 63kHz-> > 1/((1/(16*10^6Hz))*255) Ne, das bedeutet nur dass du maximal alle 1/63k Sekunden einen neuen Wert für das Sägezahn-Signal ins entsprechende Register laden kannst, damit hast du am Ausgang noch lange kein Sägezahnsignal. In jedem OVF-Interrupt wird lediglich der nächste Wert zur Signalerzeugung ins PWM-Register geladen. Damit hat man aber noch lange nicht das Signal erzeugt. ;) Peter schrieb: > und ja optimal jetzt läufts bekomme allerdings einen sauberen 50 Hz > Sinus raus für i=1 :) Daaankeee ! Top top Sehr schön ;)
M. K. schrieb: > Ich hab ein Beispiel geschrieben, schau es dir mal an. Ich gehe davon > aus, dass du C kannst. Ich kann kein C, würde es aber trotzdem gerne sehen. Welche Datei muss ich da womit wie öffnen? Oder kannst Du den Code hier bitte so einstellen, dass man ihn mit der Foren-Codeansicht lesen kann? Vielen Dank, LG old.
Aus der W. schrieb: > Ich kann kein C, würde es aber trotzdem gerne sehen. > Welche Datei muss ich da womit wie öffnen? Einfach das Zip entpacken und die Dateien mit einem Texteditor betrachten, am besten einer, der auch Syntaxt Highlighting für C-Files kann, dann ist der Code recht gut lesbar ;) Interessant sind die Files mit .c und .h, der Rest ist für Compiler/Linker und Co ;)
:
Bearbeitet durch User
Vielen Dank sylaina. Mit WordPad konnte ich mal reinschnuppern. Da ich kein C kann, ist auch nicht mehr als reinschnuppern. Geschweige denn das in meinen Code zu fummeln. Hier die Codeansicht: https://www.mikrocontroller.net/attachment/highlight/450237 Beitrag "Re: Arduino Kondensatormotor Drehzahlsteuerung Schaltplan und Sketch 200327" Wo läge denn da der Vorteil gegenüber der Methode Multivibrator, Zähler und if-Liste allgemein und speziell bezogen auf meine FU Anwendung? LG old.
Aus der W. schrieb: > Da ich kein C kann, ist auch nicht mehr als reinschnuppern. > Geschweige denn das in meinen Code zu fummeln. Ich hab mal reingeschaut in deinen Code. Also ich empfehle dir erstmal was einfaches in C zu machen, so Back to the Roots like. Schon die ganzen If-Statements sind da ziemlich unnötig. Kleines Beipsiel hierzu mal:
1 | ...
|
2 | int myVariable; |
3 | ...
|
4 | myVariable = 1; |
5 | if (myVariable == 1) doSomething_1(); |
6 | if (myVariable == 2) doSomething_2(); |
7 | if (myVariable == 3) doSomething_3(); |
8 | ...
|
Hierzu jetzt die Frage an dich: Meinst du es ist sinnvoll alle If-Statements abzuarbeiten? Oder liese sich das nicht geschickt anders formulieren?
M. K. schrieb: > Hierzu jetzt die Frage an dich: Meinst du es ist sinnvoll alle > If-Statements abzuarbeiten? Es ist schlicht egal. Die Zeit zum Überspringen addiert sich zur Periodendauer und senkt die Frequenz unmerklich. Wo ich drauf aufpassen muss ist, nicht irgendwelche Dinge sporadisch zu tun. Das gibt einen Jitter in der Periodendauer. > Oder liese sich das nicht geschickt anders > formulieren? Keine Ahnung. Mir erscheint das nicht ungeschickt weil ich das nicht anders kenne. Was verstehst Du unter if-Statements? In Deinem Beispiel sind die if Bedingungen doch auch. myvariable = zahl doSomething_1() = (enup = LOW; enun = HIGH; enzp = LOW; enzn = LOW; usin = 100; zsin = 0;) Ich erkenne da jetzt keine Abkürzung. LG old. PS: Falls das hier stört, können wir gerne in meinen Thread dafür wechseln.
Aus der W. schrieb: > Es ist schlicht egal. Die Zeit zum Überspringen addiert sich zur > Periodendauer und senkt die Frequenz unmerklich. Wenn du beim ersten If-Statement einen Treffer bei dir hast und da noch 19 andere folgen...das macht keinen Sinn die 19 anderen Statements auch noch abzuarbeiten. Aus der W. schrieb: > Was verstehst Du unter if-Statements? > In Deinem Beispiel sind die if Bedingungen doch auch. > myvariable = zahl > doSomething_1() = (enup = LOW; enun = HIGH; enzp = LOW; enzn = LOW; usin > = 100; zsin = 0;) > Ich erkenne da jetzt keine Abkürzung. Ist ja auch keine Abkürzung aber man könnte stattdessen schreiben:
1 | ...
|
2 | int myVariable; |
3 | ...
|
4 | myVariable = 1; |
5 | switch (myVariable){ |
6 | case 1: |
7 | doSomething_1(); |
8 | break; |
9 | case 2: |
10 | doSomething_2(); |
11 | break; |
12 | case 3: |
13 | doSomething_3(); |
14 | break; |
15 | default:
|
16 | doNothing(); |
17 | break; |
18 | }
|
Das wäre 1. lesbarer als die ganzen Sachen mit if-Statements zu machen und 2. auch noch schneller weil nicht so viel geprüft werden muss (es wird immer nur ein Case abgearbeitet bzw. ohne Treffer das default). Warum du hier kein if...else... benutzt hast verstehe ich auch gar nicht, schon das hätte wenigstens etwas Rechenaufwand reduziert (wenngleich auch nicht alle if-Prüfungen damit eleminiert werden, wenn z.B. erst das letzte if ein Treffer ist werden ja immer noch alle vorhergehenden abgearbeitet) Und jetzt gehe ich noch ein wenig weiter rein bei dir:
1 | ...
|
2 | if(zahl == 5) |
3 | {enup = LOW; enun = LOW; enzp = HIGH; enzn = LOW; usin = 0; zsin = 100;} |
4 | if(zahl == 6) |
5 | {enup = LOW; enun = LOW; enzp = HIGH; enzn = LOW; usin = 0; zsin = 100;} |
6 | if(zahl == 7) |
7 | {enup = HIGH; enun = LOW; enzp = HIGH; enzn = LOW; usin = 45; zsin = 89;} |
8 | if(zahl == 8) |
9 | {enup = HIGH; enun = LOW; enzp = HIGH; enzn = LOW; usin = 71; zsin = 71;} |
10 | if(zahl == 9) |
11 | {enup = HIGH; enun = LOW; enzp = HIGH; enzn = LOW; usin = 89; zsin = 45;} |
Die Statements unterscheiden sich doch nur in usin und zsin, enup und Co sind hier immer gleich. Das liese sich doch vereinfachen ;) Aber, und das sieht man das nächste Manko, die ersten beiden if-Statments unterscheiden sich gar nicht, es ist schlicht egal ob zahl 5 oder 6 ist. Generell sieht es mir so aus als seien hier alle Variablen von Zahl abhängig. Wie hast du das berechnet? Wäre es hier nicht eine Idee generell die Variablen direkt aus der Variable zahl zu berechnen? Die ganzen ifs zeigen nämlich eins: Die Variablen enup, enun, enzp, enzn, usin und zsin sind anscheinend nur von zahl abhängig, es sind also f(zahl)-Funktionen. Statt also hunderte von ifs abzuarbeiten könntest du auch einfach 6 Werte ausrechnen lassen. Das dürfte erheblich schneller sein als deine if-Orgien abzuabreiten.
Hallo sylaina, ich werde versuchen die switch-case-Struktur umzusetzen. Vielen Dank. Weil das jetzt speziell um meinen FU geht, habe ich Dir da Beitrag "Re: Arduino Kondensatormotor Drehzahlsteuerung" geantwortet. LG old.
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.