Forum: Mikrocontroller und Digitale Elektronik STM32F407V GPIO Toggle-Frequenz


von Sebastian Adler (Gast)


Lesenswert?

Liebes Forum,

ich habe ein STM32F407V Discovery Board und möchte an einen Ausgangspin 
möglichst schnell togglen. An sich kein großes Ding; gibt ja haufenweise 
Anleitungen im Internet. Doch bei mir will die Frequenz am Ausgang nicht 
ansatzweise an die theoretischen 100MHz herankommen. Hier mein minimaler 
Code:
1
#define HSE_VALUE ((uint32_t)8000000) //externer kristall mit 8MHz
2
3
#include "stm32f4xx_conf.h"
4
#include "stm32f4xx.h"
5
#include "stm32f4xx_gpio.h"
6
#include "stm32f4xx_rcc.h"
7
#include "system_stm32f4xx.h"
8
9
void Configure_PB1(void)
10
{
11
    GPIO_InitTypeDef GPIO_InitStruct;
12
13
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
14
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
15
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
16
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
17
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
18
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
19
    GPIO_Init(GPIOB, &GPIO_InitStruct);
20
}
21
22
int main(void)
23
{
24
  SystemInit();
25
  Configure_PB1();
26
  while(1){GPIO_ToggleBits(GPIOB, GPIO_Pin_1);}
27
}


Ich habe zuvor folgende Schritte ( 
http://clockspeeds.blogspot.de/2013/01/stm32f4-discovery-clock-frequency.html 
) durchgeführt. Denn viele Seiten empfehlen ja, den HSE-Wert uÄ. zu 
ändern um tatsächlich 168MHz zu erreichen...

Das Signal am Ausgangspin ist zwar da, allerdings mit lächerlichen 
768kHz. Was mache ich falsch?
Vielen lieben Dank für eure Hilfe!
Sebastian

von ollib. (Gast)


Lesenswert?

Also 168 MHz erreichst du nie an normalen IOs. Es geht glaube ich bei 
einem speziellen, den man so einstellen kann dass er die CPU-Clock 
ausgibt.

Die anderen sind limitiert auf die APB-Bus Geschwindigkeit, die ist 
abhängig vom Code bzw. der Clock-System konfiguration.

von ollib. (Gast)


Lesenswert?

Achso STM32F4 hängen die IOs am AHB nicht am APB aber dennoch sind 
niemals 168 MHz drin. Mehr als 800kHz sollten es aber doch sein denke 
ich.

von EFA (Gast)


Lesenswert?

Abgesehen davon, warum sollte man das tun wollen? Zur erzeugung 
schneller Signale hat der F407 ja reichlich 
Hardware-Peripherie-Einheiten.

von Hans (Gast)


Lesenswert?

DMA konfigurieren und ein array mit {0x0, 0x1} rausschieben... ggf das 
fsmc nutzen...

73

von BastiDerBastler (Gast)


Lesenswert?

Man muss halt mal schauen, was da an Assembler-Code für die Schleife 
ausgespuckt wird. Dann wird man recht schnell einsehen, dass da nix 
schnelles bei rauskommt...

von Dr. Sommer (Gast)


Lesenswert?

Der AHB läuft maximal mit 168 MHz, eine Store-Operation braucht 2 Takte, 
also kann man maximal mit 84 MHz Togglen, bzw Frequenzen mit 42MHz 
ausgeben. Alle soundsoviel Zyklen brauchts aber 1 Takt mehr, denn 
irgendwann muss man ja einen Schleifen-Rücksprung haben.
So etwas erreicht man aber nicht mit der Funktion GPIO_ToggleBits, 
sondern da muss man schön von Hand Assembler schreiben.

Sinnvoll ist das natürlich nicht, wenn man Daten seriell rausgeben will 
verwendet man die SPI/USART/I²C/... Peripherie, wenn man Signalformen 
machen will die Timer (+ DMA).

von Sebastian Adler (Gast)


Lesenswert?

Vielen Dank für die vielen schnellen Antworten. :) Ich weiß schon, dass 
ich keine 168MHz am Ausgang kriegen werde... Es geht mir nur um die 
Größenordnung.

