تضمين الملاحظات الموجزة للمبرمج: تكرار المقطع في ذاكرة وحدة التحكم الدقيقة

الشروط الأولية


هناك جهاز يعتمد على متحكم (على سبيل المثال ، سيتم أخذ stm32f405rgt6). عند تشغيله ، يقوم بضبط ملحقاته بناءً على تفضيلات المستخدم أو الإعدادات الافتراضية. يمكن للمستخدم تغيير الإعدادات أثناء تشغيل الجهاز (كقاعدة عامة ، فقط أثناء الاندماج في المجمع) من خلال إحدى الواجهات المحتملة (قائمة CLI أو الأداة المساعدة لتعيين معلمات العمل التي تعمل عبر البروتوكول الثنائي). بعد ضبط المعلمات ، يحفظ المستخدم الإعدادات بأمر خاص (أيضًا من خلال إحدى الواجهات المحتملة).

مهمة


  • لتقليل الوقت المستغرق للوصول إلى متغيرات إعدادات الجهاز أثناء التشغيل ، تحتاج إلى الاحتفاظ بالقيم الحالية في ذاكرة الوصول العشوائي (عادةً ما تختلف كمية هذه البيانات من اثني عشر متغيرات إلى 3 كيلوبايت اعتمادًا على الجهاز).
  • من الضروري تخزين إعدادات المستخدم بشكل مكرر في ومضة وحدة التحكم الدقيقة.
  • يجب أن ينتهي كل مثيل لإعدادات المستخدم بـ CRC32 مباشرة بعد الحمولة.
  • لكل مثيل من إعدادات المستخدم ، يتم استخدام صفحة منفصلة في ذاكرة فلاش (حتى لو كانت البيانات المفيدة 2 كيلو بايت ، والصفحات مقسمة على 128 كيلو بايت ، ثم يتم إعطاء الصفحة بأكملها تحت كتلة واحدة)
  • يجب أن يخزن رمز برنامج وحدة التحكم الدقيقة الإعدادات الافتراضية ، والتي يجب أن تكون كتل إعدادات المستخدم إذا كان لكل منهما بيانات تالفة

محاولات لحلها


ليس لدى دول مجلس التعاون الخليجي (في وقت كتابة هذا التقرير) علامة للحصول على نسخة من القسم. فشل أيضًا في إكمال النص البرمجي LD باستخدام "الخطوط السحرية". يمكن للمرء أن يعمل مع إعاقة ، ولكن هذا النهج ضمني للغاية ويؤدي إلى أخطاء دقيقة.

القرار


الحل هو إنشاء نسخ ضمنية من الكيان المطلوب (متغير ، بنية ، صفيف ، إلخ) في رمز المستخدم ، متبوعًا بموقعها في الذاكرة.

إنشاء ماكرو للنسخ المخفي للبنيات


لنقم بإنشاء ماكرو سنحجز به مكانًا للهيكل في ذاكرة الوصول العشوائي ، ومثالين من ذاكرة فلاش على صفحات منفصلة وفي رمز المستخدم (للقيم الأولية).

#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__;

باستخدام الماكرو ، سيتم إنشاء 4 مثيلات للهيكل بأسماء مختلفة. في رمز المشروع (باستثناء النظام للعمل مع صفحات ذاكرة إعدادات المستخدم) ، يجب أن تستخدم جميع الوحدات هيكلًا بدون بادئة (الاسم المحدد في الماكرو) سيكون في ذاكرة الوصول العشوائي (سيكون من الممكن تغيير الوحدات المسؤولة عن تفاعل المستخدم. يجب أن يكون الباقي فقط اقرأ منه).

مثال على استخدام ماكرو في كود المستخدم:

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

بعد أن يتوفر مثيل بنية name_st من رمز المشروع.

إنشاء وحدات ماكرو للنسخ المخفي للكيانات الأخرى


لإنشاء متغير ، ما عليك سوى إجراء استبدال الماكرو لإنشاء بنى.
بالنسبة للمصفوفات ، أضف عدد العناصر.

#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__;

مبدأ الاستخدام مشابه لاستخدام الهيكل.

وضع اللمسات الأخيرة على البرنامج النصي LD


عند إنشاء نسخ من الكيانات ، تمت الإشارة إلى الأقسام التي تقع فيها. الآن يجب عليك إنشاء هذه الأقسام في البرنامج النصي LD. بالنسبة إلى F4 ، سيبدو نص LD الإضافي المعزز من ST كما يلي:

النصي LD
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) }
}

في هذا المثال ، لا تكمن بنية التكوين في منطقة ذاكرة الوصول العشوائي ، ولكن في CCMRAM ، لتحسين الأداء. وألاحظ أيضًا أن كتل بيانات إعدادات المستخدم هي آخر صفحتين فلاش ، كل منهما 128 كيلو بايت.

التهيئة والتحكم في النسخ


رعاية ما سيقع في ذاكرة الوصول العشوائي تقع على عاتق المبرمج. قبل استخدام البيانات من هذه المنطقة للمرة الأولى ، تحتاج إلى تهيئة المنطقة. في الفلاش ، عند تحميل البرنامج في وحدة التحكم الدقيقة ، سيتم تسجيل البيانات مسبقًا بالقيم الأولية المحددة في رمز البرنامج. سيؤدي ذلك إلى إنشاء نسخ غير صالحة من كتل إعدادات المستخدم في صفحات منفصلة إذا كانت تنتهي بـ CRC32 (نظرًا لأنه لم يتم إجراء حساب CRC32. يمكن لكودك القيام بذلك. حتى الاستخدام الأول لمعلمات التكوين).

لكتابة وحدة للعمل مع كتل التكوين ، ستحتاج إلى استخدام المتغيرات من البرنامج النصي LD. ما يلي مطلوب:

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;

ولا تنس أيضًا أنه يمكن تعيين user_cfg_data_flash_page_size و user_cfg_data_ram_page_size كقيم عادية. ولكن يجب تحديد user_cfg_data_flash_page_1_start والمتغيرات الأخرى التي تخزن العنوان عبر &.

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


All Articles