Hallo,
ich möchte den Raspberry Pico als Frequenzgenerator nutzen.
Ausgangsfrequenz soll 10 Hz - 2MHz betragen.
Um die Auflösung zu verbessern möchte ich den "Fractional Clock Divider"
nutzen.
Klar, die Lösung ist nicht perfekt, aber irgendeinen Tod muss man
sterben ;)
Ich habe ein Bild von der Architektur der 16-bit-Timer angehängt.
Daraus ergibt sich ja diese Formel (aus RP2040 Datenblatt Seite 530).
1
Wertebereiche für Parameter:
2
TOP = 0 - 65535
3
DIV_INT = 1 - 255
4
DIV_FRAC = 0 - 15
Das Dumme ist nur: Ich möchte die Frequenz Soll-Frequenz "eingeben" und
die Werte für DIV_INT, DIV_FRAC, TOP erhalten, bei denen der Fehler zur
Soll-Frequenz am geringsten ist.
1
Beispiel:
2
für Soll-Frequenz = 18673 Hz
3
4
Errechnet durch Durchlaufen und Prüfen aller möglichen Werte:
5
DIV_INT = 4
6
DIV_FRAC = 13
7
TOP = 1390
8
Proberechnung ergibt für f = 18672,9160... Hz
Kann man das effizient lösen?
Aktuell fällt mir nur nur ein, die 256 x 16 x 65536 Werte zu durchlaufen
und zu überprüfen bei welcher Konfiguration der Fehler am geringsten
ist.
Eventuell kann man's zum Beschleunigen vorher etwas eingrenzen ...
Bin etwas mit meinem Latein am Ende.
Ich bin kein Hardcore Mathematiker, und ohne das genauer untersucht zu
haben, könnte dich ev. die Recherche Richtung CFRAC (continued fraction
factorization method) in den Bereich einer Lösung bringen.
just my 2ct
Erst mit DIV_FRAC = 0 eine Lösung für TOP finden, die im erlaubten
Wertebereich liegt (uint16).
Dabei DIV_INT inkrementieren, bis es passt.
Wenn TOP und DIV_INT gefunden sind DIV_FRAC inkrementieren, bis der
Fehler minimal ist.
Das sind dann im schlimmsten Fall 256 + 16 Durchläufe
Je nach gesuchter Frequenz kann man dann mit DIV_INT und DIV_FRAC
entweder am oberen oder unteren Ende des Wertebereichs starten und so
das ganze nochmal Beschleunigen.
donvido schrieb:> Erst mit DIV_FRAC = 0 eine Lösung für TOP finden, die im erlaubten> Wertebereich liegt (uint16).> Dabei DIV_INT inkrementieren, bis es passt.> Wenn TOP und DIV_INT gefunden sind DIV_FRAC inkrementieren, bis der> Fehler minimal ist.> Das sind dann im schlimmsten Fall 256 + 16 Durchläufe
es gibt sicher cleverere Schrittweiten als "+1", Stichwort "sukzessive
Approximation"
TUX 4eva schrieb:> KAuf dir eine anständigen uC, nicht dies "overpriced, underdelivering"> hype teil.
Gehirnjogging hat noch keinem Gehirnträger geschadet, aber da zählst Du
nicht mit.
imschritthalter schrieb:> es gibt sicher cleverere Schrittweiten als "+1", Stichwort "sukzessive> Approximation"
Ja das kann man mit Sicherheit noch beliebig optimieren.
Die Frage ist nur, ob es den Aufwand (und Lesbarkeit) bei 256+16
Durchläufen rechtfertigt.
donvido schrieb:> Das sind dann im schlimmsten Fall 256 + 16 Durchläufe
Der Ansatz gefällt mir gut, sollte ich auch mit meinen Mathe-Kentnissen
umgesetzt bekommen
TUX 4eva schrieb:> KAuf dir eine anständigen uC, nicht dies "overpriced, underdelivering"> hype teil.
@donvido hat's schon richtig gesagt, hier gehts auch ein kleines
bisschen um die Machbarkeit.
Zum anderem sieht das Projekt so aus: Ich möchte PWMs generieren.
* 4x unabhängige PWM
* 0.0Hz-1000.0Hz mit 0.0-100.0% DutyCycle
* 1.000k-20.00kHz mit 0-100% DutyCycle
Mit Sicherheit habe ich auch Einbußen bei der Genauigkeit. Wie stark
versuche ich gerade auszurechnen.
Ich wäre gerne in der Arduino/Atmega/RPI Pico Ecke geblieben, da ich da
schon einiges gemacht habe. Aber auch ein grobe Recherche in Richtung
STM32 haben gezeigt, dass auch die größeren uCs keine eierlegende
Wollmilchsau abgeben.
Die 8 unabhängigen 16bit Timer vom RP 2040 finde ich eigentlich ganz
interessant.
Meine aktuelle Idee:
* 4 Timer des RP2040 erzeugen eine einstellbare Frequenz
* diese Speise ich bei den anderen 4 Timern als Takt ein und erzeuge ein
PWM-Signal.
* Dann werde ich mal testen ob mich der Jitter stört. Falls ja, gibts ja
die Option mit einem PLL um die Frequenz vor dem Einspeisen zu glätten.
Ein älterer Ansatz war mit einem DDS die Frequenz zu erzeugen
(AD9833/34) um den PWM zu füttern.
Aber ich möchte die Kirche erst einmal im Dorf lassen.
Sollte jemand zufällig die perfekte LowBudget-Idee haben, nehm ich diese
natürlich auch gerne.
Hallo,
Wenn es um ein Rectecksignal geht, dann schau Dir mal Pico's Pios an.
Damit ist die Lösung ein Kinderspiel:
F_PIO auf 100MHz stellen, 25 Takte Pin auf low, 25 Takte Pin auf high =
2 MHz
F_PIO auf 2000Hz stellen, 25 Takte Pin auf low, 25 Takte Pin auf high =
40 Hz.
Der Zwischenbereich kann linear interpoliert werden.
Für den Bereich unter 40 Hz (2_000 > F_PIO < 125_000_000)
muss das Delay halt grösser werden (der Pico kann nicht langsamer).
Das Programm benötigt ca. 5 Instruktionen.
Es gibt Beispiele, bei denen die Pios sogar Sinussignale erzeugen.
Michael S.
Michael S. schrieb:> Hallo,>> Wenn es um ein Rectecksignal geht, dann schau Dir mal Pico's Pios an.>> Damit ist die Lösung ein Kinderspiel:>> F_PIO auf 100MHz stellen, 25 Takte Pin auf low, 25 Takte Pin auf high => 2 MHz> F_PIO auf 2000Hz stellen, 25 Takte Pin auf low, 25 Takte Pin auf high => 40 Hz.> Der Zwischenbereich kann linear interpoliert werden.>> Für den Bereich unter 40 Hz (2_000 > F_PIO < 125_000_000)> muss das Delay halt grösser werden (der Pico kann nicht langsamer).> Das Programm benötigt ca. 5 Instruktionen.>> Es gibt Beispiele, bei denen die Pios sogar Sinussignale erzeugen.>> Michael S.
Klingt echt interessant. Wie darf ich mir das mit der Auflösung bei z.B.
2MHz vorstellen?
die PIOs hatte ich dafür noch nicht auf dem Schirm.
Michael S. schrieb:> Wenn es um ein Rectecksignal geht, dann schau Dir mal Pico's Pios an.
Ähem nö. So sehr ich die PIOs auch mag, das hier wäre wirklich Perlen
vor die Säue. Die PIOs spart man natürlich für höhere Aufgaben auf.
Zumal die Taktrechnerei für ein optimales Ergebnis dadurch auch kein
bissel einfacher wird. Auch die PIOs haben schließlich INT_DIV und
FRAC_DIV in ihren Vorteilern.
Also nö, was mit den Timern geht, wird natürlich auch mit den Timern
gemacht.
Die Rechnerei ist übrigens garnicht so wild. Man kann das Problem so
umformen, dass aus INT_DIV, FRAC_DIV und TOP eine binäre Teilerkette
(sprich eine Zahl) mit 28 Bit entsteht. Und diese Zahl läßt sich ganz
einfach aus Eingangstakt und gewünschtem Takt berechnen.
Etwas komplizierter ist dann, aus den gefundenen 28Bit die drei
benötigten Werte sinnvoll zu extrahieren. Aber mit ein wenig Nachdenken
sollte das jeder Programmierer, der den Namen verdient, problemlos
hinbekommen.
Was ich außerdem noch anmerken möchte: die 125MHz clk_per sind nur der
Standardwert, aber keinesfalls eine Art Naturkonstante. Man kann diesen
Takt ändern! Alle selbstgebastelte Rechnerei sollte also immer davon
ausgehen, dass dieser Takt auch mal ein anderer sein kann und
dementsprechend den tatsächlichen Takt als Eingangsgröße verwenden,
nicht den Standardwert...
label(start) # zur Veranschaulichung, einfacerr mit wrap target
4
set(pins, 0) [24] # Pin auf low setzen + 24 Takte nix tun
5
set(pins, 1) [24] # dito, aber high
6
jmp(start)
Das Programm mit der Angabe der gewünschten Frequenz×50 für F_PIO
starten.
Für Frequenzen unterhalb 40 Hz müssen nop()s eingefügt werden.
Um 10 Hz zu erreichen bei F_PIO 2000 muss 1 Takt 200 Pio-Takte
verbummeln.
1
# label(start) # zur Veranschaulichung, einfacerr mit wrap target
2
set(pins, 0) [24] # Pin auf low setzen + 24 Takte nix tun
3
nop() [24] # 1 + 24 Takte nix tun
4
nop() [24] # 1 + 24 Takte nix tun
5
nop() [24] # 1 + 24 Takte nix tun
6
set(pins, 1) [24] # dito, aber high
7
nop() [24] # 1 + 24 Takte nix tun
8
nop() [24] # 1 + 24 Takte nix tun
9
nop() [24] # 1 + 24 Takte nix tun
10
wrap
11
12
[xx] sind Wartecycles, der max. Wert ist 31 (5 Bit).
Macht zusammen 200 Takte.
In Verbindung mit dem Takt, mit dem die Statemachine rennt, ergibt sich
eine genau definierte Frequenz am gewählten Ausgangspin (es dürfen sogar
mehrere Pins sein, bei Bedarf mit invertiertem Pegel).
Michael S.
c-hater schrieb:> Zumal die Taktrechnerei für ein optimales Ergebnis dadurch auch kein> bissel einfacher wird. Auch die PIOs haben schließlich INT_DIV und> FRAC_DIV in ihren Vorteilern.
Stimmt so nicht wirklich.
Denn es gibt einen kleinen, aber dafür entscheidenden Unterschied
zwischen Timer und Statemachine:
The clock divider slows the state machine’s execution by a constant
factor, represented as a 16.8 fixed-point fractional number.
Die Timer haben nur einen 8.4 divider.
Wenn ich das Beispiel 'Soll 18693Hz' bei 50 Takten / Wave rechne, dann
muss F_PIO 50 × 18693Hz = 934650Hz betragen.
Daraus ergibt sich DIV_INT = 133 und DIV_FRAC = 189/256.
Zurückgerechnet wird ein Rectecksignal von 18693.23Hz erzeugt.
Ganz ohne aufwändige Rechnerei.
Michael S.
Norbert schrieb:> Falls ich mich nicht irgendwo verrechnet haben sollte
Alles korrekt.
Im Register CLOCKDIV steht 0x0d0f7f00.
Je grösser DIV_INT, um so feiner wird die Auflösung.
Aber der abdeckbsre Wertebereich verändert sich.
Die 50 Takte hatte ich nur deshalb gewählt, um die gewünschten 2MHz noch
erreichen zu können, ohne am unteren Ende eine zu grosse Lücke zu
lassen.
Michael S.
Michael S. schrieb:> Norbert schrieb:>> Falls ich mich nicht irgendwo verrechnet haben sollte>> Alles korrekt.> Im Register CLOCKDIV steht 0x0d0f7f00.>> Je grösser DIV_INT, um so feiner wird die Auflösung.> Aber der abdeckbsre Wertebereich verändert sich.> Die 50 Takte hatte ich nur deshalb gewählt, um die gewünschten 2MHz noch> erreichen zu können, ohne am unteren Ende eine zu grosse Lücke zu> lassen.>> Michael S.
Prima, danke für's Korrekturlesen.
Baue gerade eine Klasse die mit variablen PIO Zyklen arbeitet.
Die berechnet immer die bestmöglichen Werte-Paare (PIO-Zyklen und DIV).
Braucht nur ca. 460µs auf'm Pico.
Da stößt man von der Auflösung her in Bereiche vor, welche weit besser
als Quarz-Genauigkeit sind.
Bis 32kHz < 1ppm
Bis 320kHz < 10ppm
Bis 1.6MHz < 50ppm
Sollte für Hobbyzwecke genügen.
(Und hat Spaß gemacht)
Norbert schrieb:> Prima, danke für's Korrekturlesen.> Baue gerade eine Klasse die mit variablen PIO Zyklen arbeitet.> Die berechnet immer die bestmöglichen Werte-Paare (PIO-Zyklen und DIV).> Braucht nur ca. 460µs auf'm Pico.>> Da stößt man von der Auflösung her in Bereiche vor, welche weit besser> als Quarz-Genauigkeit sind.> Bis 32kHz < 1ppm> Bis 320kHz < 10ppm> Bis 1.6MHz < 50ppm>> Sollte für Hobbyzwecke genügen.> (Und hat Spaß gemacht)
Du möchtest einem Quarz Konkurrenz machen? Überlege dir einmal welchen
gemeinsamen Nenner dein Programm und dein Pico als Taktquelle haben. Und
dann überlegst du dir nochmal ob deine Jitter Messung das macht was sie
soll. Du wärest nicht der Erste der sich damit vertut.
Veit D. schrieb:> Du möchtest einem Quarz Konkurrenz machen?
NEIN, SELBSTVERSTÄNDLICH NICHT.
Die Berechnung sollte zeigen das die Auflösung des PIO-Systems weit
größer ist, als sinnvoll für die Quarze.