Kondisi awal
Ada perangkat berbasis mikrokontroler (misalnya, stm32f405rgt6 akan diambil). Ketika dihidupkan, ia menyesuaikan periferalnya berdasarkan preferensi pengguna atau pengaturan default. Pengguna dapat mengubah pengaturan selama operasi perangkat (sebagai aturan, hanya selama integrasi ke dalam kompleks) melalui salah satu antarmuka yang mungkin (menu CLI atau utilitas untuk pengaturan parameter operasi yang bekerja melalui protokol biner). Setelah mengatur parameter, pengguna menyimpan pengaturan dengan perintah khusus (juga melalui salah satu antarmuka yang mungkin).Tugas
- Untuk mengurangi waktu yang diperlukan untuk mengakses variabel pengaturan perangkat selama operasi, Anda perlu menjaga nilai saat ini dalam RAM (biasanya jumlah data tersebut bervariasi dari selusin variabel hingga 3 kilobyte tergantung pada perangkat).
- Hal ini diperlukan untuk menyimpan pengaturan pengguna dalam rangkap dalam flash mikrokontroler.
- Setiap instance pengaturan pengguna harus diakhiri dengan CRC32 segera setelah payload.
- Untuk setiap instance pengaturan pengguna, halaman terpisah digunakan dalam memori flash (bahkan jika data berguna adalah 2 kb, dan halaman dibagi dengan 128 kb, maka seluruh halaman diberikan di bawah satu blok)
- Kode perangkat lunak mikrokontroler harus menyimpan pengaturan default, yang seharusnya menjadi blok pengaturan pengguna jika keduanya memiliki data yang rusak
Upaya untuk dipecahkan
GCC (pada saat penulisan) tidak memiliki bendera untuk mendapatkan salinan bagian tersebut. Menambahkan skrip LD dengan << garis ajaib >> juga gagal. Seseorang dapat bekerja dengan keberatan, tetapi pendekatan ini sangat implisit dan mengarah pada kesalahan halus.Keputusan
Solusinya adalah membuat salinan implisit dari entitas yang diinginkan (variabel, struktur, array, dll.) Dalam kode pengguna, diikuti dengan lokasinya di memori.Membuat makro untuk penyalinan tersembunyi struktur
Mari kita buat makro dengan mana kita akan memesan tempat untuk struktur dalam RAM, 2 contoh flash-memory pada halaman terpisah dan dalam kode pengguna (untuk nilai awal).#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__;
Menggunakan makro, 4 instance dari struktur dengan nama yang berbeda akan dibuat. Dalam kode proyek (dengan pengecualian sistem untuk bekerja dengan halaman memori pengaturan pengguna), semua modul harus menggunakan struktur tanpa awalan (nama yang ditentukan dalam makro) yang akan di RAM (akan mungkin untuk mengubah modul yang bertanggung jawab untuk interaksi pengguna. Sisanya hanya boleh baca dari itu).Contoh menggunakan makro dalam kode pengguna: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
})
Setelah contoh struktur name_st tersedia dari kode proyek.Membuat makro untuk penyalinan tersembunyi dari entitas lain
Untuk membuat variabel, Anda hanya perlu melakukan substitusi makro untuk membuat struktur.Untuk array, tambahkan jumlah elemen.#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__;
Prinsip penggunaannya mirip dengan penggunaan struktur.Finalisasi skrip LD
Saat membuat salinan entitas, ditunjukkan di bagian mana mereka berada. Sekarang Anda harus membuat bagian ini dalam skrip LD. Untuk F4, skrip LD yang diperbesar dari ST akan terlihat seperti ini:Skrip 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) }
}
Dalam contoh ini, struktur konfigurasi tidak terletak di area RAM, tetapi di CCMRAM, untuk meningkatkan kinerja. Saya juga mencatat bahwa blok data pengaturan pengguna adalah 2 halaman flash terakhir, yang masing-masing adalah 128 kb.Inisialisasi dan kontrol salinan
Perawatan apa yang akan terletak pada RAM terletak pada programmer. Sebelum menggunakan data dari area ini untuk pertama kalinya, Anda perlu menginisialisasi area. Dalam sekejap, saat memuat program ke dalam mikrokontroler, data akan direkam sebelumnya dengan nilai awal yang ditentukan dalam kode program. Ini akan membuat salinan yang tidak valid dari blok pengaturan pengguna di halaman terpisah jika mereka berakhir dengan CRC32 (karena tidak ada perhitungan CRC32 dilakukan. Kode Anda dapat melakukan ini. Juga sampai penggunaan pertama parameter konfigurasi).Untuk menulis modul agar dapat bekerja dengan blok konfigurasi, Anda harus menggunakan variabel dari skrip LD. Berikut ini akan diperlukan: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;
Juga, jangan lupa bahwa user_cfg_data_flash_page_size dan user_cfg_data_ram_page_size dapat ditetapkan sebagai nilai normal. Tetapi user_cfg_data_flash_page_1_start dan variabel lain yang menyimpan alamat harus ditentukan melalui &.