STM32. Работа с Flash-памятью.

Сегодняшняя статья, как вы уже поняли из названия, будет посвящена микроконтроллерам STM32 и работе со встроенной flash-памятью 😉 Да-да, именно с той памятью, в которой хранится прошиваемая нами программа ) Поскольку в STM32 нет EEPROM (энергонезависимой памяти) для хранения данных можно использовать flash-память контроллера, и сегодня мы как раз и разберемся как же это работает.

Сразу же скажу, что согласно документации flash-память в STM32 позволяет осуществить минимум 10000 циклов перезаписи, что в принципе достаточно для того, чтобы использовать ее в качестве энергонезависимой памяти.

Давайте для начала разберемся со структурой памяти. Возьмем в качестве примера контроллер семейства STM32F10x, относящийся к High-Density устройствам (например, STM32F103VET6). Его память выглядит следующим образом:

Flash-память

Как видите, все жестко структурировано ) Information Block содержит 2 раздела:

  • System memory — тут хранится аппаратный bootloader (забегая вперед скажу, что следующие статьи на нашем сайте будут целиком и полностью посвящены именно работе с bootloader’ом ;))
  • Option bytes — а тут хранится информация о защите основной области памяти.

И, собственно, второй блок — Main memory — именно тут хранится записанная нами в микроконтроллер программа. Этот блок в свою очередь разделен на страницы по 2 Кб (в данном случае мы имеем 256 страниц и, соответственно, общий объем памяти составляет целых 512 Кб). Как вы  уже поняли, flash-памяти у STM32 более чем достаточно, почти всегда остается достаточно много свободных от основной прошивки страниц, которые как раз-таки можно использовать для хранения данных после выключения питания контроллера.

Но тут нельзя не упомянуть о некоторых ограничениях при работе с flash. Перед записью определенной страницы она должна быть предварительна стерта («стертому» состоянию памяти соответствуют все биты, установленные в единицу). Соответственно, во время записи нужные биты могут быть «обнулены». Это приводит к ряду неудобств — например, у нас уже сохранено некоторое количество байт в определенной странице flash-памяти. Для перезаписи одного байта нам нужно считать все ранее записанные, стереть страницу, а потом записать все байты обратно, включая измененный байт, который мы хотим сохранить.

Вот так вот в общем чертах это и работает 😉 Кстати, лучше всего для своих целей использовать последние страницы памяти, которые с большой вероятностью будут свободны от основной прошивки, но, конечно же, нужно четко представлять сколько места в памяти занимает основная программа и сколько есть свободного места.

С теорией все понятно, давайте рассмотрим некоторые практические моменты. Я буду, как и обычно, использовать SPL, а значит нам понадобятся файлы stm32f10x_flash.c и stm32f10x_flash.h в нашем проекте. И для того, чтобы работать с flash-памятью нужно сначала ее разблокировать. Для этого в специальный регистр FLASH_KEYR надо записать два числа, одно за другим:

FLASH_KEYR = 0x45670123;
FLASH_KEYR = 0xCDEF89AB;

В SPL для этого реализована функция FLASH_Unlock(). После разблокировки уже можно стирать и записывать данные. Для очистки мы будем использовать функцию:

FLASH_ErasePage(uint32_t Page_Address)

В качестве параметра мы должны передать в функцию адрес стираемой страницы.

Итак, страница стерта, как же записать данные? А для этого у нас есть:

FLASH_ProgramWord(uint32_t Address, uint32_t Data)

С аргументами тут все понятно — передаем адрес ячейки памяти и собственно записываемые данные =)

Осталось понять как же считать данные из flash-памяти. А для этого просто нужно поколдовать с указателями 😉

uint32_t FLASH_Read(uint32_t address)
{
    return (*(__IO uint32_t*)address);
}

Вот и все, ничего сложного )

На этом сегодняшняя небольшая статья подходит к концу, вот в следующий раз мы будем обсуждать Bootloader и там уже будет информации побольше… До скорых встреч!

Понравилась статья? Поделись с друзьями!

