STM32L011 vom Benutzercode zum Bootloader springen

Ich versuche, es so zu machen, dass mein STM32L011 vom Benutzercode zum ST-Bootloader springen kann, mit dem Flash über USART2 neu programmiert werden kann. Dies ist hypothetisch dieselbe Frage wie STM32F091 Jump to Bootloader from application , aber ich habe kein Glück mit den Korrekturen zur Deaktivierung von Peripheriegeräten aus dieser Frage. ST hat ihre Foren neu gestaltet und alle alten Links zu Threads zerstört, also habe ich eine schreckliche Zeit damit, Hinweise auf andere mögliche Probleme zu finden, und ich bin ziemlich ratlos, wo ich als nächstes suchen soll.

Meine Sprungfunktion ist wie folgt (geändert von der obigen Frage für meine Anwendung und die Verwendung der ST LL-Bibliotheken). Mein Anwendungscode verwendet den LPUART auf PA2/PA3, der dann von USART2 für den Bootloader verwendet werden sollte. Auf dem LPUART wird ein Befehl im Benutzeranwendungscode empfangen, der die Sprungfunktion aufruft. Der Anwendungscode bestätigt den Befehl ordnungsgemäß gemäß meinem seriellen Protokoll, aber dann reagiert der USART-Bootloader nicht auf Flasher-Dienstprogramme. Sie senden das anfängliche 0x7F-Byte per AN2606, aber der Bootloader sendet das ACK nicht zurück.

#define STM32L01_SYSTEM_MEMORY  0x1FF00000

void jump_to_bootloader()
{
    // Disable global interrupts
    __disable_irq();

    // Disable interrupt requests from peripherals
    NVIC_DisableIRQ(SysTick_IRQn);
    NVIC_DisableIRQ(LPUART1_IRQn);
    NVIC_DisableIRQ(EXTI0_1_IRQn);
    NVIC_DisableIRQ(DMA1_Channel2_3_IRQn);
    NVIC_DisableIRQ(I2C1_IRQn);

    typedef void (*pFunction)(void);
    volatile uint32_t addr = STM32L01_SYSTEM_MEMORY;
    uint32_t jumpaddr = *(__IO uint32_t*) (STM32L01_SYSTEM_MEMORY + 4);
    pFunction jump_to_application;

    // Application-specific shutdown of HSE and switch to MSI
    clock_HSE_to_MSI();

    // Reset ALL peripherals
    LL_AHB1_GRP1_ForceReset(LL_AHB1_GRP1_PERIPH_ALL);
    LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_ALL);
    LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_ALL);
    LL_mDelay(5);

    LL_AHB1_GRP1_ReleaseReset(LL_AHB1_GRP1_PERIPH_ALL);
    LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_ALL);
    LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_ALL);
    LL_mDelay(5);

    // Fully reset RCC to power-up state
    LL_RCC_DeInit();

    // Zero out SysTick
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;


    // Remap memory to system flash
    SYSCFG->CFGR1 = 0x01;

    jump_to_application = (pFunction)jumpaddr;

    /* Initialize user application's Stack Pointer */
    __set_MSP(*(__IO uint32_t*) addr);


    jump_to_application();
}

Diese Probleme habe ich ausgeschlossen:

  1. Die Steuerpins an meinem RS232-Transceiver MAX3221 (NEN, NFORCEOFF, FORCEON) befinden sich in den richtigen aktivierten Zuständen. Wenn also USART2 spricht, sollte ich etwas auf meinem Logikanalysator sehen, der an der RS232-Seite angeschlossen ist. Leider bedeutet die Notwendigkeit, GPIO-Pins beim Booten anzusteuern, um diese Steuerpins zu setzen, dass ich den Test "Call my jump function at the start of main()" nicht durchführen kann.
  2. Die Funktion jump_to_bootloader() wird vollständig ausgeführt (kein Hängenbleiben am Uhrschalter usw.). Ich sehe, wie einer der MAX3221-Steuerstifte für etwa 160 us hoch geht, bevor er wieder niedrig wird, was, wie ich bestätigt habe, nach jedem Punkt in der Funktion passiert, an dem ich es selbst so umschalten könnte. Ich gehe davon aus, dass dies im Rahmen der erneuten Initialisierung des GPIO-Peripheriegeräts auftritt.
  3. Ich habe verschiedene Permutationen ausprobiert, um die in der oben genannten Frage enthaltenen Speicherbarrierenbefehlsaufrufe ohne Wirkung einzuschließen. Ich habe keinen Hinweis auf solche gefunden, die in einem anderen ähnlichen Code online enthalten sind.
  4. Ich setze alle Peripheriegeräte zurück, um sicherzustellen, dass ich nicht das gleiche Problem wie die obige Frage habe. Dies sollte theoretisch die Notwendigkeit beseitigen, die Sprungfunktion am Anfang von main() aufzurufen. Ich deaktiviere aus Sicherheitsgründen auch speziell Interrupt-Anforderungen, die in meiner Anwendung verwendet werden.
  5. Ich sehe nie, dass der Chip-Reset-Pin auf Low geht, also vermute ich zu diesem Zeitpunkt nichts mit dem BOOT0-Pin. Der Pin wird mit einem Pulldown-Widerstand niedrig gehalten. Wenn also ein Reset auftritt, würde BOOT0 den Chip veranlassen, wieder mit dem Benutzercode im Flash zu booten. Die Chipoptionsbytes sind wie folgt eingestellt:

