STM32F7 bleibt in der externen Interrupt-Rückruffunktion hängen

7

Ich arbeite an einem Projekt, um eine Kamera über die DCMI-Schnittstelle an eine STM32F7 Discovery-Karte anzuschließen. Der Kamerateil funktioniert gut, aber ich habe ein seltsames Problem mit externen Interrupts über den integrierten Druckknopf.

Ich aktiviere externe Interrupts für die Schaltfläche mit der im STM BSP-Paket bereitgestellten Funktion:

BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_EXTI);

Der externe Interrupt für diese Schaltfläche befindet sich jetzt auf GPIO_PIN_11. Dieser Interrupt wird von der Funktion HAL_GPIO_EXTI_Callback behandelt, die ich in meiner Datei main.c implementieren kann.

Ich verwende STM HAL / BSP-Bibliotheken.

Der Interrupt bei einem Tastendruck funktioniert und die Rückruffunktion ist korrekt eingegeben, aber hier beginnt das Problem.

Folgendes möchte ich auf Knopfdruck tun:

{
        if(capture_status == 0)
        {
            start_capture_continuous((uint8_t*)CAMERA_FRAME_BUFFER);//start continuous grabbing, if not already running
            if(suspended == 1)
            {
                Camera_Resume();//resume DCMI and DMA if necessary and wakeup sensor from standby
            }
        }
        else
        {
            Camera_status = stop_capture();//stop if already running and update the status
            Camera_Suspend();//halt DCMI and DMA and put sensor in standby mode
        }
    HAL_Delay(50);
}

Erläuterung dieses Codes:

Dieser Code dient zum Umschalten der Live-Vorschau der Kamera auf dem LCD.

start_capture_continuous((uint8_t*)CAMERA_FRAME_BUFFER);

Diese Funktion startet das kontinuierliche Abrufen von Bildern von der Kamera und aktualisiert den Bildpuffer. Es verweist grundsätzlich auf die Funktion HAL_DCMI_Start_DMA.

stop_capture();

Diese Funktion stoppt die DMA-Übertragung.

Camera_Suspend und Camera_Resume deaktivieren / aktivieren die DCMI-Schnittstelle und senden Standby- / Weckbefehle über I2C an meinen Kamerasensor.

Hier beginnt also das Problem:

Wenn ich diesen Code in die Rückruffunktion setze, bleibt die MCU irgendwo in dieser Funktion stecken. Nur ein Reset kann mich wieder in den normalen Zustand bringen.

Irgendwo im Internet habe ich gelesen, dass dieses Problem mit I2C zusammenhängen könnte, aber selbst wenn ich die I2C-Teile in der Rückruffunktion lösche, ist das Verhalten nicht wie beabsichtigt: Manchmal funktioniert es ungefähr dreimal oder es bleibt sofort wieder hängen. Ich glaube, das Problem liegt in der Funktion HAL_DCMI_Start_DMA, bin mir aber nicht sicher.

Gibt es häufige Fehler, die zu einem solchen Problem führen?

Ich hoffe, es ist klar geworden, was mein Problem ist, und jemand kann mir einige Tipps geben, um es zu lösen.

Übrigens: Wenn ich die Taste im Polling-Modus in der Endlosschleife verwende und genau die gleichen Dinge auf einer Taste mache, funktioniert alles gut.

Um meine Interruptroutinen zu verdeutlichen:

Hauptinterrupt-Handler:

void EXTI15_10_IRQHandler(void)
{
//if button pressed
if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_11) != RESET)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);
}
//if touchscreen interrupt
if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_13) != RESET)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}
}

Anrufe:

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}

Anrufe nach dem Zurücksetzen des IT-Flags:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
sprintf((char*)text, "EXTI pin: %d", GPIO_Pin);
BSP_LCD_DisplayStringAt(5, LINE(8), (uint8_t*)text, LEFT_MODE);
//if button pressed -> toggle preview
if(GPIO_Pin == GPIO_PIN_11)
{
    BSP_LED_Toggle(LED1);
}
//ts interrupt
if(GPIO_Pin == GPIO_PIN_13)
{

}
}

Dies funktioniert einwandfrei, aber wenn ich BSP_LED_Toggle (LED1) ersetze; Mit dem obigen Code bleibt die Funktion hängen.

Update: Ich habe den Fehler gefunden. Die SysTick-Interrupt-Priorität wurde auf die niedrigste (15) gesetzt, sodass das Aufrufen von HAL_Delay () von einem ISR mit derselben oder höherer Priorität eine Endlosschleife in der HAL_Delay-Funktion verursachte.

Seien Sie also vorsichtig: Wenn Sie die von ST bereitgestellten HAL-Standardeinstellungen verwenden, wird die Priorität für SysTick IRQ beim Aufrufen von HAL_Init () auf 15 festgelegt. Sie müssen dies in der Datei stm32f7xx_hal_conf.h oder mithilfe der Funktion HAL_InitTick (TickPriority) ändern.

In der HAL-Dokumentation von HAL_InitTick heißt es zu diesem Problem:

