الآن لا يمكن لأحد أن يفاجأ بواسطة ميكروكنترولر مع ذاكرة غير متقلبة (في معظم الأحيان فلاش) من 512 كيلو بايت أو أكثر. تكلفتها تتناقص تدريجيا ، وإمكانية الوصول ، على العكس من ذلك ، آخذ في الازدياد. إن وجود مثل هذا الحجم من الذاكرة غير المتطايرة يجعل من الممكن كتابة تطبيقات "ثقيلة" من حيث الذاكرة المشغولة ، مع تسهيل الصيانة اللاحقة للرمز من خلال استخدام حلول جاهزة من مختلف المكتبات القياسية. ومع ذلك ، يؤدي هذا إلى زيادة في حجم ملف البرنامج الثابت للجهاز الهدف ، وهو مطلوب في كل مرة لإعادة تحميلها بالكامل في الذاكرة غير المتطايرة من متحكم في أدنى تغيير في التعليمات البرمجية.
الغرض من هذه المقالة هو التحدث عن طريقة إنشاء مشروع في C و / أو C ++ ، وفي حالة تغيير قسم التعليمات البرمجية ، والذي يتم تصحيحه في أغلب الأحيان ، لا يحتاج معظم المشروع إلى إعادة كتابته. ونوضح أيضًا لماذا هذه الطريقة ليست دائمًا حلاً فعالًا.
متطلبات القارئ
في سياق السرد ، سأفترض أن القارئ:
- وهو يجيد C و C ++ ؛
- لديه خبرة في العمل مع ميكروكنترولر على أساس كورتيكس- M3 / كورتيكس- M4 (على سبيل المثال ، سلسلة stm32f4) ؛
- يعرف كيفية إنشاء ملف الخياطة النهائي (قزم / بن) من مصادر المشروع ؛
- تخيل ملفات البرنامج النصي linker
- لديه فكرة عن النص و bss والبيانات والأقسام الأخرى ؛
- عملت مع أي من توزيعات لينكس ؛
- يملك باش الحد الأدنى ؛
- لديه خبرة مع مجلس التعاون الخليجي لهندسة معمارية Cortex-M3 / Cortex-M4 معالجات (toolchain arm-none-eabi) ؛
- لديه مهارات أولية مع cmake.
جوهر الطريقة
في مشروع "كلاسيكي" للميكروكنترولر ، عادةً ما تكون جميع البيانات غير القابلة للتغيير (نص ، أقسام روداتا ، قيم البيانات الأولية وغيرها) موجودة "في صف واحد" ، بدءًا من عنوان البدء للذاكرة غير المتطايرة (في حالة متحكم دقيق يعتمد على كورتيكس- إم 3 / كورتيكس- إم 4 الأساسية العنوان 0x08000000). بشكل مبسط ، تبدو خريطة استخدام الذاكرة غير المتقلبة لبرنامج متحكم يعتمد على كورتيكس- M3 / كورتيكس- إم 4 ، المكتوبة باستخدام C ++ ، مثل هذا:

غالبًا ما يبدو ملف mem.ld لمثل هذا المشروع شيئًا مثل هذا:
MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 768K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K }
هنا ، جميع الذاكرة غير المتطايرة هي قسم واحد يسمى "FLASH" ، وكل RAM هي قسم يسمى "RAM". في هذا النموذج ، عندما يتغير أحد أقسام الكود ، يبدأ كل الباقي في "التحول". لتجنب ذلك ، يمكنك "تقسيم" ملف البرنامج الثابت إلى بعض الكتل المنطقية. على سبيل المثال ، كما يلي:
- جدول متجهات المقاطعة ؛
- المكتبات الخاصة
- مكتبات الجهات الخارجية (التي لم يتم التخطيط لتغييرها) ؛
- رمز قابل للتعديل في كثير من الأحيان.
في هذه الحالة ، عند تغيير قسم من التعليمات البرمجية ، في ملف bin النهائي ، سيتم فقط تغيير القسم الذي تم فيه تغيير الرمز والجزء الذي كان مرتبطًا به بطريقة ما (على سبيل المثال ، جدول متجهات المقاطعة إذا كان موضع المعالج في بعض من المقاطع).
وبشكل أساسي ، تتم إضافة
المكتبات الثابتة إلى المشروع.
بعد استلام صندوق ملف المشروع ، يمكن تقسيمه إلى أقسام وفلاش كل قسم بشكل مستقل. وبالتالي ، سيتم مخيط المناطق المتغيرة فقط. يحمل هذا أيضًا نقص الحاجة إلى البرامج الثابتة قبل تصحيح الأخطاء ، حيث يُفترض أن المتحكم الدقيق سيحتوي على الفور على أحدث البرامج الثابتة في متحكم ويمكنك البدء في تصحيح الأخطاء على الفور.
بعد ذلك ، سوف أصف بالتفصيل كيفية تنفيذ ذلك على مشروع حقيقي. سيتم إعطاء إيجابيات وسلبيات مثل هذا القرار في نهاية المقال.
مجال للتجربة
قبل أن أقترح أي نوع من الابتكار في العمل ، أجرب ذلك على
مشروع منزلي . نظرًا لأن حجمه قريب من حجم المشاريع الروتينية في العمل ، فمن الممكن فهم ما إذا كان الابتكار قابلاً للتطبيق أم لا وما هي الفروق الدقيقة التي يحملها.
وصف المشروع
يحتوي المشروع على:
- رمز المشروع الرئيسي في C ++ 14 باستخدام جداول افتراضية وجديدة / حذف (العمل من خلال مجموعة من FreeRTOS) و share_ptr (وغيرها من المؤشرات الذكية) وغيرها من المكتبات القياسية لـ C ++ 14 ؛
- يستخدم FreeRTOS حوالي 6 مهام للمحافظة على البنية الأساسية للأجهزة الطرفية وحوالي 10 مهام في منطق الأعمال ( مكتبة الرسومات MakiseGUI ، ومعالجة النقرات ، والعمل باستخدام الدهون ( FatFS ) ، وما إلى ذلك) ؛
- 16 مستودعًا مع مكتباتهم الخاصة للتفاعل مع الأجهزة الطرفية للأجهزة على السبورة ، دعامات لمكالمات النظام ، وأكثر من ذلك ؛
مع معلمات التجميع -O0 -g3 ، تأخذ التعليمات البرمجية في تطبيق كامل مع دعم يونيكود ، السيريلية وأشياء أخرى حوالي 700 كيلو بايت. ومع ذلك ، في المرحلة الحالية ، عندما تكون الأجهزة الطرفية للأجهزة مستقرة ، ويتعين تصحيح منطق الأعمال فقط ، فإن مقدار الكود المطلوب تغييره يبلغ 20 كيلو بايت تقريبًا. لهذا السبب ، للوهلة الأولى ، يبدو أن النهج الحالي هو الحل الأمثل للمشكلة (لا يعتبر خيار المحاكاة على جهاز كمبيوتر لسبب ما).
قائمة العمل
لتنفيذ الطريقة الموضحة ، ستحتاج إلى:
- تجميع جميع الوحدات الفرعية كمكتبات ثابتة (لم يتم تضمين وصف هذا العنصر في قائمة العناصر التي تم تحليلها في هذه المقالة) ؛
- أعد كتابة mem.ld؛
- أعد كتابة قسم.
- إضافة أداة مساعدة للمشروع الرئيسي لاستخراج مقاطع من ملف bin النهائي ؛
- إضافة إلى المشروع مكالمة إلى البرنامج النصي لتحديث الذاكرة غير متقلبة من متحكم عند تحديث ملف البرنامج الثابت.
إعادة كتابة mem.ld
الخطوة الأولى هي صقل mem.ld "القياسي" للمفهوم الحالي. عند الانتهاء ، يجب أن تؤخذ في الاعتبار أنه يتم تنظيف الذاكرة غير المتطايرة بواسطة القطاعات. اقرأ المزيد حول كيفية هيكلة القطاعات في متحكم معين يجب قراءته في الوثائق (في حالة المتحكم الدقيق stm32 - في الدليل المرجعي). يمكن أن يشغل كل قسم قطاعًا واحدًا على الأقل (يمكن أن يكون أكثر من ذلك) ، وإلا فسيتم استبدال قسم آخر بقطاع آخر.
يجب أن يؤخذ في الاعتبار أنه إذا كانت المكتبة تستخدم متغيرات عمومية ، فعندئذ تحتاج لهذه المكتبة إلى حجز مكان في ذاكرة الوصول العشوائي في مرحلة الربط. خلاف ذلك ، قد تواجه أخطاء غير سارة سيكون من الصعب للغاية اللحاق بها. على سبيل المثال ، سيكون رمز مكتبة FatFS في قسم ROM_EXTERNAL_LIBRARIES ، لكنه يتطلب 4 بايت في ذاكرة الوصول العشوائي في مرحلة الإنشاء. لذلك تحتاج إلى التأكد من وجود قسم في ذاكرة الوصول العشوائي للحقول التي سيستخدمها الرمز من ROM_EXTERNAL_LIBRARIES. في هذا المثال ، يكون RAM_EXTERNAL_LIBRARIES.
الجزء الأخير في الذاكرة غير المتقلبة يستحق عناية خاصة. كل ما لم يتم تحليله في الأقسام المقابلة سابقًا ، وفقًا للفرع .ld (حوله لاحقًا) ، سوف يدخل فيه.
في سياق المشروع الحالي ، سيبدو mem.ld بهذا الشكل. /* 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 }
أعد كتابة قسم
بعد تقسيم بطاقة الذاكرة الحالية إلى أقسام ، ينبغي وصف القسم الذي سيتم وضعه. لكل مكتبة (إذا كان هناك قسم مطابق في المكتبة) ، يرجى الإشارة إلى مكان وجود أقسام .text و .rodata و .data و .bss والأقسام الأخرى. يمكن عرض قائمة المقاطع المتوفرة في المكتبة باستخدام objdump. على سبيل المثال ، بالنسبة لمكتبة libstdc ++ _ nano.a ، يلزمك تحديد مكان وضع النص ، وخصائص ARM ، و rodata ، والبيانات ، و bss ، و COMMON.
في سياق المشروع الحالي ، سيبدو القسم .ld بهذا الشكل. /* 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) } }
إضافة أداة مساعدة للمشروع الرئيسي لاستخراج مقاطع من ملف bin النهائي
لسوء الحظ ، لم يكن من الممكن العثور على إشارات في objcopy أو objdump لاستخراج الشفرة بين عناوين محددة من ملف elf. هناك علامة
- فقط - قسم ، ومع ذلك ، فإنه لا يأخذ بعين الاعتبار حقيقة أنه بعد كل كيانات القسم المذكورة في المقطع .ld ، لا تزال معلومات تصحيح الأخطاء موجودة في ذاكرة غير متقلبة. وبدون ذلك ، لن تعمل البرامج الثابتة النهائية ، المجمعة من قطع ، (لأسباب واضحة). لذلك ، اضطررت إلى كتابة
أداة مساعدة بسيطة تأخذ ملف صندوق شائع وتستخرج القسم المطلوب في ملف منفصل لنطاق العنوان المحدد. ومع ذلك ، فإن الفروق الدقيقة التالية تنشأ هنا. بشكل افتراضي ، يملأ objcopy المسافة بين المقاطع بـ 0 ثانية. ومع ذلك ، فإن المساحة الفارغة في ذاكرة الفلاش هي 0xFF. لحل هذه المشكلة ، تحتاج إلى إنشاء ملف حاوية الإخراج مع العلم
- gap-fill = 0xff .
إضافة إلى المشروع مكالمة إلى البرنامج النصي لتحديث الذاكرة غير متقلبة من متحكم عند تحديث ملف البرنامج الثابت
لتتبع التغييرات في المشروع ، بعد كل عملية إعادة إنشاء لملف قزم ، ستحتاج إلى استدعاء برنامج نصي للتحقق الذي سيستخرج ملف الحاوية النهائية من ملف قزم ، ومقارنة القسم المرغوب فيه منه ، ومقارنته مع الملف الذي تم استخراجه مسبقًا ، وإذا حدث أي اختلافات ، قم بتحديث القسم الموجود في ذاكرة وحدة التحكم الدقيقة.
مقارنة رمز البرنامج النصي في المشروع نفسه ، يمكنك استدعاء وظيفة cmake ، والتي سوف تفعل كل ما تحتاجه:Cmake وظيفة التحديث 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)
تستخدم الدالة stm32programmer للكتابة.مثال على استخدام دالة من كود المشروع 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 ()
الاستنتاجات
مزايا هذا النهج:- في 95 ٪ من الحالات ، يتم تحديث ما هو مطلوب حقا ؛
سلبيات هذا النهج:- لا توجد مكاسب في السرعة ، لأنه قبل كل برنامج ثابت ، يلزم تحميل أداة تحميل التشغيل في وحدة التحكم الدقيقة من أجل وميض الذاكرة غير المتطايرة (يتم ذلك تلقائيًا بواسطة برنامج stm32programmer). على العكس من ذلك ، عندما يتم إعادة تجميع المشروع بالكامل ، فغالبًا ما يتعين عليك خياطة جميع الأقسام مرة أخرى ؛
- حجم section.ld لا يشجع أي رغبة في إضافة أو تغيير أي شيء فيه. إذا كنت بحاجة إلى تطبيق هذه المنهجية في مشروع حقيقي ، فسوف يتعين عليك كتابة واجهة مستخدم رسومية مناسبة لتحرير هذا الملف ؛
- إذا كان الجهاز يتحكم في قوته ، فقد لا تلاحظ أن أحد الأقسام لم يكن سلكيًا بشكل صحيح (مع انخفاض الجهد ، على سبيل المثال) وأقسام تصحيح الأخطاء من مجموعات مختلفة لفترة طويلة :).
يمكنك مشاهدة إصدار العمل الخاص بالطريقة الحالية في هذا الالتزام . يمكن تجميع المشروع في CLion ، وقد قام سابقًا بتجميع أداة مساعدة لاستخراج القسم من ملف bin.