Optionsbytes

Arbeitstheorien:

  1. Da, wo ich springe, könnte etwas schief gehen. Soweit ich das beurteilen kann, habe ich die richtige Adresse von AN2606. Ich habe Probleme, dies zu bestätigen, wenn ich die Register mit einem Debugger betrachte. Nachdem ich jump_to_bootloader() ganz durchlaufen habe, scheint der Code zu hängen, zumindest laut Debugger ... Ich verstehe nicht ganz, ob ich dem Debugger immer noch vertrauen kann, nachdem ich aus meinem Benutzercode gesprungen bin. Die SP- und MSP-Register zeigen SRAM-Adressen (0x20000000-Bereich) und der PC befindet sich im Flash (0x80000000-Start).

Vor __set_MSP():

Werte registrieren

Nach dem Springen zum Ende der Sprungfunktion (Debugger-Schrittschaltflächen nicht mehr aktiviert):Registerwerte 2

Wenn ich dann im Debugger auf Ausführung aussetzen klicke, bringt mich das zu einem von Eclipse generierten Haltepunkt am Anfang von main() zurück. Ich verstehe nicht, wie das der Fall sein kann, da mein Anwendungscode sowohl mit als auch ohne den vorhandenen Debugger nachweislich nicht mehr richtig funktioniert.

  1. Etwas verursacht einen Reset, den ich aus irgendeinem Grund nicht auf dem Hardware-Pin sehe, was dann einen Neustart in den Benutzer-Flash verursacht. Das fühlt sich wie ein Ablenkungsmanöver an, aber ich bin misstrauisch angesichts der Art und Weise, wie der Debugger mich zum Anfang von main() zurückgebracht hat.
Eine verzweifelte Maßnahme, aber eine, die definitiv funktioniert und all diese Schlachten vermeidet, ist, einen magischen Wert im RAM oder in den RTC-Sicherungsregistern festzulegen, mit Ihrem eigenen Code neu zu starten, dieses Flag vor jedem Setup zu sehen (d.h. idealerweise in Assembler vor C /C++ init) und den Stapelzeiger reparieren und sofort zum Bootloader springen. Ich würde nicht versuchen, dies zu debuggen, sondern es einfach vom Design her richtig machen und sehen, dass es als Ergebnis funktioniert.
@ChrisStratton: Dies ist eigentlich der einzig zuverlässige Weg. Es wird außerdem von ST empfohlen - oder zumindest vom Guru der inzwischen nicht mehr existierenden Foren, die von OP zitiert werden. Endlich ist es so wie ich es gemacht habe und es funktioniert so wie es soll. Sie sollten Ihren Kommentar also wirklich als Antwort posten.
Verwendet der Bootloader Interrupts? Versuchen Sie vielleicht, den VTOR so zu ändern, dass er auf den Bootloader zeigt, bevor Sie springen.
Kein Glück mit dem Zurücksetzen, der ST-Support lässt mich testen, ob die in AN2606 aufgeführten Chipfehler vom F042 möglicherweise auch für den L011 gelten. "Aufgrund des leeren Prüfmechanismus dieses Produkts ist es nicht möglich, vom Benutzercode zum System-Bootloader zu springen. Ein solcher Sprung führt zu einem Rücksprung zum Benutzer-Flash-Speicher."
Wenn Sie das Stepping von Anweisungen aktivieren, sollten Sie in der Lage sein, das Bootloader-ROM schrittweise zu durchlaufen, wobei Sie die disassemblierten Anweisungen als Richtlinie verwenden. Ich habe dies zuvor für den F042 mit Python-GDB-Skripting getan, um alle Registerwerte und Lese-/Schreibvorgänge im Speicher zu verfolgen, um zu sehen, wie der Bootloader entscheidet, zum Benutzer-Flash zurückzukehren: gist.github.com/devanlai/f9636f68c57fde2c5da912231f03c47e

Antworten (1)

Sie könnten versuchen, zu 0x1FF00FFE anstelle von 0x1FF00000 zu springen. (eingebettete Bootloader-Adresse nach AN2606 Rev 35, S. 26)

Mein erster Kommentar war völlig falsch. Aber laut den Kommentaren oben hat der Themenstarter gefunden, was falsch war. Aufgrund des auf dem L011-Gerät präsentierten leeren Prüfmechanismus können Sie nicht vom Benutzercode zum Systemspeicher springen und dort bleiben. Ich bin auf meinem L07x-Gerät mit Dual-Banking-Mechanismus auf ähnliche Probleme gestoßen (ähnliches Problem https://stackoverflow.com/questions/42020893/stm32l073rz-rev-z-iap-jump-to-bootloader-system-memory ). Es stellte sich heraus, dass der Bootloader auf jeden Fall zum Benutzercode zurückspringt, solange ein gültiger Code in einer der Banken vorhanden ist. Natürlich ist es möglich, dies zu umgehen, indem man nach der Leerprüfung zur Adresse springt, aber es ist schwierig, die richtige Adresse zu finden, und es gibt keine Garantie, dass STM diese Adresse beim nächsten Drucken des Chips nicht ändert.

Der Fragecode liest den Startvektor aus der Vektortabelle des Boot-ROMs und springt nicht zum Anfang.