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
}


Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org