STM32 L4 HAL_FLASH Schreibt in eine Funktion, aber nicht in eine andere

Ich arbeite mit einem STM32 L476RG Evaluierungsboard an einem Projekt. Das aktuelle Ziel ist es, die Massenspeicherbibliothek von STM zu nutzen, um Daten über USB auf Flash zu schreiben. Ich habe die USB-Bibliothek zum Laufen gebracht (zunächst nur das Lesen und Schreiben von Daten aus dem/in den RAM), aber bei der Verwendung von Flash bin ich auf ein seltsames Problem gestoßen.

Wenn ich in der Funktion STORAGE_Init_FS in den Flash schreibe (mit HAL_FLASH_Program(....)), kann ich problemlos in den Flash schreiben. Ich lösche die Seite, schreibe beliebige Daten darauf und sie erscheint in der Speicheransicht des Debuggers.

Wenn ich jedoch versuche, genau dasselbe in STORAGE_Write_FS zu tun (versucht mit Zeile für Zeile, derselbe Code), gibt die Flash-Operation einen Fehler zurück und schreibt niemals Daten.

Hier sind die relevanten Codeteile:

{
  /* USER CODE BEGIN 2 */
    USB_Flash_Init();
  return (USBD_OK);
  /* USER CODE END 2 */
}

HAL_StatusTypeDef USB_Flash_Init() {
    __HAL_RCC_SYSCFG_CLK_ENABLE();
    __HAL_RCC_FLASH_CLK_ENABLE();
    /* Clear flash flags */
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
    HAL_FLASH_Lock();

    HAL_FLASH_Unlock();
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x8080000, 0x0001);
    HAL_FLASH_Lock();
    return HAL_OK;
}

Im obigen Code werden die Daten 0x0001 ordnungsgemäß an die Speicheradresse 0x8080000 geschrieben. (Der Löschaufruf ist nicht da, aber die Seite wurde gelöscht).

In der folgenden Funktion jedoch

int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */
    __HAL_RCC_SYSCFG_CLK_ENABLE();
    __HAL_RCC_FLASH_CLK_ENABLE();
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
    HAL_FLASH_Lock();
    HAL_FLASH_Unlock();
    FLASH_PageErase(0, FLASH_BANK_2);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x8080000, 0x0001);
    FLASH_PageErase(0, FLASH_BANK_2);
    HAL_FLASH_Lock();
  return (USBD_OK);
  /* USER CODE END 7 */
}

Die Daten werden ordnungsgemäß gelöscht (Anzeige der Speicheradresse durch den Debugger), aber dann nie geschrieben, sie bleiben 0xFFFFFFFF. Ich weiß, dass einige der Init-Sachen bereits erledigt waren, aber ich wollte sicher sein, dass es nicht die Ursache war, also habe ich es auch dort hineingeworfen. Das einzige, woran ich wirklich denken kann, wäre, dass der Stapel möglicherweise nicht genügend Speicherplatz hat (unwahrscheinlich bei der enormen Menge an Speicher, die dieses Ding hat). Um zu überprüfen, ob dies nicht die Ursache war, habe ich versucht, sowohl meinen 2k-Puffer (hier nicht gezeigt) aus der Funktion zu nehmen, um keine lokale Variable mehr zu sein, als auch die Stack- und Heap-Größen im RAM auf 0x4096 (übermäßig) zu erhöhen .ld.

Beides hat das Verhalten nicht geändert. Jeder, mit dem ich gesprochen habe, war ratlos, was die Ursache dafür angeht. Was könnte ich falsch machen?

Ich erhalte zwei Fehler, wenn ich HAL_FLASH_CheckError() überprüfe (möglicherweise nicht der genaue Funktionsname), 0xA0, was HAL_FLASH_ERROR_PGS und HAL_FLASH_ERROR_PGA entspricht, Programmiersequenz- bzw. Programmierausrichtungsfehler. Ich bin mir nicht sicher, ob diese Fehler relevant sind, wenn man bedenkt, dass die tatsächlichen Funktionsaufrufe und die Einrichtung identisch sind, nur der Umfang ist unterschiedlich.

Wenn jemand einen Rat hat, würde ich mich freuen! Hoffentlich fehlt mir nur ein Steuerregister oder etwas irgendwo.

