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

Работа с памятью M25P40. Часть 8. Работа с секторами

После ознакомления с приёмами записи и чтения основных типов данных в микросхему M25P40VP, установленную на плате uniSensors nRF24, мы можем перейти к изучению более сложных аспектов работы с M25P40VP, а именно, к вопросу работы с секторами памяти этой микросхемы.

Организация памяти M25P40VP

Вся память микросхемы M25P40VP разбита на несколько зон и доступ к записи и данным в этих зонах может осуществляться разными способами. Возможны три варианта доступа:

Побайтовый доступ. 512 кБ (524228 байт). Это доступ с простой линейной адресаций, когда доступно всё адресное пространство от 0 до 524228 байт.

Постраничный доступ. 2048 страниц по 256 байт. Это разбиение всего адресного пространства на 2048 страниц по 256 байт в каждой.

Посекторный доступ. 8 секторов по 65536 байт. Это разбиение всего адресного пространства на 8 секторов по 65536 байт в каждом. Например, микросхема M25P40VP поддерживает посекторное стирание информации, когда вы можете стереть любой сектор, при этом не затронув информацию, находящуюся в других секторах. Это очень полезная особенность о которой мы поговорим чуть ниже.

Таблица соответствий секторов и их шестнадцатеричных и десятичных адресов:

Сектор 0 — 00000h (0)

Сектор 1 — 10000h (65536)

Сектор 2 — 20000h (131072)

Сектор 3 — 30000h (196608)

Сектор 4 — 40000h (262144)

Сектор 5 — 50000h (327680)

Сектор 6 — 60000h (393216)

Сектор 7 — 70000h (458752)

Каждая страница (2048/256) может быть запрограммирована (изменена) индивидуально (биты устанавливаются от 1 к 0). Вся память посекторно или целиком может быть стёрта (биты устанавливаются от 0 к 1), но постраничное стирание не поддерживается.

Запись в сектора

В этом примере мы разберём запись произвольного значения байта в разные (во все восемь) сектора микросхемы M25P40VP. Таблица начальных адресов каждого сектора дана выше, эти значения вычисляются путём умножения номера сектора на количество байтов в одном секторе (65536).

Записывать мы будем байт в нулевую ячейку каждого сектора (итого мы запишем 8 байт данных), а в качестве значения байта мы возьмём номер того сектора, в который записывается этот байт.

Код скетча M25P40 Read & Write sector byte:

/*
  M25P40 Read & Write sector byte
*/

#include <SPI.h>
#include <SPIFlash.h>

#define FLASH_SS 8

#define SECTOR_SIZE 65536

#define TEST_ADR 0

SPIFlash flash(FLASH_SS);

void setup() {
  Serial.begin(115200);
  Serial.println(F("M25P40 Read & Write sector byte start..."));

  Serial.print(F("Init "));
  if (flash.initialize()) {Serial.println(F("OK"));}
                     else {Serial.println(F("FAIL"));}
  
  erase();
  readSectors();  Serial.println();

  writeSectors(); Serial.println();
  readSectors();  Serial.println();
}

void readSectors() {
  for (byte i = 0; i < 8; i++) {
    read_Byte(i);
  }
}

void writeSectors() {
  for (byte i = 0; i < 8; i++) {
    write_Byte(i);
  }
}

String hexy(byte b) {
  String s = F("0x");
  if (b < 16) {s += '0';}
  String s2 = String(b, HEX);
  s2.toUpperCase();
  return s + s2;
}

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 read_Byte(byte sec) {
  uint32_t adr = sec * SECTOR_SIZE;
  byte res = flash.readByte(adr);
  Serial.print(F("Read adr #")); Serial.print(adr); Serial.print(F(": ")); Serial.print(hexy(res));
  checkBusy();
}

void write_Byte(byte sec) {
  uint32_t adr = sec * SECTOR_SIZE;
  flash.writeByte(adr, sec);
  Serial.print(F("Write adr #")); Serial.print(adr); Serial.print(F(": ")); Serial.print(hexy(sec));
  checkBusy();
}

void loop() {
  
}

Теперь подробнее рассмотрим работу этого скетча. Вначале мы определяем размер каждого из секторов микросхемы M25P40VP.

#define SECTOR_SIZE 65536

Затем мы определяем смещение (номер ячейки) для записи байта в каждом из секторов памяти. Для простоты это будет нулевая (самая первая) ячейка каждого сектора.

#define TEST_ADR 0

Затем мы полностью стираем всё содержимое памяти чипа M25P40VP и проводим операцию выборочного чтения нужных нам байт из изо всех 8-и секторов.

  erase();
  readSectors(); Serial.println();

