Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage STM32 GPIO maximale Frequenz


von Peter H. (peter_hilleb)


Angehängte Dateien:

Lesenswert?

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:
1
void SystemClock_Config(void)
2
{
3
4
  RCC_OscInitTypeDef RCC_OscInitStruct;
5
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
6
7
    /**Initializes the CPU, AHB and APB busses clocks 
8
    */
9
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
10
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
11
  RCC_OscInitStruct.HSICalibrationValue = 16;
12
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
13
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
14
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
15
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
16
  {
17
    _Error_Handler(__FILE__, __LINE__);
18
  }
19
20
    /**Initializes the CPU, AHB and APB busses clocks 
21
    */
22
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
23
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
24
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
25
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
26
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
27
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
28
29
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
30
  {
31
    _Error_Handler(__FILE__, __LINE__);
32
  }
33
34
    /**Configure the Systick interrupt time 
35
    */
36
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
37
38
    /**Configure the Systick 
39
    */
40
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
41
42
  /* SysTick_IRQn interrupt configuration */
43
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
44
}

: Bearbeitet durch User
Beitrag #5322613 wurde vom Autor gelöscht.
von Mike R. (thesealion)


Lesenswert?

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!

von Ingo L. (corrtexx)


Lesenswert?

Peter H. schrieb:
> Ich hätte mir da eigentlich deutlich mehr erwartet, oder ist das normal?
Schau dir mal an, was die Funktion
1
HAL_GPIO_TogglePin(SpiClk_GPIO_Port, SpiClk_Pin);
macht. Wirste dich wundern. Ein direkter Registerzugriff is deutlich 
schneller.

von Peter H. (peter_hilleb)


Lesenswert?

1
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
2
{
3
  /* Check the parameters */
4
  assert_param(IS_GPIO_PIN(GPIO_Pin));
5
6
  GPIOx->ODR ^= GPIO_Pin;
7
}

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.

von Mike R. (thesealion)


Lesenswert?

Auch der Befehl
1
GPIOx->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.

von Torsten (Gast)


Lesenswert?

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

von Olaf (Gast)


Lesenswert?

> 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

von Schwanzus Longus (Gast)


Lesenswert?

Gibt doch genug Infos dazu im Netz, einfach mal eine dieser Seiten 
durchlesen.
Z.B.
https://vjordan.info/log/fpga/stm32-bare-metal-start-up-and-real-bit-banging-speed.html

von Daniel B. (dbuergin)


Lesenswert?

Pack das mal in deine main(), damit solltest du das Maximum bekommen,
was Du mit den Clocks eingestellt hast.
1
while(1)
2
{
3
   SpiClk_GPIO_Port->BSSRL = SpiClk_Pin; // Set the pin high
4
   SpiClk_GPIO_Port->BSSRH = SpiClk_Pin; // Set the pin low
5
}

von STM Apprentice (Gast)


Lesenswert?

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.

von Markus M. (adrock)


Lesenswert?

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 &?

von W.S. (Gast)


Lesenswert?

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.

von dasrotemopped (Gast)


Lesenswert?

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.

von STM Apprentice (Gast)


Lesenswert?

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.

von Markus M. (adrock)


Lesenswert?

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?

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

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.

von Johannes S. (Gast)


Lesenswert?

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:   sub     sp, #8
3
08000262:   str     r0, [sp, #4]
4
08000264:   mov     r3, r1
5
08000266:   strh.w  r3, [sp, #2]
6
509         GPIOx->ODR ^= GPIO_Pin;
7
0800026a:   ldr     r3, [sp, #4]
8
0800026c:   ldr     r2, [r3, #12]
9
0800026e:   ldrh.w  r3, [sp, #2]
10
08000272:   eors    r2, r3
11
08000274:   ldr     r3, [sp, #4]
12
08000276:   str     r2, [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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.