Принципы чтения и записи FLASH памяти в STM32F0 мало чем отличаются от подобных принципов для других семейств. Описываемый далее метод так же неплохо работает и в STM32F1 (проверено на STM32F103C6T6A). Библиотека HAL позволит унифицировать знания, то есть пользоваться функцией стирания/записи можно аналогично, независимо от модели микроконтроллера.
Первое, что стоит хорошо понять:
- память FLASH всегда начинается с адреса 0x08000000
- память FLASH всегда разбита на страницы и стирать данные мы можем только страницами (это 1 килобайт или 2 килобайта, читайте datasheet), а не по одному байту или слову.
- нельзя записать новые данные поверх старых данных. Перед записью новых данных необходимо сделать стирание нужной страницы.
- адрес страницы это не 1,2,3,4,5.. это абсолютный адрес в адресном пространстве, пример 0x08000000, 0x08000400, 0x08000800 и т.д. (внимание, адреса в примере актуальны для страниц размером 1 кБ).
- Исполняемая программа записывается в первую страницу, начиная с адреса 0x08000000 и занимает ровно столько страниц, сколько ей требуется, остальные страницы не записываются при прошивке (и не стираются тоже). Конфигурацию записывайте в неиспользуемые страницы. Например, в STM32F030F4P6 всего 16 страниц памяти по 1 кБ и если размер прошивки меньше 15 кБ, то смело пишите настройки в последний (16-ый) сектор.
- Не записывайте данные на флешку слишком часто, у нее ограниченное количество циклов стирания/записи (как правило, 10000 циклов). Флешкой нельзя пользоваться так же как EEPROM.
Чтение флешки
Это очень простая процедура - мы просто обращаемся к адресному пространству микроконтроллера и извлекаем оттуда значения. Можно обернуть это извлечение в функцию:
uint32_t FlashRead(uint32_t address) {
return (*(__IO uint32_t*)address);
}
Функция позволяет читать по одному слову (4 байта) за раз.
Запись флешки
Перед тем, как записать данные на флешку, необходимо стереть старые данные. А стереть придется целую страницу памяти, так как стереть меньший участок памяти невозможно.
Допустим, вы определились с номером страницы для хранения настроек. Например, у меня, в STM32F030F4P6 это 16-ая страница, начинающаяся по адресу 0x08003C00. Именно этот адрес я буду указывать в функции стирания. Для удобства можно определить константу, в которой хранится этот адрес
#define SETTINGS_ADDRESS 0x08003C00
В дальнейшем, если вы захотите использовать совсем другие страницы для хранения информации (например, при переходе на другой камень), то достаточно будет изменить лишь эту константу, не меняя остального кода.
void WriteConfig() {
HAL_FLASH_Unlock(); // Открыть доступ к FLASH (она закрыта от случайной записи)
// В структуре settings хранятся настройки, преобразую ее в 16-битный массив для удобства доступа
uint16_t* data = (uint16_t*) &settings;
FLASH_EraseInitTypeDef ef; // Объявляю структуру, необходимую для функции стирания страницы
HAL_StatusTypeDef stat;
ef.TypeErase = FLASH_TYPEERASE_PAGES; // Стирать постранично
ef.PageAddress = SETTINGS_ADDRESS; // Адрес страницы для стирания
ef.NbPages = 1; //Число страниц = 1
uint32_t temp; // Временная переменная для результата стирания (не использую)
HAL_FLASHEx_Erase(&ef, &temp); // Вызов функции стирания
// Будьте уверены, что размер структуры настроек кратен 2 байтам
for (int i = 0; i < sizeof(settings); i += 2) { // Запись всех настроек
stat = HAL_FLASH_Program (FLASH_TYPEPROGRAM_HALFWORD, SETTINGS_ADDRESS + i, *(data++));
if (stat != HAL_OK) break; // Если что-то пошло не так - выскочить из цикла
}
HAL_FLASH_Lock(); // Закрыть флешку от случайной записи
}
А далее, пример использования функции для чтения конфигурации
// Пример чтения только 4 байт настроек. Для бОльшего объема данных используйте цикл
void ReadConfig() {
// Структуру настроек превращаю в указатель на массив 8-ми битных значений
uint8_t* setData = (uint8_t*)&settings;
uint32_t tempData = FlashRead(SETTINGS_ADDRESS); // Прочесть слово из флешки
if (tempData != 0xffffffff) { // Если флешка не пустая
setData[0] = (uint8_t)((tempData & 0xff000000) >> 24); // Извлечь первый байт из слова
setData[1] = (uint8_t)((tempData & 0x00ff0000) >> 16); // Извлечь второй байт из слова
setData[2] = (uint8_t)((tempData & 0x0000ff00) >> 8); // Излечь третий байт из слова
setData[3] = tempData & 0xff; // Извлечь четвертый байт из слова
}
}
- S. При разработке ПО я столкнулся с одним неприятным моментом, флешка стиралась со статусом ffffffff - это значит, что все отлично; запись данных тоже имела статус HAL_OK - успешно. Однако область на FLASH по факту не записывалась совсем, оставалась стертой (заполнена 0xFF). Помогла случайность: я стер всю память микроконтроллера в ST-Link Utility и залил прошивку заново. С радостью заметил, что все заработало. Хотя застряли мы с этой проблемой почти на 2 рабочих дня.