Anfangsbedingungen
Es gibt ein Gerät, das auf einem Mikrocontroller basiert (zum Beispiel wird stm32f405rgt6 verwendet). Beim Einschalten werden die Peripheriegeräte basierend auf Benutzereinstellungen oder Standardeinstellungen angepasst. Der Benutzer kann die Einstellungen während des Betriebs des Geräts (in der Regel nur während der Integration in den Komplex) über eine der möglichen Schnittstellen (CLI-Menü oder Dienstprogramm zum Einstellen von Arbeitsparametern über das Binärprotokoll) ändern. Nach dem Einstellen der Parameter speichert der Benutzer die Einstellungen mit einem speziellen Befehl (auch über eine der möglichen Schnittstellen).Aufgabe
- Um die Zeit zu verkürzen, die für den Zugriff auf die Geräteeinstellungsvariablen während des Betriebs benötigt wird, müssen Sie die aktuellen Werte im RAM behalten (normalerweise variiert die Menge dieser Daten je nach Gerät zwischen einem Dutzend Variablen und 3 Kilobyte).
- Es ist notwendig, Benutzereinstellungen doppelt im Flash des Mikrocontrollers zu speichern.
- Jede Instanz von Benutzereinstellungen muss unmittelbar nach der Nutzlast mit CRC32 enden.
- Für jede Instanz von Benutzereinstellungen wird eine separate Seite im Flash-Speicher verwendet (selbst wenn die nützlichen Daten 2 KB groß sind und die Seiten durch 128 KB geteilt werden, wird die gesamte Seite unter einem Block angezeigt).
- Der Software-Code des Mikrocontrollers muss die Standardeinstellungen speichern. Dies sollten die Benutzereinstellungsblöcke sein, wenn beide Daten beschädigt haben
Versuche zu lösen
GCC (zum Zeitpunkt des Schreibens) hat keine Flagge, um eine Kopie des Abschnitts zu erhalten. Das Ergänzen des LD-Skripts mit << magischen Linien >> schlägt ebenfalls fehl. Man könnte mit Objcopy arbeiten, aber dieser Ansatz ist sehr implizit und führt zu subtilen Fehlern.Entscheidung
Die Lösung besteht darin, implizite Kopien der gewünschten Entität (Variable, Struktur, Array usw.) im Benutzercode zu erstellen, gefolgt von ihrer Position im Speicher.Erstellen eines Makros zum versteckten Kopieren von Strukturen
Erstellen wir ein Makro, mit dem wir einen Platz für die Struktur im RAM, 2 Instanzen des Flash-Speichers auf separaten Seiten und im Benutzercode (für Anfangswerte) reservieren.#define USER_CFG_DATA_STRUCT(TYPE,NAME,...) \
__attribute__((aligned(4), section (".user_cfg_data_ram_page"))) \
TYPE NAME = __VA_ARGS__; \
__attribute__((aligned(4), section (".user_cfg_data_flash_default_page"))) \
TYPE flash_default_page_##NAME = __VA_ARGS__; \
__attribute__((aligned(4), section (".user_cfg_data_flash_page_1"))) \
TYPE flash_page_1_##NAME = __VA_ARGS__; \
__attribute__((aligned(4), section (".user_cfg_data_flash_page_2"))) \
TYPE flash_page_2_##NAME = __VA_ARGS__;
Mit einem Makro werden 4 Instanzen der Struktur mit unterschiedlichen Namen erstellt. Im Projektcode (mit Ausnahme des Systems für die Arbeit mit Speicherseiten für Benutzereinstellungen) müssen alle Module eine Struktur ohne Präfix (den im Makro angegebenen Namen) verwenden, die sich im RAM befindet (es ist möglich, die Module zu ändern, die für die Benutzerinteraktion verantwortlich sind. Der Rest sollte nur daraus lesen).Ein Beispiel für die Verwendung eines Makros im Benutzercode:typedef struct _test_st {
uint32_t a1;
uint32_t a2;
} test_st_t;
USER_CFG_DATA_STRUCT(test_st_t, name_st, {
.a1 = 1,
.a2 = 2
})
Nach der Instanz der Struktur ist name_st aus dem Projektcode verfügbar.Erstellen von Makros zum versteckten Kopieren anderer Entitäten
Um eine Variable zu erstellen, müssen Sie nur die Makrosubstitution zum Erstellen von Strukturen durchführen.Fügen Sie für Arrays die Anzahl der Elemente hinzu.#define USER_CFG_DATA_VAR USER_CFG_DATA_STRCUT
#define USER_CFG_DATA_ARRAY(TYPE,NAME,SIZE,...) \
__attribute__((aligned(4), section (".user_cfg_data_ram_page"))) \
TYPE NAME[SIZE] = __VA_ARGS__; \
__attribute__((aligned(4), section (".user_cfg_data_flash_default_page"))) \
TYPE flash_default_page_##NAME[SIZE] = __VA_ARGS__; \
__attribute__((aligned(4), section (".user_cfg_data_flash_page_1"))) \
TYPE flash_page_1_##NAME[SIZE] = __VA_ARGS__; \
__attribute__((aligned(4), section (".user_cfg_data_flash_page_2"))) \
TYPE flash_page_2_##NAME[SIZE] = __VA_ARGS__;
Das Verwendungsprinzip ähnelt der Verwendung von Struktur.Finalisierung des LD-Skripts
Beim Erstellen von Kopien von Entitäten wurde angegeben, in welchen Abschnitten sie liegen. Jetzt sollten Sie diese Abschnitte im LD-Skript erstellen. Für F4 sieht das erweiterte LD-Skript von ST folgendermaßen aus:LD-SkriptMEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K - 256K
USER_CFG_PAGE_1 (rx) : ORIGIN = 0x080C0000, LENGTH = 128K - 4
USER_CFG_PAGE_2 (rx) : ORIGIN = 0x080E0000, LENGTH = 128K - 4
CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
user_cfg_data_flash_page_1_size = LENGTH(user_cfg_PAGE_1) + 4;
user_cfg_data_flash_page_2_size = LENGTH(user_cfg_PAGE_2) + 4;
user_cfg_data_flash_page_size = user_cfg_data_flash_page_1_size;
__stack = ORIGIN(RAM) + LENGTH(RAM);
_estack = __stack;
__Main_Stack_Size = 1024 ;
PROVIDE ( _Main_Stack_Size = __Main_Stack_Size ) ;
__Main_Stack_Limit = __stack - __Main_Stack_Size ;
PROVIDE ( _Main_Stack_Limit = __Main_Stack_Limit ) ;
_Minimum_Stack_Size = 512 ;
PROVIDE ( _Heap_Begin = _end_noinit ) ;
PROVIDE ( _Heap_Limit = __stack - __Main_Stack_Size ) ;
SECTIONS
{
.isr_vector :
{
KEEP(*(.isr_vector))
KEEP(*(.cfmconfig))
*(.after_vectors .after_vectors.*)
. = ALIGN(4);
} >FLASH
.inits :
{
. = ALIGN(4);
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array_sysinit .preinit_array_sysinit.*))
KEEP(*(.preinit_array_platform .preinit_array_platform.*))
KEEP(*(.preinit_array .preinit_array.*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);
} >FLASH
.flashtext :
{
. = ALIGN(4);
*(.flashtext .flashtext.*)
. = ALIGN(4);
} >FLASH
.text :
{
. = ALIGN(4);
*(.text .text.*)
*(.rodata .rodata.*)
*(vtable)
KEEP(*(.eh_frame*))
*(.glue_7)
*(.glue_7t)
} >FLASH
.user_cfg_data_flash_default_page :
{
. = ALIGN(4);
user_cfg_data_flash_default_page_start = .;
KEEP(*(.user_cfg_data_flash_default_page .user_cfg_data_flash_default_page.*))
. = ALIGN(4);
user_cfg_data_flash_default_page_stop = .;
} >FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
. = ALIGN(4);
_etext = .;
__etext = .;
_sidata = _etext;
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = . ;
__data_start__ = . ;
*(.data_begin .data_begin.*)
*(.data .data.*)
*(.data_end .data_end.*)
*(.ramfunc*)
. = ALIGN(4);
_edata = . ;
__data_end__ = . ;
} >RAM
.bss (NOLOAD) :
{
. = ALIGN(4);
__bss_start__ = .;
_sbss = .;
*(.bss_begin .bss_begin.*)
*(.bss .bss.*)
*(COMMON)
*(.bss_end .bss_end.*)
. = ALIGN(4);
__bss_end__ = .;
_ebss = . ;
} >RAM
.user_cfg_data_ram_page :
{
. = ALIGN(4);
user_cfg_data_ram_page_start = .;
KEEP(*(.user_cfg_data_ram_page .user_cfg_data_ram_page.*))
. = ALIGN(4);
user_cfg_data_ram_page_stop = .;
} > CCM
user_cfg_data_ram_page_size = user_cfg_data_ram_page_stop - user_cfg_data_ram_page_start;
.user_cfg_data_page_1 :
{
. = ALIGN(4);
user_cfg_data_flash_page_1_start = .;
KEEP(*(.user_cfg_data_flash_page_1 .user_cfg_data_flash_page_1.*))
. = ALIGN(4);
user_cfg_data_flash_page_1_stop = .;
} > user_cfg_PAGE_1
.user_cfg_data_page_2 :
{
. = ALIGN(4);
user_cfg_data_flash_page_2_start = .;
KEEP(*(.user_cfg_data_flash_page_2 .user_cfg_data_flash_page_2.*))
. = ALIGN(4);
user_cfg_data_flash_page_2_stop = .;
} > user_cfg_PAGE_2
.noinit (NOLOAD) :
{
. = ALIGN(4);
_noinit = .;
*(.noinit .noinit.*)
. = ALIGN(4) ;
_end_noinit = .;
} > RAM
PROVIDE ( end = _end_noinit );
PROVIDE ( _end = _end_noinit );
PROVIDE ( __end = _end_noinit );
PROVIDE ( __end__ = _end_noinit );
._check_stack :
{
. = ALIGN(4);
. = . + _Minimum_Stack_Size ;
. = ALIGN(4);
} >RAM
.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) }
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
.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) }
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
}
In diesem Beispiel liegt die Konfigurationsstruktur nicht im RAM-Bereich, sondern im CCMRAM, um die Leistung zu verbessern. Ich stelle auch fest, dass die Benutzereinstellungsdatenblöcke die letzten 2 Flash-Seiten sind, von denen jede 128 KB groß ist.Initialisierung und Kontrolle von Kopien
Die Sorge darüber, was im RAM liegen wird, liegt beim Programmierer. Bevor Sie Daten aus diesem Bereich zum ersten Mal verwenden, müssen Sie den Bereich initialisieren. Beim Laden des Programms in den Mikrocontroller werden die Daten in Flash mit den im Programmcode angegebenen Anfangswerten aufgezeichnet. Dadurch werden ungültige Kopien der Benutzereinstellungsblöcke auf separaten Seiten erstellt, wenn sie mit CRC32 enden (da keine CRC32-Berechnung durchgeführt wird. Ihr Code kann dies tun. Auch bis zur ersten Verwendung von Konfigurationsparametern).Um ein Modul für die Arbeit mit Konfigurationsblöcken zu schreiben, müssen Sie die Variablen aus dem LD-Skript verwenden. Folgendes wird benötigt:extern uint32_t user_cfg_data_flash_default_page_start;
extern uint32_t user_cfg_data_flash_page_1_start;
extern uint32_t user_cfg_data_flash_page_2_start;
extern uint32_t user_cfg_data_ram_page_start;
extern uint32_t user_cfg_data_flash_page_size;
extern uint32_t user_cfg_data_ram_page_size;
Vergessen Sie auch nicht, dass user_cfg_data_flash_page_size und user_cfg_data_ram_page_size als normale Werte zugewiesen werden können. Aber user_cfg_data_flash_page_1_start und anderen Variablen die Adresse zu speichern müssen über & angegeben werden.