STM32. Работа с Flash-памятью.: 30 комментариев
  1. Спасибо! думаю пригодиться! А еще было бы интересно узнать про LOCK биты для защиты программы от чтения, да и вообще в целом про Option bytes

  2. Спасибо за статью.
    Почему я не могу написать несколько раз в флэш-памяти, только после сброса?

    void Write_to_Flash(void)
    {
    flash_unlock();
    flash_erase_page(ADRESS);
    flash_write(ADRESS,333);

    flash_erase_page(ADRESS);
    flash_write(ADRESS,111);
    flash_lock();
    }

    После чтения будет 333 но не 111
    Спасибо.

  3. в моей программы должно регулярно сохранять данные во Flash — 18 коэффициент. После первого запись в память не могу стереть прошлые значения чтобы записать новые значения — только после сброса

  4. Здравствуйте.
    Пытаюсь писать во FLASH мк stm32f103 с библиотекой HAL.
    Пишу двойное слово по адресу — записывается, но по следующему адресу появляются нули.
    Даже если пишу что-то по следующему адресу, там всё равно нули. Не пойму в чём дело…подскажите.
    Спасибо.
    Вот код:
    address = PAGE_32 + 0;
    if(*(__IO uint32_t*) address == 0xffffffff)
    HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, address, 0x77777777);
    address = address + 4;
    if(*(__IO uint32_t*) address == 0xffffffff) HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, address, 0x88888888);
    address = address + 4;
    if(*(__IO uint32_t*) address == 0xffffffff) HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, address, 0x99999999);
    Результат:
    0x08008000: 0x77777777 0x00000000 0x99999999 0x00000000
    0x08008010: 0xffffffff 0xffffffff…….

    • Кажется разобрался — можно удалять…
      Оказывается, WORD это не 2 байта, а 4 и, соответственно,
      DOUBLEWORD это не 4 байта а 8…..
      А вот константы для байта в библиотеке не нашёл….

  5. Вообще, пробовал создать USB Mass Storage, при помощи CubeMX, хотел попробовать при этом использовать внутреннюю память.
    Всё равно не выходит — в функции STORAGE_Write_FS параметры blk_addr и blk_len почему-то нулевые….

    При попытке отформатировать устройство Windows пишет, что не может завершить форматирование.

    • Возможно что-то не совсем так с передачей в драйвер USB данных об объеме памяти/количестве блоков итд.

  6. Пересмотрел, не нашёл ничего…м.б. Вы глянете? Спасибо. Вот код из usbd_storage_if.c:
    #define STORAGE_LUN_NBR 1
    #define STORAGE_BLK_SIZ 0x400
    #define ADDRESS 0x08008000
    #define LENGTH 0x08008010
    int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size)
    {
    *block_size = STORAGE_BLK_SIZ;
    *block_num = 32;
    return (USBD_OK);
    }
    int8_t STORAGE_Write_FS (uint8_t lun,
    uint8_t *buf,
    uint32_t blk_addr,
    uint16_t blk_len)
    {
    HAL_GPIO_WritePin(led_green_GPIO_Port, led_green_Pin, GPIO_PIN_RESET);

    if((*(__IO uint32_t*) (ADDRESS)) == 0xFFFFFFFF) HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, ADDRESS, blk_addr);

    if((*(__IO uint32_t*) (LENGTH)) == 0xFFFFFFFF) HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, LENGTH, blk_len);

    HAL_GPIO_WritePin(led_green_GPIO_Port, led_green_Pin, GPIO_PIN_SET);
    return (USBD_OK);
    }

    • Я честно говоря не понял идею, почему в функцию одну и ту же равнозначными аргументами могут приниматься как адрес, так и длина. При это сами данные, которые нужно записать нигде не фигурируют (buf).

  7. это для отладки — проверки с какими значениями вызвана функция, эти значения записываются в память, если не были записаны ранее, чтобы исключить повторную запись.

  8. Здравствуйте.
    Наверное, это не совсем интересно для всех — м.б. в личку?
    1. Т.к. сразу не получилось реализовать задумку, начал пытаться смотреть значения параметров.
    2. Проверяю чистоту ячейки памяти и записываю значение адреса, то же самое с длинной данных.

    пс. только начинаю знакомство с stm32? ранее «сидел» на avr, так что прошу сильно не пинать.
    ппс. ткните носом… может как чего смотреть типа методики

    Спасибо.

  9. Вот, кое-что проясняется — параметр blk_len принимает значение 0, если установлен размер блока 1024байт, 1 при 512 байтах и 2 при 256байтах… Надо полагать, это размер сектора в блоках…
    Тогда размер блока должен быть не более размера сектора, т.е. 512байт. У меня stm32f103c8, там страницы по 1024байт.
    Кроме этой статьи, ориентировался также на статью http://microtechnics.ru/stm32-i-usb-mass-storage-device/
    Однако, воспользовался сгенерированным проектом с помощью куба. И все функции оказались не такими как в статье. Поэтому вот, разбираю…
    Изначальная идея — попробовать сделать в части памяти маленькую флешку с целью записи конфигурационных данных.

  10. День добрый. Пытаюсь реализовать эмуляцию EEPROM с применением библиотеки от ST. Там, перед тем, как начать производить запись во Flash, используют функцию HAL_FLASH_Unlock(). Пишут, а затем производят запись переменных. Нужно ли после записи вызывать функцию HAL_FLASH_Lock() или можно так оставить и к чему негативному это может привести? Заранее спасибо!

  11. Тогда еще вопрос в догонку. Функции HAL_FLASH_Program_IT() и HAL_FLASHEx_Erase_IT() как использовать? Как их активизировать, где тот кусок кода, в который мы попадаем, когда вызываем соответствующее прерывание? И есть ли колбеки, так как они нужны, а в библиотеке их не видно

    • В Cube во вкладке Configuration можно зайти в NVIC — там включается прерывание Flash — Flash global interrupt. А в файле stm32fxxx_hal_flash.c есть callback — HAL_FLASH_EndOfOperationCallback().

  12. У меня процессоры F103 и F407, и ни в одном такой строчки во вкладке NVIC нету. Что странно, вот пример:
    http://henryomd.blogspot.ru/2016/06/stm32l476-based-usb-audio-device.html
    В нем есть включение прерывания для Flash. Нашел данный проц в кубе. Там опять же нет такой настройки. Чего сделать, чтобы она там появилась? В чем подвох? Заранее спасибо!

  13. Тогда другой вопрос. А где может пригодиться прерывание RCC и зачем оно надо?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *