Jetzt kann niemand mehr von Mikrocontrollern mit einem nichtflüchtigen (meistens Flash) Speicher von 512 Kilobyte oder mehr überrascht werden. Ihre Kosten sinken allmählich und die Zugänglichkeit nimmt im Gegenteil zu. Das Vorhandensein eines solchen Volumens an nichtflüchtigem Speicher ermöglicht es, Anwendungen zu schreiben, die im Hinblick auf den belegten Speicher "schwer" sind, und gleichzeitig die anschließende Codepflege durch die Verwendung vorgefertigter Lösungen aus verschiedenen Standardbibliotheken zu erleichtern. Dies führt jedoch zu einer Erhöhung des Volumens der Firmware-Datei des Zielgeräts, die jedes Mal bei der geringsten Änderung des Codes vollständig in den nichtflüchtigen Speicher des Mikrocontrollers neu geladen werden muss.
Der Zweck des Artikels besteht darin, über die Methode zum Erstellen eines Projekts in C und / oder C ++ zu sprechen, bei der im Falle einer Änderung des am häufigsten debuggten Codeabschnitts der größte Teil des Projekts nicht neu geschrieben werden musste. Und zeigen Sie auch, warum diese Methode nicht immer eine effektive Lösung ist.
Leseranforderungen
Im Verlauf der Erzählung gehe ich davon aus, dass der Leser:
- Er spricht fließend C und C ++;
- Erfahrung in der Arbeit mit Mikrocontrollern auf Basis von Cortex-M3 / Cortex-M4-Kernen (z. B. der Serie stm32f4);
- weiß, wie man die endgültige Nähdatei (elf / bin) aus den Projektquellen erstellt;
- Stellen Sie sich vor, wofür Linker-Skriptdateien gedacht sind.
- hat eine Vorstellung von Text, BSS, Daten und anderen Abschnitten;
- arbeitete mit einer der Linux-Distributionen;
- besitzt minimal Bash;
- hat Erfahrung mit gcc für die Architektur der Cortex-M3 / Cortex-M4-Prozessoren (Toolchain arm-none-eabi);
- hat anfängliche Fähigkeiten mit cmake.
Die Essenz der Methode
In einem „klassischen“ Projekt für Mikrocontroller befinden sich alle unveränderlichen Daten (Text, Rodata-Abschnitte, Anfangsdatenwerte usw.) normalerweise „in einer Reihe“, beginnend mit der Startadresse des nichtflüchtigen Speichers (im Fall eines Mikrocontrollers auf Basis des Cortex-M3 / Cortex-M4-Kerns - c Adresse 0x08000000). In vereinfachter Form sieht die nichtflüchtige Speichernutzungskarte eines Mikrocontroller-Programms, das auf dem Cortex-M3 / Cortex-M4-Kern basiert und mit C ++ geschrieben wurde, ungefähr so aus:

