![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Работая над более свежей версией хронометра для разнообразных соревнований (пока что все еще на STM32, с ESP32 я немного поковырялся, испугался и забил до поры до времени), опять столкнулся с необходимостью сохранения настроек. Сначала взял свой старый сниппет, немножко подкорректировал (сниппет разрабатывался для STM32F0x2), но потом мне стало как-то неуютно от того, что из более чем 110кБ свободной флеш-памяти я использую лишь 1-2кБ. После поиска решений на разных форумах пришел к такому результату.
Проще всего оказалось определить переменную, пользуясь gcc'шным параметром __attribute__:
А для того, чтобы эта секция находилась точно после всех остальных данных во флеш-памяти, нужно запихнуть после секции .data в линкер-скрипте вот это:
Кроме этого улучшения я добавил еще бинарный поиск (потому как искать линейно во флеш-памяти среди десятка тысяч записей — процесс очень долгий, а бинарный поиск позволяет за десяток итераций найти последнюю запись):
Проще всего оказалось определить переменную, пользуясь gcc'шным параметром __attribute__:
__attribute__((section(".myvars"))) static const flash_storage Flash_Storage = {
.all_stored = USERCONF_INITIALIZER
};
А для того, чтобы эта секция находилась точно после всех остальных данных во флеш-памяти, нужно запихнуть после секции .data в линкер-скрипте вот это:
.myvars :
{
. = ALIGN(1024);
KEEP(*(.myvars))
} > rom
Кроме этого улучшения я добавил еще бинарный поиск (потому как искать линейно во флеш-памяти среди десятка тысяч записей — процесс очень долгий, а бинарный поиск позволяет за десяток итераций найти последнюю запись):
static int binarySearch(int l, int r){
while(r >= l){
int mid = l + (r - l) / 2;
uint16_t sz = Flash_Data[mid].userconf_sz;
if(sz == sizeof(user_conf)){
if(Flash_Data[mid+1].userconf_sz == 0xffff){
return mid;
}else{ // element is to the right
l = mid + 1;
}
}else{ // element is to the left
r = mid - 1;
}
}
return -1; // not found
}
static int get_gooddata(){
static uint8_t firstrun = 1;
if(firstrun){
firstrun = 0;
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
uint32_t flsz = FLASH_SIZE * 1024; // size in bytes
flsz -= (uint32_t)Flash_Data - FLASH_BASE;
uint32_t usz = (sizeof(user_conf) + 1) / 2;
maxnum = flsz / 2 / usz;
}
}
return binarySearch(0, maxnum-2); // -1 if there's no data at all & flash is clear; maxnum-1 if flash is full
}