eddy_em: (Default)
eddy_em ([personal profile] eddy_em) wrote2019-07-11 02:28 pm

Оптимальная эмуляция EEPROM во флеш-памяти STM32

Работая над более свежей версией хронометра для разнообразных соревнований (пока что все еще на STM32, с ESP32 я немного поковырялся, испугался и забил до поры до времени), опять столкнулся с необходимостью сохранения настроек. Сначала взял свой старый сниппет, немножко подкорректировал (сниппет разрабатывался для STM32F0x2), но потом мне стало как-то неуютно от того, что из более чем 110кБ свободной флеш-памяти я использую лишь 1-2кБ. После поиска решений на разных форумах пришел к такому результату.

Проще всего оказалось определить переменную, пользуясь 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
}