Seite 1 von 1
3 Unbekannte effizient ermitteln
Verfasst: 15.08.2022 15:32
von Thomas
Hallo Leute,
in einem anderem Projekt nutze ich ein Raspberry Pico um mit dem internen Timer eine einstellbare Frequenz zu erzeugen.
PureBasic nutze ich gerade um herzauszufinden, wie ich die Parameter effizient errechnen (effizent ausprobieren ist auch ok) kann.
Wunschfrequenz 10Hz - 2MHz
Die Formel (nach f aufgelöst) ist:
Code: Alles auswählen
#CPU_FREQ = 125000000 ; 125MHz
; Wertebereiche
; TOP = 0 - 65535 (16 bit)
; DIV_INT = 1 - 255 ( 8 bit)
; DIV_FRAC = 0 - 15 ( 4 bit)
; => Sollte es ein gutes Ergebnis sowohl mit DIV_FRAC = 0 und DIV_FRAC > 0 geben,
; ist DIV_FRAC = 0 immer vorzuziehen, da DIV_FRAC > 0 zusätzlichen Jitter verursacht
f = #CPU_FREQ / ((TOP + 1) * (DIV_INT + (DIV_FRAC / 16)))
Das Problem: Ich habe eine "Soll-Frequenz" und soll die Parameter
TOP,
DIV_INT,
DIV_FRAC errechnen.
Aktuell sehe ich nur den Weg für 65536x256x16 Schleifendurchläufe:
- Alle 65536x256x16 Werte zu durchlaufen
- Dabei die Abweichung zur Soll-Frequenz bei den einzelnen Durchläufen zu ermitteln. Die Sache mit dem DIV_FRAC = 0 zu berücksichtigen.
- Am Ende die "besten Parameter" auszugeben.
- => Dauert sogar auf dem PC ein paar Sekunden, was zu lange ist
Code: Alles auswählen
Beispiel:
für Soll-Frequenz = 18673 Hz
Bestes Ergebnis ermittelt durch Durchlaufen und Prüfen aller möglichen Werte:
DIV_INT = 4
DIV_FRAC = 13
TOP = 1390
Proberechnung ergibt für f = 18672,9160... Hz
Hat jemand eine effiziente Idee für das Problem?
Re: 3 Unbekannte effizient ermitteln
Verfasst: 15.08.2022 15:42
von Thomas
Ich habe schon ein bisschen getestet, nur ist es einfach viel zu Träge:
Code: Alles auswählen
#CPU_FREQ = 125000000
#FREQ_OUT_MIN = 10 ; 10
#FREQ_OUT_MAX = 11 ; wenns schnell genug ist: 2000000
Procedure Frequency(freq)
temp1.f = 0
calc_div_int = 1
calc_top = 0
calc_div_frac = 0
calc_freq_out.f = 0
calc_error.f = 0
calc_error_alt.f = freq
For n_div_int = 1 To 255 ; 0 bis 255 => 0 Timer aus
For n_div_frac = 0 To 15 ; 0 bis 15
temp1 = #CPU_FREQ / freq / (n_div_int + (n_div_frac/16))
If temp1 < 65536
For n_top = 0 To 65535 ; 0 bis 65535 => ab 1 machts Sinn, vorher ists iwie aus
calc_freq_out = #CPU_FREQ / ((n_top + 1) * (n_div_int + (n_div_frac/16)))
calc_error = Abs(Abs(freq)-Abs(calc_freq_out))
If calc_error < calc_error_alt
calc_error_alt = calc_error
calc_div_frac = n_div_frac
calc_div_int = n_div_int
calc_top = n_top
EndIf
Next
EndIf
Next
Next
DIV_INT = calc_div_int
TOP = calc_top
DIV_FRAC = calc_div_frac
FREQ_OUT.f = #CPU_FREQ / ((TOP + 1) * (DIV_INT + (DIV_FRAC/16))) ; Formel aus Datenblatt RP2040 Seite 530
error_diff.f = freq - FREQ_OUT
error_perc.f = 100 / freq * error_diff
error_ppm = error_perc * 10000
AddGadgetItem(0, -1, Str(freq)+Chr(10)+StrF(DIV_INT)+Chr(10)+StrF(DIV_FRAC)+Chr(10)+StrF(TOP)+Chr(10)+StrF(FREQ_OUT)+Chr(10)+StrF(error_diff)+Chr(10)+Str(error_ppm))
EndProcedure
If OpenWindow(0, 100, 100, 1000, 1000, "Frequenz-Calc", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ListIconGadget(0, 5, 5, 990, 990, "Freq Soll", 200, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
AddGadgetColumn(0, 1, "DIV_INT", 100)
AddGadgetColumn(0, 2, "DIV_FRAC", 100)
AddGadgetColumn(0, 3, "TOP", 100)
AddGadgetColumn(0, 4, "FREQ_OUT", 200)
AddGadgetColumn(0, 5, "error_diff", 100)
AddGadgetColumn(0, 6, "error_ppm", 100)
; Einzelne Frequenz
; Frequency(18480680)
; Frequenz-Liste
For f = #FREQ_OUT_MIN To #FREQ_OUT_MAX
Frequency(f)
Next
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf
Hier berücksichtige ich die Sache mit dem DIV_FRAC = 0 noch nicht.
Bei 10Hz wären z.B.
TOP = 62499,
DIV_INT = 200,
DIV_FRAC = 0 besser.
Re: 3 Unbekannte effizient ermitteln
Verfasst: 15.08.2022 16:23
von Thomas
// Update - ich hab es etwas eingegrenzt, nur denke ich geht hier noch mehr:
Code: Alles auswählen
#CPU_FREQ = 125000000
#FREQ_OUT_MIN = 10 ; 10
#FREQ_OUT_MAX = 2000 ; wenns schnell genug ist: 2000000
Procedure Frequency(freq)
temp1.f = 0
temp2.f = 0
calc_div_int = 1
calc_top = 0
calc_div_frac = 0
calc_freq_out.f = 0
calc_error.f = 0
calc_error_alt.f = freq
For n_div_int = 1 To 255 ; 0 bis 255 => 0 Timer aus
For n_div_frac = 0 To 15 ; 0 bis 15
temp1 = #CPU_FREQ / freq / (n_div_int + (n_div_frac/16))
If temp1 < 65536
For n_top = temp1-1 To temp1+1 ; 0 bis 65535 => ab 1 machts Sinn, vorher ists iwie aus
calc_freq_out = #CPU_FREQ / ((n_top + 1) * (n_div_int + (n_div_frac/16)))
calc_error = Abs(Abs(freq)-Abs(calc_freq_out))
; Debug "freq: " + StrF(freq) + " calc_freq_out: " + StrF(calc_freq_out, 6) + " n_div_int: " + StrF(n_div_int) + " n_div_frac: " + StrF(n_div_frac) + " n_top: " + StrF(n_top)
If calc_error <= calc_error_alt
If (calc_error_alt = 0 And calc_div_frac = 0)
Else
calc_error_alt = calc_error
calc_div_frac = n_div_frac
calc_div_int = n_div_int
calc_top = n_top
EndIf
EndIf
Next
EndIf
Next
Next
DIV_INT = calc_div_int
TOP = calc_top
DIV_FRAC = calc_div_frac
FREQ_OUT.f = #CPU_FREQ / ((TOP + 1) * (DIV_INT + (DIV_FRAC/16))) ; Formel aus Datenblatt RP2040 Seite 530
error_diff.f = freq - FREQ_OUT
error_perc.f = 100 / freq * error_diff
error_ppm = error_perc * 10000
AddGadgetItem(0, -1, Str(freq)+Chr(10)+StrF(DIV_INT)+Chr(10)+StrF(DIV_FRAC)+Chr(10)+StrF(TOP)+Chr(10)+StrF(FREQ_OUT)+Chr(10)+StrF(error_diff)+Chr(10)+Str(error_ppm))
EndProcedure
If OpenWindow(0, 100, 100, 1000, 1000, "Frequenz-Calc", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ListIconGadget(0, 5, 5, 990, 990, "Freq Soll", 200, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
AddGadgetColumn(0, 1, "DIV_INT", 100)
AddGadgetColumn(0, 2, "DIV_FRAC", 100)
AddGadgetColumn(0, 3, "TOP", 100)
AddGadgetColumn(0, 4, "FREQ_OUT", 200)
AddGadgetColumn(0, 5, "error_diff", 100)
AddGadgetColumn(0, 6, "error_ppm", 100)
; Einzelne Frequenz
; Frequency(18480680)
; Frequenz-Liste
StartTime.q = ElapsedMilliseconds()
For f = #FREQ_OUT_MIN To #FREQ_OUT_MAX
Frequency(f)
Debug "Frequenz: "+Str(f)+ " Millisekunden: "+Str(ElapsedMilliseconds()-StartTime)
Next
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf
Re: 3 Unbekannte effizient ermitteln
Verfasst: 15.08.2022 16:32
von Nino
Tatsächlich muss nur 1 Unbekannte ermittelt, genauer gesagt
berechnet werden.
f = #CPU_FREQ / ((TOP + 1) * (DIV_INT + (DIV_FRAC / 16)))
Die Lösung ist einfach, wenn die Formel umgestellt wird.
f ist ja bekannt, auf der linken Seite sollte aber etwas stehen, was unbekannt ist.
Umgeformt:
Code: Alles auswählen
TOP = #CPU_FREQ / (f * (DIV_INT + (DIV_FRAC / 16))) - 1
Als Nebenbedingung haben wir:
Wenn möglich DIV_FRAC = 0. Das ergibt:
Also:
- DIV_FRAC = 0 setzen
- Für DIV_INT irgendeinen gültigen Wert wählen.
- Mit Hilfe der letzten Formel oben den dazu gehörenden Wert für TOP berechnen.

Re: 3 Unbekannte effizient ermitteln
Verfasst: 15.08.2022 22:23
von Thomas
Vielen Dank, probier ich gleichaus
