Kurznotizen des Programmierers einbetten: Abschnittsduplizierung im Speicher des Mikrocontrollers

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-Skript
MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K - 256K /* 256   user_cfg */
   USER_CFG_PAGE_1 (rx) : ORIGIN = 0x080C0000, LENGTH = 128K - 4     /* 4  CRC32 */
    USER_CFG_PAGE_2 (rx) : ORIGIN = 0x080E0000, LENGTH = 128K - 4     /* 4  CRC32 */
    CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}

/*       CRC32.
       .      - . */
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;

/*
 * The '__stack' definition is required by crt0, do not remove it.
 */
__stack = ORIGIN(RAM) + LENGTH(RAM);

_estack = __stack;     /* STM specific definition */

/*
 * Default stack sizes.
 * These are used by the startup in order to allocate stacks
 * for the different modes.
 */

__Main_Stack_Size = 1024 ;

PROVIDE ( _Main_Stack_Size = __Main_Stack_Size ) ;

__Main_Stack_Limit = __stack  - __Main_Stack_Size ;

/*"PROVIDE" allows to easily override these values from an object file or the command line. */
PROVIDE ( _Main_Stack_Limit = __Main_Stack_Limit ) ;

/*
 * There will be a link error if there is not this amount of
 * RAM free at the end.
 */
_Minimum_Stack_Size = 512 ;

/*
 * Default heap definitions.
 * The heap start immediately after the last statically allocated
 * .sbss/.noinit section, and extends up to the main stack limit.
 */
PROVIDE ( _Heap_Begin = _end_noinit ) ;
PROVIDE ( _Heap_Limit = __stack - __Main_Stack_Size ) ;

SECTIONS
{
    .isr_vector :
    {
        KEEP(*(.isr_vector))         /* Interrupt vectors */
        KEEP(*(.cfmconfig))            /* Freescale configuration words */
        *(.after_vectors .after_vectors.*)    /* Startup code and ISR */
        . = ALIGN(4);
    } >FLASH

    .inits :
    {
        . = ALIGN(4);

        /*
         * These are the old initialisation sections, intended to contain
         * naked code, with the prologue/epilogue added by crti.o/crtn.o
         * when linking with startup files. The standalone startup code
         * currently does not run these, better use the init arrays below.
         */
        KEEP(*(.init))
        KEEP(*(.fini))

        . = ALIGN(4);

        /*
         * The preinit code, i.e. an array of pointers to initialisation
         * functions to be performed before constructors.
         */
        PROVIDE_HIDDEN (__preinit_array_start = .);

        /*
         * Used to run the SystemInit() before anything else.
         */
        KEEP(*(.preinit_array_sysinit .preinit_array_sysinit.*))

        /*
         * Used for other platform inits.
         */
        KEEP(*(.preinit_array_platform .preinit_array_platform.*))

        /*
         * The application inits. If you need to enforce some order in
         * execution, create new sections, as before.
         */
        KEEP(*(.preinit_array .preinit_array.*))

        PROVIDE_HIDDEN (__preinit_array_end = .);

        . = ALIGN(4);

        /*
         * The init code, i.e. an array of pointers to static constructors.
         */
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP(*(SORT(.init_array.*)))
        KEEP(*(.init_array))
        PROVIDE_HIDDEN (__init_array_end = .);

        . = ALIGN(4);

        /*
         * The fini code, i.e. an array of pointers to static destructors.
         */
        PROVIDE_HIDDEN (__fini_array_start = .);
        KEEP(*(SORT(.fini_array.*)))
        KEEP(*(.fini_array))
        PROVIDE_HIDDEN (__fini_array_end = .);
        . = ALIGN(4);

    } >FLASH

    /*
     * For some STRx devices, the beginning of the startup code
     * is stored in the .flashtext section, which goes to FLASH.
     */
    .flashtext :
    {
        . = ALIGN(4);
        *(.flashtext .flashtext.*)    /* Startup code */
        . = ALIGN(4);
    } >FLASH


    /*
     * The program code is stored in the .text section,
     * which goes to FLASH.
     */
    .text :
    {
        . = ALIGN(4);

        *(.text .text.*)            /* all remaining code */

        *(.rodata .rodata.*)         /* read-only data (constants) */

        *(vtable)                    /* C++ virtual tables */

        KEEP(*(.eh_frame*))

        /*
         * Stub sections generated by the linker, to glue together
         * ARM and Thumb code. .glue_7 is used for ARM code calling
         * Thumb code, and .glue_7t is used for Thumb code calling
         * ARM code. Apparently always generated by the linker, for some
         * architectures, so better leave them here.
         */
        *(.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 magic sections */
    .ARM.extab :
       {
       *(.ARM.extab* .gnu.linkonce.armextab.*)
       } > FLASH

       __exidx_start = .;
       .ARM.exidx :
       {
       *(.ARM.exidx* .gnu.linkonce.armexidx.*)
       } > FLASH
       __exidx_end = .;

    . = ALIGN(4);
    _etext = .;
    __etext = .;

    /*
     * This address is used by the startup code to
     * initialise the .data section.
     */
    _sidata = _etext;

    /* MEMORY_ARRAY */
    /*
    .ROarraySection :
    {
         *(.ROarraySection .ROarraySection.*)
    } >MEMORY_ARRAY
    */

    /*
     * The initialised data section.
     * The program executes knowing that the data is in the RAM
     * but the loader puts the initial values in the FLASH (inidata).
     * It is one task of the startup to copy the initial values from
     * FLASH to RAM.
     */
    .data  : AT ( _sidata )
    {
        . = ALIGN(4);

        /* This is used by the startup code to initialise the .data section */
        _sdata = . ;            /* STM specific definition */
        __data_start__ = . ;
        *(.data_begin .data_begin.*)

        *(.data .data.*)

        *(.data_end .data_end.*)

        *(.ramfunc*)
        . = ALIGN(4);

        /* This is used by the startup code to initialise the .data section */
        _edata = . ;            /* STM specific definition */
        __data_end__ = . ;

    } >RAM


    /*
     * The uninitialised data section. NOLOAD is used to avoid
     * the "section `.bss' type changed to PROGBITS" warning
     */
    .bss (NOLOAD) :
    {
        . = ALIGN(4);
        __bss_start__ = .;         /* standard newlib definition */
        _sbss = .;              /* STM specific definition */
        *(.bss_begin .bss_begin.*)

        *(.bss .bss.*)
        *(COMMON)

        *(.bss_end .bss_end.*)
        . = ALIGN(4);
        __bss_end__ = .;        /* standard newlib definition */
        _ebss = . ;             /* STM specific definition */
    } >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

    /* Mandatory to be word aligned, _sbrk assumes this */
    PROVIDE ( end = _end_noinit ); /* was _ebss */
    PROVIDE ( _end = _end_noinit );
    PROVIDE ( __end = _end_noinit );
    PROVIDE ( __end__ = _end_noinit );

    /*
     * Used for validation only, do not allocate anything here!
     *
     * This is just to check that there is enough RAM left for the Main
     * stack. It should generate an error if it's full.
     */
    ._check_stack :
    {
        . = ALIGN(4);
        . = . + _Minimum_Stack_Size ;
        . = ALIGN(4);
    } >RAM


    /* After that there are only debugging sections. */

    /* This can remove the debugging information from the standard libraries */
    /*
    DISCARD :
    {
     libc.a ( * )
     libm.a ( * )
     libgcc.a ( * )
     }
     */

    /* 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) }
}

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.

Source: https://habr.com/ru/post/undefined/


All Articles