Где хранить настройки?
Грубо говоря, всю память можно разделить на два вида: энергозависимую и энергонезависимую. Содержимое ячеек в первой зависит от наличия питания, а во второй нет. Оперативная память и регистры процессора и периферии являются энергозависимыми, т.е. при перезагрузке микроконтроллера или сбое питания все данные будут утеряны. Другие данные, например прошивка, должны быть в памяти в любом случае; такие данные хранятся в постоянном запоминающем устройстве (ПЗУ). При отсутствии питания оно сохраняет содержимое ячеек сколь угодно долго.
На самом деле нет. Например, во флеш-памяти есть такое понятие, как удержание информации (англ. retention). Время хранения информации зависит от разных факторов, например, от количества циклов перезаписи и температуры. В презентации «STM32L4 — Flash» компания гарантирует удержание информации до 30 лет после 10000 циклов перезаписи при температуре 55 градусов, 15 лет после 10000 циклов и температуре 85 градусов. // Презентация STM32L4 Memory Flash
Иногда возникает необходимость сохранять состояние устройства или какие-то внутрненние параметры (переменные). Представьте, что при каждом запуске автомобиля вам приходилось бы заново искать и настраивать приемник на любимую радиостанцию. Питать магнитолу с незаведенным двигателем не самая лучшая идея — аккумулятор можно разрядить. В таких случаях нужно использовать ПЗУ. В старых микроконтроллерах довольно часто наряду с памятью под программу можно встретить модуль EEPROM. Однако в современных МК это скорее исключение, чем правило. Следовательно, нужно искать другие пути решения задачи.
Решений может быть несколько: а) использовать внешнюю микросхему памяти; б) использовать память программы; в) использовать backup-регистры. У каждого способа есть свои плюсы и минусы. Отдельная микросхема имеет (потенциально) большой объем памяти, но удорожает устройство: микросхема стоит денег, плюс придется писать драйвер для работы с ней. Использовать память программы не всегда возможно, а иногда и опасно: ее может не хватить на основную программу. Backup-регистры имеет очень маленький объем, при этом требуют источник резервного питания (подключенный к специальной ножке, обычно именуемой Vbat).
Рассматривать подробно внешние микросхемы мы не будем, так как интерфейсы и технология самой памяти может быть разной. Описание работы с backup-регистрами можно найти в документации к микроконтроллеру, да и принцип работы с ними не сильно отличается от работы с обычными регистрами. Остановимся на работе с flash-памятью, но сначала разберемся, какие виды памяти есть.
ПЗУ могут базироваться на разных физических принципах и обладать разными характеристиками. Так, например, существует тип памяти ROM (англ. Read Only Memory) — запись производится на заводе, и в дальнейшем у пользователя нет возможности внести изменения. В память PROM (англ. Programmable ROM) можно записать только один раз, а вот в EPROM (англ. Erasable PROM) стирание производится под воздействием ультрафиолета. Дальнейшем развитием EPROM стала EEPROM (англ. Electrically EPROM) — в ней память стирается электрически, при этом особенности ее структуры не позволяют делать модули памяти большого объема. Отношение размера кристалла и объема данных, который можно в него записать, называют плотностью записи.
Данный тип памяти сейчас не используется, но был популярен раньше. На корпусе микросхем предусматривалось окошко с кварцевым стеклом, предназначенное для стирания содержимого.
Вершиной эволюции памяти на сегодня является технология flash. Как и в EEPROM, в данном типе используются транзисторы, только в этом случае они подключаются группами. В зависимости от способа соединения flash-память можно разбить на NOR и NAND (есть и другие, но они не прижились). NOR использует классическую двумерную матрицу проводников, а NAND — трехмерный массив. Мы не будем подробно останавливаться на внутреннем устройстве данного типа памяти. Необходимо лишь уяснить структуру и принцип действия.
21 января 2004 года, на 18 сол с момента посадки, марсоход Спирит (англ. Spirit) резко прекратил сеанс связи с Землёй. На следующий день ровер на низкой скорости передал сигнал о том, что находится в аварийном режиме. Были запрошены данные о техническом состоянии, после анализа которых инженеры пришли к выводу, что ровер попал в «перезагрузочную петлю», что может привести к полному разряду батареи и потере аппарата. Впоследствии оказалось, что неполадка была связана с программным модулем управления файлами — их оказалось слишком много. После удаления части данных с flash-памяти на 33 сол Спирит был приведён в рабочее состояние. // https://en.wikipedia.org/wiki/Spirit_(rover)
Все ячейки группируются в страницы (англ. page), те в свою очередь в блоки (англ. block), а они в модули. Поскольку исток каждого транзистора одного блока подключен к общей подложке, то стирать можно только сразу целую страницу. Например, в NOR-памяти при стирании все ячейки устанавливаются в состояние высокого логического уровня. Процесс записи предусматривает только перевод ячейки из высокого уровня в низкий. Обратная операция невозможна для одной ячейки — установить высокий уровень можно только в процессе стирания, т.е. стирая всю страницу. Стоит помнить об этом ограничении.
Несмотря на такие ограничения, flash-память стала довольно популярна ввиду относительной простоты изготовления и высокой плотности записи. NOR обычно применяется в устройствах хранения программного кода, например, в микроконтроллере stm32. В такой архитектуре хорошо организован произвольный доступ к памяти, а вот процесс записи и стирания данных происходит довольно медленно. В NAND-архитектуре транзисторы размещаются компактнее, что позволяет лучше масштабировать, а значит, и увеличивать объем. Кроме того, процесс записи происходит быстрее, но скорость произвольного доступа падает.
Очевидно, записывать настройки на ту же страницу, где и прошивка, нельзя. При попытке перезаписи настроек придется стереть всю страницу, затерев часть прошивки. Это непременно приведет к исключительной ситуации, и устройство перестанет работать. Следовательно, необходимо использовать свободную страницу. Размер страницы указывается в документации на микроконтроллер, обычно это 1 Кб. Если прошивка занимает 10 Кб и 27 байт, то будет занято 11 страниц, и под настройки можно использовать только 12-ю. Вычислим, для примера, ее адрес:
// 0x0800 0000 + 12 * 0x0000 0400 = 0x0800 3000
#define BASE_ADDRESS 0x08000000
#define PAGE_SIZE 0x0400
#define SETTINGS_PAGE 12
#define SETTINGS_PAGE_ADDRESS BASE_ADDRESS + PAGE_SIZE * SETTINGS_PAGE
Всеми операциями, будь то чтение, запись или блокировка, управляет специальный контроллер Flash Program/Erase Controller (FPEC). Процесс работы с ним детально описан в документе PM0063.
Ниже приведен фрагмент кода, использующий стандартную библиотеку периферии stm32, который записывает содержимое переменной data во flash-память.
u32 data = 0x0B0B0B0B;
// ...
FLASH_Unlock();
FLASH_ErasePage(SETTINGS_PAGE_ADDRESS);
FLASH_ProgramWord(SETTINGS_PAGE_ADDRESS, data);
FLASH_Lock();
По умолчанию доступ к flash-памяти заблокирован, приложение может считывать, но не записывать в нее. Поэтому при совершении любой операции с памятью ее необходимо сначала разблокировать, а по завершении работы (в целях безопасности) заблокировать.