STM32 EXTI zu langsam

Gast #7438186
Lesenswert?

Hallo

Ich habe einen STM32F072RB auf einem Nucleo-Board und möchte damit einen I2C-Bus belauschen. Ich verwende die aktuelle Cube IDE.

Da die I2C Hardware kein Sniffing kann, wollte ich die Funktion mit externen IRQs realisieren. Leider ist die Reaktionszeit sehr schlecht.

Die Clock-Config ist Standard, 48MHz für HCLK und APB1. PB3 ist als GPIO_EXTI3, fallende Flanke, eingestellt.

Das ist mein IRQ Handler:

1
void EXTI2_3_IRQHandler(void)
2
{
3
  /* USER CODE BEGIN EXTI2_3_IRQn 0 */
4
  GPIOC->BSRR = GPIO_PIN_3;
5
[...]
6
}

Die Funktion wird direkt im Startup-Code ausgeführt. Der Debugpin wird sofort ohne HAL gesetzt. Viel schneller sollte es eigentlich nicht mehr gehen.

Wenn ich die Zeit zwischen fallender Flanke und der Rekation des Debugpins mit einem Logic 16 ausmesse, beträgt die Rekationszeit 3us. Erwartet hätte ich <1us.

Es ist leider schon eine Weile her, dass ich programmiert habe.

Hat jemand noch ein paar Tips, was die Verzögerung auslösen könnte?

Vielen Dank,

#7438197
Lesenswert?

Tilo L. schrieb:

die Rekationszeit 3us. Erwartet hätte ich <1us.

Nö. Die 3µs sind ungefähr richtig für einen 48MHz Cortex-M0 Core.

Ich habe die Zahen nicht komplett im Kopf, aber ein Cortex-M3 braucht min 12 Takte für Interrupt Entry. Cortex-M0 kann dabei nicht den Stack Frame geleichzeitig zum Code Fetch aufsetzen und brauch daher deutlich mehr Takte.

Falls nach dem BSRR Schreibzugriff noch Code folgt, wird der Compiler vorher noch ein paar Register am Funktionsanfang pushen. Das ist dann der Rest der 3µs.

Gast #7438257
Lesenswert?

Danke für den Hinweis. Ich hab nochmal nachgeschaut. Es wird die HSI48 verwendet, welche mit 48MHz läuft. Die Prescaler sind alle auf /1 gesetzt. Ich habe die Einstellungen von Cube MX im Code geprüft, das scheint zu passen. Zum Compilieren verwende ich die Standard-Debugeinstellungen, -g3 und -O0.

#7438262
Lesenswert?

Die Interrupt-Latency bei M0 bzw. M0+ beträgt (inkl. Stacking) 16 bzw. 15 Takte. Dazu kommen noch Wait-Zyklen beim Flash. Bei asynchronen Eingängen muss man noch einige Takte für die Synchronisationslogik einkalkulieren, aber insgesamt kommt man wohl kaum über 30 Takte hinaus, selbst bei vielen Wait-Zyklen. Das ist die interne Reaktionszeit.

Bis das Setzen des GPIO-Pins aber außen ankommt, kann es ein ganzes Weilchen dauern, einfach nach dem Setzen noch ein Rücksetzen hinterher und mal messen ... Das hat ARM wohl selbst gemerkt und deshalb beim M0+ den IOPORT-Bus drangestrickt.

Gast #7439016
Lesenswert?

Du hast schon recht, mit -O0 wird nichts optimiert. Das sollte hier aber nicht das Problem sein.

Ich habe heute überprüft, ob die Taktrate stimmt. Dafür habe ich einfach einen GPIO Wackeln in main() lassen:

1
  Trigger_GPIO_Port->BSRR = Trigger_Pin; // Set debug pin
2
  Trigger_GPIO_Port->BRR = Trigger_Pin; // Clear debug pin
3
  Trigger_GPIO_Port->BSRR = Trigger_Pin; // Set debug pin
4
  Trigger_GPIO_Port->BRR = Trigger_Pin; // Clear debug pin
5
  Trigger_GPIO_Port->BSRR = Trigger_Pin; // Set debug pin
6
  Trigger_GPIO_Port->BRR = Trigger_Pin; // Clear debug pin
7
  Trigger_GPIO_Port->BSRR = Trigger_Pin; // Set debug pin
8
  Trigger_GPIO_Port->BRR = Trigger_Pin; // Clear debug pin