Ein Wert im zweistelligen Megahertz-Bereich sollte schon möglich sein, 
aber davon bin ich aus einem Grund (den ich nicht kenne) weit entfernt 
und ich dachte jemand sieht vielleicht am Code meinen Fehler.

Bin weiterhin für alle Tipps dankbar

von Dr. Sommer (Gast)


Lesenswert?

Sebastian Adler schrieb:
> und ich dachte jemand sieht vielleicht am Code meinen Fehler.
Wie gesagt, ein C-Funktionsaufruf + Bitoperationen in der Funktion pro 
Bit-Toggle senkt die Frequenz drastisch. Versuch direkte 
Register-Zugriffe oder gleich Assembler.

von holger (Gast)


Lesenswert?

>So etwas erreicht man aber nicht mit der Funktion GPIO_ToggleBits,

Du könntest SPI1 mit DMA betreiben. Die Clockleitung
gibt dir dann theoretisch 42MHz.

von Sebastian Adler (Gast)


Lesenswert?

ollib. schrieb:
> Achso STM32F4 hängen die IOs am AHB nicht am APB aber dennoch sind
> niemals 168 MHz drin. Mehr als 800kHz sollten es aber doch sein denke
> ich.

Und ich sehe eben nicht, wo bei mir der Fehler sitzt. Ich habe ab den 
ganzen Header Dateien bis auf die wenigen Zeilen (siehe Link) nichts 
verändert.

von holger (Gast)


Lesenswert?

>Und ich sehe eben nicht, wo bei mir der Fehler sitzt.

Du hast eine Funktion aufgerufen: GPIO_ToggleBits
Die hat auch noch Parameter die übergeben werden müssen.
In der Funktion wird noch rumgehühnert......

