logo
+7 (951) 999-89-94
428003, г. Чебоксары, ул. Федора Гладкова, д.9, оф.319

Работа с памятью M25P40. Часть 2. Sleep, Wakeup, Erase и Busy

Продолжаем знакомство с микросхемой 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

Программирование 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

Аналогичные товары