Hallo,
ich bin gerade dabei mit dem STM32CubeMX und SW4STM32 ein Programm für
einen STM32f446RE zu schreiben.
Da ich das erste mal SW4STM32 und den CubeMX für die STM's nutze habe
ich relativ wenig Erfahrung damit.
Mein aktuelles Problem ist, das wenn ich in der Mainschleife einen Pin
Toggeln lasse ohne irgendwelche Delays, erhalte ich lediglich eine
Frequenz von 6Mhz an dem Pin.
Dafür das ich die vollen 180MHz als SystemTakt eingestellt habe finde
ich das sehr wenig. Die 180Mhz habe ich auch schon über den MCO Output
überprüft und die sind auch vorhanden.
Was kann die Hauptschleife noch so verlangsamen? Ich habe bis jetzt nur
das Grundgerüst vom CubeMX und das Toggeln als Code. Da müsste doch
eigentlich mehr gehen oder?
Irgendwer schrieb:> Wenn man zum schnöden ändern von einem Pin sich den Luxus eines Aufrufs> einer aufgeblasenen Funktion gönnt darf man sich über nichts wundern.
HAL oder die LL Lib sind ja nicht total kacke ...
Es gibt genug grunde das zeug einzusetzen
Aber nur um den PIN so schnell wie möglich wackeln zu lassen ... ist das
auch der der falsche ansatz
selbst der Weg mit leerer Main loop ist der total falsche !!
wenn man zB eine PWM nutzt oder andere HW features ... ist der einsatz
der HAL oder LL Lib sicher undramatisch
der vorteil wäre:
man klickt sich das im Cube zusammen und hat 10 sek später den Ausgang
am wackeln...
oder man liest sich durchs manual und sucht sich entsprechende Register
und bits raus
6MHz, also 12 Mio Durchläufe, also 15 Quarz-takte für
Zustand abfragen,
togglen,
schreiben
Sprung.
Das ist ok. Verbesserungsmöglichkeiten: togglen ausrollen, also High,
Low.
Noch besser: direkt, ohne hal
Wie meine Vorredner schon geschrieben haben, ist
Systemclock!=Peripherieclock. Die HAL-Bibliothek ist zwar praktisch,
aber von der Geschwindigkeit her bist du wesentlich schneller, wenn du
direkte Registerzugriffe machst. Viel Glück!
Ich kenn mich nich aus, aber:
>GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
"LOW" impliziert "laangsaam". Wird aber mit dem Problem hier nichts zu
tun haben, oder?
StromTuner
Trotz der Einstellung "GPIO_SPEED_LOW" sollte der Pin periodisch
schneller zu toggeln sein. Aus eigener Erfahrung weis ich, dass die
HAL-Lib einfach sehr viele Schritte macht, bis das richtige
GPIO-Register gesetzt wird.
Irgendwer schrieb:> while (1)> {> HAL_GPIO_TogglePin(LI_CH1_GPIO_Port,LI_CH1_Pin);> }>> Wenn man zum schnöden ändern von einem Pin sich den Luxus eines Aufrufs> einer aufgeblasenen Funktion gönnt darf man sich über nichts wundern.
Hast du schon mal in diese achso aufgeblasene Funktion reingeschaut?
Wenn nicht hier bitteschön:
Irgendein anderer schrieb:> /* Check the parameters */> assert_param(IS_GPIO_PIN(GPIO_Pin));
Und da hast du dir schon gleich die nächsten Funktionsaufrufe
eingefangen:-)
Axel R. schrieb:> Ich kenn mich nich aus, aber:>>GPIO_InitStruct.Speed = GPIO_SPEED_LOW;> "LOW" impliziert "laangsaam". Wird aber mit dem Problem hier nichts zu> tun haben, oder?
Nein.
Durch diese Einstellung wird die Treiberstärke der Ausgangsstufe
eingestellt. Damit ist die physikalische Schaltgeschwindigkeit geringer
und es gibt weniger EMV-Störungen. Am Oszi wird die Rechteck-Kurve etwas
verschliffener aussehen.
Mit der Geschwindigkeit, die das Programm zum Schalten des Pins
benötigt, hat das aber nichts zu tun.
Natürlich braucht das Pin-toggeln über die HAL deutlich länger als ein
optimierter Registerzugriff. Auf der anderen Seite: wer wirklich mit
High-Speed Pins schalten will, der hat beim STM32 wesentlich bessere
Möglichkeiten zur Verfügung.
Viele Grüße, Stefan
Irgendwer schrieb:> Und da hast du dir schon gleich die nächsten Funktionsaufrufe> eingefangen:-)
was ist falsch daran ein generisches Argument auf Gültigkeit zu prüfen?
Wenn man alles dem Performancegott opfern möchte und lieber lange Fehler
sucht kann man solche Abfangjäger ja weglassen.
Das assert wird auch nur wirksam wenn man es möchte und das Symbol
USE_FULL_ASSERT setzt, sonst wird das wegoptimiert.
Irgendwer schrieb:> Irgendein anderer schrieb:>> /* Check the parameters */>> assert_param(IS_GPIO_PIN(GPIO_Pin));>> Und da hast du dir schon gleich die nächsten Funktionsaufrufe> eingefangen:-)
Dachte, asserts sind nur im Debug-Modus aktiv ...^^
Mathias _. schrieb:> Wie meine Vorredner schon geschrieben haben, ist> Systemclock!=Peripherieclock. Die HAL-Bibliothek ist zwar praktisch,> aber von der Geschwindigkeit her bist du wesentlich schneller, wenn du> direkte Registerzugriffe machst. Viel Glück!
Habe eben ins Datenblatt geschaut und dort steht der GPIO hängt an dem
AHB1 mit 180MHz...
Ich hatte irgendwann mal gelesen, dass der IO Pin beim STM mehrere
Takte braucht um einen Zustand zu setzen (ab einer bestimmten Frequenz),
finde ich aber grade nicht... Hat hier einer mehr Details dazu?
G Ert
Das Toggle hat ja minimum schon 3 Takte. Read, modify, write. Dann ggf.
vorher noch Push. Eigentliches Register laden. Nachher Pop.
Einfach mal ins Assembly gucken.
Und vielleicht läuft die Prefetch Engine nicht so gut. Kannst ja
testweise den Code mal in den RAM schupsen und gucken.
Da ist aber noch Luft nach oben. Auf einem F411 mit 96MHz hatte ich nen
toggle ohne unroll mit etwas über 18MHz am laufen ohne HAL.
Interessantes Thema.
Hier die Assembly im Compiler Explorer:
https://godbolt.org/g/3yhzYd
Bei -O3 sieht das so aus:
1
.L3:
2
ldr r3, [r2, #20] | 3 Takte
3
eor r3, r3, #1 | 1 Takt
4
str r3, [r2, #20] | 3 Takte
5
b .L3 | 1 + P Takte
Die ARM ISA sagt zu P folgendes: /The number of cycles required for a
pipeline refill. This ranges from 1 to 3 depending on the alignment and
width of the target instruction, and whether the processor manages to
speculate the address early./
(http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100166_0001_00_en/ric1417449098079.html).
LDR und STR können unter günstigen Umständen (pipelining) auch schneller
sein, das ist mir nicht so klar wann das ist.
b kann also 2-4 Takte brauchen, ich nehme mal den Worst-Case von b=3 an.
In Summe sind das dann 11 Takte pro Toggle, x2 für eine komplette
Periode. 180/22 = 8MHz. Du bist also nah an der Grenze des technisch
machbaren.
Das doofe am Toggeln ist ja dass du den Pin lesen musst, obwohl in
deiner Funktion das gar nicht nötig ist. Schneller sollte es gehen wenn
du einfach hard-coded deine Schleife durchfährst, dann sind die stores
gepipelined (https://www.arm.com/files/pdf/CortexM3_Uni_Intro.pdf, Seite
31)
https://godbolt.org/g/UHEHHV
1
.L4:
2
str r1, [r3, #24] | 1 Takt (+2 Latenz)
3
str r2, [r3, #24] | 1 Takt
4
b .L4 | 1 + P Takte
Damit dauert deine Periode nur noch 6 Takte (= 30MHz), auf Kosten eines
asymmetrischen Rechtecks.
1
.L4:
2
str r1, [r3, #24] | 1 Takt
3
nop | 1 Takt
4
nop | 1 Takt
5
str r2, [r3, #24] | 1 Takt
6
b .L4 | 1 + P Takte
Das ist ein symmetrisches Rechteck mit 22,5MHz (=180MHz/8). Wenn es noch
schneller sein sollte, würde ich mir die Peripherie anschauen,
vielleicht kann man irgendwelche Hochgeschwindigkeits-Peripherie mit
180MHz Daten ausspucken lassen.
Ich hatte gerade die gleiche Frage wie OP. Allerdings sieht mein
Hauptprogramm so aus:
1
uint32_ti=0;
2
while(1){
3
if(i>2000000L){
4
HAL_GPIO_TogglePin(LED2_GPIO_PORT,LED2_PIN);
5
i=0;
6
}
7
++i;
8
}
Die LED blink im 1-Sekunden-Takt, d.h. die Schleife läuft mit 2 "MHz",
entsprechend der Durchläufe. Der Takt ist allerdings 64 MHz, d.h. wo
werden hier 32 Zyklen pro Schleife verbraten? (Die HAL o.ä. spielt hier
keine Rolle.)
Wie kann ein STM32 so langsam sein? Ich wollte ihn eigentlich als
Bus-Treiber (1-3 MHz) einsetzen. Sind die anderen Hersteller schneller
(ich vermute nicht)? Gibt es schnellere Alternativen (außer FPGA)?
Ja, bei mir wird die LED nur einmal pro 2 Mio. Durchläufen angesprochen,
d.h. deren Performance ist vernachlässigbar.
Aber -Os werde ich mal ausprobieren.
Ulf schrieb:> Ob> -O3 oder -Os besser ist, kann ich mit dem bloßen Auge nicht erkennen.
Kann ja auch zufällig das gleiche Optimierungs-Ergebnis herauskommen.