Wie unterbreche ich eine ISR mit einer ISR höherer Priorität auf einem STM32F105?

Ich habe zwei externe Interrupt-Quellen, die in einen STM32F105 kommen. Ich möchte, dass einer von ihnen (nennen Sie ihn "IRQHigh") den anderen ("IRQLow") vorwegnimmt. Wenn IRQHigh während der IRQLow-ISR ausgelöst wird, wartet der Programmablauf derzeit, bis ich das IRQLow-ITPending-Bit lösche, bevor er in die IRQHigh-ISR verzweigt.

Der STM32F105 ist ein Cortex-M3-basierter Mikrocontroller. Es unterstützt verschachtelte Interrupts. Meine Anwendung ist in C geschrieben, mit GCC (arm-none-eabi-gcc) in Eclipse, mit der STM32F1 Standard Peripheral Library.

Ich denke, ich habe die Prioritäten richtig konfiguriert, aber mir muss etwas fehlen.

Hier ist der entsprechende Initialisierungscode. Ich habe AFB-Uhrbefehle, GPIO-Konfiguration usw. entfernt, da jedes Subsystem für sich allein gut zu funktionieren scheint:

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

Die IRQ-Handler sind wie folgt eingerichtet:

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

Ein paar Dinge zu beachten:

  1. IRQHigh() und IRQLow() nehmen jeweils viel Zeit in Anspruch (weshalb ich möchte, dass einer den anderen unterbricht).

  2. IRQLow ist anfänglich nicht aktiviert, wird aber später mit aktiviertNVIC_EnableIRQ(IRQLOW_EXTI_IRQn);

  3. Innerhalb von EXTI15_10_IRQHandler() erhalte ich einen Rückgabewert von IRQLow().

  4. Ich habe die xxx_IRQHandler() Funktionen mit und ohne deklariert __attribute__ ((interrupt ("IRQ"))). Ich verstehe, dass dieses Attribut bei Cortex-M3 nicht erforderlich ist, aber ich habe es trotzdem versucht (und die gleichen Ergebnisse erhalten).

Was mache ich falsch?


Update: Mit Hilfe eines Kommentars von @Jeroen3 habe ich entdeckt, dass IRQLow IRQHigh unterbricht. Jetzt muss ich herausfinden warum...

Ihr ISR ist sehr kurz, es könnte sie tatsächlich verketten. Wie sicher sind Sie, dass sie zusammen auftreten? Sehen Sie sich NVIC->IABR an.
Ich sehe nichts Falsches an dem, was du gezeigt hast. Was ist in CleanUpOverflow(); ... Gibt es dort etwas, das Interrupts deaktivieren würde?
@ Jeroen3 Danke dafür - ich werde nachsehen, wenn ich ins Büro komme. Beide ISR-Handler rufen eine Funktion (ISRHigh() und ISRLow()) auf, die jeweils sehr viel Zeit in Anspruch nehmen. Ich habe die Frage aktualisiert, um dies zu erwähnen.
Hoppla, ich habe ISRLow() verpasst. Ich hätte fragen sollen: Gibt es irgendetwas, das Interrupts in ISRLow() oder CleanUpOverflow() deaktiviert/aktiviert, was die Ausführung Ihres Interrupts mit höherer Priorität verzögern könnte.
Auch (bezweifle, dass dies verwandt ist) ist CC1101_INT_EXTI_LINE dasselbe wie IRQLOW_EXTI_LINE?
@Tut Danke! Ich muss nur arbeiten; Ich werde das überprüfen. Und ja, diese CC1101-Referenz ist anscheinend meinen Anonymisierungsversuchen entgangen :)
@Jeroen3 Ihr Vorschlag, NVIC->IABR zu überprüfen, hat mir geholfen, die Lösung zu finden. Wenn Sie möchten, sehen Sie sich bitte die Antwort an, die ich gepostet habe. Danke!
@Tut Danke für deine Hilfe, es stellt sich heraus, dass diese Funktionen in Ordnung waren. Ich fand die Lösung und sie war überraschend (zumindest für mich!). Bitte lesen Sie die Antwort, wenn Sie interessiert sind ...
Ich würde vorschlagen, keine Bloatware in den Interrupt-Einstellungen zu verwenden. Sie haben schöne CMSIS-Funktionen und -Definitionen auf niedriger Ebene.

Antworten (1)

Die Prioritäten werden nicht korrekt initialisiert.

Der Code in der Frage initialisiert das NVIC wie folgt:

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

Später wird IRQLow dann explizit mit aktiviert NVIC_EnableIRQ().

Das Problem ist, dass die NVIC_Init()Funktion (aus der SPL) nichts (!) Initialisiert, wenn IRQChannelCmd = DISABLE . Es setzt einfach das ICER-Register, um diesen Interrupt zu deaktivieren.

Das scheint mir ein Bug zu sein, aber sei es so. Jetzt muss ich die anderen xxx_Init()Funktionen in der SPL überprüfen und sehen, ob mich später etwas anderes beißen könnte :)

Es gibt ein paar Lösungen. Ich ziehe es vor, alle fünf Zeilen der IRQLow-Init-Sequenz wegzulassen und durch diese zu ersetzen:

NVIC_SetPriority(IRQLOW_EXTI_IRQn, 5);

Dann, wenn es Zeit ist, NVIC_EnableIRQ()kann es wie vorgesehen verwendet werden.

Beachten Sie, dass dies funktioniert, weil das NVIC mit NVIC_PriorityGroup_4(keine Unterprioritäten) konfiguriert ist. Bei unterschiedlichen PriorityGroup-Einstellungen müssten Sie auch die Unterpriorität konfigurieren.

+1 ... Guter Fund und es ist das gleiche in der SPL für STM32F4 (ich habe gerade den Quellcode für v1.8.0 überprüft). Ich muss einen Code überprüfen!
Vermeiden Sie besser HAL & SPL. Vor allem letzteres.
@ PeterJ_01 Es ist lustig; Ich habe die SPL als niedrigere, weniger aufgeblähte Alternative zu STM32Cube verwendet :)
@bitsmack was ist "low level" in der SPL. Dies ist eine ältere Version von HAL (vom STM aufgegeben). CubeMx ist nur der HAL-Init-Code-Generator - sonst nichts. Sie haben gerade ähnliche, aber ältere (und fehlerhaftere) Bibliotheken ausgewählt (Sie sind vom Schlechten zum Schlechteren gegangen). Low-Level-Definitionen und -Funktionen, die Sie im CMSIS haben
@PeterJ_01 Meine Güte, ich meinte STM32Cube. Aber Ihr Punkt steht immer noch! Danke für die Info :)
@bitsmack was ist der STM32Cube? FX - Beispiele, MX - Init-Code-Generator. Gleiche HAL-Bibliotheken. Kein Unterschied.
FYI: Dies kann strittig sein, da Sie NVIC_PriororityGroup_4 verwenden, aber CMSIS bietet NVIC_EncodePriority(), das als Argumente akzeptiert: PriorityGroup, PreemptPriority und SubPriority. "Der zurückgegebene Prioritätswert kann für die Funktion NVIC_SetPriority(...) verwendet werden". (gefunden in core_cm4.h)