Auf welchem ​​Weg erreicht die Ausführung jeden Punkt? Versuchen Sie, die Funktion in eine eigene Datei zu packen und sie sowohl früh in main() als auch an jedem Punkt aufzurufen, an dem Sie sich mit den eigentlichen Daten befassen. Hoffentlich versteht es sich von selbst, dass Sie dies nicht in einem Interrupt-Kontext tun können. Stellen Sie sicher, dass Ihr Flash-Timing für die Systemuhr geeignet ist, die Sie für USB benötigen ...
Sie löschen die Seite (FLASH_PageErase) nach dem Programmieren (HAL_FLASH_Program) ... Wenn es sich um einen "Code-Extraktionsfehler" handelt, schauen Sie sich Ihre Flash-Latenz und Prefetch und Co. an.
@ChrisStratton Der Pfad zum Aufruf führt über einige Schichten von STM-Bibliotheken, um jede Funktion zu erreichen. Ich glaube, es hat etwas damit zu tun, von wo es aufgerufen wird, wenn man bedenkt, dass es in einer Funktion funktioniert, aber nicht in einer anderen. Flash schreibt gut in main() und wo immer ich es sonst versuche, schlägt es nur in dieser USB-Schreibfunktion fehl. Bearbeiten: Entschuldigung, dass das Drücken der Eingabetaste den Kommentar gesendet hat. Warum ist dies nicht in einem Interrupt-Kontext möglich? Beide Funktionen, die ich demonstriert habe, werden in einem USB-Interrupt aufgerufen, der von den STM-Bibliotheken verwaltet wird. Ich habe keinen Einfluss darauf, wann dies aufgerufen wird.
Nur um sicherzugehen, es ist kein Uhrfehler, da ich die gesamte USB-Kommunikation beim Schreiben in den RAM zum Laufen gebracht habe. @rom1nux Ich lösche es in diesem Beispiel tatsächlich danach, aber der eigentliche Code, der sich mit den USB-Datenblöcken und allem befasst, tat es nicht, und ich gehe es mit dem Debugger durch und beobachte, wie sich die Speicheradresse ändert (oder nicht, in der problematischen eins). Das Herausnehmen dieses zusätzlichen Löschvorgangs bewirkt keinen Unterschied im Verhalten.
Nur um das klarzustellen, ich bin mir bewusst, dass es im Allgemeinen eine schlechte Praxis ist, "zu viel" in einem Interrupt zu tun, und Sie möchten die Interrupt-Logik einfach halten, um eine Unterbrechung des Hauptprogrammflusses zu verhindern. Flash-Programmierung ist sicherlich geeignet. Aber an diesem Punkt wird buchstäblich nichts anderes ausgeführt (leere While-Schleife), und wenn das Gerät tatsächlich für USB-Massenspeicher verwendet wird, befindet es sich in einem Download-Firmware-Modus, in dem die normale Ausführung gestoppt wird.

Antworten (2)

Es scheint, dass das eigentliche Problem darin besteht, dass das FLASH_PECR_ERASE(oder zumindest das, was auf dem L0 so genannt wird) von den typischen Flash-Löschroutinen gesetzt bleibt. Dies führt dazu, dass nachfolgende Programmierversuche eine seltsame und möglicherweise ungültige Mischung aus einer Programmier- und einer Löschoperation sind. Wie tief in den Kommentaren unten erwähnt, habe ich dies zuvor auf einem STM32L0 gesehen, und es scheint auch hier ein Problem mit dem STM32L4 zu geben.

Es folgen originelle und scheinbar irrelevante Ideen

Flash schreibt gut in main() und wo immer ich es sonst versuche, schlägt es nur in dieser USB-Schreibfunktion fehl.

Warum ist dies nicht in einem Interrupt-Kontext möglich? Beide Funktionen, die ich demonstriert habe, werden in einem USB-Interrupt aufgerufen, der von den STM-Bibliotheken verwaltet wird.

Nehmen wir den (möglicherweise falschen) Glauben, dass dies von einem Interrupt-Kontext aus aufgerufen wird, für bare Münze, so können Sie definitiv nicht mit gewöhnlichen Mitteln aus einem Interrupt in Flash schreiben. Das Schreiben in Flash ist ein sehr komplexer Prozess, eine echte Abkehr vom gewöhnlichen Chipbetrieb, den Chips kaum jemals ausführen sollten, und auch sehr zeitaufwändig.