9
  Trigger_GPIO_Port->BSRR = Trigger_Pin; // Set debug pin
10
  Trigger_GPIO_Port->BRR = Trigger_Pin; // Clear debug pin

Jede Zeile erzeugt 3 Assembler-Befehle:

1
135         Trigger_GPIO_Port->BSRR = Trigger_Pin; // Set debug pin
2
080008b8:   ldr     r3, [pc, #68]   ; (0x8000900 <EXTI2_3_IRQHandler+76>)
3
080008ba:   movs    r2, #8
4
080008bc:   str     r2, [r3, #24]
5
136         Trigger_GPIO_Port->BRR = Trigger_Pin; // Clear debug pin
6
080008be:   ldr     r3, [pc, #64]   ; (0x8000900 <EXTI2_3_IRQHandler+76>)
7
080008c0:   movs    r2, #8
8
080008c2:   str     r2, [r3, #40]   ; 0x28

Der Flash läuft bei 48MHz mit einem Waitstate. Damit komme ich auf 6Zyklen. Bei 48MHZ komme ich damit auf 125ns. Das passt genau zu dem, was ich mit dem Logicanalyzer messe.

Wenn ich den Code in den IRQ kopiere, bleibt das Disassembly gleich. Die gemessene Zeit vergrößert sich aber auf 750ns.

Es sieht so aus, also ob der IRQ um Faktor 6 langsamer ist, also nur noch mit 8MHz läuft. Das würde zu dem passen, was Daniel geschrieben hat. Ich habe das Register RCC_CFGR geprüft. Beim Start ist HSI48 ausgewählt, im IRQ plötzlich HSI. So richtig erklären kann ich mir das leider noch nicht.

#7439108
Lesenswert?

https://github.com/JojoS62/NucleoG071RB-Test/tree/master

Ich denke das hat nicht mit dem ISR Code zu tun. Wie wird das Signal bei dir generiert? Flanke steil genug? Ich habe PB4 auf OutputSpeed 'Very High' gestellt. Wenn I2C Signale triggern sollen, dann können die Flanken schon langsamer sein. Dann sind Port C Pins teilweise etwas anders, könnte auch einen Unterschied machen. Release und Debug Build machte auch keinen Unterschied. Bei 64 MHz Clock sind es 578 ns.

Und nochmal PC3 als Ack Signal: Output freq low: 688 ns Output freq very high: 664 ns

Also einen Tick schneller durch die steilere Flanke, aber nicht im µs Bereich. Auch PC3 verhält sich da nicht anders.

Trigger und Ack auf freq. low: 700 ns PB3 ohne Pullup: 702 ns Ich schaffe die µs einfach nicht...

Moderator Persönliche Seite #7439439
Lesenswert?

Tilo schrieb:

Du hast schon recht, mit -O0 wird nichts optimiert. Das sollte hier aber nicht das Problem sein.

Trotzdem schadet es nicht, das mal kurz mit -Os zu compilieren und gegenzuprüfen.

Hinweise, die gegen die eigene Überzeugung laufen, einfach rauszufiltern und abzutun, wäre hier falsch. Du suchst Hilfe im Forum, um eine andere Meinung zu hören. Bringt aber nichts, wenn Du diese nicht wenigstens testest.

#7439572
Lesenswert?

Frank M. schrieb:

Trotzdem schadet es nicht, das mal kurz mit -Os zu compilieren und gegenzuprüfen.

das ist richtig, zumal das Umschalten in der IDE nur ein Klick ist. Release kompiliert als Voreinstellung mit -Os, Debug mit -O0. Ich hatte das ja gemacht und es gab keinen Unterschied. Es war zu erwarten, aber testen ist immer besser. EXTI2_3_IRQHandler ist der echte nackte IRQ Handler, kein HAL Code dazwischen der einen Callback aufruft.

Tilo schrieb:

Ich habe heute überprüft, ob die Taktrate stimmt. Dafür habe ich einfach einen GPIO Wackeln in main() lassen:

Das geht noch einfacher und eleganter: Der Controller hat einen Ausgang MCO auf den der Sysclk gelegt werden kann, incl. einstellbaren Teiler. MCO wird in CubeMX in RCC aktiviert, in den Clockeinstellungen kann ausgewählt werden welcher Takt da ausgegeben werden soll. Default ist allerdings PF2 was auch NRST ist, das kann aber auf PA8 oder PA9 umkonfiguriert werden.

#7439598
Lesenswert?

Tilo schrieb:

Beim Start ist HSI48 ausgewählt, im IRQ plötzlich HSI.

So kannst du den OSC wählen:

1
 RCC_DeInit();
2
 RCC_PLLCmd( DISABLE );
3
 RCC_SYSCLKConfig( RCC_SYSCLKSource_HSI );
4
 SystemCoreClockUpdate();

mit:

1
/**
2
  * @brief  Configures the system clock (SYSCLK).
3
  * @note   The HSI is used (enabled by hardware) as system clock source after
4
  *         startup from Reset, wake-up from STOP and STANDBY mode, or in case
5
  *         of failure of the HSE used directly or indirectly as system clock
6
  *         (if the Clock Security System CSS is enabled).
7
  * @note   A switch from one clock source to another occurs only if the target
8
  *         clock source is ready (clock stable after startup delay or PLL locked).
9
  *         If a clock source which is not yet ready is selected, the switch will
10
  *         occur when the clock source will be ready.
11
  *         You can use RCC_GetSYSCLKSource() function to know which clock is
12
  *         currently used as system clock source.
13
  * @param  RCC_SYSCLKSource: specifies the clock source used as system clock source
14
  *          This parameter can be one of the following values:
15
  *            @arg RCC_SYSCLKSource_HSI:    HSI selected as system clock source
16
  *            @arg RCC_SYSCLKSource_HSE:    HSE selected as system clock source
17
  *            @arg RCC_SYSCLKSource_PLLCLK: PLL selected as system clock source
18
  *            @arg RCC_SYSCLKSource_HSI48:  HSI48 selected as system clock source, applicable only for STM32F072 devices
19
  * @retval None
20
  */
#7439604
Lesenswert?

Der TO scheint beschäftigt zu sein und außerdem hat er die Ursache (praktisch) schon gefunden:

Tilo schrieb:

Ich habe das Register RCC_CFGR geprüft. Beim Start ist HSI48 ausgewählt, im IRQ plötzlich HSI.

Ich erlaube mir deshalb mal eine Zwischenfrage an jojos:

Wo gibt es bei deinen Meßwerten ein Problem? Deine 702ns entsprächen 0.94µs bei f=48MHz und damit wäre das Ziel erreicht. Ich stehe da auf dem Schlauch.

#7439610
Lesenswert?

Daniel V. schrieb:

Wo gibt es bei deinen Meßwerten ein Problem?

ich sehe kein Problem, die Zeiten sind auch alle bei 48 MHz gemessen, bis auf den einen Wert bei 64 MHz. Das es beim TO langsamer läuft muss eine andere Ursache haben, möglicherweise schlappe Signalflanken wenn da noch seine I2C Hardware mitspielt. Ich hatte ein minimales CubeMX generiertes Projekt das ich für den Test genommen habe.

Oder der TO hat einen Sleep Mode aktiv? Die Taktquelle verstellt sich doch auch nicht von alleine, da muss doch etwas im Code sein was das macht.

#7439664
Lesenswert?

Bei einem STM32F042 Nucleo Board mit 48 MHz Systemtakt ohne Benutzung der HAL und dem EXTI0_1 Interrupt Handler im SRAM ist die Reaktionszeit ca. 626 ns (30 Taktzyklen).

Das externe Signal liegt an PB0 (10 Hz Rechteck), im Interrupt Handler wird ein kurzer Impuls an PB4 erzeugt, das Ausmessen des Timing erfolgt mit einem Oszilloskop.

Die gemesse Impulsbreite ist ca. 148 ns (7 Taktzyklen), der Code ist nicht optimiert, der Anfang des Interrupt Handler sieht so aus:

1
isr_EXTI0_1
2

3
    PUSH  {R7,LR}
4
    ADD  R7, SP,  #0
5

6
    LDR  R3, =0x48000418 ; PB4 High
7
    MOVS  R2, #0x10
8
    STR  R2, [R3]
9

10
    NOP
11

12
    LDR  R3, =0x48000418 ; PB4 Low
13
    MOVS  R2, #0x100000
14
    STR  R2, [R3]

Der "NOP" ist bewusst per Inline-Assembler eingefügt.

Gast #7439722
Lesenswert?

Vielen Dank für eure Antworten.

Wie bereits geschrieben hab ich bereits etwas gefunden, nämlich das vom HSI48 auf HSI umgeschaltet wird. Das Projekt wurde nur per Cube MX erzeugt. Eigenen Code bis aufs Portwackeln hab ich bisher keinen drin. Das komplette Projekt wollte ich hier nicht posten, weil die Sachen einfach zu groß sind.

Es ist in Cube MX einfach was zusammenzuklicken aber schwer herauszufinden, wenn dann etwas schief läuft.

Was ich bereits herausgefunden habe ist, dass wenn USB nicht aktiv ist, der Takt nicht auf HSI gewechselt wird. Für mehr hatte ich bisher keine Zeit.

An den USB Pins ist bisher nichts angeschlossen. Eines der Features dieses uC ist, dass USB ohne Quarz funktioniert. Dafür wird der SOF mit 1ms Periode verwendet. Mein Verdacht ist, dass dies nicht funktioniert, weil USB nicht verbunden ist. Da ich im Code nichts gefunden habe das absichtlich umschaltet vermute ich, dass das Clock Recovery System hier die Finger mit drin hat.

Im Reference Manual habe ich bisher nichts gefunden, dass auf so etwas schließen lässt. Das wollte ich als nächstes untersuchen und die Ergebnisse mitteilen.

Gast #7439736
Lesenswert?

Tilo schrieb:

Es ist in Cube MX einfach was zusammenzuklicken aber schwer herauszufinden, wenn dann etwas schief läuft.

Das war auch mein erster Eindruck.

Da ich nicht die Zeit und Lust habe, mich ernsthaft in die HAL einzuarbeiten, habe ich für mich beschlossen, sie lieber nicht zu verwenden. Mir reicht das Reference-Manual, das ist schon komplex genug.

Die Nutzung der HAL entbindet einen ja nicht davon, das Reference-Manual zu lesen und zu verstehen - auch wenn das einige gerne so hätten.

Würde ich in dem Bereich beruflich tätig sein, dann würde ich mir die nötige Zeit nehmen.

#7439901
Lesenswert?

Tilo schrieb:

Was ich bereits herausgefunden habe ist, dass wenn USB nicht aktiv ist, der Takt nicht auf HSI gewechselt wird. Für mehr hatte ich bisher keine Zeit.

An den USB Pins ist bisher nichts angeschlossen. Eines der Features dieses uC ist, dass USB ohne Quarz funktioniert. Dafür wird der SOF mit 1ms Periode verwendet. Mein Verdacht ist, dass dies nicht funktioniert, weil USB nicht verbunden ist. Da ich im Code nichts gefunden habe das absichtlich umschaltet vermute ich, dass das Clock Recovery System hier die Finger mit drin hat.

Im Reference Manual habe ich bisher nichts gefunden, dass auf so etwas schließen lässt. Das wollte ich als nächstes untersuchen und die Ergebnisse mitteilen.

Naja, schon, da steht ausdrücklich in 6.2.3, dass der HSI48 primär für USB vorgesehen ist. Wenn also USB vorgesehen, aber nicht angeschlossen ist, überrascht es kaum, das der HSI48 aus dem Takt kommt. Und dass die nette, aber stellenweise undurchsichtige HAL da "überschlau" ist, und auf das Abdriften hin einfach die Reißleine zieht.

Vmtl. wird da AUTOTRIMEN im CRS_CR gesetzt und einer der Interrupts scharf gemacht. Da müsste man im Map-File den Interrupt-Handler für RCC_CRS finden können, wahrscheinlich setzt der auf HSI zurück.

#7439902
Lesenswert?

Tilo schrieb:

Wie bereits geschrieben hab ich bereits etwas gefunden, nämlich das vom HSI48 auf HSI umgeschaltet wird.

Der STM32F042 hat ebenfalls den HSI48. Wenn ich bei mir die 48 MHz anstelle aus dem HSI und der PLL direkt mit dem HSI48 erzeuge funktioniert das genauso, die Zeiten beim EXTI sind wie weiter oben beschrieben.

Kann es sein dass bei Dir die CPU in den Standby geht? Nach dem Aufwachen, z.B. durch den EXTI, läuft die CPU mit 8 MHz weil beim Standby die Clock abgeschalten wurde. Das hat aber nichts mit dem HSI48 zu tun, bei HSI und PLL ist das genauso.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren