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:
IRQHigh() e IRQLow() tardan mucho en completarse (es por eso que quiero que uno interrumpa al otro)
IRQLow no está habilitado inicialmente, pero se habilita más adelante conNVIC_EnableIRQ(IRQLOW_EXTI_IRQn);
Dentro de EXTI15_10_IRQHandler(), obtengo un valor de retorno de IRQLow().
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é...
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.
Jeroen3
Gesto de desaprobación
bitsmack
Gesto de desaprobación
Gesto de desaprobación
bitsmack
bitsmack
bitsmack
0___________