Sie müssen die Absicht, einen Schreibvorgang auszuführen, zwischenspeichern (den Wunsch und die Daten speichern), ihn später von der Hauptschleife aus ausführen und dann eine Erfolgs- oder Fehlerantwort in die Warteschlange stellen, um sie über USB zurückzusenden (d. h. die Daten sofort akzeptieren, aber antworten Sie nicht, ob das Schreiben funktioniert hat oder nicht, bis Sie es abgeschlossen haben). Denken Sie daran, die Kommunikation zwischen dem Interrupt und der Hauptschleife über sicher atomare Flags herzustellen, oder verwenden Sie einen angemessen kurzen kritischen Abschnitt, um eine Kopie von allem Nicht-Atomischen zu erstellen. Sie müssen wahrscheinlich weitere Änderungen des Datenpuffers vom Interrupt aus sperren, bis Sie den Schreibvorgang abgeschlossen haben. Wenn Sie dies auf der Ebene des USB-Puffers selbst nicht tun können, müssen Sie möglicherweise von einem USB-Puffer an einer anderen Stelle im RAM DMA ausführen und dann schreibe von dort.

Beispiele für USB-Massenspeicher sind sehr verbreitet. Sie können wahrscheinlich nützliche Dinge darüber lernen, wie die erforderliche Entkopplung von einem Beispiel durchgeführt wird, das von SPI-Flash statt intern oder von einem Beispiel für einen anderen STM32 unterstützt wird, wenn der USB-Funktionsblock bedeutende Ähnlichkeiten in seiner Architektur in Bezug auf Pufferung usw. aufweist.

Wenn wirklichSTORAGE_Write_FS() von einem Interrupt aufgerufen wird , ist das ein unglücklicher Beispielcode. Es kann sein, dass es Teile der Architektur des Beispiels gibt, die noch nicht ausreichend verstanden werden - wenn sie ein Beispiel geben, das durch tatsächlichen nichtflüchtigen Speicher unterstützt wird (im Gegensatz zu einer Fälschung mit einem winzigen bisschen RAM), wäre eine detaillierte Untersuchung davon Taste.

