Hacemos que el proceso de desarrollo de software pesado para microcontroladores sea m√°s conveniente (no)

Ahora nadie puede sorprenderse por los microcontroladores con memoria no volátil (con mayor frecuencia Flash) de 512 kilobytes o más. Su costo está disminuyendo gradualmente, y la accesibilidad, por el contrario, está creciendo. La presencia de tal volumen de memoria no volátil hace posible escribir aplicaciones que son "pesadas" en términos de la memoria ocupada, al tiempo que facilita el mantenimiento posterior del código mediante el uso de soluciones preparadas de varias bibliotecas estándar. Sin embargo, esto lleva a un aumento en el volumen del archivo de firmware del dispositivo de destino, que se requiere cada vez que se vuelve a cargar por completo en la memoria no volátil del microcontrolador al menor cambio en el código.

El propósito del artículo es hablar sobre el método de construcción de un proyecto en C y / o C ++, en el cual, en caso de cambiar la sección de código, que se depura con mayor frecuencia, la mayoría del proyecto no necesitaba ser reescrito. Y también muestre por qué este método no siempre es una solución efectiva.

Requerimientos del lector


En el curso de la narración, asumiré que el lector:

  • Habla con fluidez C y C ++;
  • tiene experiencia trabajando con microcontroladores basados ‚Äč‚Äčen n√ļcleos Cortex-M3 / Cortex-M4 (por ejemplo, la serie stm32f4);
  • sabe c√≥mo construir el archivo de costura final (elf / bin) a partir de las fuentes del proyecto;
  • Imagina para qu√© son los archivos de script vinculadores;
  • tiene una idea del texto, bss, datos y otras secciones;
  • trabaj√≥ con cualquiera de las distribuciones de linux;
  • posee m√≠nimamente bash;
  • tiene experiencia con gcc para la arquitectura de los procesadores Cortex-M3 / Cortex-M4 (toolchain arm-none-eabi);
  • tiene habilidades iniciales con cmake.

La esencia del método.


En un proyecto "cl√°sico" para microcontroladores, todos los datos inmutables (texto, secciones de rodata, valores de datos iniciales y otros) generalmente se ubican "en una fila", comenzando desde la direcci√≥n de inicio de la memoria no vol√°til (en el caso de un microcontrolador basado en el n√ļcleo Cortex-M3 / Cortex-M4 - c Direcci√≥n 0x08000000). En una forma simplificada, el mapa de uso de memoria no vol√°til de un programa de microcontrolador basado en el n√ļcleo Cortex-M3 / Cortex-M4, escrito usando C ++, se parece a esto:



El archivo mem.ld para un proyecto de este tipo suele verse más o menos así:

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

Aqu√≠, toda la memoria no vol√°til es una √ļnica partici√≥n llamada "FLASH", y toda la RAM es una partici√≥n llamada "RAM". De esta forma, cuando una de las secciones del c√≥digo cambia, el resto comienza a "cambiar". Para evitar esto, puede "dividir" el archivo de firmware en algunos bloques l√≥gicos. Por ejemplo, como sigue:

  • tabla de vectores de interrupci√≥n;
  • bibliotecas propias;
  • bibliotecas de terceros (que no se planea cambiar);
  • c√≥digo frecuentemente modificable.

En este caso, al cambiar una sección de código, en el archivo bin final, solo se cambiará la sección en la que el código ha cambiado y la sección que de alguna manera estaba conectada con él (por ejemplo, la tabla de vectores de interrupción si la posición del controlador en algunos de secciones).

B√°sicamente, las bibliotecas est√°ticas se agregan al proyecto.

Despu√©s de recibir el contenedor del archivo del proyecto, se puede dividir en secciones y mostrar cada secci√≥n de forma independiente. Por lo tanto, solo las √°reas cambiadas se coser√°n. Esto tambi√©n conlleva la falta de necesidad de firmware antes de la depuraci√≥n, ya que se supone que el microcontrolador tendr√° inmediatamente el √ļltimo firmware en el microcontrolador y puede comenzar de inmediato la depuraci√≥n.

A continuación, describiré en detalle cómo implementar esto en un proyecto real. Los pros y los contras de tal decisión se darán al final del artículo.

Campo para experimentar


Antes de sugerir cualquier tipo de innovaci√≥n en el trabajo, lo intento en el proyecto de mi casa . Dado que su tama√Īo es cercano al tama√Īo de los proyectos de rutina en el trabajo, es posible entender si la innovaci√≥n es viable o no y qu√© matices conlleva.

Descripción del proyecto


El proyecto contiene:

  • el c√≥digo del proyecto principal en C ++ 14 usando tablas virtuales, nuevo / eliminar (trabajando a trav√©s de un mont√≥n de FreeRTOS), shared_ptr (y otros punteros inteligentes) y otras delicias de las bibliotecas est√°ndar de C ++ 14;
  • FreeRTOS utiliza alrededor de 6 tareas para mantener la infraestructura perif√©rica de hardware y alrededor de 10 en l√≥gica de negocios ( biblioteca de gr√°ficos MakiseGUI , procesamiento de clics, trabajo con fat ( FatFS ), etc.)
  • 16 repositorios con sus propias bibliotecas para interactuar con perif√©ricos de hardware en la placa, ap√©ndices para llamadas al sistema y m√°s;

