Продолжаем знакомство с микросхемой Flash памяти M25P40VP, установленной на беспроводном контроллере uniSensors nRF24, и в этой статье мы разберём работу функций библиотеки SPIFlash.
Sleep и Wakeup
Начнём с функций sleep() и wakeup(), которые, соответственно, переводят M25P40VP в режим «сна» с малым энергопотреблением и выводят из него.
Здесь всё просто и не вызывает каких-либо проблем в понимании и реализации. Код скетча M25P40 Sleep & Wakeup:
/* M25P40 Sleep & Wakeup */ #include <SPI.h> #include <SPIFlash.h> #define FLASH_SS 8 SPIFlash flash(FLASH_SS); void setup() { Serial.begin(115200); Serial.println(F("Start M25P40 Sleep & Wakeup...")); Serial.print(F("Init ")); if (flash.initialize()) {Serial.println(F("OK"));} else {Serial.println(F("FAIL"));} Serial.print(F("Sleep... ")); flash.sleep(); Serial.println(F("Ok")); Serial.print(F("Wakeup... ")); flash.wakeup(); Serial.println(F("Ok")); } void loop() { }
Вначале мы подключаем необходимые библиотеки.
#include <SPI.h> #include <SPIFlash.h>
Затем указываем на какой пин подсоединён вывод CS (на контроллерах uniSensors nRF24 это пин D8).
#define FLASH_SS 8
И создаём объект flash.
SPIFlash flash(FLASH_SS);
Далее выводим сообщение о старте скетча.
Serial.begin(115200); Serial.println(F("Start M25P40 Sleep & Wakeup..."));
И производим инициализацию работы с M25P40VP.
Serial.print(F("Init ")); if (flash.initialize()) {Serial.println(F("OK"));} else {Serial.println(F("FAIL"));}
Уводим в сон.
Serial.print(F("Sleep... ")); flash.sleep(); Serial.println(F("Ok"));
И «будим» микросхему Flash памяти.
Serial.print(F("Wakeup... ")); flash.wakeup(); Serial.println(F("Ok"));
Вывод в Serial нашего скетча:
Erase и Busy
Далее нам нужно разобраться и хорошо уяснить для себя принципы записи информации в чип M25P40VP. Здесь уже не всё так просто, как в предыдущем примере, и есть много неочевидных моментов.
Например, работая с чипом Flash памяти M25P40VP, мы не можем просто записать нужное нам значение в выбранную ячейку памяти (как мы привыкли это делать с EEPROM памятью микроконтроллеров) — здесь сначала требуется провести операцию стирания (приведения к исходному состоянию 0xFF) нужной нам ячейки.
Дело осложняется тем, что чип M25P40VP, согласно даташиту, поддерживает только 2 варианта стирания — либо всю память целиком, либо посекторное стирание по 64К (65536 байт). Напомню, что наш чип содержит 8 секторов (0-7) по 64К.
Вначале подобная информация просто обескураживает — получается, что для того, чтобы изменить один байт, нам нужно сначала стереть, как минимум, полный сектор в 65536 байтов. Но такова архитектура чипа M25P40VP и мы с этим ничего поделать не можем, нам остаётся только приспосабливаться к использованию того, что есть.
Есть один момент: стирание приводит к заполнению всех ячеек памяти значениями 0xFF, то есть все биты устанавливаются к 1. Дальнейшая работа с ячейками памяти предполагает изменение этих битов (при записи чисел) от 1 к 0. Без операции стирания невозможно изменить состояние битов в ячейках от 0 к 1. Поэтому, если логика вашей программы предполагает только уменьшение значений (изменение от 1 к 0), то вы можете не производить стирание перед записью новых данных (но это очень специфический и редкий случай).
Есть ещё один неочевидный момент: операция стирания занимает довольно значительное время, как мы увидим далее, это примерно 3400 мс для полного стирания и 600 мс для посекторного, что просто колоссально много по меркам микроконтроллеров. Это накладывает дополнительные ограничения на разработчика — вам нужно учитывать эти нюансы. Но есть и хорошая новость — хоть операция стирания занимает значительное время, но она не блокирует работу контроллера на время своего выполнения — вы можете «поставить в очередь» стирание и учесть этот момент в работе вашей системы.
Теперь рассмотрим скетч M25P40 Erase & Busy, который делает две вещи: полностью стирает чип и измеряет время задержки операции стирания.
/* M25P40 Erase & Busy */ #include <SPI.h> #include <SPIFlash.h> #define FLASH_SS 8 SPIFlash flash(FLASH_SS); void setup() { Serial.begin(115200); Serial.println(F("M25P40 Erase & Busy start...")); Serial.print(F("Init ")); if (flash.initialize()) {Serial.println(F("OK"));} else {Serial.println(F("FAIL"));} erase(); } void checkBusy() { uint32_t stt = millis(); while (flash.busy()) { } uint32_t del = millis() - stt; if (del) { Serial.print(F(" (")); Serial.print(del); Serial.print(F(" ms)")); } Serial.println(); } void erase() { Serial.print(F("Full erase...")); flash.chipErase(); checkBusy(); } void loop() { }
Во время инициализации скетча, в функции setup(), вызывается функция erase(),
void erase() { Serial.print(F("Full erase...")); flash.chipErase(); checkBusy(); }
которая выводит соответствующую надпись и запускает процесс полного стирания чипа.
flash.chipErase();
Затем вызывается функция checkBusy(), которая подсчитывает время задержки операции полного стирания чипа M25P40VP.
void checkBusy() { uint32_t stt = millis(); while (flash.busy()) { } uint32_t del = millis() - stt; if (del) { Serial.print(F(" (")); Serial.print(del); Serial.print(F(" ms)")); } Serial.println(); }
Здесь используется библиотечная функция busy(), которая является флагом, сигнализирующим о занятости или доступности чипа M25P40VP.
while (flash.busy()) { }
Далее просто выводятся данные о задержке выполнения операции стирания.
uint32_t del = millis() - stt; if (del) { Serial.print(F(" (")); Serial.print(del); Serial.print(F(" ms)")); } Serial.println();
Вот результат работы нашего скетча:
Задержка выполнения операции стирания составляет более 3-х секунд.
Посекторное стирание
Теперь рассмотрим посекторное стирание. Библиотека поддерживает стирание блоков (секторов) по 64К, которых в нашем чипе 8 (от 0 до 7).
Сектор 0 — 00000h (0)
Сектор 1 — 10000h (65536)
Сектор 2 — 20000h (131072)
Сектор 3 — 30000h (196608)
Сектор 4 — 40000h (262144)
Сектор 5 — 50000h (327680)
Сектор 6 — 60000h (393216)
Сектор 7 — 70000h (458752)
Это значительно лучший и более применимый во многих случаях вариант, чем полное стирание всего чипа. Код скетча M25P40 Sector erase:
/* M25P40 Sector erase */ #include <SPI.h> #include <SPIFlash.h> #define FLASH_SS 8 #define SECTOR 0 #define SECTOR_SIZE 65536 SPIFlash flash(FLASH_SS); void setup() { Serial.begin(115200); Serial.println(F("M25P40 Sector erase start...")); Serial.print(F("Init ")); if (flash.initialize()) {Serial.println(F("OK"));} else {Serial.println(F("FAIL"));} erase64K(SECTOR); // 0-7 } void checkBusy() { uint32_t stt = millis(); while (flash.busy()) { } uint32_t del = millis() - stt; if (del) { Serial.print(F(" (")); Serial.print(del); Serial.print(F(" ms)")); } Serial.println(); } void erase() { Serial.print(F("Full erase...")); flash.chipErase(); checkBusy(); } uint32_t sectorAdr(byte i) { return SECTOR_SIZE * i; } void erase64K(byte sec) { uint32_t adr = sectorAdr(sec); Serial.print(F("Sector ")); Serial.print(sec); Serial.print(F(" (")); Serial.print(adr); Serial.print(F(" + 64K) erase...")); flash.blockErase64K(adr); checkBusy(); } void loop() { }
Этот скетч похож на предыдущий, в нём присутствуют только специфические настройки для работы с секторами и библиотечной функцией blockErase64K(). Вначале мы объявляем сектор (0-7), с которым будем работать.
#define SECTOR 0
Затем указываем размер сектора 64К в байтах.
#define SECTOR_SIZE 65536
И в функции setup() вызываем функцию erase64K().
void erase64K(byte sec) { uint32_t adr = sectorAdr(sec); Serial.print(F("Sector ")); Serial.print(sec); Serial.print(F(" (")); Serial.print(adr); Serial.print(F(" + 64K) erase...")); flash.blockErase64K(adr); checkBusy(); }
Здесь мы используем функцию sectorAdr() для вычисления начального адреса любого заданного сектора.
uint32_t sectorAdr(byte i) { return SECTOR_SIZE * i; }
Выводим информацию по текущей операции.
Serial.print(F("Sector ")); Serial.print(sec); Serial.print(F(" (")); Serial.print(adr); Serial.print(F(" + 64K) erase..."));
Стираем заданный (в данном случае нулевой) сектор.
flash.blockErase64K(adr);
И проверяем задержку выполнения посекторного стирания.
checkBusy();
Вот результат работы скетча:
Как вы видите, стирание сектора занимает значительно меньше времени, чем стирание чипа целиком и во многих случаях это более предпочтительный вариант. Но 569 миллисекунд — это всё ещё очень много и вам нужно учитывать это при разработке ваших проектов (как и то,что стирается целый сектор в 64 КБ).
Заключение
В этой статье мы рассмотрели важные вопросы начала работы с микросхемой M25P40VP и уделили своё внимание таким функциям как перевод чипа в режим энергосбережения, вывод его из режима сна, стирание всего чипа и его секторов, а также рассмотрели важные нюансы использования M25P40VP ваших проектах. В следующей статье мы продолжим изучение этого чипа и рассмотрим вопросы записи различных типов данных во Flash память контроллера uniSensors nRF24.
Ссылки по теме
Обзор контроллера uniSensors nRF24
Программирование uniSensors nRF24
Работа с памятью M25P40. Часть 1. Спецификации и библиотека
Работа с памятью M25P40. Часть 2. Sleep, Wakeup, Erase и Busy
Работа с памятью M25P40. Часть 3. Read и Write Byte и Arrays
Работа с памятью M25P40. Часть 4. Работа с беззнаковыми типами данных
Работа с памятью M25P40. Часть 5. Работа со знаковыми типами данных
Работа с памятью M25P40. Часть 6. Read и Write Float
Работа с памятью M25P40. Часть 7. Read и Write Char array и String
Работа с памятью M25P40. Часть 8. Работа с секторами
Работа с памятью M25P40. Часть 9. Выборочное стирание секторов
Работа с памятью M25P40. Часть 10. Копирование секторов
Работа с памятью M25P40. Часть 11. Восстановление (backup) секторов
Работа с памятью M25P40. Часть 12. Работа с блоками памяти
Работа с памятью M25P40. Часть 13. Пишем библиотеку для M25P40
Работа с памятью M25P40. Часть 14. Пишем библиотеку для M25P40 (2)
Работа с памятью M25P40. Часть 15. Пишем библиотеку для M25P40 (3)
Где купить?
uniSensors nRF24 в магазине «Electromicro»
Техническая поддержка
Мы внимательно относимся к потребностям наших клиентов и осуществляем техническую поддержку всей выпускаемой продукции. Вы можете написать нам письмо с вашим вопросом или позвонить по телефону и специалист нашей компании проконсультирует вас и поможет решить вашу проблему.
- Емейл для вопросов по нашей продукции: electromicro@bk.ru
- Наш телефон: +7 (495) 997-37-74