7 votos

STM32F7 se atasca en la función de devolución de la interrupción externa

Estoy trabajando en un proyecto para conectar una cámara a una placa STM32F7 Discovery utilizando la interfaz DCMI. La parte de la cámara funciona bien, pero tengo un extraño problema con las interrupciones externas del pulsador de la placa.

Habilito las interrupciones externas para el botón usando la función provista en el paquete STM BSP:

BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_EXTI);

La interrupción externa para este botón está ahora en GPIO_PIN_11. Esta interrupción es manejada por la función HAL_GPIO_EXTI_Callback que puedo implementar en mi archivo main.c.

Estoy utilizando las bibliotecas HAL/BSP de STM.

La interrupción al pulsar un botón funciona y la función de devolución de llamada se introduce correctamente, pero aquí es donde empieza el problema.

Esto es lo que quiero hacer al pulsar un botón:

{
        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);
}

Explicación de este código:

Este código es para alternar la vista previa en vivo de la cámara en la pantalla LCD.

start_capture_continuous((uint8_t*)CAMERA_FRAME_BUFFER);

Esta función inicia la captura continua de fotogramas de la cámara y actualiza el buffer de fotogramas. Básicamente hace referencia a la función HAL_DCMI_Start_DMA.

stop_capture();

Esta función detiene la transferencia DMA.

Camera_Suspend y Camera_Resume deshabilitan/habilitan la interfaz DCMI y envían comandos de espera/despertar a mi sensor de cámara vía I2C.

Así que aquí es donde empieza el problema:

Si pongo este código en la función callback, el MCU se queda atascado en alguna parte de esta función. Sólo un reinicio puede devolverme al estado normal.

En algún lugar de Internet leí que este problema podría estar relacionado con I2C, pero incluso si elimino las partes de I2C en la función de devolución de llamada el comportamiento no es el previsto: A veces funciona durante unas tres veces o se atasca inmediatamente de nuevo. Creo que el problema está en la función HAL_DCMI_Start_DMA, pero no estoy seguro.

¿Hay algún error común que lleve a un problema como éste?

Espero que haya quedado claro cuál es mi problema y alguien pueda darme alguna pista para solucionarlo.

btw: Si uso el botón en modo de sondeo en el bucle infinito principal y hago exactamente las mismas cosas al pulsar el botón todo funciona bien.

Para aclarar mis rutinas de interrupción:

Manejador de la interrupción principal:

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);
}
}

llamadas:

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);
}
}

llamadas después de restablecer la bandera de TI:

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)
{

}
}

Esto funciona perfectamente, pero si reemplazo BSP_LED_Toggle(LED1); con el código anterior, la función se atasca.

Actualización: He encontrado el error. La prioridad de la interrupción SysTick estaba establecida en la más baja (15), por lo que llamar a HAL_Delay() desde un ISR con la misma o mayor prioridad causaba un bucle infinito en la función HAL_Delay.

Por lo tanto, tenga cuidado: Si estás usando la configuración por defecto de la HAL proporcionada por ST, la prioridad para la IRQ de SysTick se establece en 15 cuando se llama a HAL_Init(). Tienes que cambiar eso en el archivo stm32f7xx_hal_conf.h o usando la función HAL_InitTick(TickPriority).

Esto es lo que dice la documentación de HAL de HAL_InitTick sobre este tema:

Hay que tener cuidado si se llama a HAL_Delay() desde un proceso ISR periférico periférico, la interrupción SysTick debe tener mayor prioridad (numéricamente inferior) que la interrupción periférica. De lo contrario, el proceso proceso ISR que llama será bloqueado. La función se declara como __débil para ser sobrescrita en caso de otra implementación en el archivo de usuario.

1 votos

¿Incluyó código para borrar la bandera de interrupción dentro de su manejador/retorno?

2 votos

Eso es una tonelada de cosas para hacer en una interrupción, y una mala práctica. Establece una bandera en la interrupción, y actúa sobre la bandera en el main.

0 votos

Sé que esto es una mala práctica, pero parece ser la práctica estándar de la biblioteca ST HAL. El manejador principal es la función EXTI15_10_IRQHandler(void) en el archivo stm32f7xx_it.c. Esta función comprueba en qué pin GPIO se ha producido la interrupción y llama a HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11). La bandera de la interrupción se borra en la función HAL que también llama a la función callback después. Así que el procedimiento es realmente más complicado de lo necesario, pero el problema no es borrar las banderas de interrupción.

9voto

jakub.g Puntos 2596

Actualización: He encontrado el error. La prioridad de la interrupción SysTick estaba establecida en la más baja (15), por lo que llamar a HAL_Delay() desde un ISR con la misma o mayor prioridad causaba un bucle infinito en la función HAL_Delay.

Por lo tanto, tenga cuidado: Si estás usando la configuración por defecto de la HAL proporcionada por ST, la prioridad de la IRQ de SysTick se establece en 15 cuando se llama a HAL_Init(). Tienes que cambiar eso en el archivo stm32f7xx_hal_conf.h o usando la función HAL_InitTick(TickPriority).

Esto es lo que dice la documentación de HAL de HAL_InitTick sobre este tema:

Hay que tener cuidado si se llama a HAL_Delay() desde un proceso ISR periférico periférico, la interrupción SysTick debe tener mayor prioridad (numéricamente inferior) que la interrupción periférica. De lo contrario, el proceso proceso ISR que llama será bloqueado. La función se declara como __débil para ser sobrescrita en caso de otra implementación en el archivo de usuario.

1voto

CHendrix Puntos 56

Asumiendo que el código no se está atascando en la ISR (por tu última afirmación, eso parece poco probable), el código no está limpiando la bandera de interrupción dentro de la rutina de servicio de interrupción. Cuando la ISR sale, el microcontrolador "ve" que la bandera de interrupción sigue activada, e inmediatamente salta de nuevo a la rutina de servicio de interrupción.

Tienes que borrar la bandera de interrupción dentro de la rutina de servicio de interrupción. No estoy lo suficientemente familiarizado con el STM32 y el entorno de desarrollo para decirle exactamente cómo borrar la bandera de interrupción, pero la bandera de interrupción tiene que ser borrado dentro de la ISR.

0 votos

Véase mi comentario anterior. Si simplemente activo un LED de interrupción todo funciona bien, por lo que el problema no es, obviamente, la limpieza de las banderas.

0 votos

@deinoppa ¿Puedes publicar tu código ISR completo?

0 votos

Lo he añadido en el post original.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X