Die mem.ld-Datei für ein solches Projekt sieht meistens ungefähr so aus:
MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 768K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K }
Hier ist der gesamte nichtflüchtige Speicher eine einzelne Partition mit dem Namen "FLASH", und der gesamte RAM ist eine Partition mit dem Namen "RAM". In dieser Form beginnt sich der Rest zu verschieben, wenn sich einer der Codeabschnitte ändert. Um dies zu vermeiden, können Sie die Firmware-Datei in einige logische Blöcke aufteilen. Zum Beispiel wie folgt:
- Tabelle der Interruptvektoren;
- eigene Bibliotheken;
- Bibliotheken von Drittanbietern (deren Änderung nicht geplant ist);
- häufig änderbarer Code.
In diesem Fall beim Ändern eines Codeabschnitts in der endgültigen Bin-Datei nur der Abschnitt, in dem sich der Code geändert hat, und der Abschnitt, der irgendwie damit verbunden war (z. B. die Tabelle der Interrupt-Vektoren, wenn die Position des Handlers in einigen aus Abschnitten).
Im Wesentlichen werden dem Projekt
statische Bibliotheken hinzugefügt.
Nach dem Empfang des Fachs der Projektdatei kann diese in Abschnitte unterteilt werden und jeden Abschnitt einzeln flashen. Somit werden nur geänderte Bereiche zusammengefügt. Dies führt auch dazu, dass vor dem Debuggen keine Firmware erforderlich ist, da davon ausgegangen wird, dass der Mikrocontroller sofort über die neueste Firmware im Mikrocontroller verfügt und Sie sofort mit dem Debuggen beginnen können.
Als nächstes werde ich detailliert beschreiben, wie dies in einem realen Projekt implementiert werden kann. Die Vor- und Nachteile einer solchen Entscheidung werden am Ende des Artikels angegeben.
Feld für Experimente
Bevor ich irgendeine Art von Innovation bei der Arbeit vorschlage, versuche ich dies bei meinem
Heimprojekt . Da seine Größe in etwa der Größe von Routineprojekten bei der Arbeit entspricht, ist es möglich zu verstehen, ob die Innovation rentabel ist oder nicht und welche Nuancen sie trägt.
Projektbeschreibung
Das Projekt enthält:
- den Code des Hauptprojekts in C ++ 14 unter Verwendung virtueller Tabellen, new / delete (durch eine Reihe von FreeRTOS), shared_ptr (und andere intelligente Zeiger) und andere Freuden von Standard-C ++ 14-Bibliotheken;
- FreeRTOS verwendet ungefähr 6 Aufgaben zur Wartung der Hardware-Peripherie-Infrastruktur und ungefähr 10 für die Geschäftslogik ( MakiseGUI- Grafikbibliothek, Klickverarbeitung, Arbeiten mit Fett ( FatFS ) usw.);
- 16 Repositorys mit eigenen Bibliotheken für die Interaktion mit Hardware-Peripheriegeräten auf der Karte, Stubs für Systemaufrufe und mehr;
Mit den Assembly-Parametern -O0 -g3 benötigt der Code in einer vollwertigen Implementierung mit Unterstützung für Unicode, Cyrillic und andere Dinge ungefähr 700 KB. In der aktuellen Phase, in der die Hardware-Peripheriegeräte stabil sind und nur die Geschäftslogik debuggt werden muss, beträgt die zu ändernde Codemenge ungefähr 20 KB. Aus diesem Grund scheint der derzeitige Ansatz auf den ersten Blick eine ideale Lösung für das Problem zu sein (die Option mit Simulation auf einem Computer wird aus irgendeinem Grund nicht in Betracht gezogen).
Aktionsliste
Um die beschriebene Methode zu implementieren, benötigen Sie:
- Assemblieren Sie alle Submodule als statische Bibliotheken (die Beschreibung dieses Elements ist nicht in der Liste der analysierten Elemente dieses Artikels enthalten).
- schreibe mem.ld um;
- rewrite section.ld;
- Fügen Sie dem Hauptprojekt ein Dienstprogramm hinzu, um Abschnitte aus der endgültigen Bin-Datei zu extrahieren.
- Fügen Sie dem Projekt einen Aufruf des Skripts hinzu, um den nichtflüchtigen Speicher des Mikrocontrollers beim Aktualisieren der Firmware-Datei zu aktualisieren.
Mem.ld umschreiben
Der erste Schritt besteht darin, das „Standard“ -Mem.ld für das aktuelle Konzept zu verfeinern. Beim Finalisieren sollte berücksichtigt werden, dass nichtflüchtiger Speicher von Sektoren bereinigt wird. Weitere Informationen zur Strukturierung von Sektoren in einem bestimmten Mikrocontroller finden Sie in der Dokumentation (im Fall von stm32-Mikrocontrollern - im Referenzhandbuch). Jeder Abschnitt kann mindestens einen Sektor belegen (mehr können sein), andernfalls überschreibt ein Abschnitt den anderen.
Es sollte auch berücksichtigt werden, dass Sie für diese Bibliothek in der Verknüpfungsphase einen Platz im RAM reservieren müssen, wenn eine Bibliothek globale Variablen verwendet. Andernfalls können unangenehme Fehler auftreten, die äußerst schwer zu erkennen sind. Der Code der FatFS-Bibliothek befindet sich beispielsweise im Abschnitt ROM_EXTERNAL_LIBRARIES, erfordert jedoch in der Erstellungsphase 4 Byte RAM. Sie müssen also sicherstellen, dass im RAM ein Abschnitt für die Felder vorhanden ist, die der Code von ROM_EXTERNAL_LIBRARIES verwenden wird. In diesem Beispiel ist es RAM_EXTERNAL_LIBRARIES.
Der letzte Abschnitt im nichtflüchtigen Speicher verdient besondere Aufmerksamkeit. Alles, was laut section.ld (später) nicht in die entsprechenden Abschnitte zerlegt wurde, wird darauf eingehen.
Im Kontext des aktuellen Projekts sieht mem.ld folgendermaßen aus. /* stm32f405rgt6 ChiptunePlayer-2.22-MainBoard-v2-Firmware. */ MEMORY { /*-----------------------------FLASH-------------------------------*/ /* 0-1 . */ ROM_BOOTLOADER (RX) : ORIGIN = 0x08000000, LENGTH = 32K /* 2 . */ ROM_SYSCFG_PAGE_1 (R) : ORIGIN = 0x08008000, LENGTH = 16K /* 3 . */ ROM_SYSCFG_PAGE_2 (R) : ORIGIN = 0x0800C000, LENGTH = 16K /* 4 . */ ROM_RESERVE (R) : ORIGIN = 0x08010000, LENGTH = 16K /* 5, 6, 7 (FATFS, FREERTOS...). */ ROM_EXTERNAL_LIBRARIES (RX) : ORIGIN = 0x08020000, LENGTH = 384K /* 8, 9 ( , ...). */ ROM_USER_LIBRARIES (RX) : ORIGIN = 0x08080000, LENGTH = 384K /* 5, 6 . */ ROM_MAIN_PROGRAMM (RX) : ORIGIN = 0x080E0000, LENGTH = 128K /*-----------------------------RAM---------------------------------*/ /* RAM . */ RAM_PAGE_1 (RW) : ORIGIN = 0x20000000, LENGTH = 112K RAM_PAGE_2 (RW) : ORIGIN = 0x2001C000, LENGTH = 16K /* FATFS FreeRTOS. */ RAM_EXTERNAL_LIBRARIES (RW) : ORIGIN = 0x20000000, LENGTH = 10K /* . */ RAM_USER_LIBRARIES (RW) : ORIGIN = 0x20002800, LENGTH = 90K /* RAM . */ RAM_MAIN_PROGRAMM (RW) : ORIGIN = 0x20019000, LENGTH = 27K /* RAM . FreeRTOS. */ RAM_MAIN_PROGRAMM_STACK (RW) : ORIGIN = 0x2001FC00, LENGTH = 1K }
Rewrite section.ld
Nachdem die vorhandene Speicherkarte in Abschnitte unterteilt wurde, sollte beschrieben werden, welche Partition platziert wird. Geben Sie für jede Bibliothek (wenn es einen entsprechenden Abschnitt in der Bibliothek gibt) an, wo sich die Abschnitte .text, .rodata, .data, .bss und andere befinden. Die Liste der in der Bibliothek verfügbaren Abschnitte kann mit objdump angezeigt werden. Für die Bibliothek libstdc ++ _ nano.a müssen Sie beispielsweise angeben, wo der Text, ARM.attributes, rodata, data, bss, COMMON-Abschnitte platziert werden sollen.
Im Kontext des aktuellen Projekts sieht section.ld folgendermaßen aus. /* RAM. */ __estack = ORIGIN(RAM_MAIN_PROGRAMM_STACK) + LENGTH(RAM_MAIN_PROGRAMM_STACK); /* . */ __stack_size = LENGTH(RAM_MAIN_PROGRAMM_STACK); /* Reset_Handler. */ ENTRY(Reset_Handler) /* . */ SECTIONS { /*---------------------ROM ------------------------*/ .section_bootloader : ALIGN(4) { /* . . .o , .*/ . = ALIGN(4); KEEP(*(.user_code_isr_vector .user_code_isr_vector*)) . = ALIGN(4); } >ROM_BOOTLOADER /*----------------ROM -----------------*/ /* . */ .section_external_libraries_text : ALIGN(4) { /* . */ . = ALIGN(4); *libstdc++_nano.a:*(.text .text*); . = ALIGN(4); *libgcc.a:*(.text .text*); . = ALIGN(4); *libg_nano.a:*(.text .text*); /* */ . = ALIGN(4); *libSTM32F4_LOW_LEVEL_BY_ST.a:*(.text .text*); . = ALIGN(4); *libFATFS.a:*(.text .text*); . = ALIGN(4); *libFREERTOS.a:*(.text .text*); . = ALIGN(4); *libMAKISE_GUI.a:*(.text .text*); . = ALIGN(4); } >ROM_EXTERNAL_LIBRARIES /* , */ .section_external_libraries_required_by_the_compiler : ALIGN(4) { /* . */ . = ALIGN(4); *libgcc.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libstdc++_nano.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libg_nano.a:*(.ARM.attributes .ARM.attributes*); /* */ . = ALIGN(4); *libSTM32F4_LOW_LEVEL_BY_ST.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libFATFS.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libFREERTOS.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libMAKISE_GUI.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); } >ROM_EXTERNAL_LIBRARIES /* . */ .section_external_libraries_rodata : ALIGN(4) { /* . */ . = ALIGN(4); *libgcc.a:*(.rodata .rodata*); . = ALIGN(4); *libstdc++_nano.a:*(.rodata .rodata*); . = ALIGN(4); *libg_nano.a:*(.rodata .rodata*); /* */ . = ALIGN(4); *libSTM32F4_LOW_LEVEL_BY_ST.a:*(.rodata .rodata*); . = ALIGN(4); *libFATFS.a:*(.rodata .rodata*); . = ALIGN(4); *libFREERTOS.a:*(.rodata .rodata*); . = ALIGN(4); *libMAKISE_GUI.a:*(.rodata .rodata*); . = ALIGN(4); } >ROM_EXTERNAL_LIBRARIES /*----------------------- ---------------------*/ /* . */ .section_user_libraries_text : ALIGN(4) { . = ALIGN(4); *libUSER_FREERTOS_LEVEL.a:*(.text .text*); . = ALIGN(4); *libUSER_BSP_LEVEL.a:*(.text .text*); . = ALIGN(4); *libMC_INTERRUPT.a:*(.text .text*); . = ALIGN(4); *libMC_HARDWARE.a:*(.text .text*); . = ALIGN(4); *libPCB_HARDWARE.a:*(.text .text*); . = ALIGN(4); *libUSER_STARTUP.a:*(.text .text*); . = ALIGN(4); *libBUTTONS.a:*(.text .text*); . = ALIGN(4); *libCHIPTUNE.a:*(.text .text*); . = ALIGN(4); *libDIGITAL_POTENTIOMETER.a:*(.text .text*); . = ALIGN(4); *libLCD_DRIVER.a:*(.text .text*); . = ALIGN(4); *libMAKISE_GUI_ELEMENTS_BY_VADIMATORIK_ELEMENTS_BY_VADIMATORIK.a:*(.text .text*); . = ALIGN(4); *libMC_HARDWARE_INTERFACES_IMPLEMENTATION_FOR_STM32.a:*(.text .text*); . = ALIGN(4); *libMICROSD_LOW_LEVEL_DRIVER.a:*(.text .text*); . = ALIGN(4); *libSHIFT_REGISTER.a:*(.text .text*); . = ALIGN(4); *libWAVE_GENERATORS.a:*(.text .text*); . = ALIGN(4); *libRUN_TIME_LOGGER.a:*(.text .text*); . = ALIGN(4); } >ROM_USER_LIBRARIES /* , */ .section_user_libraries_required_by_the_compiler : ALIGN(4) { . = ALIGN(4); *libUSER_FREERTOS_LEVEL.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libUSER_BSP_LEVEL.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libMC_INTERRUPT.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libMC_HARDWARE.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libPCB_HARDWARE.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libUSER_STARTUP.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libUSER_CODE.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libBUTTONS.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libCHIPTUNE.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libDIGITAL_POTENTIOMETER.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libLCD_DRIVER.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libMAKISE_GUI_ELEMENTS_BY_VADIMATORIK_ELEMENTS_BY_VADIMATORIK.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libMC_HARDWARE_INTERFACES_IMPLEMENTATION_FOR_STM32.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libMICROSD_LOW_LEVEL_DRIVER.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libSHIFT_REGISTER.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libWAVE_GENERATORS.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); *libRUN_TIME_LOGGER.a:*(.ARM.attributes .ARM.attributes*); . = ALIGN(4); } >ROM_EXTERNAL_LIBRARIES /* . */ .section_user_libraries_rodata : ALIGN(4) { . = ALIGN(4); *libUSER_FREERTOS_LEVEL.a:*(.rodata .rodata*); . = ALIGN(4); *libUSER_BSP_LEVEL.a:*(.rodata .rodata*); . = ALIGN(4); *libMC_INTERRUPT.a:*(.rodata .rodata*); . = ALIGN(4); *libMC_HARDWARE.a:*(.rodata .rodata*); . = ALIGN(4); *libPCB_HARDWARE.a:*(.rodata .rodata*); . = ALIGN(4); *libUSER_STARTUP.a:*(.rodata .rodata*); . = ALIGN(4); *libBUTTONS.a:*(.rodata .rodata*); . = ALIGN(4); *libCHIPTUNE.a:*(.rodata .rodata*); . = ALIGN(4); *libDIGITAL_POTENTIOMETER.a:*(.rodata .rodata*); . = ALIGN(4); *libLCD_DRIVER.a:*(.rodata .rodata*); . = ALIGN(4); *libMAKISE_GUI_ELEMENTS_BY_VADIMATORIK_ELEMENTS_BY_VADIMATORIK.a:*(.rodata .rodata*); . = ALIGN(4); *libMC_HARDWARE_INTERFACES_IMPLEMENTATION_FOR_STM32.a:*(.rodata .rodata*); . = ALIGN(4); *libMICROSD_LOW_LEVEL_DRIVER.a:*(.rodata .rodata*); . = ALIGN(4); *libSHIFT_REGISTER.a:*(.rodata .rodata*); . = ALIGN(4); *libWAVE_GENERATORS.a:*(.rodata .rodata*); . = ALIGN(4); *libRUN_TIME_LOGGER.a:*(.rodata .rodata*); . = ALIGN(4); } >ROM_USER_LIBRARIES /*------------------------- ------------------------*/ /* . */ .section_user_code_text : ALIGN(4) { . = ALIGN(4); *(.text .text.*) . = ALIGN(4); } >ROM_MAIN_PROGRAMM /* , */ .sections_user_code_required_by_the_compiler : ALIGN(4) { . = ALIGN(4); *(.glue_7 .glue_7*) /* - ARMv7 */ . = ALIGN(4); *(.glue_7t .glue_7t*) . = ALIGN(4); *(.vfp11_veneer .vfp11_veneer*) /* . */ . = ALIGN(4); *(.v4_bx .v4_bx*) . = ALIGN(4); *(.iplt .iplt*) . = ALIGN(4); *(.rel.dyn .rel.dyn*) . = ALIGN(4); KEEP(*(.eh_frame .eh_frame*)) /* CPP. */ . = ALIGN(4); *(.eh_framehdr .eh_framehdr*) . = ALIGN(4); *(.ARM.attributes .ARM.attributes.*) /* , . */ . = ALIGN(4); *(vtable) /* C++ virtual tables */ PROVIDE_HIDDEN (__preinit_array_start = .); /* , . */ . = ALIGN(4); KEEP(*(.preinit_array_sysinit .preinit_array_sysinit*)) . = ALIGN(4); KEEP(*(.preinit_array_platform .preinit_array_platform.*)) . = ALIGN(4); KEEP(*(.preinit_array .preinit_array.*)) PROVIDE_HIDDEN (__preinit_array_end = .); PROVIDE_HIDDEN (__init_array_start = .); /* . */ . = ALIGN(4); KEEP(*(SORT(.init_array.*))) . = ALIGN(4); KEEP(*(.init_array)) . = ALIGN(4); PROVIDE_HIDDEN (__init_array_end = .); PROVIDE_HIDDEN (__fini_array_start = .); /* . */ . = ALIGN(4); KEEP(*(SORT(.fini_array.*))) . = ALIGN(4); KEEP(*(.fini_array)) . = ALIGN(4); PROVIDE_HIDDEN (__fini_array_end = .); . = ALIGN(4); KEEP(*(.cfmconfig)) . = ALIGN(4); *(.after_vectors .after_vectors.*) . = ALIGN(4); } >ROM_MAIN_PROGRAMM /* . */ .section_user_code_rodata : ALIGN(4) { . = ALIGN(4); *(.rodata .rodata.*) . = ALIGN(4); } >ROM_MAIN_PROGRAMM /* stack trace. */ .ARM.exidx : { . = ALIGN(4); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(4); *(.ARM.exidx* .gnu.linkonce.armexidx.*) . = ALIGN(4); } > ROM_MAIN_PROGRAMM /*-------------------------------RAM-----------------------------*/ /* . */ .section_external_libraries_data : ALIGN(4) { . = ALIGN(4); __external_lib_data_start = . ; /* . */ . = ALIGN(4); *libgcc.a:*(.data .data*); . = ALIGN(4); *libstdc++_nano.a:*(.data .data*); . = ALIGN(4); *libg_nano.a:*(.data .data*); /* */ . = ALIGN(4); *libSTM32F4_LOW_LEVEL_BY_ST.a:*(.data .data*); . = ALIGN(4); *libFATFS.a:*(.data .data*); . = ALIGN(4); *libFREERTOS.a:*(.data .data*); . = ALIGN(4); *libMAKISE_GUI.a:*(.data .data*); . = ALIGN(4); __external_lib_data_end = . ; } >RAM_EXTERNAL_LIBRARIES AT> ROM_EXTERNAL_LIBRARIES /* RAM */ .section_external_libraries_bss : ALIGN(4) { . = ALIGN(4); __external_lib_bss_start = .; /* . */ . = ALIGN(4); *libgcc.a:*(.bss .bss*); . = ALIGN(4); *libstdc++_nano.a:*(.bss .bss*); . = ALIGN(4); *libg_nano.a:*(*COMMON); . = ALIGN(4); *libgcc.a:*(*COMMON); . = ALIGN(4); *libstdc++_nano.a:*(*COMMON); . = ALIGN(4); *libg_nano.a:*(*COMMON); /* */ . = ALIGN(4); *libSTM32F4_LOW_LEVEL_BY_ST.a:*(.bss .bss*); . = ALIGN(4); *libFATFS.a:*(.bss .bss*); . = ALIGN(4); *libFREERTOS.a:*(.bss .bss*); . = ALIGN(4); *libMAKISE_GUI.a:*(.bss .bss*); . = ALIGN(4); *libSTM32F4_LOW_LEVEL_BY_ST.a:*(*COMMON); . = ALIGN(4); *libFATFS.a:*(*COMMON); . = ALIGN(4); *libFREERTOS.a:*(*COMMON); . = ALIGN(4); *libMAKISE_GUI.a:*(*COMMON); . = ALIGN(4); __external_lib_bss_end = .; } >RAM_EXTERNAL_LIBRARIES /* . */ .section_user_libraries_data : ALIGN(4) { . = ALIGN(4); __user_lib_data_start = . ; . = ALIGN(4); *libUSER_FREERTOS_LEVEL.a:*(.data .data*); . = ALIGN(4); *libUSER_BSP_LEVEL.a:*(.data .data*); . = ALIGN(4); *libMC_INTERRUPT.a:*(.data .data*); . = ALIGN(4); *libMC_HARDWARE.a:*(.data .data*); . = ALIGN(4); *libPCB_HARDWARE.a:*(.data .data*); . = ALIGN(4); *libUSER_STARTUP.a:*(.data .data*); . = ALIGN(4); *libBUTTONS.a:*(.data .data*); . = ALIGN(4); *libCHIPTUNE.a:*(.data .data*); . = ALIGN(4); *libDIGITAL_POTENTIOMETER.a:*(.data .data*); . = ALIGN(4); *libLCD_DRIVER.a:*(.data .data*); . = ALIGN(4); *libMAKISE_GUI_ELEMENTS_BY_VADIMATORIK_ELEMENTS_BY_VADIMATORIK.a:*(.data .data*); . = ALIGN(4); *libMC_HARDWARE_INTERFACES_IMPLEMENTATION_FOR_STM32.a:*(.data .data*); . = ALIGN(4); *libMICROSD_LOW_LEVEL_DRIVER.a:*(.data .data*); . = ALIGN(4); *libSHIFT_REGISTER.a:*(.data .data*); . = ALIGN(4); *libWAVE_GENERATORS.a:*(.data .data*); . = ALIGN(4); *libRUN_TIME_LOGGER.a:*(.data .data*); . = ALIGN(4); __user_lib_data_end = . ; } >RAM_USER_LIBRARIES AT> ROM_USER_LIBRARIES .section_user_libraries_bss : ALIGN(4) { . = ALIGN(4); __user_lib_bss_start = .; . = ALIGN(4); *libUSER_FREERTOS_LEVEL.a:*(.bss .bss*); . = ALIGN(4); *libUSER_BSP_LEVEL.a:*(.bss .bss*); . = ALIGN(4); *libMC_INTERRUPT.a:*(.bss .bss*); . = ALIGN(4); *libMC_HARDWARE.a:*(.bss .bss*); . = ALIGN(4); *libPCB_HARDWARE.a:*(.bss .bss*); . = ALIGN(4); *libUSER_CODE.a:*(.bss .bss*); . = ALIGN(4); *libBUTTONS.a:*(.bss .bss*); . = ALIGN(4); *libCHIPTUNE.a:*(.bss .bss*); . = ALIGN(4); *libDIGITAL_POTENTIOMETER.a:*(.bss .bss*); . = ALIGN(4); *libLCD_DRIVER.a:*(.bss .bss*); . = ALIGN(4); *libMAKISE_GUI_ELEMENTS_BY_VADIMATORIK_ELEMENTS_BY_VADIMATORIK.a:*(.bss .bss*); . = ALIGN(4); *libMC_HARDWARE_INTERFACES_IMPLEMENTATION_FOR_STM32.a:*(.bss .bss*); . = ALIGN(4); *libMICROSD_LOW_LEVEL_DRIVER.a:*(.bss .bss*); . = ALIGN(4); *libSHIFT_REGISTER.a:*(.bss .bss*); . = ALIGN(4); *libWAVE_GENERATORS.a:*(.bss .bss*); . = ALIGN(4); *libUSER_FREERTOS_LEVEL.a:*(.bss .bss*); . = ALIGN(4); *libRUN_TIME_LOGGER.a:*(.bss .bss*); . = ALIGN(4); *libUSER_BSP_LEVEL.a:*(*COMMON); . = ALIGN(4); *libMC_INTERRUPT.a:*(*COMMON); . = ALIGN(4); *libMC_HARDWARE.a:*(*COMMON); . = ALIGN(4); *libPCB_HARDWARE.a:*(*COMMON); . = ALIGN(4); *libUSER_CODE.a:*(*COMMON); . = ALIGN(4); *libBUTTONS.a:*(*COMMON); . = ALIGN(4); *libCHIPTUNE.a:*(*COMMON); . = ALIGN(4); *libDIGITAL_POTENTIOMETER.a:*(*COMMON); . = ALIGN(4); *libLCD_DRIVER.a:*(*COMMON); . = ALIGN(4); *libMAKISE_GUI_ELEMENTS_BY_VADIMATORIK_ELEMENTS_BY_VADIMATORIK.a:*(*COMMON); . = ALIGN(4); *libMC_HARDWARE_INTERFACES_IMPLEMENTATION_FOR_STM32.a:*(*COMMON); . = ALIGN(4); *libMICROSD_LOW_LEVEL_DRIVER.a:*(*COMMON); . = ALIGN(4); *libSHIFT_REGISTER.a:*(*COMMON); . = ALIGN(4); *libWAVE_GENERATORS.a:*(*COMMON); . = ALIGN(4); *libRUN_TIME_LOGGER.a:*(.COMMON*); . = ALIGN(4); __user_lib_bss_end = .; } >RAM_USER_LIBRARIES /* . */ .section_user_code_data : ALIGN(4) { . = ALIGN(4); __user_code_data_start = . ; . = ALIGN(4); *(.data .data.*) . = ALIGN(4); __user_code_data_end = . ; } >RAM_MAIN_PROGRAMM AT> ROM_MAIN_PROGRAMM .section_user_code_bss : ALIGN(4) { . = ALIGN(4); __bss_start__ = .; __user_code_bss_start = .; *(.bss .bss.*) *(COMMON) . = ALIGN(4); __bss_end__ = .; __user_code_bss_end = .; } >RAM_MAIN_PROGRAMM __external_lib_data_in_rom_start = LOADADDR(.section_external_libraries_data); __user_lib_data_in_rom_start = LOADADDR(.section_user_libraries_data); __user_code_data_in_rom_start = LOADADDR(.section_user_code_data); /*------------------------- -----------------*/ /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } /* * DWARF debug sections. * Symbols in the DWARF debugging sections are relative to the beginning * of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } .debug_macro 0 : { *(.debug_macro) } .debug_ranges 0 : { *(.debug_ranges) } }
Fügen Sie dem Hauptprojekt ein Dienstprogramm hinzu, um Abschnitte aus der endgültigen Bin-Datei zu extrahieren
Leider war es weder in objcopy noch in objdump möglich, Flags zum Extrahieren von Code zwischen bestimmten Adressen aus der Elf-Datei zu finden. Es gibt ein Flag
--only-section , das jedoch nicht die Tatsache berücksichtigt, dass Debugging-Informationen nach allen Entitäten des in section.ld aufgelisteten Abschnitts immer noch im nichtflüchtigen Speicher abgelegt werden. Ohne sie funktioniert die endgültige Firmware, die aus Teilen zusammengesetzt ist, nicht (aus offensichtlichen Gründen). Daher musste ich ein einfaches
Dienstprogramm schreiben, das eine gemeinsame Bin-Datei verwendet und den erforderlichen Abschnitt in eine separate Datei für den angegebenen Adressbereich extrahiert. Hier ergibt sich jedoch die folgende Nuance. Standardmäßig füllt objcopy den Raum zwischen Abschnitten mit 0s. Der leere Speicherplatz im Flash-Speicher ist jedoch 0xFF. Um dieses Problem zu lösen, müssen Sie die Ausgabe-Bin-Datei mit dem Flag
--gap-fill = 0xff erstellen .
Fügen Sie dem Projekt einen Aufruf des Skripts zum Aktualisieren des nichtflüchtigen Speichers des Mikrocontrollers beim Aktualisieren der Firmware-Datei hinzu
Um Änderungen im Projekt zu verfolgen, müssen Sie nach jeder Neuerstellung der Elf-Datei ein Validierungsskript aufrufen, das die endgültige Bin-Datei aus der Elf-Datei extrahiert, den gewünschten Abschnitt daraus vergleicht, ihn mit dem zuvor extrahierten vergleicht und bei Abweichungen den Abschnitt im Speicher des Mikrocontrollers aktualisiert.
Im Projekt selbst können Sie die cmake-Funktion aufrufen, die alles Notwendige erledigt:Cmake Update-Funktion function(write_sector SECTOR ADDR_BASE ADDR_START ADDR_END) add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD COMMAND ${ARM_OBJCOPY} --output-target=binary --gap-fill=0xff ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.elf ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_all.bin COMMENT "Creating a binary file of the <<${SECTOR}>> sector" COMMAND ${BIN_EXTRACTOR} -p ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_all.bin -o ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_section_${SECTOR}_new.bin -b ${ADDR_BASE} -s ${ADDR_START} -e ${ADDR_END} COMMAND cd ${CMAKE_SOURCE_DIR} && ./cmp.sh ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_section_${SECTOR}.bin ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_section_${SECTOR}_new.bin "${STM32PROG} -c port=${STM32PROG_PORT} freq=${STM32PROG_FREQ} -w ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_section_${SECTOR}.bin ${ADDR_START}") endfunction(write_sector)
Die Funktion verwendet stm32programmer zum Schreiben.Ein Beispiel für die Verwendung einer Funktion aus dem Projektcode if (STM32PROG_USE STREQUAL "ON") write_sector("bootloader" ${SECTION_BOOTLOADER_ADDRESS} ${SECTION_BOOTLOADER_ADDRESS} ${SECTION_SYSCFG_PAGE_1_ADDRESS}) write_sector("external_libraries" ${SECTION_BOOTLOADER_ADDRESS} ${SECTION_EXTERNAL_LIB_ADDRESS} ${SECTION_USER_LIBRARIES_ADDRESS}) write_sector("user_libraries" ${SECTION_BOOTLOADER_ADDRESS} ${SECTION_USER_LIBRARIES_ADDRESS} ${SECTION_USER_CODE_ADDRESS}) write_sector("main_programm" ${SECTION_BOOTLOADER_ADDRESS} ${SECTION_USER_CODE_ADDRESS} ${ADDR_END_FLASH}) endif ()
Schlussfolgerungen
Die Vorteile dieses Ansatzes:- In 95% der Fälle wird das, was wirklich benötigt wird, aktualisiert.
Nachteile dieses Ansatzes:- Es gibt keinen Geschwindigkeitsgewinn, da vor jeder Firmware ein Bootloader in den Mikrocontroller geladen werden muss, um den nichtflüchtigen Speicher zu flashen (dies wird automatisch vom stm32-Programmierer durchgeführt). Im Gegenteil, wenn das Projekt vollständig wieder zusammengesetzt wird, müssen Sie häufig alle Abschnitte erneut nähen.
- Die Größe von section.ld entmutigt jeden Wunsch, etwas hinzuzufügen oder zu ändern. Wenn Sie diese Methode in einem realen Projekt anwenden müssen, müssen Sie eine praktische Benutzeroberfläche schreiben, um diese Datei zu bearbeiten.
- Wenn das Gerät seine eigene Stromversorgung steuert, bemerken Sie möglicherweise nicht, dass eine der Partitionen nicht richtig verdrahtet war (z. B. mit einem Spannungsabfall), und debuggen Partitionen von verschiedenen Baugruppen für eine lange Zeit :).
Sie können die Arbeitsversion der aktuellen Methode in diesem Commit sehen . Das Projekt kann in CLion zusammengestellt werden, nachdem zuvor ein Dienstprogramm zum Extrahieren des Abschnitts aus der Bin-Datei kompiliert wurde.