Kami membuat proses pengembangan perangkat lunak berat untuk mikrokontroler menjadi lebih mudah (tidak)

Sekarang tidak ada yang bisa dikejutkan oleh mikrokontroler dengan memori non-volatile (paling sering Flash) dari 512 kilobytes atau lebih. Biaya mereka secara bertahap menurun, dan aksesibilitas, sebaliknya, tumbuh. Kehadiran volume memori non-volatile sedemikian memungkinkan untuk menulis aplikasi yang "berat" dalam hal memori yang ditempati, sementara memfasilitasi pemeliharaan kode selanjutnya melalui penggunaan solusi yang sudah jadi dari berbagai perpustakaan standar. Namun, ini mengarah pada peningkatan volume file firmware dari perangkat target, yang setiap kali harus diisi ulang sepenuhnya ke dalam memori non-volatile dari mikrokontroler dengan sedikit perubahan dalam kode.

Tujuan artikel ini adalah untuk berbicara tentang metode membangun proyek dalam C dan / atau C ++, di mana, dalam hal mengubah bagian kode, yang paling sering ditukar, sebagian besar proyek tidak perlu ditulis ulang. Dan juga menunjukkan mengapa metode ini tidak selalu merupakan solusi yang efektif.

Persyaratan Pembaca


Dalam perjalanan narasi, saya akan berasumsi bahwa pembaca:

  • Ia fasih berbahasa C dan C ++;
  • memiliki pengalaman bekerja dengan mikrokontroler berdasarkan inti Cortex-M3 / Cortex-M4 (misalnya, seri stm32f4);
  • tahu cara membuat file jahit terakhir (elf / bin) dari sumber proyek;
  • Bayangkan untuk apa file skrip linker;
  • memiliki gagasan tentang teks, bss, data, dan bagian lain;
  • bekerja dengan distribusi linux apa pun;
  • minimal memiliki bash;
  • memiliki pengalaman dengan gcc untuk arsitektur prosesor Cortex-M3 / Cortex-M4 (toolchain arm-none-eabi);
  • memiliki keterampilan awal dengan cmake.

Esensi dari metode ini


Dalam proyek "klasik" untuk mikrokontroler, semua data yang tidak dapat diubah (teks, bagian rodata, nilai data awal, dan lain-lain) biasanya ditempatkan "berturut-turut", dimulai dari alamat awal memori non-volatile (dalam kasus mikrokontroler berdasarkan inti Cortex-M3 / Cortex-M4 - c Alamat 0x08000000). Dalam bentuk yang disederhanakan, peta penggunaan memori yang tidak mudah menguap dari program mikrokontroler berbasis pada inti Cortex-M3 / Cortex-M4, ditulis menggunakan C ++, terlihat seperti ini:



File mem.ld untuk proyek semacam itu paling sering terlihat seperti ini:

MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 768K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K } 

Di sini, semua memori non-volatile adalah satu partisi tunggal bernama "FLASH", dan semua RAM adalah partisi bernama "RAM". Dalam formulir ini, ketika salah satu bagian kode berubah, semua yang lain mulai "bergeser". Untuk menghindari ini, Anda dapat "membagi" file firmware menjadi beberapa blok logis. Misalnya, sebagai berikut:

  • tabel vektor interupsi;
  • perpustakaan sendiri;
  • perpustakaan pihak ketiga (yang tidak direncanakan untuk diubah);
  • kode yang sering dimodifikasi.

Dalam hal ini, ketika mengubah bagian kode, dalam file nampan terakhir, hanya bagian di mana kode telah berubah dan bagian yang terhubung dengan itu akan diubah (misalnya, tabel vektor interupsi jika posisi pawang di beberapa dari bagian).

Pada dasarnya, perpustakaan statis ditambahkan ke proyek.

Setelah menerima nampan dari file proyek, itu dapat dibagi menjadi beberapa bagian dan mem-flash setiap bagian secara independen. Dengan demikian, hanya area yang diubah yang akan dijahit. Ini juga membawa kurangnya kebutuhan untuk firmware sebelum debugging, karena diasumsikan bahwa mikrokontroler akan segera memiliki firmware terbaru dalam mikrokontroler dan Anda dapat segera mulai debugging.

Selanjutnya, saya akan menjelaskan secara terperinci bagaimana menerapkan ini pada proyek nyata. Pro dan kontra dari keputusan semacam itu akan diberikan pada akhir artikel.

Bidang untuk eksperimen


