Forum: Mikrocontroller und Digitale Elektronik STM32 EXTI zu langsam


von Tilo (Gast)


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,

von Jim M. (turboj)


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.

von Wastl (hartundweichware)


Lesenswert?

Tilo L. schrieb:
> Es ist leider schon eine Weile her, dass ich programmiert habe.

Es ist leider schon eine Weile her dass Mikrocontroller mit
48 MHz Core Takt zu den schnellsten ihrer Art gehören.

: Bearbeitet durch User
von Tilo (Gast)


Lesenswert?

3us sind 144 Zyklen.
Der M0 hat eine IRQ Latency von 16 Takten.
Mir kommt es immer noch zu viel vor.

von Daniel V. (danielv2)


Lesenswert?

Ein STMF030 würde selbst mit Debug-Kompilat (gcc) unter 1µs bleiben. 
Läuft dein STM32 wirklich mit 48MHz? Bei 8MHz-Standardfrequenz und einem 
Release-Kompilat wären nämlich deine 3µs realistisch.

von Tilo (Gast)


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.

von Andreas B. (abm)


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.

von Peter D. (peda)


Lesenswert?

Tilo L. schrieb:
> Zum Compilieren verwende ich die Standard-Debugeinstellungen, -g3 und
> -O0.

-O0 heißt, der MC läuft mit Bremse und Wurfanker.
Als Standard nimmt man -Os.

von Tilo (Gast)


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.

von J. S. (jojos)


Angehängte Dateien:

Lesenswert?

Ich habe das Board auch und das mal nachgebaut. PB3 für den EXTI, PB4 um 
den auszulösen und PB5 als Ack Signal. Das kommt 0,66 µs nach dem 
Triggersignal.

von K. S. (the_yrr)


Lesenswert?

Magst du vllt. mal den relevanten Code posten, und dazu auch den 
Assembler Ausschnitt mit der ISR?

von J. S. (jojos)


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...

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


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.

von J. S. (jojos)


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.

von Ingo L. (corrtexx)


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
  */

von Daniel V. (danielv2)


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.

von J. S. (jojos)


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.

: Bearbeitet durch User
von Dieter S. (ds1)


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.

von Tilo (Gast)


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.

von Monk (roehrmond)


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.

: Bearbeitet durch User
von Andreas B. (abm)


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.

von Dieter S. (ds1)


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.

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.