Vorsicht ist geboten, wenn HAL_Delay () von einem peripheren ISR-Prozess aufgerufen wird. Der SysTick-Interrupt muss eine höhere Priorität (numerisch niedriger) als der periphere Interrupt haben. Andernfalls wird der Anrufer-ISR-Prozess blockiert. Die Funktion wird als __schwach deklariert und im Falle einer anderen Implementierung in der Benutzerdatei überschrieben.

deinoppa
quelle
1
Haben Sie Code eingefügt, um das Interrupt-Flag in Ihrem Handler / Rückruf zu löschen?
SoreDakeNoKoto
2
Das ist eine Menge Dinge, die in einer Unterbrechung zu tun sind, und schlechte Übung. Setzen Sie im Interrupt ein Flag und wirken Sie auf das Flag in main.
Scott Seidman
Ich weiß, dass dies eine schlechte Praxis ist, aber dies scheint die Standardpraxis der ST HAL-Bibliothek zu sein. Der Haupthandler ist die Funktion EXTI15_10_IRQHandler (void) in der Datei stm32f7xx_it.c. Diese Funktion prüft, an welchem ​​GPIO-Pin der Interrupt aufgetreten ist, und ruft HAL_GPIO_EXTI_IRQHandler (GPIO_PIN_11) auf. Das Interrupt-Flag wird in der HAL-Funktion gelöscht, die anschließend auch die Rückruffunktion aufruft. Das Verfahren ist zwar komplizierter als nötig, aber das Problem besteht nicht darin, Interrupt-Flags zu löschen.
Deinoppa
Welches Programm verwenden Sie zum Debuggen? Ich hatte einmal ein ähnliches Problem und musste korrigieren, was ich für einen Fehler in einer ST HAL-Bibliothek halte. Um herauszufinden, wo mein Programm stecken geblieben ist, habe ich Visual Studio mit VisualGDB verwendet. Ohne ein schrittweises Debugging wäre es schwierig, Ihr Problem genau zu bestimmen. Wenn ich finde, wo ich den Code geändert habe, werde ich damit antworten, aber ich bin nicht sicher, ob es Ihr Problem lösen wird.
FMarazzi
1
Sie sollten Ihre Lösung veröffentlichen und zur akzeptierten Antwort machen, damit die Leute keine Zeit damit verschwenden, diese zu lesen.
Tut

Antworten:

7

Update: Ich habe den Fehler gefunden. Die SysTick-Interrupt-Priorität wurde auf die niedrigste (15) gesetzt, sodass das Aufrufen von HAL_Delay () von einem ISR mit derselben oder höherer Priorität eine Endlosschleife in der HAL_Delay-Funktion verursachte.

Seien Sie also vorsichtig: Wenn Sie die von ST bereitgestellten HAL-Standardeinstellungen verwenden, wird die Priorität für SysTick IRQ beim Aufrufen von HAL_Init () auf 15 festgelegt. Sie müssen dies in der Datei stm32f7xx_hal_conf.h oder mithilfe der Funktion HAL_InitTick (TickPriority) ändern.

In der HAL-Dokumentation von HAL_InitTick heißt es zu diesem Problem:

Vorsicht ist geboten, wenn HAL_Delay () von einem peripheren ISR-Prozess aufgerufen wird. Der SysTick-Interrupt muss eine höhere Priorität (numerisch niedriger) als der periphere Interrupt haben. Andernfalls wird der Anrufer-ISR-Prozess blockiert. Die Funktion wird als __schwach deklariert und im Falle einer anderen Implementierung in der Benutzerdatei überschrieben.

deinoppa
quelle
1

Unter der Annahme, dass der Code nicht im ISR hängen bleibt (nach Ihrer letzten Aussage scheint dies unwahrscheinlich), löscht der Code das Interrupt-Flag innerhalb der Interrupt-Serviceroutine nicht. Wenn ISR beendet wird, "sieht" der Mikrocontroller, dass das Interrupt-Flag noch gesetzt ist, und springt sofort zur Interrupt-Serviceroutine zurück.

Sie müssen das Interrupt-Flag innerhalb der Interrupt-Serviceroutine löschen. Ich bin mit dem STM32 und der Entwicklungsumgebung nicht vertraut genug, um Ihnen genau zu sagen, wie das Interrupt-Flag gelöscht werden soll, aber das Interrupt-Flag muss innerhalb des ISR gelöscht werden.

CHendrix
quelle
Siehe meinen Kommentar oben. Wenn ich nur eine LED auf Interrupt schalte, funktioniert alles gut, so dass das Problem offensichtlich nicht darin besteht, Flags zu löschen.
Deinoppa
@deinoppa Kannst du deinen vollständigen ISR-Code posten?
CHendrix
fügte es im ursprünglichen Beitrag hinzu.
Deinoppa
habe den Fehler gefunden, siehe Originalbeitrag.
Deinoppa
2
@deinoppa Ich würde empfehlen, diese Fixbeschreibung in eine Antwort einzufügen und zu akzeptieren.
CHendrix