Sebelum saya menyarankan segala jenis inovasi di tempat kerja, saya mencoba ini di proyek rumah saya. Karena ukurannya dekat dengan ukuran proyek rutin di tempat kerja, dimungkinkan untuk memahami apakah inovasi itu layak atau tidak dan nuansa apa yang dibawanya.

Deskripsi Proyek


Proyek ini berisi:

  • kode proyek utama dalam C ++ 14 menggunakan tabel virtual, baru / hapus (bekerja melalui sekelompok FreeRTOS), shared_ptr (dan pointer pintar lainnya) dan kelezatan lain dari perpustakaan standar C ++ 14;
  • FreeRTOS menggunakan sekitar 6 tugas untuk memelihara infrastruktur perangkat keras dan sekitar 10 pada logika bisnis ( perpustakaan grafis MakiseGUI , penekanan tombol, bekerja dengan lemak ( FatFS ), dll.);
  • 16 repositori dengan perpustakaan mereka sendiri untuk berinteraksi dengan periferal perangkat keras di papan tulis, bertopik untuk panggilan sistem, dan banyak lagi;

Dengan parameter assembly -O0 -g3, kode dalam implementasi penuh dengan dukungan untuk unicode, Cyrillic, dan hal-hal lain membutuhkan sekitar 700 KB. Namun, pada tahap saat ini, ketika periferal perangkat keras stabil, dan hanya logika bisnis yang perlu di-debug, jumlah kode yang akan diubah sekitar 20 KB. Untuk alasan ini, pada pandangan pertama, tampaknya pendekatan saat ini adalah solusi ideal untuk masalah (opsi dengan simulasi pada komputer tidak dipertimbangkan untuk beberapa alasan).

Daftar tindakan


Untuk menerapkan metode yang dijelaskan, Anda perlu:

  • kumpulkan semua submodul sebagai pustaka statis (deskripsi item ini tidak termasuk dalam daftar item parsing artikel ini);
  • menulis ulang mem.ld;
  • menulis ulang section.ld;
  • tambahkan utilitas ke proyek utama untuk mengekstrak bagian dari file bin terakhir;
  • menambah proyek panggilan ke skrip untuk memperbarui memori non-volatile dari mikrokontroler ketika memperbarui file firmware.

Menulis ulang mem.ld


Langkah pertama adalah memperbaiki mem.ld "standar" untuk konsep saat ini. Ketika menyelesaikan, harus diingat bahwa memori non-volatile dibersihkan oleh sektor. Baca lebih lanjut tentang bagaimana sektor disusun dalam mikrokontroler tertentu harus dibaca dalam dokumentasi (dalam kasus mikrokontroler stm32 - dalam manual referensi). Setiap bagian dapat menempati setidaknya satu sektor (lebih banyak dapat), jika tidak satu bagian akan menimpa yang lain.

Juga harus diingat bahwa jika perpustakaan menggunakan variabel global, maka untuk perpustakaan ini Anda perlu memesan tempat di RAM pada tahap penautan. Jika tidak, Anda mungkin menemukan bug yang tidak menyenangkan yang akan sangat sulit ditangkap. Misalnya, kode pustaka FatFS akan berada di bagian ROM_EXTERNAL_LIBRARY, tetapi membutuhkan 4 byte dalam RAM pada tahap pembuatan. Jadi, Anda perlu memastikan bahwa ada bagian dalam RAM untuk bidang yang akan digunakan kode dari ROM_EXTERNAL_LIBRARY. Dalam contoh ini, ini adalah RAM_EXTERNAL_LIBRARY.

Bagian terakhir dalam memori non-volatile patut mendapat perhatian khusus. Segala sesuatu yang tidak diuraikan menjadi bagian yang sesuai sebelumnya, menurut section.ld (tentang itu nanti), akan masuk ke dalamnya.

