Condiciones iniciales
Hay un dispositivo basado en un microcontrolador (por ejemplo, se tomar谩 stm32f405rgt6). Cuando se enciende, ajusta sus perif茅ricos seg煤n las preferencias del usuario o la configuraci贸n predeterminada. El usuario puede cambiar la configuraci贸n durante la operaci贸n del dispositivo (por regla general, solo durante la integraci贸n en el complejo) a trav茅s de una de las interfaces posibles (men煤 CLI o utilidad para configurar los par谩metros de operaci贸n que funcionan a trav茅s del protocolo binario). Despu茅s de configurar los par谩metros, el usuario guarda la configuraci贸n con un comando especial (tambi茅n a trav茅s de una de las posibles interfaces).Tarea
- Para reducir el tiempo que lleva acceder a las variables de configuraci贸n del dispositivo durante la operaci贸n, debe mantener los valores actuales en la RAM (por lo general, la cantidad de dichos datos var铆a de una docena de variables a 3 kilobytes, seg煤n el dispositivo).
- Es necesario almacenar la configuraci贸n del usuario por duplicado en el flash del microcontrolador.
- Cada instancia de configuraci贸n del usuario debe terminar con CRC32 inmediatamente despu茅s de la carga 煤til.
- Para cada instancia de configuraci贸n del usuario, se utiliza una p谩gina separada en la memoria flash (incluso si los datos 煤tiles son 2 kb, y las p谩ginas se dividen por 128 kb, entonces toda la p谩gina se da en un bloque)
- El c贸digo del software del microcontrolador debe almacenar la configuraci贸n predeterminada, que debe ser el bloque de configuraci贸n del usuario si ambos tienen datos corruptos.
Intentos de resolver
GCC (al momento de escribir este art铆culo) no tiene una bandera para obtener una copia de la secci贸n. Complementar el script LD con << l铆neas m谩gicas >> tambi茅n falla. Se podr铆a trabajar con objcopy, pero este enfoque es muy impl铆cito y conduce a errores sutiles.Decisi贸n
La soluci贸n es crear copias impl铆citas de la entidad deseada (variable, estructura, matriz, etc.) en el c贸digo del usuario, seguido de su ubicaci贸n en la memoria.Crear una macro para la copia oculta de estructuras
Creemos una macro con la que reservaremos un lugar para la estructura en RAM, 2 instancias de memoria flash en p谩ginas separadas y en el c贸digo de usuario (para valores iniciales).#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__;
Usando una macro, se crear谩n 4 instancias de la estructura con diferentes nombres. En el c贸digo del proyecto (con la excepci贸n del sistema para trabajar con p谩ginas de memoria de configuraci贸n del usuario), todos los m贸dulos deben usar una estructura sin prefijo (el nombre especificado en la macro) que estar谩 en la RAM (ser谩 posible cambiar los m贸dulos que son responsables de la interacci贸n del usuario. El resto solo debe leer de ella).Un ejemplo de uso de una macro en el c贸digo de usuario: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
})
Despu茅s de la instancia de la estructura name_st est谩 disponible desde el c贸digo del proyecto.Crear macros para la copia oculta de otras entidades.
Para crear una variable, solo necesita hacer la macro sustituci贸n para crear estructuras.Para matrices, agregue el n煤mero de elementos.#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__;
El principio de uso es similar al uso de la estructura.Finalizaci贸n del script LD
Al crear copias de entidades, se indic贸 en qu茅 secciones se encuentran. Ahora debe crear estas secciones en el script LD. Para F4, el script LD aumentado de ST se ver谩 as铆:Secuencia de comandos LDMEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K - 256K
USER_CFG_PAGE_1 (rx) : ORIGIN = 0x080C0000, LENGTH = 128K - 4
USER_CFG_PAGE_2 (rx) : ORIGIN = 0x080E0000, LENGTH = 128K - 4
CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
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;
__stack = ORIGIN(RAM) + LENGTH(RAM);
_estack = __stack;
__Main_Stack_Size = 1024 ;
PROVIDE ( _Main_Stack_Size = __Main_Stack_Size ) ;
__Main_Stack_Limit = __stack - __Main_Stack_Size ;
PROVIDE ( _Main_Stack_Limit = __Main_Stack_Limit ) ;
_Minimum_Stack_Size = 512 ;
PROVIDE ( _Heap_Begin = _end_noinit ) ;
PROVIDE ( _Heap_Limit = __stack - __Main_Stack_Size ) ;
SECTIONS
{
.isr_vector :
{
KEEP(*(.isr_vector))
KEEP(*(.cfmconfig))
*(.after_vectors .after_vectors.*)
. = ALIGN(4);
} >FLASH
.inits :
{
. = ALIGN(4);
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array_sysinit .preinit_array_sysinit.*))
KEEP(*(.preinit_array_platform .preinit_array_platform.*))
KEEP(*(.preinit_array .preinit_array.*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);
} >FLASH
.flashtext :
{
. = ALIGN(4);
*(.flashtext .flashtext.*)
. = ALIGN(4);
} >FLASH
.text :
{
. = ALIGN(4);
*(.text .text.*)
*(.rodata .rodata.*)
*(vtable)
KEEP(*(.eh_frame*))
*(.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.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
. = ALIGN(4);
_etext = .;
__etext = .;
_sidata = _etext;
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = . ;
__data_start__ = . ;
*(.data_begin .data_begin.*)
*(.data .data.*)
*(.data_end .data_end.*)
*(.ramfunc*)
. = ALIGN(4);
_edata = . ;
__data_end__ = . ;
} >RAM
.bss (NOLOAD) :
{
. = ALIGN(4);
__bss_start__ = .;
_sbss = .;
*(.bss_begin .bss_begin.*)
*(.bss .bss.*)
*(COMMON)
*(.bss_end .bss_end.*)
. = ALIGN(4);
__bss_end__ = .;
_ebss = . ;
} >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
PROVIDE ( end = _end_noinit );
PROVIDE ( _end = _end_noinit );
PROVIDE ( __end = _end_noinit );
PROVIDE ( __end__ = _end_noinit );
._check_stack :
{
. = ALIGN(4);
. = . + _Minimum_Stack_Size ;
. = ALIGN(4);
} >RAM
.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) }
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
.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) }
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
}
En este ejemplo, la estructura de configuraci贸n no se encuentra en el 谩rea de RAM, sino en CCMRAM, para mejorar el rendimiento. Tambi茅n noto que los bloques de datos de configuraci贸n del usuario son las 煤ltimas 2 p谩ginas flash, cada una de las cuales tiene 128 kb.Inicializaci贸n y control de copias.
El cuidado de lo que se encuentra en la RAM recae en el programador. Antes de utilizar datos de esta 谩rea por primera vez, debe inicializar el 谩rea. En flash, al cargar el programa en el microcontrolador, los datos se grabar谩n previamente con los valores iniciales especificados en el c贸digo del programa. Esto har谩 copias no v谩lidas de los bloques de configuraci贸n del usuario en p谩ginas separadas si terminan en CRC32 (ya que no se realiza ning煤n c谩lculo de CRC32. Su c贸digo puede hacerlo. Tambi茅n hasta el primer uso de los par谩metros de configuraci贸n).Para escribir un m贸dulo para trabajar con bloques de configuraci贸n, necesitar谩 usar las variables del script LD. Se requerir谩 lo siguiente: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;
Adem谩s, no olvide que user_cfg_data_flash_page_size y user_cfg_data_ram_page_size se pueden asignar como valores normales. Pero user_cfg_data_flash_page_1_start y otras variables que almacenan la direcci贸n deben especificarse mediante &.