Hat jemand mit dem Pico2 (RP2350) schon mal eine saubere PWM mit zwei Ausgängen für low- und high- Side mit Totzeit erzeugen können?
Bei Taktraten um 200kHz, Modulationssignal wenige Kiloherz.
Hatte gehofft, mit der PIO- Programmierung ginge das. Eine sichere Totzeit für die Halbbrücke bekomme ich hin, aber es gibt immer wieder kleine Ausreißer in den Pulsbreiten. Woher? Keine Ahnung, vielleicht läuft der Fifo zur PIO leer, der wird per Interrupt nachgefüttert.
Wie auch immer, ist der erste Versuch mit der PIO. Vielleicht mag jemand was lauffähiges teilen, wäre nett.
Ansonsten probiere ich die Hardware-PWM und muss die Totzeit extern erzeugen.
Das geht sowohl mit PWM als auch mit PIO völlig problemlos.
Wichtig ist, dass man die Systeme und (Vor)Teiler vorher sauber zurücksetzt und dann synchron startet.
Für PWM ist für den synchronen Start das Register PWM_BASE+EN zuständig. (Seite 1088)
Für PIO ist für den synchronen Start das Register PIO[012]_BASE+CTRL zuständig. (Seite 939)
vielleicht läuft der Fifo zur PIO leer, der wird per Interrupt
nachgefüttert.
Eines noch, wenn die StateMachine nur Daten von der Applikation empfängt, kann man die FIFO Tiefe verdoppeln. Des Weiteren sollte man bei einigen hundert kHz vielleicht Abstand von Interrupts nehmen (oder die Prioritäten im NVIC anpassen). Ein Nachfüllen der FIFOs kann man ohne Prozessorlast lieber mit einer in einer Schleife laufenden DMA machen. Gerade der 2350 hat diesbezüglich einiges an Funktionalität dazu gelegt, obschon es auch bequemstens mit 'nem 2040 ginge.
Die mit der PIO nachgestellte PWM startet sauber und läuft auch durch. Nur ab- und zu entsteht entweder auf der High-Side oder der Low-Side ein längerer Puls, der sich natürlich negativ auf das Ausgangssignal auswirkt.
Anbei ein Bild was ich meine:
gelb: low side
türkis: high side
lila: (gefiltertes) Ausgangssignal.
Rot reingemalt der fehlerhafte Puls und gestrichelt das korrekte Ausgangssignal.
Der Fehler tritt ca alle 20-30 Pulse auf, nicht vorhersagbar. Könnte mit der Befüllung des PIO-Fifo zu tun haben.
Vielleicht führt das zur Lösung: kann man das Fifo so steuern, dass immer genügend Daten für die PIO vorhanden sind und nichts leerlaufen kann. Sorry, ist mein erster Versuch mit Fifo und PIO.
Der PIO-Code
1
; GPIO2 = Low-Side, GPIO3 = High-Side
2
; Systemtakt: 150 MHz
3
4
.wrap_target
5
; --- HIGH-SIDE Phase ---
6
pull block ; High-Side-Dauer aus FIFO holen
7
mov x, osr ; Verschiebe in das X-Register
8
jmp !x skip_high ; Wenn 0, ueberspringe
9
set pins, 2 ; High-Side AN (GPIO3 = 1), Low-Side AUS (GPIO2 = 0)
10
high_loop:
11
jmp x-- high_loop ; High-Zeit runterzaehlen
12
13
skip_high:
14
set pins, 0 ; Beide AUS -> Totzeit (~40ns)
15
nop ; nops fuer Totzeit
16
nop ;
17
nop
18
nop
19
20
; --- LOW-SIDE Phase ---
21
pull block ; Low-Side-Dauer aus FIFO
22
mov x, osr ; Verschiebe in das X-Register
23
jmp !x skip_low ; Wenn 0, ueberspringe
24
set pins, 1 ; Low-Side AN (GPIO2 = 1), High-Side AUS (GPIO3 = 0)
25
low_loop:
26
jmp x-- low_loop ; Zähle Low-Zeit runter
27
28
skip_low:
29
set pins, 0 ; Beide AUS -> Totzeit vor dem nächsten Puls
Wenn da mit dem Nachfüllen etwas zeitlich schief geht, manifestiert sich das Problem sofort. Das ist das Problem mit IRQs, wenn noch andere Mitspieler (zB. USB) im System sind.
Also NVIC massieren und diesem PIO-IRQ die höchste Prio verpassen, oder besser: DMA mit Buffergröße 8Bytes (2WORDS) im DMA-Loop laufen lassen. Dann muss man sich gar nicht mehr kümmern.
Ach ja, mit dem 2350 kann man den FIFO indexieren und als Ablageplatz für Variablen benutzen. SO muss man diesen nicht ständig mit den gleichen Werten befüllen. Nur wenn sich etwas ändert.
Zweite Möglichkeit: Zwei korrespondierende StateMachine teilen sich die Aufgabe in einer Art Ping-Pong Spiel. Eine bekommt den HI-Teil, eine den LO-Teil. Die kann man dann so programmieren, dass sie sich einen neuen Zählwert ins OSR (NOBLOCK) laden. Und wenn nichts in der FIFO ist, wird der (alte) X-Wert übernommen.
Die DMA habe ich bisher gemieden, da mir nach Aktivierung immer die Debug-Probe ausgestiegen ist. Vielleicht hat sich das erledigt, habe die ganze Rechnerumgebung neu aufgesetzt.
Danke erst mal für die Tipps: der RP2350 hat schon ein fettes Datenblatt.
Habe mich für ein Ping-Pong mit zwei DMA-Kanälen entschieden, die die PIO füttern. Die DMA-Kanäle starten sich wechselweise in der Art
1
channel_config_set_chain_to(&c_A, chan_B); // Wenn A fertig, starte B
2
...
3
channel_config_set_chain_to(&c_B, chan_A); // Wenn B fertig, starte A
Über ein SDK-Makro frage ich die DMA-Kanäle ab, ob die noch busy sind oder schon neue Daten vertragen. Momentan pollend, zum testen halt.
1
if(!dma_channel_is_busy(chan_A)){
2
fill_buffer(buffer_A,BUFFER_SIZE);// Neue Daten nachschieben
3
dma_channel_set_read_addr(chan_A,buffer_A,false);
4
SEGGER_RTT_printf(0,"chan_A %d\n",sample_index);
5
}
Und das gleiche nochmal für Kanal B.
So klappt das nicht, mir läuft scheinbar(!) genau 10x hintereinander der Kanal A leer, dann wieder 10x hintereinander der Kanal B usw. Das sehe ich am Log des SEGGER_RTT_printf.
Das erzeugte Signal sieht zerhackt aus, wahrscheinlich sind die DMA-Kanäle doch noch nicht frei und ich überschreibe beim Nachladen in Benutzung befindliche Buffer.
Wie bekommt man denn wirklich raus, wenn ein DMA-Kanal durch ist?
Wie bekommt man denn wirklich raus, wenn ein DMA-Kanal durch ist?
Die befruchten sich gegenseitig. Heraus finden musst du gar nichts. Eingreifen musst du auch nicht. Falls trotzdem wissbegierig, in COUNT oder CTRL schauen.
Wichtig ist das Pacing richtig einzustellen, DREQ_PIO0_TX0, DREQ_PIO0_TX1
Immer wenn die jeweilige Statemachine signalisiert (DREQ), dass der FIFO nicht gefüllt ist, lüppt das automatisch. Ganz zu Anfang hämmern die also vier mal los, dann immer wenn ein Wert aus dem FIFO gezogen (PULL) wird.
Und die SMs triggern sich abwechselnd gegenseitig.
Also die DMAs starten, dann die SMs.
Nutze nur eine SM, befülle die aber abwechselnd von zwei DMA-Kanälen.
Das funktioniert jetzt auch, hatte einen Logikfehler drin.
Was aus der SM rauskommt, ist aber noch immer abschnittsweise Schrott. Timing passt, aber die Daten nicht. Jetzt wird's kniffelig, so richtig kommt man ja an die Innereien nicht ran.
Vielleicht kann ich es mit Log-Zeitstempeln beim Befüllen der Buffer eingrenzen, ob da etwas überschrieben wird.
Nutze nur eine SM, befülle die aber abwechselnd von zwei DMA-Kanälen.
Das funktioniert jetzt auch, hatte einen Logikfehler drin.
Das ist der (milde formuliert) seltsamste Ansatz den ich je gesehen habe.
Der DREQ für einen einzelnen FIFO geht dann an beide DMA Kanäle gleichzeitig? Welcher soll denn gewinnen, da ja immer beide DMA aktiv und in Lauerstellung sind?
Oder machst du das alles zirkular von Hand und DMA ist im Grunde genommen überflüssig?
Auf der digitalen Seite sieht alles plausibel aus, sowohl die DMA-Zeiten als auch die von der SM erzeugten Pulsweiten sehen mit einem Sinus als Testsignal gut aus.
Möglich, dass der analoge Filter hinter der PWM das Signal so heftig verzerrt, muss da genauer hinsehen.
Der DREQ für einen einzelnen FIFO geht dann an beide DMA Kanäle
gleichzeitig? Welcher soll denn gewinnen, da ja immer beide DMA aktiv
und in Lauerstellung sind?
Der DREQ DREQ_PIO0_TX0 der Fifo geht an beide Kanäle. Aber die beiden DMA-Kanäle werfen sich gegenseitig die Trigger zu, das hatte ich oben schon im Codebeispiel gezeigt.
Am Beginn wird nur ein Kanal gestartet.
Zwei SM schienen mir nicht plausibel. Will unbedingt vermeiden, dass beide I/O gleichzeitig high sind. Das lässt sich in einer SM sicherstellen, der Code läuft sequenziell durch.
Ach ja, mit dem 2350 kann man den FIFO indexieren und als Ablageplatz
für Variablen benutzen. SO muss man diesen nicht ständig mit den
gleichen Werten befüllen. Nur wenn sich etwas ändert.
Habe ich gerade probiert, funktioniert aber nicht. Setzt das Pacing außer Kraft, sobald man auf den FiFo z.B. mit
1
mov OSR, RXFIFO[0]
zugreifen will und dann sind die Daten im TX-Fifo futsch, wegen Überlauf. Hab ein wenig gegoogelt, ist wohl ein bekannter HW-Bug im Pico2. Finde leider keinen überzeugenden workaround.
Schade, nur zwei scratch-Register x & y sind ein bisschen wenig, vier zusätzlich würden einiges beschleunigen.
Hab's gerade mal Testprogrammiert. Schön langsam, einmal mit 'ner grünen LED gegen Vcc und einmal mit 'ner roten LED gegen Gnd.
Diese Problemstellung kann man mit reiner PIO Programmierung lösen.
Ohne DMA, ohne IRQ, ganz (im Sinne von völlig) ohne CPU Interaktion.
Braucht nur einmal in beide SM-FIFOs je einen PUT.
Die Periode (also 1/F_PWM) in die Eine und Pulsdauer in die Andere. Dann starten (synchronisieren sich selbst).
Die merken sich diese Einstellung bis in alle Ewigkeit. Man kann natürlich zu jeder beliebigen Zeit eine neue Pulsdauer in den FIFO schütten. Der wird ab dem nächsten Puls-Start genommen.
ok, hört sich gut an, auch wenn ich nicht sicher bin ob wir übers gleiche reden.
Mit dem Put habe ich angefangen, das hat auch soweit funktioniert. Also erst Pulsdauer high-side gesendet, dann Pulsdauer low-side. Eine SM hat das mit zwei I/O-Pins mit Totzeit dazwischen umgesetzt.
Sollten durch einen Programmierfehler beide i/o auf high schalten, würde die Halbbrücke im realen Betrieb abbrennen. Mit einer SM, die die beiden Pins ansteuert, kann man das sicher ausschließen.
Die Sache mit den zwei Ping-Pong DMAs gefällt mir eigentlich gut, entlastet die CPU und verlängerte Pulse, weil ein TX-Fifo leer läuft, sind damit erledigt.
Da die Taktfrequenz der PWM wegen Bauteilgrößen nicht zu niedrig liegen soll (~200kHz), wird 10-fach interpoliert. Das sollte die SM hinbekommen. Wegen des Bugs im .fifo putget - Mode, halt mit zwei SM: eine empfängt die Daten der beiden Ping-Pong DMAs und schickt immer ein Pulsdauer-Paar per dritter DMA zu einer zweiten SM in der gleichen PIO-Einheit. Da kann ich dann ohne Überlaufgefahr das RX-Fifo als zusätzliches scratch-Register nutzen und sowohl die Pulse auf die Pins setzen als, auch die 10-fache Wiederholung.
Ohne Überlaufgefahr, weil kein Pacing zwischen dritter DMA und zweiter SM gebraucht wird. Die erste SM sendet ja immer nur ein (oder max zwei) Wertepaare.
Synchronisiert werden die beiden SM mittels irq wait 0 / irq set 0
Soweit die Theorie.
Funktioniert leider noch nicht.
Edit: Es bewegen sich keine I/Os mehr. Übertragung zwischen den beiden SM funktioniert, die TX-Fifos beider SM sind voll.
Habe jetzt den Debug-Probe stabil am Laufen, müsste doch zu lösen sein.
ok, hört sich gut an, auch wenn ich nicht sicher bin ob wir übers
gleiche reden.
Mit dem Put habe ich angefangen, das hat auch soweit funktioniert. Also
erst Pulsdauer high-side gesendet, dann Pulsdauer low-side. Eine SM hat
das mit zwei I/O-Pins mit Totzeit dazwischen umgesetzt.
Zwei SMs, beide steuern die beiden Ausgänge an. Die erste erzeugt Totzeit plus Highside on. Die zweite im weiteren Verlauf ebenfalls Totzeit plus Lowside on.
Sollten durch einen Programmierfehler beide i/o auf high schalten, würde
die Halbbrücke im realen Betrieb abbrennen.
Ein Programmfehler ist unmöglich, außer man führt ihn absichtlich herbei.
Zur Zeit ist das Ding so angelegt, das im Totbereich der Highside Ausgang High und der Lowside Ausgang Low ist. Aber die Logik lässt sich so invertieren, dass immer abwechselnd beide Ausgänge auf High gehen (mit Totzeit Low,Low dazwischen)
Die Sache mit den zwei Ping-Pong DMAs gefällt mir eigentlich gut,
entlastet die CPU und verlängerte Pulse, weil ein TX-Fifo leer läuft,
sind damit erledigt.
Diese Lösung hier braucht während der Laufzeit 0% CPU Zeit, keine Interaktion, kein Intervall-artiges Refresh und ist auch nicht Zeitkritisch.
Da die Taktfrequenz der PWM wegen Bauteilgrößen nicht zu niedrig liegen
soll (~200kHz), wird 10-fach interpoliert. Das sollte die SM
hinbekommen.
PWM Frequenz ist praktisch beliebig. Bei 100MHz SM-Frequenz und 200kHz PWM-Frequenz kann man ~500 Abstufungen hinbekommen.
Bei 200MHz SM-Frequenz und 200kHz PWM-Frequenz entsprechend ca. 1000.
Wegen des Bugs im .fifo putget - Mode, halt mit zwei SM:
eine empfängt die Daten der beiden Ping-Pong DMAs und schickt immer ein
Pulsdauer-Paar per dritter DMA zu einer zweiten SM in der gleichen
PIO-Einheit. Da kann ich dann ohne Überlaufgefahr das RX-Fifo als
zusätzliches scratch-Register nutzen und sowohl die Pulse auf die Pins
setzen als, auch die 10-fache Wiederholung.
Ohne Überlaufgefahr, weil kein Pacing zwischen dritter DMA und zweiter
SM gebraucht wird. Die erste SM sendet ja immer nur ein (oder max zwei)
Wertepaare.
Synchronisiert werden die beiden SM mittels irq wait 0 / irq set 0
Soweit die Theorie. Funktioniert leider noch nicht, es klemmt die
Übertragung zwischen den beiden SM. Der TX-Fifo der ersten SM ist voll,
also klappt schon mal das Füttern von der CPU zur ersten SM. Der TX-Fifo
der zweiten füllt sich leider nicht. Die RX-Fifo sind alle leer.
Habe jetzt den Debug-Probe stabil am Laufen, müsste doch zu lösen sein.
Wird alles nicht gebraucht. Aber die Synchronisation wird nahezu gleich über IRQ Flags realisiert.
Im laufenden Betrieb muss nur wenn sich Duty ändert ein einzelner Wert in den FIFO geschrieben werden. Ansonsten laufen die Dinger autark und ohne Eingriff.
Ja, das funktioniert nach deiner Idee natürlich auch, ist aber eher eine Optimierung auf statischen Betrieb. Das habe ich nicht vor, die PWM soll um 30kHz moduliert werden. Diese Daten kommen später vom ADC, nach entsprechender Aufbereitung. Der Pico2 hat genügend CPU-Leistung zum Signal Processing. Momentan liegt noch eine Sinus-LUT im Flash.
Immerhin bekomme ich jetzt einen Burst von je 10 Pulsen aus den I/0s, dann klemmt die Sache. Vielleicht das irq wait/set falsch verstanden, mal schauen.
Ja, das funktioniert nach deiner Idee natürlich auch, ist aber eher eine
Optimierung auf statischen Betrieb. Das habe ich nicht vor, die PWM soll
um 30kHz moduliert werden.
Kein Problem. Wenn die ADC Daten im Speicher aufbereitet wurden, kann man sie per DMA direkt in den Pulsweiten-FIFO hinein saugen lassen.
Nachteil: Man hat immer noch keine Prozessorlast. ;-)
Nachteil: Man hat immer noch keine Prozessorlast. ;-)
Ok, glaube du hast mich verstanden, von wegen Signal Processing :-)
Aber für heute gebe ich auf, suche wohl an der falschen Stelle (SM). Vermutlich hat eine DMA nach eine Runde keine Lust mehr und stellt den Betrieb ein.
Ahm, bist Du sicher? Der Flash wird doch über SPI angesprochen, der muss Blockweise gelesen werden und die CPU will Ihren Code auch noch holen. Aus dem kannst Du nicht deterministisch lesen. Und dann auch noch printf im kritischen Pfad, schon mal nachgemessen wie lange ein printf braucht? Wie groß ist denn ein DMA Puffer? Wenn du es einmal nicht schaffst den unbenutzten Puffer zu füllen ist es vorbei und das Ping Pong verklemmt sich. Du solltest alle Funktionen im zeitkritischen Pfad in den RAM legen.
Wenn schon mit viel zu kurzen DMA Puffern gearbeitet wird, dann müssen die unbedingt als Ringpuffer angelegt werden. Dann laufen die selbstständig. Denn bei 200kHz die Dinger ständig von Hand zu massieren ohne zu stolpern, das setzt schon einiges an Vorarbeit voraus.
Apropos Ringpuffer, gerade der 2350 hat nun die Möglichkeit bekommen die Dinger auf unendlich zu stellen und sogar direkt wieder zu sich selbst zu verketten.
Aber ich vertrete weiterhin die Ansicht:
Erst alle vorhandene Hardware zu Nutzen und solange ausquetschen bis es süß kommt.
Alle ständigen laufenden Massentransporte mit DMA (Aber nur wenn's auch notwendig ist)
Alle repetitiven Aufgaben über ISRs
Den übrigbleibenden Rest mit den zwei Kernen weg frühstücken.
Noch mal ne Frage zu der Lösung: wie hältst du das Datum in der SM?
pull block auf das TX-Fifo hält bei leeren Fifo an.
Hab' den Teil mal mit fünf Sternen (Zeile 35) markiert.
Gestartet wird erst die SM0 mit ›pio_period()‹, dann die SM1 mit ›pio_pulse()‹.
Dann die initiale Pulslänge in den SM1.FIFO, dann einmalig die Zyklen in den SM0.FIFO. Ab da läuft das Ding rund.
Im weiteren Verlauf nur noch bei Bedarf die gewünschte Pulslänge in den SM1.FIFO.
Ach ja, ist in der hier verpönten Sprache geschrieben, aber die Logik ist ja die gleiche.
Je nach Ansteuerung müssen die Bitmuster für TOT,HIGHSIDE,LOWSIDE angepasst werden. Auch die gewünschte Totzeit. Hab's aber hoffentlich hinreichend dokumentiert.
Viel Spaß.
1
@asm_pio(set_init=(PIO.OUT_LOW,PIO.OUT_HIGH))
2
defpio_period():
3
FLAG=3
4
TOT=0b10# (HS:High LS:Low) █▋AUS▐█
5
HIGHSIDE=0b00# (HS:Low LS:Low) █▋GRÜN▐█
6
7
irq(FLAG,clear)# Pulse Blockieren (Flag auf ›0‹)
8
pull(block)# Anzahl Zyklen für die Periode der Frequenz laden
9
mov(x,osr)# in X sichern
10
wrap_target()#─────[ Schleife ]─────
11
# Zu Beginn der Periode erst Totzeit einhalten
12
set(pins,TOT)[31]# Totzeit
13
nop()[31]# … noch'n bissches zum Zuschauen
14
# Und nun den Highside Treiber aktivieren
15
set(pins,HIGHSIDE)# Highside Pulse EIN
16
irq(FLAG)# Beginn an ›pio_pulse‹ signalisieren (Flag auf ›1‹)
17
# Nun einfach die komplette Periode abwarten
18
mov(y,x)# Periode in Zähler holen
19
label('loop')#
20
jmp(y_dec,'loop')#
21
wrap()# Rinse and repeat
22
23
@asm_pio(set_init=(PIO.OUT_LOW,PIO.OUT_HIGH))
24
defpio_pulse():
25
FLAG=3
26
TOT=0b10# (HS:High LS:Low) █▋AUS▐█
27
LOWSIDE=0b11# (HS:High LS:High) █▋ROT▐█
28
29
pull(block)# Anzahl Zyklen für die Pulslänge laden
30
mov(x,osr)# in X sichern
31
wrap_target()#─────[ Schleife ]─────
32
# Warten auf Signal von ›pio_period‹ StateMachine
33
wait(1,irq,FLAG)# Warten auf Beginn der Periode (Flag geht auf ›1‹)
34
# irq (FLAG, clear) # Unnötig, Flag wird automagisch gelöscht
35
# Anzahl Zyklen für die Pulslänge laden *****
36
pull(noblock)# Entweder FIFO ──▶ OSR oder X ──▶ OSR falls FIFO leer
Das DMA Ping-Pong incl 10x Interpolation in EINER SM (und zwei DMA)
läuft jetzt auch, dazu später mehr.
Sehr schön.
Mal im Gesamt-Kontext überlegen welcher Ansatz mehr Sinn macht.
Der Dritte. ;-)
PWM ──▶ PIO ──▶ zwei Ausgänge.
Fiel mir vorhin noch ein und läuft prima. Komplettes Programm in 45 Zeilen. Wie immer, 0% CPU, kein DMA, ein PWM slice, eine StateMachine.
In der StateMachine läuft nur ein Edge-Detector und Shaper. Hat den Vorteil, dass man nun sowohl 0% Duty als auch 100% Duty mit der PWM problemlos darstellen kann.
Das kam mir zu Beginn in den Sinn, wusste aber nicht ob und wie das umsetzbar ist. Also die Verknüpfung von PCM und PIO/SM. Mittlerweile schon. Aber ist eine PWM nicht auch nur eine vorgefertigte Implementierung einer SM?
Wie auch immer, das wäre eine dritte sinnvolle Lösung.
Jetzt nochmal zur Auflösung meines letzten Fehlversuchs mit
Missverständnis des DMA-Pacing. Dachte ich brauche für DMA_C kein DREQ, da ich mit SM0 immer nur zwei Werte in den Fifo stecke. Mir war nicht klar, dass dann DMA_C ständig den leeren RX-Fifo von SM0 liest und den vollen TX-Fifo von SM1 überschreibt.
damit verbunden ein Logikfehler von irq wait / irq set, so dass die ganze Kette nach einem Durchlauf stand.
klar, war noch total umständlich weil ich nicht auf die Idee kam, die osr und isr als scratch-Register zu nutzen.
War bis zum gestrigen Abend nicht auf den Fehler gekommen und heute standen andere Dinge auf dem Plan. Aber ein Bekannter hatte eine Idee. Zwar keine Ahnung von Embedded, aber Informatiker und er wollte mir unbedingt etwas zeigen.
Konnte kaum fassen was dann kam. Ich nutze unter Ubuntu VS-Code. Er hat die Claude Code Extension geladen und sich mit seinem Account eingeloggt. Das Model Opus 5.5 mit "high effort" gewählt VS-Code das Projekt schreibend freigegeben, Teile des restlichen Rechner lesend. Absichtlich eine ziemlich hemdsärmelige Beschreibung des Problems eingetippt.
Was dann passierte, verschlägt einem ziemlich die Sprache: die Kiste fing erstmal an alle möglichen Infos einzusammeln, analysierte und hatte das Problem schnell gefunden. Irgend wo her hat die einen Simulator hergezaubert. Woher weiß ich nicht.
Dann wurde mein Code angepasst, eine SM und eine DMA rausgeworfen und das ganze Timing überarbeitet. Woher die Maschine wusste, welches Timing ich anstrebte, ist mr auch ein Rätsel. Vermutlich noch andere Doku durchstöbert.
Jedenfalls hat die KI gleich den Code in die noch betriebsbereite HW geladen und auf dem Oszi erschien die perfekte ausbalancierte PWM mit symetrischen Totzeiten.
In Zeiten, in denen die Hausfrau eine Suchmaschine für eine Mehlschwitze braucht, ein Mathematiker einen Taschenrechner für Quadratwurzel 49, warum nicht auch für eine simple Logikaufgabe ein LLM für einen Informatiker.
Nimm's mir nicht übel, aber das finde ich gruselig.
Ich sehe ›selbst denken‹ als eine einfache Gehirnjogging Möglichkeit um die graue Masse geschmeidig zu halten. Und sich, gleich einem Snickers, das kurze Erfolgserlebnis, morgens um halb zehn zu verpassen. Befürchte allerdings, da bin ich vermutlich mittlerweile in der Minderzahl.
Ich mache trotzdem weiter damit, wenn's irgendwann nicht mehr klappt, wird's Zeit auf den Berg zu gehen. ;-)
Aber ist eine PWM nicht auch nur eine vorgefertigte Implementierung
einer SM?
Im Grunde genommen, ja. Aber…
Die PWM lässt sich bequem von 0% (Dauerpegel, keine Schaltvorgänge) bis 100% (ebenfalls Dauerpegel, keine Schaltvorgänge) einstellen. Bei der momentanen SM Lösung hat man immer ein (wenn auch verschwindend geringes) Umschalten in den Endbereichen. Man müsste diese Fälle noch gesondert bearbeiten (also Totzeit eliminieren).
Mit einer SM nur als Edge-Detektor und Shaper muss man das nicht. Ist so wesentlich einfacher und eleganter.
Fiel mir vorhin noch ein und läuft prima. Komplettes Programm in 45
Zeilen. Wie immer, 0% CPU, kein DMA, ein PWM slice, eine StateMachine.
In der StateMachine läuft nur ein Edge-Detector und Shaper.
Kannst Du genauer erklären wie Du den Ausgabe des PWM-Moduls als Input in ein PIO-Modul bekommst?
Ich könnte mir höchstens vorstellen dass das ginge wenn man dafür ein ganzen GPIO-Pin verbrät der dann extern nicht verbunden sein darf.
Ich könnte mir höchstens vorstellen dass das ginge wenn man dafür ein
ganzen GPIO-Pin verbrät der dann extern nicht verbunden sein darf.
Das würde ich Treffer, Versenkt nennen! Zielsicher! ;-)
Genau so wird's gemacht, die onboard LED bietet sich zuerst an.
Aber auch die (nicht vorhandenen oder nach außen geführten) GPIO 30 und 31 blind. GPIO 29 kann man auch noch nehmen, wenn man die Spannungsmessung nicht braucht (und die braucht man fast nie).
Man braucht übrigens nur einen einzelnen dafür, nach außen liegt die PWM Frequenz darauf, intern schnüffelt die StateMachine mittels JMP Pin.
Und wenn die 29 GPIO nicht reichen, dann den RP2350B nehmen.
Einen weiteren kleinen Nachteil hätte der PWM-Slice bei der max möglichen Auflösung. Bei 200kHz PWM-Taktfrequenz und 150MHz Systemtakt müsste der Rücksprung auf 375 gesetzt werden. Das halbiert die Auflösung auf die Hälfte der reinen PIO-Implementierung.
Ist die Frage nach Relevanz, nutze eh Noise Shaping.
Momentan hab ich aber ganz profane Analog-Herausforderungen. Glaube ich jedenfalls. Das gefilterte PWM-Signal sieht abschnittsweise ziemlich übel aus. In der LUT ist ein 16Bit Sinus abgelegt. Raus kommt so was wie im Bild.
Der Tiefpass besteht aus einer Power-Induktivität mit 64µH und der C = 1µF TU2 Folie von Wima. Last ist R = 15 Ohm.
Beim Bild ist gelb low-side, türkis high-side und lila gefiltertes Analogsignal. In zwei Zeitauflösungen.
Zugegeben, der low-side Treiber ist zu schwach angesteuert. Aber solche Verzerrungen? Da steckt doch der Ferrit im L dahinter?
Bei 200kHz PWM-Taktfrequenz und 150MHz Systemtakt müsste der Rücksprung
auf 375 gesetzt werden.
Bei DIV 1:0 ergibt sich:
150E6 ÷ 200E3 = 750 (TOP)
375 TOP hättest du nur bei PH_CORRECT=1, also bei vollem UP/DOWN count.
Aber diese Limitierung bestünde doch bei angepasster PIO ganz genauso. (Die gezeigten PIO Lösungen sind alle nicht phase correct ).
Ich finde ja dass sowohl 375 als auch 750, bei dieser PWM Frequenz eine ziemlich gute Auflösung darstellt.
Analogtreiber ist repariert und beide PWM-Ansätze mit Totzeit miteinander verglichen. Also keine PIO-PWM vs PWM-Slice auf LED-Port+PIO-Totzeit. Sehe keine Unterschiede bezüglich Geräusch oder Klirr auf dem gefiltertem Ausgangssignal.
Allerdings taugt mein noise sharping Algorithmus nicht, der vergrößert das Geräusch statt zu glätten :-(