STM32 Flash как EEPROM
Библиотека FlashPROM для использования Flash памяти как EEPROM.
Перед тем как приступать к изучению, настоятельно рекомендую прочесть эту статью.
Большинство микроконтроллеров stm32 не имеют EEPROM-памяти что конечно же вызывает трудности с долговременным хранением каких-либо данных. Для этих целей можно использовать флеш, благо её в избытке, однако проблема в том, что что у флеша довольно таки ограниченное количество циклов перезаписи, производитель гарантирует 10000. В связи с этим, нужно записывать сохраняемые данные каждый раз в новое место. Производитель предлагает свой механизм реализации этой задачи в документе EEPROM emulation AN2594, однако всегда хочется придумать что-то своё.
Представленная ниже библиотека не моя идея, в сети много подобного. Суть заключается в следующем: выбирается участок флеша не используемый программой, то есть несколько страниц в самом конце, и очищается — заполняется единицами (0хFF). Далее по ходу программы производится запись сохраняемых данных в самое начало этого участка, а перед каждой последующей записью производится поиск места где закончилась запись предыдущих данных, и новые данные записываются с этого места. Так продолжается пока не закончится вся память. Когда память заканчивается, весь участок очищается и всё повторяется заново. Таким образом количество циклов перезаписи увеличивается в десятки тысяч раз.
Далее пояснения делать лучше наглядно, поэтому скачайте пример. Если у вас BluePill, тогда ничего делать не нужно, если же плата другая, тогда надо создать проект с включённым USART'ом и активированным блоком CRC…
Подключить в проект файлы
/* USER CODE BEGIN Includes */
#include "FlashPROM.h"
/* USER CODE END Includes */
И объявить глобальную переменную…
/* USER CODE BEGIN PV */
uint32_t res_addr = 0;
/* USER CODE END PV */
Теперь идём в файл
#define STARTADDR ((uint32_t)0x0801F800) — тут надо указать адрес начала страницы, с которой будет начинаться область памяти используемая как EEPROM. В примере указана страница №126, то есть будут использоваться 126 и 127 страница. Для примера я использую всего две страницы, но в реальном проекте можно использовать все свободные странички. Адрес смотрите в мануале на свой камень.
#define ENDMEMORY ((uint32_t)0x0801FC00 + 1024) — адрес последней страницы плюс её размер, то есть это адрес последней ячейки флеша. Когда поиск свободного пространства доберётся до этого адреса, вся память будет очищена и запись пойдёт по новому кругу. Размер страницы см. в мануале.
#define PAGES 2 — количество страниц для очистки. Этот параметр нужен в функции
#define BUFFSIZE 5 — сохраняемые данные складываются в массив, и он записывается во флеш. В данном случае размер массива состоит из пяти элементов, четыре элемента используются для полезных данных, а в последний нужно записать ноль. То есть размер массива нужно указывать исходя из количества полезных данных, плюс ячейка для нуля (см. ниже).
Буфер может быть либо из 16-ти битных, либо из 32-ух битных элементов. В принципе можно сделать массив из 8-ми битных элементов, но не все микроконтроллеры умеют записывать байт во флеш. Для записи 64-ёх битных значений эта библиотека не подходит. Точнее сделать то конечно можно, но придётся мудрить с CRC-суммой. Там 32-ух битный регистр.
#define DATAWIDTH 2 — размер элемента буфера. Если 16 бит, то пишем 2, если 32 бита, то пишем 4.
#define WIDTHWRITE FLASH_TYPEPROGRAM_HALFWORD — это аргумент прописываемый в функции
#define DEBUG 1 — 1 -включает вывод инфы в уарт, 0 — отключает.
typedef uint16_t myBuf_t; — если элемент буфера 16-ти битный, тогда оставляем как есть, если 32-ух битный, то меняем
Теперь возвращаемся в
Очищаем память один раз и комментируем эту функцию…
//erase_flash();
Далее запускаем функцию поиска места с которого будет происходить запись новых данных…
res_addr = flash_search_adress(STARTADDR, BUFFSIZE * DATAWIDTH);
При старте микроконтроллера поиск будет сделан с самого начала выделенной памяти, об этом говорит аргумент
Далее объявляем буфер из пяти 16-ти битных значений, четыре полезных, и один с нулём…
myBuf_t wdata[BUFFSIZE] = {0x1111, 0x2222, 0x3333, 0x4444, 0x0000};
Разумеется буфер может быть любых размеров, главное чтоб был завершающий ноль.
Теперь самое основное, как работает поиск: когда мы запускаем функцию…
res_addr = flash_search_adress(STARTADDR, BUFFSIZE * DATAWIDTH);
… она начиная с адреса указанного в первом аргументе ищет незаписанные ячейки
Вне зависимости сделаны ли уже какие-то записи ранее, или память только что очищена, функция начинает перебирать ячейки и искать место с которого будут идти подряд 10 ячеек со значением 0хFF.
Визуально это выглядит так…
Ищем десять ячеек после последней записи.
Программно это происходит так…
Если 10 ячеек найдено, тогда возвращается адрес с которого нужно записывать очередные данные. Если память закончилась
Последний элемент буфера должен быть равен нулю потому, что если его не будет, и в последнем элементе полезных данных будет записано значение 0хFF, тогда функция поиска воспримет это как незаписанную ячейку.
В дальнейшем, по ходу программы, поиск будет происходить уже не с начала памяти, а с адреса который был возвращён при последнем поиске. То есть поиск с начала памяти ведётся только при старте МК. Может возникнуть вопрос — «зачем делать последующие поиски, ведь мы и так уже знаем где закончилась предыдущая запись» — да, это так, но нам в любом случае нужно проверить не закончилась ли память.
В бесконечном цикле создаём демонстрационный код, который раз в 10 секунд делает новую запись, и раз в 30 секунд читает последнюю запись…
/* USER CODE BEGIN WHILE */
while (1)
{
if((HAL_GetTick() - timme) > 10000) // интервал 10сек
{
wdata[0] = wdata[0] + 1; // просто для разнообразия
wdata[1] = wdata[1] + 1;
wdata[2] = wdata[2] + 1;
wdata[3] = wdata[3] + 1;
write_to_flash(wdata); // запись данных во флеш
timme = HAL_GetTick();
count++;
}
if(count > 2)
{
count = 0;
myBuf_t rdata[BUFFSIZE] = {0,}; // буфер для чтения (не обязательно его создавать, можно использовать буфер для записи)
read_last_data_in_flash(rdata); // чтение данных из флеша
char str[64] = {0,};
snprintf(str, 64, "Read data: 0x%x 0x%x 0x%x 0x%x 0x%x\n", rdata[0], rdata[1], rdata[2], rdata[3], rdata[4]);
HAL_UART_Transmit(&huart1, (uint8_t*)str, strlen(str), 100);
}
В функцию
С помощью функции
Если нужно прочитать сохранённые данные при старте программы, тогда добавляем функцию чтения сразу после поиска адреса…
res_addr = flash_search_adress(STARTADDR, BUFFSIZE * DATAWIDTH);
myBuf_t rdata[BUFFSIZE] = {0,};
read_last_data_in_flash(rdata);
Это всё, всем спасибо
Библиотека
Телеграм-чат istarik
Телеграм-чат STM32
- 0
- stD
41453
Поддержать автора
Комментарии (0)