Hallo, ich mal wieder.
Ich habe die Clock so wie im Bild ersichtlich konfiguriert.
Nun verwende ich folgenden Code:
1
while(1)
2
{
3
HAL_GPIO_TogglePin(SpiClk_GPIO_Port,SpiClk_Pin);
4
HAL_GPIO_TogglePin(SpiClk_GPIO_Port,SpiClk_Pin);
5
}
Wenn ich nun entsprechenden Ausgang am Oszi messe toggelt der mit ca 660
kHz.
Ich hätte mir da eigentlich deutlich mehr erwartet, oder ist das normal?
Danke für jeden Hinweis!
Hier noch der generierte Code von CubeMx:
Theoretisch sollte das schneller gehen, aber je nachdem mit welchen
Einstellungen du deinen Code kompiliert hast und wie die Funktion
"HAL_GPIO_TogglePin()" aussieht kann das schon hinkommen.
Den Luxus einer einfachen Schnittstelle / eines HAL erkauft man sich
immer auf kosten von Codegröße und Laufzeit!
Kam mir auf den ersten Blick nicht so verdächtig vor.
Das IS_GPIO_PIN macht schon ein paar Sachen... Dachte aber nicht dass es
so viel ausmacht.
Denn noch danke, ich kann zum testen ja mal nur den unteren teil selbst
ausführen.
ist nicht ganz billig. Außerdem hast du (je nach Optimierung) zwei
Funktionsaufrufe mit allem was dazu gehört alleine um die Unterfunktion
zu erreichen.
Ein ARM Mcu hat heute mehrere Busse, die CPU läuft von den Peripherie
Clocks unabhängig. Am schnellsten geht das toggeln, wenn man nur das
Outputregister genau mit dem wert beschreibt, der benötigt wird.
Lesen kostet extra.
Deine Schleife und HAL kosten natürlich auch - werden aber mit der Core
clock abgearbeitet, die schneller ist, als die Peripherie clock.
Speicher und Flash Zugriffe Können je nach Controller auch noch Mal
extra kosten. Z.b. das Peripherie Basis Register, kann im Rom/Flash
liegen und wenn der Mcu keinen Cache hat oder den Flash nicht schnell
genug lesen kann, dauert das auch.
Last but not least habe ich im Raspberry Pi beobachtet, dass die Arm und
VPU die gpio gleich schnell toggeln können. Nur nach einigen cyclen war
die Arm CPU langsamer. Das lag daran, dass der soc ein Feature hat, das
sie bei zu schnellen Arm Zugriffen auf den Bus den ARM Core warten lässt
> Ein ARM Mcu hat heute mehrere Busse, die CPU läuft von den Peripherie> Clocks unabhängig.
Und die Zugriffe dazwischen muessen syncronisiert werden. Das kostet
auch nochmal Zeit!
Olaf
Da hammas wieda:
Entweder HAL oder schnell.
HAL ist ja sooooo schöööön bequem.
Da kann auch nix zu schnell werden. Könnt ja sein dass
man sich sonst "der-rennt", wie der Bayer so schön zu
sagen pflegt.
BTW: Ich verstehe diesen Test aus assert_param() nicht:
#define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) &&
((PIN) != (uint16_t)0x00))
Also der zweite Teil ist klar, PIN darf nicht 0 sein (fängt bei 1 an,
wie sinnvoll (sic)). Aber wozu am Anfang das mit dem &?
Peter H. schrieb:> GPIOx->ODR ^= GPIO_Pin;
Jaja, das kommt davon, wenn man partout NICHT das Manual zum Chip zu
lesen bereit ist und deshalb seine Uralt-Ideen vom AVR oder sonstigem µC
auch auf einen 32Bitter überträgt ohne weiter drüber nachzudenken.
Versuche mal lieber sowas:
GPIO.BSRR = (1<<Pinnummer);
GPIO.BSRR = (16<<Pinnummer);
in deiner Schleife.
W.S.
CubeMX und schnell ohne CPU Last geht mit DMA auf GPIO Port
Einfach in CubeMX einen 16bit DMA konfigurieren, Memory to peripheral
Modus, dann geht es so schnell wie der DMA Controller kann, meist zu
schnell. Also den DMA von einem Timer triggern lassen, dann kann man
sich die Geschwindigkeit einstellen. AN4031 hilft beim Einstieg.
dasrotemopped schrieb:> CubeMX und schnell ohne CPU Last geht mit DMA auf GPIO Port
Äusserst sinnvoll!
Einfach per DMA einen Port dauernd togglen lassen.
Bloss keinen unnötigen Hardware-Leerlauf erzeugen.
Das hilft auch beim Programmieren sehr viel weiter
wie es der TO vor hat.
W.S. schrieb:> Peter H. schrieb:>> GPIOx->ODR ^= GPIO_Pin;>> Jaja, das kommt davon, wenn man partout NICHT das Manual zum Chip zu> lesen bereit ist und deshalb seine Uralt-Ideen vom AVR oder sonstigem µC> auch auf einen 32Bitter überträgt ohne weiter drüber nachzudenken.
Du hast aber schon gesehen, dass der Codeschnipsel offenbar aus der HAL
Library von ST war?
Der Codeschnipsel aus der HAL ist ja nicht falsch, denn die Funktion
soll den Pin 1x toggeln, egal in welchem Zustand er vorher war.
Wenn man aber weiß, dass man den Pin schneller setzen bzw. rücksetzen
kann, dann macht man das, anstatt zu toggeln.
Mike R. schrieb:> Auch der BefehlGPIOx->ODR ^= GPIO_Pin;>> ist nicht ganz billig. Außerdem hast du (je nach Optimierung) zwei> Funktionsaufrufe mit allem was dazu gehört alleine um die Unterfunktion> zu erreichen.
so ist das, das wird zu:
1
HAL_GPIO_TogglePin:
2
08000260:subsp,#8
3
08000262:strr0,[sp,#4]
4
08000264:movr3,r1
5
08000266:strh.wr3,[sp,#2]
6
509GPIOx->ODR^=GPIO_Pin;
7
0800026a:ldrr3,[sp,#4]
8
0800026c:ldrr2,[r3,#12]
9
0800026e:ldrh.wr3,[sp,#2]
10
08000272:eorsr2,r3
11
08000274:ldrr3,[sp,#4]
12
08000276:strr2,[r3,#12]
13
510}
Peter H. schrieb:> Kam mir auf den ersten Blick nicht so verdächtig vor.> Das IS_GPIO_PIN macht schon ein paar Sachen... Dachte aber nicht dass es> so viel ausmacht.
das steckt aber im assert_param(), und das verpufft zu Luft wenn das
define USE_FULL_ASSERT nicht gesetzt ist. Da man bei den vielen
Parametern viel falsch machen kann ist das gar nicht verkehrt das im
debug build zu aktivieren.