¿Cómo interrumpo un ISR con un ISR de mayor prioridad en un STM32F105?

Tengo dos fuentes de interrupción externas que ingresan a un STM32F105. Quiero que uno de ellos (llámelo "IRQHigh") se adelante al otro ("IRQLow"). Actualmente, si se activa IRQHigh durante el ISR IRQLow, el flujo del programa espera hasta que borre el bit pendiente de ITP de IRQLow antes de que se ramifique en el ISR IRQHigh.

El STM32F105 es un microcontrolador basado en Cortex-M3. Admite interrupciones anidadas. Mi aplicación está escrita en C, usando GCC (arm-none-eabi-gcc) en Eclipse, con la biblioteca de periféricos estándar STM32F1.

Creo que tengo las prioridades configuradas correctamente pero me debe faltar algo.

Aquí está el código de inicialización correspondiente. He eliminado los comandos del reloj AFB, la configuración de GPIO, etc., ya que cada subsistema parece funcionar bien por sí mismo:

#define IRQHIGH_EXTI_PORT  GPIO_PortSourceGPIOA
#define IRQHIGH_EXTI_PIN   GPIO_PinSource3
#define IRQHIGH_EXTI_LINE  EXTI_Line3
#define IRQHIGH_EXTI_IRQn  EXTI3_IRQn

#define IRQLOW_EXTI_PORT   GPIO_PortSourceGPIOC
#define IRQLOW_EXTI_PIN    GPIO_PinSource11
#define IRQLOW_EXTI_LINE   EXTI_Line11
#define IRQLOW_EXTI_IRQn   EXTI15_10_IRQn

NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;

// Sixteen levels of pre-emption priority, no subpriorities
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

// IRQHigh

    // Connect EXTI Line to GPIO Pin
    GPIO_EXTILineConfig(IRQHIGH_EXTI_PORT, IRQHIGH_EXTI_PIN);

    // Configure EXTI line
    EXTI_InitStructure.EXTI_Line = IRQHIGH_EXTI_LINE;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    // Configure and enable EXTI Interrupt
    NVIC_InitStructure.NVIC_IRQChannel = IRQHIGH_EXTI_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

// IRQLow

    // Connect EXTI Line to GPIO Pin
    GPIO_EXTILineConfig(IRQLOW_EXTI_PORT, IRQLOW_EXTI_PIN);

    // Configure EXTI line
    EXTI_InitStructure.EXTI_Line = IRQLOW_EXTI_LINE;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    // Configure, but do not enable, EXTI Interrupt
    NVIC_InitStructure.NVIC_IRQChannel = IRQLOW_EXTI_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init(&NVIC_InitStructure);

Los controladores de IRQ están configurados de la siguiente manera:

void EXTI3_IRQHandler(void)
{
    IRQHigh();
    EXTI_ClearITPendingBit(IRQHIGH_EXTI_LINE);
}

void EXTI15_10_IRQHandler(void)
{
    if (EXTI_GetITStatus(IRQLOW_EXTI_LINE) == SET)
    {
        if (IRQLow()) // This returns a non-zero value if an overflow happened
        {
            CleanUpOverflow();
        }

        // Clear interrupt bit.
        EXTI_ClearITPendingBit(IRQLOW_EXTI_LINE);
    }
    else // unknown EXTI source
    {
        ErrorHandler(ERR_UNKNOWN_EXTI15_10_IRQ); // This never happens
    }
}

Algunas cosas a tener en cuenta:

  1. IRQHigh() e IRQLow() tardan mucho en completarse (es por eso que quiero que uno interrumpa al otro)

  2. IRQLow no está habilitado inicialmente, pero se habilita más adelante conNVIC_EnableIRQ(IRQLOW_EXTI_IRQn);

  3. Dentro de EXTI15_10_IRQHandler(), obtengo un valor de retorno de IRQLow().

  4. He declarado las funciones xxx_IRQHandler() con y sin __attribute__ ((interrupt ("IRQ"))). Entiendo que este atributo no es necesario con Cortex-M3, pero lo intenté de todos modos (y obtuve los mismos resultados).

¿Qué estoy haciendo mal?


Actualización: con la ayuda de un comentario de @Jeroen3, descubrí que IRQLow está interrumpiendo IRQHigh. Ahora tengo que averiguar por qué...