Далее мы записываем нужные нам, согласно условиям задачи, данные и снова читаем их для проверки корректности процедуры записи.

  writeSectors(); Serial.println();
  readSectors();  Serial.println();

Поскольку читаем мы из всех 8-и секторов, то перебор и вызов функции read_Byte() выполняет цикл в функции readSectors().

void readSectors() {
  for (byte i = 0; i < 8; i++) {
    read_Byte(i);
  }
}

Само чтение байтов осуществляется функцией read_Byte(), которой мы в качестве параметра передаём номер текущего сектора.

void read_Byte(byte sec) {
  uint32_t adr = sec * SECTOR_SIZE;
  byte res = flash.readByte(adr);
  Serial.print(F("Read adr #")); Serial.print(adr); Serial.print(F(": ")); Serial.print(hexy(res));
  checkBusy();
}

Здесь мы вычисляем реальный («линейный») адрес нужной нам ячейки памяти.

  uint32_t adr = sec * SECTOR_SIZE;

И производим чтение по этому адресу.

  byte res = flash.readByte(adr);

Далее выводим результат чтения на печать для контроля корректности результата.

Serial.print(F("Read adr #")); Serial.print(adr); Serial.print(F(": ")); Serial.print(hexy(res));

Функция hexy() форматирует вывод под шестнадцатеричное представление чисел (это нужно только для «красивого» вывода данных).

String hexy(byte b) {
  String s = F("0x");
  if (b < 16) {s += '0';}
  String s2 = String(b, HEX);
  s2.toUpperCase();
  return s + s2;
}

Мультисекторная запись осуществляется функцией writeSectors(), где в цикле перебираются все 8 секторов памяти M25P40VP.

void writeSectors() {
  for (byte i = 0; i < 8; i++) {
    write_Byte(i);
  }
}

Непосредственно запись байтов осуществляется функцией write_Byte() по тому же принципу формирования адреса, что и чтение байтов, рассмотренное выше.

void write_Byte(byte sec) {
  uint32_t adr = sec * SECTOR_SIZE;
  flash.writeByte(adr, sec);
  Serial.print(F("Write adr #")); Serial.print(adr); Serial.print(F(": ")); Serial.print(hexy(sec));
  checkBusy();
}

А точнее, функцией flash.writeByte().

  flash.writeByte(adr, sec);

Далее результат выводится на печать.

Serial.print(F("Write adr #")); Serial.print(adr); Serial.print(F(": ")); Serial.print(hexy(sec));

И проверяется задержка выполнения операции микросхемой M25P40VP (о задержках выполнения операций читайте в предыдущих статьях этого цикла).

Вот результат работы нашего скетча:

Разберём подробнее этот скриншот. В первой части мы видим вывод о полном стирании всей памяти чипа M25P40VP, которое происходит примерно за 3,5 секунды.

Затем видим чтение из нулевых (самых первых) ячеек памяти каждого сектора, видим, что во всех протестированных ячейках находятся значения 0xFF (255 в десятичном формате или b11111111 в двоичном) и задержку операции чтения из некоторых ячеек в 2 миллисекунды.

Значения 0xFF в ячейках говорят о том, что операция стирания памяти чипа прошла успешно и он готов для записи в него новой информации.

Далее мы пишем в нужные нам ячейки (согласно заданию) байты данных. В данном случае (для теста) в каждую ячейку записывается номер сектора, содержащего эту ячейку. В случае успешной записи мы должны прочитать из этих ячеек эти же байты.

Затем проводим тестовое чтение байтов из нужных нам (нулевых) ячеек памяти каждого сектора. Результат полностью соответствует нашим ожиданиям — в прочитанных ячейках находятся именно те байты, которые мы записали на предыдущей операции.

0-й сектор, 0-я ячейка — 0x00

1-й сектор, 0-я ячейка — 0x01

2-й сектор, 0-я ячейка — 0x02

3-й сектор, 0-я ячейка — 0x03

4-й сектор, 0-я ячейка — 0x04

5-й сектор, 0-я ячейка — 0x05

6-й сектор, 0-я ячейка — 0x06

7-й сектор, 0-я ячейка — 0x07

Отличный и полностью корректный результат!

Заключение

На этом уроке мы подробно разобрали посекторную организацию памяти микросхемы M25P40VP и операции чтения и записи байтов в различные её сектора. На следующем уроке мы ещё более подробно разберём другие аспекты работы с секторами памяти M25P40VP.

Заключение

На этом уроке мы научились читать и записывать в память микросхемы M25P40VP, установленной на беспроводном контроллере uniSensors nRF24, строковые типы данных char array и String. В следующей статье мы продолжим увлекательное знакомство с чипом M25P40VP и разберём работу с секторами памяти этой микросхемы.

Ссылки по теме

Обзор контроллера 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

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