> Sie können definitiv nicht mit normalen Mitteln von einem Interrupt aus in den Flash schreiben. Das Schreiben in Flash ist ein sehr komplexer Prozess, eine echte Abkehr vom gewöhnlichen Chipbetrieb, den Chips kaum jemals ausführen sollten, und auch sehr zeitaufwändig. Warum funktioniert es dann in einem Interrupt-Aufruf und nicht in dem anderen? Ich habe Probleme zu erkennen, warum dies nicht über einen Interrupt möglich ist. Ein Interrupt ist einfach eine andere Funktion mit einem nicht standardmäßigen Zugriffspunkt, nein? Ich würde verstehen, wenn der Interrupt zeitlich begrenzt war oder wenn anderer kritischer Code ausgeführt wurde.
Dies ist besonders rätselhaft, wenn es in einem Interrupt funktioniert und in einem anderen nicht. Ich könnte die Daten puffern und den Schreibvorgang in main() durchführen, aber ich müsste genug Speicher für die maximale Schreibanforderung puffern, die der Host ausführen könnte, was zu einem Puffer von mindestens Kilobyte führen würde (ich kenne die genaues Maximum hier) ständig im Speicher gehalten und außer in diesem speziellen Fall nicht verwendet werden. Ich werde mich nach Beispielen mit ähnlichen Boards umsehen (es schien einige für die F4-Serie zu geben, aber sie verwenden eine etwas andere Bibliothek als die L4, ich habe an einem USB-Trainings-STM-Video gearbeitet
Update: Ich habe einige Beispiele von Leuten gefunden, die dasselbe tun und den internen Flash als USB-MSC verwenden. Sie tun, was ich tue, indem sie einfach die Flash-Programmierfunktion in der Funktion Storage_Write_FS aufrufen. Außerdem habe ich Storage_Write_FS geändert, um ein Flag zu setzen, das main() anweist, den gewünschten Schreibvorgang durchzuführen (noch nicht wirklich mit USB-Daten befasst, immer noch 1 an eine fest codierte Adresse zu schreiben), und überraschenderweise ergibt sich das gleiche Problem. Gleicher Fehlercode beim schrittweisen Durchlaufen der Flash-Programmierung, und der Flash wird nie aktualisiert! Sehr neugierig.
Ich bin ziemlich ratlos, wie es den Flash in USB_Flash_Init() (einfach aufgerufen von STORAGE_Init_FS, ausgelöst durch einen Interrupt vom Betriebssystembefehl) programmieren würde, aber nicht nur in STORAGE_Write_FS(), sondern auch in main() fehlschlägt. Irgendwelche Ratschläge oder Ideen würden sehr geschätzt!
Es ist nicht klar, ob Ihre Annahme, dass irgendetwas davon in einem Interrupt-Kontext aufgerufen wird, tatsächlich richtig ist - es wäre ziemlich unglücklich, wenn dies der Fall wäre. Es kann jedoch sein, dass das Flash-Schreiben gegen das Auslösen anderer Interrupts geschützt werden muss. Zuvor sollten Sie sich Quellen für USB-basierte Bootloader ansehen. In einigen früheren Hardwarearchitekturen konnten Sie nicht in eine Flash-Bank schreiben, während Sie von ihr aus liefen (Sie mussten früher einen Stub in den RAM laden, um den eigentlichen Schreibvorgang durchzuführen, oder ihn in einer anderen Flash-Bank platzieren). Ich hatte das Gefühl, dass das Problem überwunden ist in aktuellen, aber vielleicht unvollkommen.
Nun, main() führt einfach eine leere While-Schleife aus, daher sehe ich nicht, wie es anders als über einen Interrupt in irgendeiner Form zu den USB-Init/Write-Funktionen gelangen könnte. Ich werfe einen Blick auf den vollständigen Stack-Trace und stelle sicher, dass nichts davon mit dem Flash interagiert oder ihn in irgendeiner Weise schützt. Der besondere Grund, warum ich die Speicheradresse gewählt habe, mit der ich geschrieben habe, war, dass es der Beginn von Bank 2 von Flash ist, wo das Hauptprogramm in Bank 1 ausgeführt wird. Dieser Mikrocontroller ermöglicht die Ausführung während des Flashens, wenn er auf eine andere Bank als die blinkt eine, von der es ausgeführt wird.
Die leere main()-Schleife klingt nach einer sehr unglücklichen Architektur. Dies verwendet kein RTOS, oder? Was auch immer die Realität ist, bitte bearbeiten Sie Ihre Frage, um auf den Quellcode zu verlinken, wenn er bereits online ist, um ihn in ein öffentliches Repository zu stellen und darauf zu verlinken.
Die leere Hauptschleife ist, weil ich daran arbeite, die absolut minimal mögliche Funktionalität für diesen Abschnitt des Projekts zu produzieren, dann plane ich, sie in das viel größere Projekt zu integrieren, sobald ich die USB-MSC wie gewünscht habe. Nur ein Proof of Concept für die Verwendung des Flash als Massenspeicher. Ich denke, es besteht eine gute Chance, dass mir an dieser Stelle etwas mit dem Blitz fehlt, und dass es eher ein Zufall als alles andere ist, dass es in separaten Fällen funktioniert. Ich werde ein bisschen mehr in der Flash-Dokumentation graben, und wenn ich immer noch ratlos bin, werde ich den Quellcode posten. Danke für deine bisherige Hilfe!
Glaube, ich habe das Problem auf das Flash-Schreiben eingegrenzt. Es scheint, dass der erste Flash-Schreibvorgang immer erfolgreich ist, aber wenn ich dann die Seite lösche (um sie neu zu schreiben), schlägt der Flash-Schreibvorgang fehl. Ein Neustart des Programms ist die einzige Möglichkeit, es nach dem Löschen der Seite wieder zum Schreiben zu bringen. Sehr neugierig..
Das klingt unheimlich nach einer Erfahrung, die ich vor einiger Zeit auf einem STM32L0 hatte. Ich glaube, ich habe festgestellt, dass ich zusätzlichen Code hinzufügen musste, um FLASH_PECR_ERASE zu löschen, bevor das Schreiben wieder funktionierte. Oder etwas ähnliches. Es kann sich lohnen, das Handbuch zu überfliegen und nicht anzunehmen, dass der tief in Bibliotheken vergrabene Code korrekt ist, sondern ihn tatsächlich zu vergleichen.
Genau das ist das Problem! Ich entdeckte letzten Mittwoch, dass das Problem meine Verwendung von FLASH_PageErase() war, das anscheinend nicht die richtigen Steuerbits zurücksetzt. Ich bin auf die Verwendung von HAL_FLASHEx_Erase() umgestiegen (was ich vermieden habe, weil es ziemlich chaotisch ist) und alles funktioniert gut. Ich verwende einfach die HAL-Löschfunktion. Vielen Dank für Ihre Hilfe!

Update: Das Problem war die Verwendung der Funktion FLASH_PageErase(), die nicht die richtigen Steuerbits für den Flash zurücksetzt, um das Schreiben erneut zu ermöglichen. Es stimmte einfach mit der Reihenfolge der Funktionsaufrufe überein, dass ich die Seite erst beim zweiten Aufruf löschte.

Ich bin mir sicher, dass das Durchsuchen der Dokumentation die relevanten Bits ergibt, die zurückgesetzt werden müssen, aber in meinem Fall habe ich einfach auf die HAL_FLASHEx_Erase-Funktion umgeschaltet. Lassen Sie dies hier, falls jemand auf ein ähnliches Problem stößt.