Das alles kostet CPU Takte. Das ist dein Fehler.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Schau doch mal in der 'system_stm32f4xx.c', wie die peripheren Vorteiler 
gesetzt sind:
1
static void SetSysClock(void)
2
{
3
/******************************************************************************/
4
/*            PLL (clocked by HSE) used as System clock source                */
5
/******************************************************************************/
6
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
7
  
8
  /* Enable HSE */
9
  RCC->CR |= ((uint32_t)RCC_CR_HSEON); 
10
  /* Wait till HSE is ready and if Time out is reached exit */
11
  do
12
  {
13
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
14
    StartUpCounter++;
15
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
16
  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
17
  {
18
    HSEStatus = (uint32_t)0x01;
19
  }
20
  else
21
  {
22
    HSEStatus = (uint32_t)0x00;
23
  }
24
  if (HSEStatus == (uint32_t)0x01)
25
  {
26
    /* Enable high performance mode, System frequency up to 168 MHz */
27
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
28
    PWR->CR |= PWR_CR_PMODE;  
29
30
    /* HCLK = SYSCLK / 1*/
31
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
32
      
33
    /* PCLK2 = HCLK / 2*/
34
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
35
    
36
    /* PCLK1 = HCLK / 4*/
37
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
 so sieht das bei mir aus, nur als Beispiel.

von holger (Gast)


Lesenswert?

/* Set PG6 and PG8 */
    GPIOD->BSRRL = GPIO_Pin_14 | GPIO_Pin_15;
    /* Reset PG6 and PG8 */
    GPIOD->BSRRH = GPIO_Pin_14 | GPIO_Pin_15;

So geht es schneller.

von Dirk K. (dekoepi)


Lesenswert?

Den Code von Holger anstatt des ToggleBit-Krempels da einsetzen. Am 
besten, gleich zehn mal hintereinander als manuelles loop unrolling; so 
sollte der Rücksprung an den Schleifenanfang sich mit vielleicht 5% 
Geschwindigkeitsverlust begnügen.
Gegebenenfalls noch an den Compiler-Flags schrauben und anstatt -O0 auf 
-O2 setzen; wahrscheinliche wirft das aber die schön selber entrollten 
Toggles raus. Auf jeden Fall sollte da die Frequenz drastisch raufgehen.

von Sebastian Adler (Gast)


Lesenswert?

Dirk K. schrieb:
> Den Code von Holger anstatt des ToggleBit-Krempels da einsetzen. Am
> besten, gleich zehn mal hintereinander als manuelles loop unrolling; so
> sollte der Rücksprung an den Schleifenanfang sich mit vielleicht 5%
> Geschwindigkeitsverlust begnügen.
> Gegebenenfalls noch an den Compiler-Flags schrauben und anstatt -O0 auf
> -O2 setzen; wahrscheinliche wirft das aber die schön selber entrollten
> Toggles raus. Auf jeden Fall sollte da die Frequenz drastisch raufgehen.

So ist es. Vielen Dank für eure Hilfe (insbesondere Dirk und Holger)!

Noch kurz für die Nachwelt: Statt der Toggle-Bit-Funktion direkt die 
Pins zu ändern bringt einen Steigerung der Frequenz um den Faktor 5. Das 
Optimierungs-Flag bringt noch zusätzlich eine Steigerung ums 5-fache. 
Also sind aus den knapp 1MHz immerhin 24MHz geworden.

Das Optimierungs-Flag alleine ohne den direkten Zugriff auf die Pins 
bringt nur einen Faktor 2.

Danke!

von Dirk K. (dekoepi)


Lesenswert?

Cool. Danke für die Rückmeldung :)

von Dr. Sommer (Gast)


Lesenswert?

Versuch mal:
1
int main () {
2
  asm volatile (  "ldr r0, =0x40020400\n"
3
          "mov r1, 2\n"
4
          "mov r2, 0x200\n"
5
          "loop:\n"
6
          "str r1, [r0, 0x18]\n"
7
          "str r2, [r0, 0x18]\n"
8
          "str r1, [r0, 0x18]\n"
9
          "str r2, [r0, 0x18]\n"
10
          "str r1, [r0, 0x18]\n"
11
          "str r2, [r0, 0x18]\n"
12
          "str r1, [r0, 0x18]\n"
13
          "str r2, [r0, 0x18]\n"
14
          "str r1, [r0, 0x18]\n"
15
          "str r2, [r0, 0x18]\n"
16
          "str r1, [r0, 0x18]\n"
17
          "str r2, [r0, 0x18]\n"
18
          "str r1, [r0, 0x18]\n"
19
          "str r2, [r0, 0x18]\n"
20
          "str r1, [r0, 0x18]\n"
21
          "str r2, [r0, 0x18]\n"
22
          "b loop" : : : "r0", "r1", "r2");
23
}

von Dr. Sommer (Gast)


Lesenswert?

PS: Also, statt der Schleife. Den Initialisierungs-Code brauchst du 
natürlich noch:
1
#define HSE_VALUE ((uint32_t)8000000) //externer kristall mit 8MHz
2
3
#include "stm32f4xx_conf.h"
4
#include "stm32f4xx.h"
5
#include "stm32f4xx_gpio.h"
6
#include "stm32f4xx_rcc.h"
7
#include "system_stm32f4xx.h"
8
9
void Configure_PB1(void)
10
{
11
    GPIO_InitTypeDef GPIO_InitStruct;
12
13
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
14
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
15
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
16
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
17
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
18
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
19
    GPIO_Init(GPIOB, &GPIO_InitStruct);
20
}
21
22
int main(void)
23
{
24
  SystemInit();
25
  Configure_PB1();
26
27
28
  asm volatile (  "ldr r0, =0x40020400\n"
29
          "mov r1, 2\n"
30
          "mov r2, 0x200\n"
31
          "loop:\n"
32
          "str r1, [r0, 0x18]\n"
33
          "str r2, [r0, 0x18]\n"
34
          "str r1, [r0, 0x18]\n"
35
          "str r2, [r0, 0x18]\n"
36
          "str r1, [r0, 0x18]\n"
37
          "str r2, [r0, 0x18]\n"
38
          "str r1, [r0, 0x18]\n"
39
          "str r2, [r0, 0x18]\n"
40
          "str r1, [r0, 0x18]\n"
41
          "str r2, [r0, 0x18]\n"
42
          "str r1, [r0, 0x18]\n"
43
          "str r2, [r0, 0x18]\n"
44
          "str r1, [r0, 0x18]\n"
45
          "str r2, [r0, 0x18]\n"
46
          "str r1, [r0, 0x18]\n"
47
          "str r2, [r0, 0x18]\n"
48
          "b loop" : : : "r0", "r1", "r2");
49
50
}

von Dirk K. (dekoepi)


Lesenswert?

Sehr schön, das Ergebnis ist spannend! Jetzt heißt's abwarten :)

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.