Dalam konteks proyek saat ini, mem.ld akan terlihat seperti ini.
 /*    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 } 

Tulis ulang section.ld


Setelah kartu memori yang ada dibagi menjadi beberapa bagian, harus dijelaskan partisi apa yang akan diletakkan. Untuk setiap perpustakaan (jika ada bagian yang sesuai di perpustakaan), tunjukkan di mana .text, .rodata, .data, .bss, dan bagian lainnya berada. Daftar bagian yang tersedia di perpustakaan dapat dilihat menggunakan objdump. Misalnya, untuk pustaka libstdc ++ _ nano.a, Anda perlu menentukan di mana harus menempatkan teks, ARM. Atribut, rodata, data, bss, bagian UMUM.

Dalam konteks proyek saat ini, section.ld akan terlihat seperti ini.
 /*             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) } } 

Tambahkan utilitas ke proyek utama untuk mengekstrak bagian dari file bin terakhir


Sayangnya, itu tidak mungkin untuk menemukan flag di objcopy atau objdump untuk mengekstraksi kode antara alamat spesifik dari file elf. Bendera --hanya-bagian ada , namun, itu tidak memperhitungkan fakta bahwa setelah semua entitas entitas yang terdaftar di section.ld, informasi debug masih ditempatkan dalam memori non-volatile. Tanpanya, firmware final, yang dikumpulkan dari potongan-potongan, tidak akan berfungsi (untuk alasan yang jelas). Oleh karena itu, saya harus menulis utilitas sederhana yang mengambil file bin umum dan mengekstrak bagian yang diperlukan ke file terpisah untuk rentang alamat yang ditentukan. Namun, nuansa berikut muncul di sini. Secara default, objcopy mengisi ruang antara bagian dengan 0s. Namun, ruang kosong dalam memori flash adalah 0xFF. Untuk mengatasi masalah ini, Anda perlu membuat file nampan keluaran dengan flag --gap-fill = 0xff .

Tambahkan ke proyek panggilan ke skrip untuk memperbarui memori non-volatile dari mikrokontroler ketika memperbarui file firmware


Untuk melacak perubahan dalam proyek, setelah setiap kali membangun kembali file elf, Anda perlu memanggil skrip verifikasi yang akan mengekstrak file bin terakhir dari file elf, membandingkan bagian yang diinginkan darinya, membandingkannya dengan yang sebelumnya diekstrak, dan jika ada perbedaan, perbarui bagian dalam memori mikrokontroler.

Kode Skrip Perbandingan
 #!/bin/bash # # $1 -  .   ,    . # $2 -     elf . # $3 -      STM32. # echo "Old file name: $1" echo "New file name: $2" # ,     . flag_rewrite=0 #    ,         #  ,      (   #     ). #    ,   ,   -    #  ,     .  ,   . if [ -e $1 ] then #         . echo "Both files exist." #  md5   ,     . buf=$(md5sum $1 --binary) md5_old=${buf:0:32} #      md5   . #   32 . buf=$(md5sum $2 --binary) md5_new=${buf:0:32} echo "Started file comparison." if [ $md5_old == $md5_new ] then #     ,  . echo "The file has not been updated." echo "The new file will be deleted." rm $2 echo "Removed." else #   ,    . echo "The file has been modified." echo "Old will be replaced by new." mv $2 $1 echo "Replaced." flag_rewrite=1 #    . fi else #    . echo "Old file does not exist." echo "New will be renamed to old." mv $2 $1 #    . flag_rewrite=1 #    . echo "Renamed." fi #       ,     . if [ $flag_rewrite -eq 1 ] then echo "Started flashing." echo "CMD params: $3" $3 fi 


Dalam proyek itu sendiri, Anda dapat memanggil fungsi cmake, yang akan melakukan semua yang diperlukan:
Buat fungsi pembaruan
 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) 

Fungsi ini menggunakan program stm32 untuk menulis.
Contoh menggunakan fungsi dari kode proyek
 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 () 


Kesimpulan


Keuntungan dari pendekatan ini:

  1. dalam 95% kasus, yang benar-benar dibutuhkan diperbarui;

Kekurangan dari pendekatan ini:
  1. Tidak ada peningkatan kecepatan, karena sebelum setiap firmware diperlukan untuk memuat bootloader ke dalam mikrokontroler untuk mem-flash memori yang tidak mudah menguap (ini dilakukan secara otomatis oleh program stm32). Sebaliknya, ketika proyek benar-benar dipasang kembali, Anda sering harus menjahit semua bagian lagi;
  2. ukuran section.ld mencegah keinginan untuk menambah atau mengubah apa pun di dalamnya. Jika Anda perlu menerapkan metodologi ini dalam proyek nyata, Anda harus menulis GUI yang nyaman untuk mengedit file ini;
  3. jika perangkat mengontrol daya sendiri, maka Anda mungkin tidak melihat bahwa salah satu partisi tidak terprogram dengan benar (dengan penurunan tegangan, misalnya) dan men-debug partisi dari majelis yang berbeda untuk waktu yang lama :).

Anda dapat melihat versi yang berfungsi dari metode saat ini di komit ini . Proyek dapat dirakit di CLion, setelah sebelumnya mengkompilasi utilitas untuk mengekstraksi bagian dari file bin.

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


All Articles