Su ISR es muy corto, en realidad podría estar encadenándolos. ¿Qué tan seguro estás de que ocurren juntos? Mire NVIC->IABR.
No veo nada malo en lo que has mostrado. ¿Qué hay en CleanUpOverflow(); ... ¿Hay algo allí que deshabilite las interrupciones?
@ Jeroen3 Gracias por eso. Verificaré cuando llegue a la oficina. Ambos controladores de ISR llaman a una función (ISRHigh() e ISRLow()), cada uno de los cuales tarda bastante tiempo en completarse. He actualizado la pregunta para mencionar esto.
Ups, me perdí ISRLow(). Debería haber preguntado: ¿Hay algo que deshabilite/habilite las interrupciones en ISRLow() o CleanUpOverflow() que podría estar retrasando la ejecución de su interrupción de mayor prioridad?
Además (duda de que esto esté relacionado) ¿CC1101_INT_EXTI_LINE es lo mismo que IRQLOW_EXTI_LINE?
@Tut ¡Gracias! Acabo de empezar a trabajar; Voy a comprobar eso. Y, sí, esa referencia CC1101 aparentemente escapó de mis intentos de anonimización :)
@ Jeroen3 Su sugerencia de consultar NVIC-> IABR me ayudó a encontrar la solución. Si lo desea, consulte la respuesta que publiqué. ¡Gracias!
@Tut Gracias por su ayuda, resulta que esas funciones estaban bien. Encontré la solución y fue sorprendente (¡al menos para mí!). Por favor, vea la respuesta si está interesado...
Sugeriría no usar bloatware en la configuración de interrupción. Tiene hermosas funciones y definiciones de CMSIS de bajo nivel.

Respuestas (1)

Las prioridades no se inicializan correctamente.

El código en la pregunta inicializa el NVIC así:

// IRQHigh, enabled immediately
NVIC_InitStructure.NVIC_IRQChannel = IRQHIGH_EXTI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

// IRQLow, to be enabled later:
NVIC_InitStructure.NVIC_IRQChannel = IRQLOW_EXTI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
NVIC_Init(&NVIC_InitStructure);

Luego, más tarde, IRQLow se habilita explícitamente con NVIC_EnableIRQ().

El problema es que la NVIC_Init()función (del SPL) en realidad no inicializa nada (!) si IRQChannelCmd = DISABLE . Simplemente configura el registro ICER para deshabilitar esa interrupción.

Esto me parece un error, pero que así sea. Ahora necesito ir y verificar las otras xxx_Init()funciones en el SPL y ver si algo más me puede molestar más tarde :)

Hay algunas soluciones. El que prefiero es omitir las cinco líneas de la secuencia de inicio IRQLow y reemplazarlas con esto:

NVIC_SetPriority(IRQLOW_EXTI_IRQn, 5);

Luego, cuando sea el momento, NVIC_EnableIRQ()se puede utilizar según lo previsto.

Tenga en cuenta que esto funciona porque el NVIC está configurado con NVIC_PriorityGroup_4(sin subprioridades). Las diferentes configuraciones de PriorityGroup requerirían que configures la subprioridad también.

+1 ... Buen hallazgo y es lo mismo en SPL para STM32F4 (acabo de verificar el código fuente para v1.8.0). ¡Necesito revisar un código!
Mejor evitar HAL y SPL. Especialmente este último.
@PeterJ_01 Es divertido; Estaba usando el SPL como una alternativa de nivel inferior y menos hinchada a STM32Cube :)
@bitsmack qué es "bajo nivel" en el SPL. Esta es una versión anterior de HAL (abandonada por STM). CubeMx es solo el generador de código de inicio HAL, nada más. Acaba de elegir bibliotecas similares pero más antiguas (y con más errores) (ha ido de mal en peor). Definiciones y funciones de bajo nivel que tiene en el CMSIS
@ PeterJ_01 Mi error, quise decir STM32Cube. ¡Pero tu punto sigue en pie! Gracias por la info :)
@bitsmack ¿qué es el STM32Cube? FX - ejemplos, MX - generador de código de inicio. Mismas bibliotecas HAL. Ninguna diferencia.
FYI: Esto puede ser discutible ya que está utilizando NVIC_PriororityGroup_4, pero CMSIS proporciona NVIC_EncodePriority() que acepta como argumentos: PriorityGroup, PreemptPriority y SubPriority. "El valor de prioridad devuelto se puede utilizar para la función NVIC_SetPriority(...)". (que se encuentra en core_cm4.h)