Можно ли использовать микросхему флэш-памяти SPI с минимальным размером стираемого сектора 4 КБ с Atmega 328p (2 КБ внутренней памяти)?
Проблема, которую я вижу: для того, чтобы данные были записаны на флэш, страница должна быть стерта. Если вы хотите обновить данные внутри сектора, вам сначала нужно прочитать все страницы внутри сектора, сохранить их с чипа, стереть сектор и записать страницы по порядку с вашими изменениями.
Но сектор размером 4 КБ нельзя сохранить в ОЗУ объемом 2 КБ, так какие решения используют другие люди в этой ситуации? Потому что я видел, как в этой установке использовались Arduinos, но не мог определить, каково их решение.
(Arduino — просто пример маленького микроконтроллера в этом вопросе)
Ну и сколько данных вам нужно? Во время стирания вам нужно хранить в ОЗУ ровно столько, сколько необходимо. Если вам действительно нужно хранить данные размером 4 КБ, вам, вероятно, следует выбрать другой тип памяти, такой как классическая EEPROM.
Вы можете просто использовать схему с двойной избыточностью страниц. Данные два страниц размером 4 КБ организованы следующим образом:
Page A Page B
+------------+ +------------+
| Sequence A | | Sequence B |
!~Sequence A | !~Sequence B |
+------------+ +------------+
| | | |
| | | |
| Data A | | Data B |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
+------------+ +------------+
Сначала вы пишете Data A
и устанавливаете Sequence A
в ноль обратное ~Sequence A
. При запуске вы проверяете порядковый номер каждой страницы и устанавливаете страницу с самым высоким допустимым порядковым номером (действительным, когда sequence ^ ~sequnece == 0xff
) как текущая прочитанная страница, а другую — как текущая страница записи.
Когда вы обновляете данные, вы стираете текущая страница записи, записываете область данные, затем устанавливаете порядковый номер равным порядковому номеру прочитанной страницы плюс 1 (по модулю 256) и устанавливаете его инверсию. Затем поменяйте местами текущие страницы чтения/записи. При обновлении вы можете копировать данные с одной страницы на другую, не буферизируя всю страницу, и изменять только ту часть, которую хотите изменить.
В случае сбоя питания или сброса во время записи порядковый номер страницы будет недействителен, поскольку он записывается последним, и запись в последовательности и в обратной последовательности должна быть завершена, поэтому частично записанные данные будут признаны недействительными, а предыдущая действительная страница будет выбираться при запуске.
При выборе текущей активной страницы вам, конечно же, приходится иметь дело с переносом (то есть, когда порядковые номера равны 255 и ноль, ноль новее).
Эта схема подходит для данных, которые будут часто считываться и редко записываться. Характер и частота ваших циклов чтения/записи энергонезависимой памяти будут диктовать подходящий механизм, поскольку для последовательной регистрации или энергонезависимых счетчиков событий подходят разные решения.
Для придирчивости, если во время стирания/программирования произойдет отключение питания или сброс, производитель не дает никаких гарантий содержания данных. Строго говоря, это должно сопровождаться контрольной суммой CRC.
@Lundin: Моя ошибка, делая это из памяти моей собственной реализации много лет назад (все еще используется, но редко пересматривается) - добавлена необходимая проверка порядкового номера (а не использование CRC). В противном случае, как вы говорите, существует небольшое окно риска при записи байта последовательности и большее окно риска при стирании страницы. CRC все еще может быть полезен, но на практике я нашел это решение надежным.
Немного непонятно, какой контент (размер) вы собираетесь писать и как часто.
Вы могли бы подумать о нескольких меньших блоках NV, которые намного меньше размера страницы. Каждый блок NV имеет некоторый заголовок блока, идентифицирующий блок (например, BlockID + Size). Затем вы можете обновить страницу, пока новый блок NV помещается на этой странице. Если нет, напишите это на следующей странице и скопируйте все последние другие блоки на новую страницу. Затем вы можете тем временем стереть и подготовить старую страницу для следующего обмена.
сохранить сектор в другом секторе, а не в ОЗУ (к тому же это безопаснее в случае потери питания), сохраняя только те данные, которые вам нужны