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.
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.
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.
Chris Stratton
rom1nux
kbraun323
kbraun323
kbraun323