Con los parámetros de ensamblaje -O0 -g3, el código en una implementación completa con soporte para unicode, cirílico y otras cosas toma alrededor de 700 KB. Sin embargo, en la etapa actual, cuando los periféricos de hardware son estables y solo se necesita depurar la lógica de negocios, la cantidad de código que se debe cambiar es de aproximadamente 20 KB. Por esta razón, a primera vista, parece que el enfoque actual es una solución ideal para el problema (la opción de simulación en una computadora no se considera por alguna razón).

Lista de acciones


Para implementar el método descrito, necesitará:

  • ensamblar todos los subm√≥dulos como bibliotecas est√°ticas (la descripci√≥n de este elemento no est√° incluida en la lista de elementos analizados de este art√≠culo);
  • reescribir mem.ld;
  • rewrite section.ld;
  • agregue una utilidad al proyecto principal para extraer secciones del archivo bin final;
  • agregue al proyecto una llamada al script para actualizar la memoria no vol√°til del microcontrolador al actualizar el archivo de firmware.

Reescribiendo mem.ld


El primer paso es refinar la memoria "estándar" para el concepto actual. Al finalizar, debe tenerse en cuenta que la memoria no volátil se limpia por sectores. Lea más sobre cómo se estructuran los sectores en un microcontrolador particular en la documentación (en el caso de los microcontroladores stm32, en el manual de referencia). Cada sección puede ocupar al menos un sector (puede haber más), de lo contrario, una sección sobrescribirá a la otra.

También debe tenerse en cuenta que si una biblioteca utiliza variables globales, entonces para esta biblioteca debe reservar un lugar en la RAM en la etapa de vinculación. De lo contrario, puede encontrar errores desagradables que serán extremadamente difíciles de atrapar. Por ejemplo, el código de la biblioteca FatFS estará en la sección ROM_EXTERNAL_LIBRARIES, pero requiere 4 bytes en RAM en la etapa de construcción. Por lo tanto, debe asegurarse de que haya una sección en la RAM para los campos que utilizará el código de ROM_EXTERNAL_LIBRARIES. En este ejemplo, es RAM_EXTERNAL_LIBRARIES.

La √ļltima secci√≥n en la memoria no vol√°til merece especial atenci√≥n. Todo lo que no se descompuso en las secciones correspondientes anteriormente, de acuerdo con section.ld (m√°s adelante), entrar√° en √©l.

En el contexto del proyecto actual, mem.ld se verá así.
 /*    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 } 

Reescribe section.ld


Después de que la tarjeta de memoria existente se haya dividido en secciones, se debe describir qué partición se colocará. Para cada biblioteca (si hay una sección correspondiente en la biblioteca), indique dónde se encuentran las secciones .text, .rodata, .data, .bss y otras. La lista de secciones disponibles en la biblioteca se puede ver usando objdump. Por ejemplo, para la biblioteca libstdc ++ _ nano.a, debe especificar dónde colocar el texto, ARM.attributes, rodata, data, bss, secciones COMUNES.

En el contexto del proyecto actual, section.ld se verá así.
 /*             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) } } 

Agregue una utilidad al proyecto principal para extraer secciones del archivo bin final


Desafortunadamente, no fue posible encontrar marcas en objcopy u objdump para extraer c√≥digo entre direcciones espec√≠ficas del archivo elf. Sin embargo, hay un indicador, solo secci√≥n , que no tiene en cuenta el hecho de que, despu√©s de todas las entidades de la secci√≥n enumeradas en section.ld, la informaci√≥n de depuraci√≥n todav√≠a se coloca en la memoria no vol√°til. Sin √©l, el firmware final, ensamblado a partir de piezas, no funcionar√° (por razones obvias). Por lo tanto, tuve que escribir una utilidad simple que toma un archivo bin com√ļn y extrae la secci√≥n requerida en un archivo separado para el rango de direcciones especificado. Sin embargo, el siguiente matiz surge aqu√≠. Por defecto, objcopy llena el espacio entre secciones con 0s. Sin embargo, el espacio vac√≠o en la memoria flash es 0xFF. Para resolver este problema, debe componer el archivo bin de salida con el indicador --gap-fill = 0xff .

Agregue al proyecto una llamada al script para actualizar la memoria no vol√°til del microcontrolador al actualizar el archivo de firmware


Para realizar un seguimiento de los cambios en el proyecto, después de cada reconstrucción del archivo elf, debe llamar a un script de validación que extraiga el archivo bin final del archivo elf, compare la sección deseada, compárelo con el extraído anteriormente y, si hay alguna diferencia, actualice la sección en la memoria del microcontrolador.

Código de script de comparación
 #!/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 


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


Conclusiones


:

  1. 95% , ;

:
  1. , ( stm32programmer-). , , ;
  2. section.ld -. , GUI ;
  3. , , , ( , ) :).

. CLion-, bin .

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


All Articles