Hallo zusamen ich bin dabei mir eine SPWM zu programmieren. Leider komme
ich nur eine Frequenz von 5kHz hin obwohl ich in meiner Berechnung des
OCR1A 80kHz ausgerechnet habe. Hat da jemand tipps für mich?
Gibt es sonst vielleicht noch eine andere eleganter Lösung das
Sinussignal zu erzeugen?
Ich habe vor längerer Zeit mal mit dem Micro einen Blitzer gebaut und
hatte ähnliche Probleme. Die Lösung war das Umstellen einer Fuse damit
das Ding mit 16MHz lief. In der Liefereinstellung waren es nur 1Mhz.
Gruß, Tom
Black D. schrieb:> Hast du dafür vielleicht noch eine Anleitung?
Die Fuses solltest du definitiv im Datenblatt oder Wiki nachlesen, bevor
du dich aussperrst!
Black D. schrieb:> Hallo zusamen ich bin dabei mir eine SPWM zu programmieren.
Was soll das denn sein? Eine Sinus PWM?
Der PWM ist es egal, mit welchem Signal sie moduliert wird, der Begriff
SPWM ist Unfug.
> Leider komme> ich nur eine Frequenz von 5kHz hin obwohl ich in meiner Berechnung des> OCR1A 80kHz ausgerechnet habe. Hat da jemand tipps für mich?
Tja. Siehe AVR Fuses.
> Gibt es sonst vielleicht noch eine andere eleganter Lösung das> Sinussignal zu erzeugen?
Naja, dein Interrupt ist etwas merkwürdig. Was soll das denn werden?
Eine PWM, welche mit einem Sinus moduliert wird, und dann noch ein
Dreieck?
Deine Sinutabelle verschwendet 1600 Bytes RAM. Die kann man direkt in
den Flash legen und auch direkt daraus lesen. Geht mit normalem C als
auch Arduino-Style.
https://www.arduino.cc/reference/en/language/variables/utilities/progmem/
In deinem Programm wird aber nirgendwo die PWM neu eingestellt. Wie soll
da ein veränderliches Tastverhältnis rauskommen?
Zum Thema Sinus und PWM siehe hier
https://www.mikrocontroller.net/articles/Pulsweitenmodulation#Siehe_auch>Hier noch mal ein Screenshot vom scope
Naja, der ist eher verwirrend. Wenn man sehen will, was die PWM so
macht, muss man wieder DEMODULIEREN, sprich, filtern. Ein RC-Tiefpass
mit vielleicht 1k+100nF sollte es tun, die Grenzfrequenz sollte im
Bereich von 1-10% der PWM-Frequenz liegen.
Ich greife einfachmal diesen Beitrag wieder auf weil es immer noch um
eine sinusförmige PWM geht nur diesmal versuche ich das ganze mit zwei
AD9833 Boards. Wie im Code zu sehen erzeugt eine Platine ein Sinussignal
mit 50Hz und die andere Platine ein Dreiecksignal mit 25kHz. Beide
Signale lese ich über A1 und A2 ein. Diese werden dann noch mit map
skaliert und in die Variable S und D geschrieben.
Ich brauche bestimmt noch ein delay oder millis um die Werte richtig
einzulesen? Wie programmiere ich die?
Die Werte muss ich dann bestimmt in einem Interrupt Timer vergleichen
damit ich auch die 25kHz des Dreiecksignals in dem sinusförmigen PWM
Signal ausgeben kann?
Black D. schrieb:> Ich greife einfachmal diesen Beitrag wieder auf weil es immer noch um> eine sinusförmige PWM geht nur diesmal versuche ich das ganze mit zwei> AD9833 Boards. Wie im Code zu sehen erzeugt eine Platine ein Sinussignal> mit 50Hz und die andere Platine ein Dreiecksignal mit 25kHz. Beide> Signale lese ich über A1 und A2 ein. Diese werden dann noch mit map> skaliert und in die Variable S und D geschrieben.
Und was soll die Aktion? Nur umständlicher und sinnfreier kann man keine
PWM erzeugen!
Black D. schrieb:> Ich möchte damit einen Vollbrücken Wechselrichter ansteuern.> So soll die PWM aussehen:
Und warum nutzt du dann nicht die Hardware-PWM IM AVR?
Mein erster Versuch war es zwei Arrays zu erzeugen, die einmal das
Sinussignal und das Dreiecksignal darstellen sollen. Durch den Timer1
der auf 80kHz eingestellt ist wird aus dem Sinusarray ein Signal mit
50Hz und aus dem Dreieckarray ein Signal mit 5kHz.
Ich habe auch schon die Werte den Timer1 und die Werte im Sinusarray auf
25kHz angepasst nur ist das wohl für den Timer1 zu viel. Gemessen
bekomme ich als sinusförmige PWM nur 7kHz. Ich weiß das mit den beiden
arrays sieht sehr umständlich aus. Auf eine andere Idee bin ich bisher
nicht gekommen.
Black D. schrieb:> Mein erster Versuch war es zwei Arrays zu erzeugen, die einmal das> Sinussignal und das Dreiecksignal darstellen sollen. Durch den Timer1> der auf 80kHz eingestellt ist wird aus dem Sinusarray ein Signal mit> 50Hz und aus dem Dreieckarray ein Signal mit 5kHz.
Bist du ein Troll oder einfach nur total begriffsstutzig?
Hast du meine Beiträge und die Links mal gelesen? Und vielleicht auch
verstanden? Ich fürchte, keine Sekunde!
Diesen Unfug mit dem Dreieck braucht man nicht, wenn man mit einem
Mikrocontroller voll digital eine PWM erzeugt! Diese
Dreieck-Vergleichsgeschichte ist ein Prinzip aus Lehrbüchern, das man
noch in analogen Lösungen mit OPVs und Komparatoren macht. Nicht aber
digital! (Naja, irgendwie schon, der Zähler ist das digitale Dreieck).
> Ich habe auch schon die Werte den Timer1 und die Werte im Sinusarray auf> 25kHz angepasst nur ist das wohl für den Timer1 zu viel. Gemessen> bekomme ich als sinusförmige PWM nur 7kHz. Ich weiß das mit den beiden> arrays sieht sehr umständlich aus. Auf eine andere Idee bin ich bisher> nicht gekommen.
LESEN und versuchen zu verstehen. Du bist mordsmäßig auf dem Holzweg!
Höchstwahrscheinlich letzteres. Hab wie du schon geschrieben hast die
ganze zeit immer an dem Sinus und Dreieckvergleich gehangen. Habe aber
in einem der Links die du gepostet hast, die ich mir auch schon vorher
mal durch gelesen habe, jetzt das passende Stichwort gefunden und werde
mich mal damit auseinander setzten. Danke nochmal.
Nun ja, hier mal die Lehrbuchvariante. Sogar zeitgemäß mit Video! Siehe
Anhang.
https://youtu.be/m22a6_-RQJ8
Ein Klassiker. Mittels DDS und PWM wird ein einfacher
DA-Wandler gebildet, welcher hier mit einem Sinusmuster gefüttert
wird. Die Parameter Amplitude, Frequenz und Phase (des 2. Kanals) sind
variabel und können vom Programm eingestellt werden.
https://www.mikrocontroller.net/articles/Pulsweitenmodulation#DA-Wandlung_mit_PWM
Das Bild zeigt auf Kanal 1 TP1, das digitale PWM SIgnal. Kanal2 zeigt
TP2, das gefilterte (demodulierte) Signal. Damit kann man was anfangen,
mit Kanal 1 eher nicht. Im Video sind dann TP2 und TP3 gemessen worden.
Viel Spaß
Danke Falk, das hat sehr geholfen. Ich habe jetzt ein kleines Problem.
Ich möchte die Frequenz von 244Hz auf 50Hz einstellen. Dafür ist
Höchswahrscheinlich ICR1 zuständig? Wenn ich den Wert auf 1250 Stelle
(62,5kHz/50Hz) sieht das Signal aus wie auf dem Screenshot. Liegt das an
der Dimensionierung der Bauteile?
1
void setup(){
2
3
pinMode(A0,INPUT);
4
pinMode(11,OUTPUT); // OCR1A
5
pinMode(12,OUTPUT); // OCR1B
6
7
// Timer1 initialisieren
8
// Mode 14, Fast PWM (ICR1), nichtinvertierte PWM, Prescaler 1
Black D. schrieb:> Danke Falk, das hat sehr geholfen.
Das wäre schön, wenn . . .
>Ich habe jetzt ein kleines Problem.
Eher ein größeres . . .
> Ich möchte die Frequenz von 244Hz auf 50Hz einstellen. Dafür ist> Höchswahrscheinlich ICR1 zuständig?
Nö.
> Wenn ich den Wert auf 1250 Stelle> (62,5kHz/50Hz) sieht das Signal aus wie auf dem Screenshot. Liegt das an> der Dimensionierung der Bauteile?
Es liegt an deiner Lesefaulheit und Begriffsstutzigkeit.
Mein Gott, wofür wird wohl die Variable frequenz gut sein?
Ok, hier nochmal eine grundlegende Erklärung. Es gibt im Projekt ja
mehrere Frequenzen.
1.) F_CPU, das ist ein #define, welches von der Arduino-IDE festgelegt
wird und meist bei 160000000 (16MHz) steht. Das ist der CPU und auch
Timer-Eingangstakt.
2.) F_Timer, das ist die Ausgangsfrequenz des Timers, welcher die
PWM-erzeugt. In diesem Beispiel auch gleichzeitig die Updatefrequenz der
PWM (ISR) und damit die Ausgabefrequenz unseres DA-Wandlers, denn
die PWM + RC Filter ist ein DA-Wandler.
https://www.mikrocontroller.net/articles/Pulsweitenmodulation#DA-Wandlung_mit_PWM
F_TIMER = F_CPU / (TIMER_RELOAD+1)
Damit wirtd aber auch die Auflösung der PWM und damit des DA-Wandlers
festgelegt. Ich habe die 62,5kHz gewählt, weil man damit auf exakt 256
Schritte für die PWM kommt. Wenn man das ändert, muss man die
Sinustabelle anpassen.
3.) Die Frequenz des Sinussignals. Diese ist VARIABEL, zumindest wenn
man es will, im Bereich 0-F_TIMER/2.
Die Formeln für die jeweiligen Beziehungen stehen im Quelltext in den
defines bzw. der Beschreibung.
Hier noch ein paar Luxusfunktionen
Vielen Dank nochmal. Ich habe schon einiges hinbekommen. Auch das mit
der Frequenz. Ist zwar nicht so elegant gelöst wie du es beschrieben
hast aber ich glaube es funktioniert. Mir ist jetzt aber eine
Kleinigkeit im Signal an TP1 aufgefallen. Und zwar gibt es da einen
Sprung und ich weiß nicht wo der her kommt. Wenn ich eine anderes
Programm mit einer Pulsdauermodulation nehme und messe gibt es da keinen
Sprung.
1
void setup(){
2
3
pinMode(A0,INPUT);
4
pinMode(9,OUTPUT); //OCR1A
5
pinMode(10,OUTPUT); //OCR1B
6
7
//Timer1 initialisieren
8
//Mode 14, Fast PWM (ICR1), nichtinvertierte PWM, Prescaler 1
9
10
TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM11);
11
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
12
ICR1 = TIMER_RELOAD; //multiplizieren mit 2 = 31kHz
Black D. schrieb:> Vielen Dank nochmal. Ich habe schon einiges hinbekommen.
Hmm. Die absolut fundamentale Änderung von frequenz auf Frequenz.
> ICR1 = TIMER_RELOAD; //multiplizieren mit 2 = 31kHz
Solche Kommentare sind sinnlos. Es gibt extra ein #define, wo man die
Frequenz eintragen kann! Das ist selbsterklärend!
> Kleinigkeit im Signal an TP1 aufgefallen.
Wie kommt man auf die Idee, vor allem DORT zu messen? Ist dir das
gefilterte Signal zu sauber, zu klar verständlich?
> Und zwar gibt es da einen> Sprung und ich weiß nicht wo der her kommt. Wenn ich eine anderes> Programm mit einer Pulsdauermodulation
Welches denn?
nehme und messe gibt es da keinen
> Sprung.
Tja. Da gibt es wohl in einer Rechnung einen arithmetischen Überlauf. Da
ist eine Variable zu klein oder hat das falsche Format (unsigned,
signed).
> 128+(((int8_t)pgm_read_byte(&Sinus[i>>8])*-(int16_t)Amplitude)>>8);
Kann es sein, daß du nicht an TP1, sondern eher an OCR1B gemessen hast?
Kann sein, daß die Zeile das macht (180° Phasenverschiebung), was du
willst, kann aber auch nicht sein. Man kann es auch so schreiben.
128-(((int8_t)pgm_read_byte(&Sinus[i>>8])*(int16_t)Amplitude)>>8);
> Hmm. Die absolut fundamentale Änderung von frequenz auf Frequenz.
Naja es halt die kleinen Dinge. Ich habe einige Sachen die ich nicht
brauche rausgeworfen und das Programm läuft noch ausserdem, was man hier
jetzt nicht sehen kann, habe ich die OCR1A geändert damit es auf einem
Arduino micro läuft. Wie gesagt es sind die kleinen Dinge.
> Wie kommt man auf die Idee, vor allem DORT zu messen? Ist dir das> gefilterte Signal zu sauber, zu klar verständlich?
Der geglättete Sinus ist zwar schon und gut aber nicht das was ich
brauche. Ich möchte mit dem Rechtecksignal einen Vollbrücken
Wechselrichter ansteuern.
> Tja. Da gibt es wohl in einer Rechnung einen arithmetischen Überlauf.> Da ist eine Variable zu klein oder hat das falsche Format (unsigned,> signed).
Dann muss ich mich mal damit genauer befassen.
> Kann es sein, daß du nicht an TP1, sondern eher an OCR1B gemessen hast?
Nee ich habe schon an TP1 gemessen.
Black D. schrieb:>> Wie kommt man auf die Idee, vor allem DORT zu messen? Ist dir das>> gefilterte Signal zu sauber, zu klar verständlich?>> Der geglättete Sinus ist zwar schon und gut aber nicht das was ich> brauche. Ich möchte mit dem Rechtecksignal einen Vollbrücken> Wechselrichter ansteuern.
Schon klar, aber das gefilterte Signal ist für die Fehlersuche
deutlich besser geeignet.
>> Kann es sein, daß du nicht an TP1, sondern eher an OCR1B gemessen hast?> Nee ich habe schon an TP1 gemessen.
Was hast du denn noch so geändert? Zeig mal das VOLLSTÄNDIGE Programm.
Black D. schrieb:> Ich habe jetzt grad die Atomic Block Funktion wieder eingebaut. Kann es> aber morgen erst testen ob es daran lag.
Meister, warum fummelst du planlos an Dingen herum, die a) funktionieren
und b) du nicht wirklich weiß, warum sie so sind, wie sie sind?
Warum hast du static int i nicht in der ISR gelassen, wo es hingehört?
Und auch die Sache mit dem Atomic? Da steht auch noch extra dabei, WARUM
das gemacht wird! Siehe Interrupt.
Aber dein Test wird sinnlos sein, denn Frequenz ist bei dir ja konstant.
Unter welchen Bedingungen tritt denn das falsche PWM Muster auf? Welche
Frequenz und welche Amplitude?
Also ich hab das nochmal gemessen. Vorher habe ich aber "static uint16_t
i;" wieder in den ISR rein geschrieben. Warum ich den da raus genommen
habe weiß ich auch nicht mehr. Als Frequenz vom geglätteten Sinus Signal
habe ich 50Hz genommen. Gemessen habe ich am OCR1A bei einer Amplitude
von 5, 128 und 245.
Miss doch einfach mal NACH dem Tiefpaß! Der ist nicht umsonst da! Die
Nullinie des Ausgangs liegt bei VCC/2, d.h das Bild mit 5% Amplitude
erscheint plausibel, denn die Aussteuerung ist gering, der Mittelwert
Ahhhhh, MOMENT!!!! Du hast ALIASING!!! Bei 200ms/DIV schaltet dein Oszi
auf eine recht niedrige Abtastfrequenz runter. Damit erscheinen
hochfrequente Signale, hier deine 62,5kHz PWM als extrem niederfrequent!
Dreh mal die Zeitauflösung auf 20us/DIV, dann sieht das ganz anders aus.
Man kann das Aliasing sehr leicht sichtbar machen.
Stell deine Amplitude auf 0. Dann dreh die Zeitauflösung auf 5us/DIV.
Dann sieht man eine Rechtecksignal mit 62,5kHz und 50% Tastverhältnis.
Jetzt dreht man die Zeitskalierung hoch, sprich in Richtung mehr
Zeit/DIV. 100us, 1ms etc. Zuerst sieht man nur ein Rauschband, die
Einzelpulse sind nicht mehr sichtbar. Aber irgendwann sieht man wieder
ein Rechtecksignal, aber mit um Größenordnung niedrigerer Frequenz. Das
ist Aliasing. Sowas passiert nur bei Digitaloszis. Analogoszis haben das
Problem prinzipiell nicht.
So sieht es bei 20us/DIV aus. Nach dem Tiefpass bekomme ich eine schöne
Sinuskurve raus.
Dann gucke ich mal wo ich ein Oszilloskop mit schneller Abtastfrequenz
her bekomme.
Danke.
Black D. schrieb:> Auf eine andere Idee bin ich bisher nicht gekommen.
Du hättest inzwischen mal die Hinweise zum Posten lesen können:
"Wichtige Regeln - erst lesen, dann posten!"
...
"Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang"
Falls du mit dem Verständnis Probleme hast - frag einfach.
Black D. schrieb:> Dann gucke ich mal wo ich ein Oszilloskop mit schneller Abtastfrequenz> her bekomme.
Das reicht nicht. Nahezu alle Digitaloszis reduzieren ihre Abtastrate
bei hohen Zeit/DIV Auflösungen. Bei 200ms/DIV arbeitet keins mit
100Msmps, dann das wären 20Mpunkte/DIV bzw. 200MPunkte/Bildschirm. Das
einzige Gegenmittel mit begrenzter Wirkung ist das erhöhen der
Speichertiefe, das kann man bei den meisten einstellen, wieviele Punkte
aufgezeichnet werden sollen. Dann kann man die Aliasinggrenze etwas
verschieben, aber nicht vollständig verhindern.
Aber all das ist nicht nötig, wenn man hinter dem Tiefpass mißt. Genau
DAFÜR (und andere Dinge) ist der da!
Hmmm, ich versuche gerade, mit meinem "ollen" Rigol DS1052E Aliasing
darzustellen, es funktioniert aber nicht! Kann es sein, daß dieses
Billigoszi an der Stelle besser ist als diverse Profigeräte ala LeCroy?
Kann es sein, daß das Oszi IMMER mit voller Abtastrate arbeitet und die
niedrigeren Samplingraten durch echte Mittelwertbildung erfolgen? Das
würde es erklären.
Danke das Programm läuft soweit auch mit meiner Schaltung. Am Ende kommt
ein Sinus raus genau so wie ich ihn vorher nach dem Tiefpass gemessen
habe.
Ich gucke mal ob ich noch ein analoges Oszilloskop finde damit ich mir
die Ausgänge direkt angucken kann. Aber ja du hast Recht es ist
wesentlich Sinnvoller nach dem Tiefpass zu messen.
Drei Fragen habe ich aber doch noch.
1. Wie kann ich das Signal von OCR1B negieren?
Ich habe erst überlegt einfach "^1" ein XOR zu nehmen aber da weiß ich
nicht wo ich das hinsetzen kann.
2. Wo muss ich das hinschreiben?
Falk B. schrieb:> // Setzte Frequenz in Hz> set_frequenz(uint16_t frequ_Hz) {> frequenz = ((uint32_t)frequ_Hz<<16)/F_TIMER;> }
3. Warum müssen OCR1A und OCR1B unterschiedliche Werte haben? Da habe
ich bisher noch keine Richtige Erklärung gefunden.
Black D. schrieb:> Drei Fragen habe ich aber doch noch.> 1. Wie kann ich das Signal von OCR1B negieren?
An verschiedenen Stellen. Entweder mit dem - in der Formel in der ISR.
> Ich habe erst überlegt einfach "^1" ein XOR zu nehmen aber da weiß ich> nicht wo ich das hinsetzen kann.
Nein, das geht hier nicht, denn OCR1B wird von der Hardware generiert.
Man kann es aber auch per Hardware invertieren. Bei der Konfiguration
mit den COM1Bx Bits. Siehe Datenblatt.
1
//Timer1 initialisieren
2
//Mode 14, Fast PWM (ICR1), nichtinvertierte PWM, Prescaler 1
> 3. Warum müssen OCR1A und OCR1B unterschiedliche Werte haben? Da habe
Das müssen sie nicht, das war nur zum statischen Test ganz am Anfang der
Entwicklung, ob beide OCR1A/B laufen, bevor die ISR freigeschaltet
wurde.
Kann ich Änderung auch im loop machen? Ich möchte mit einem Schalter
zwischen der Negierung von OCR1B und einer 180° Phasenverschiebung
wechseln können.
Black D. schrieb:> Falk B. schrieb:>> Entweder mit dem - in der Formel in der ISR.>> Du meinst so?OCR1B => 128-(((int8_t)pgm_read_byte(&Sinus[i>>8])*(int16_t)Amplitude)>>8);> Dadurch bekomme ich nur eine Verschiebung des zweiten Signals hin.
Was das nicht dein Ziel? Außerdem ist es keine Verschiebung, es ist eine
echte, frequenzunabhängige Invertierung
> Mit den beiden Zeilen klappt die Negierung.TCCR1A = (1<<COM1A1) |> (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11);> TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
Ist doch toll, oder?
> Kann ich Änderung auch im loop machen? Ich möchte mit einem Schalter> zwischen der Negierung von OCR1B und einer 180° Phasenverschiebung> wechseln können.
Das ist das Gleiche . . .
Falk B. schrieb:> Das Bild zeigt auf Kanal 1 TP1, das digitale PWM SIgnal. Kanal2 zeigt> TP2, das gefilterte (demodulierte) Signal. Damit kann man was anfangen,> mit Kanal 1 eher nicht. Im Video sind dann TP2 und TP3 gemessen worden.
Wenn man die RC-Glieder weglässt kann man dann mit den beiden Signalen
direkt auf die Eingänge eines L6202 gehen?