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

Работа с памятью M25P40. Часть 12. Работа с блоками памяти

После подробного разбора работы с секторами памяти микросхемы M25P40VP мы переходим к изучению работы с отдельными блоками информации внутри этой микросхемы. Под «блоками информации» здесь подразумеваются любые участки памяти, размером менее целого сектора M25P40VP (а сектор имеет размер 65536 байт).

Это может понадобиться вам в практической работе с M25P40VP, когда нужно стереть, скопировать или переместить некий объём данных внутри одного сектора или из одного сектора в другой.

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

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

Посекторный доступ — это разбиение всего адресного пространства микросхемы M25P40VP на 8 секторов по 65536 байт в каждом. Вот таблица соответствий секторов и их шестнадцатеричных и десятичных адресов:

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

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

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

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

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

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

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

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

Также нужно помнить, программирование (изменение) данных (битов) в памяти M25P40VP происходит от 1 к 0, а стирание (посекторно или целиком всей памяти) от 0 к 1.

Работа с блоками памяти

Теперь давайте рассмотрим практический пример работы с блоками памяти M25P40VP. Сценарий, который нам предстоит реализовать:

1-й шаг. Посекторно стираем всю память чипа M25P40VP и подготавливаем микросхему для дальнейших экспериментов.

2-й шаг. Читаем (первый) блок в 256 байт из 0-го сектора со смещением 100 внутри этого сектора и проверяем корректность стирания памяти микросхемы M25P40VP.

3-й шаг. Заполняем (записываем) первый блок в 256 байт в 0-м секторе со смещением 100 внутри этого сектора байтами со значением 65 (символ «A»).

4-й шаг. Проверяем корректность заполнения первого блока в 0-м секторе символами «A».

5-й шаг. Читаем (второй) блок в 256 байт из 1-го сектора со смещением 200 внутри этого сектора (проверяем корректность проведённого ранее стирания).

6-й шаг. Производим копирование 1-го блока в 256 байт из 0-го сектора (по смещению 100 внутри сектора) во 2-й блок в первом секторе со смещением 200 (256 символов «A»).

7-й шаг. Проверяем корректность проведения операции копирования первого блока в одном секторе во второй блок в другом секторе.

Пояснение. Байт со значением 65 и символ «A» — это одно и то же, это просто разные варианты представления (интерпретации или визуализации) числа 65. Число 65 и его представление в виде символа «A» выбраны нами просто для удобства и компактного представления информации на экране.

Теперь код скетча M25P40 Blocks:

/*
  M25P40 Blocks
*/

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

#define FLASH_SS 8

#define SECTOR_SIZE 65536

#define SHIFT1 100
#define SHIFT2 200

byte tmpArray[256];

SPIFlash flash(FLASH_SS);

void setup() {
  Serial.begin(115200);
  Serial.println(F("M25P40 Blocks start..."));

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

  eraseSectors(); Serial.println();
  
  Serial.println(F("Read Block1 in Sector 0 (+shift 100)"));
  readBlock(0, SHIFT1); Serial.println();
  
  Serial.println(F("Write Block1 in Sector 0 (+shift 100) (A)"));
  writeBlock(0, SHIFT1, 'A'); Serial.println();
  
  Serial.println(F("Read Block1 in Sector 0 (+shift 100)"));
  readBlock(0, SHIFT1); Serial.println();
  
  Serial.println(F("Read Block2 in Sector 1 (+shift 200)"));
  readBlock(1, SHIFT2); Serial.println();

  Serial.println(F("Copy Block 1->2"));
  copyBlock(0, SHIFT1, 1, SHIFT2); Serial.println();

  Serial.println(F("Read Block2 in Sector 1 (+shift 200)"));
  readBlock(1, SHIFT2); Serial.println();
  
  Serial.println(F("DONE"));
} // setup()

void eraseSectors() {
  for (byte i = 0; i < 8; i++) {
    erase64K(i);
  }
}

void fillTmpArray(byte val) {
  for (int i = 0; i < 256; i++) {
    tmpArray[i] = val;
  }
}

void printArray(byte* data, int len) {
  for (int i = 0; i < len; i++) {
    Serial.print((char)data[i]);
  }
}

void readBlock(byte sec, word sft) {
  uint32_t adr = sec * SECTOR_SIZE + sft;
 
  flash.readBytes(adr, tmpArray, 256);
  printArray(tmpArray, 256);

  Serial.println();
}

void writeBlock(byte sec, word sft, byte val) {
  uint32_t adr = sec * SECTOR_SIZE + sft;
  
  fillTmpArray(val);

  flash.writeBytes(adr, tmpArray, 256);
}

void copyBlock(byte s1, word sft1, byte s2, word sft2) {
  byte tmpArray[256];

  uint32_t adr1 = s1 * SECTOR_SIZE + sft1;
  uint32_t adr2 = s2 * SECTOR_SIZE + sft2;

  flash.readBytes (adr1, tmpArray, 256);
  flash.writeBytes(adr2, tmpArray, 256);
}

uint32_t sectorAdr(byte i) {
  return SECTOR_SIZE * 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 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() {
  
}

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

#define SECTOR_SIZE 65536

Также мы определяем смещения внутри секторов для первого и второго тестовых блоков.

#define SHIFT1 100
#define SHIFT2 200

Затем вводим вспомогательный массив (буфер) tmpArray для временного хранения 256 байт данных при чтении и записи секторов M25P40VP.

byte tmpArray[256];

Далее мы (посекторно) стираем всё содержимое памяти чипа M25P40VP.

  eraseSectors(); Serial.println();

при помощи функции eraseSectors()

void eraseSectors() {
  for (byte i = 0; i < 8; i++) {
    erase64K(i);
  }
}

которая, в свою очередь, вызывает функцию erase64K() посекторного стирания информации в памяти M25P40VP, которой в качестве параметра передаётся номер текущего (нужного нам в данный момент) сектора.

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();
}

Далее проводим операцию чтения первого блока в 256 байт из 0-го сектора памяти M25P40VP (со смещением 100 в этом секторе).

  Serial.println(F("Read Block1 in Sector 0 (+shift 100)"));
  readBlock(0, SHIFT1); Serial.println();

Чтение блока данных производится функцией readBlock(), которой в качестве параметра передаётся номер нужного нам сектора и смещения внутри этого сектора. В этом скетче мы работаем с тестовыми блоками размером 256 байт.

void readBlock(byte sec, word sft) {
  uint32_t adr = sec * SECTOR_SIZE + sft;
 
  flash.readBytes(adr, tmpArray, 256);
  printArray(tmpArray, 256);

  Serial.println();
}

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

  uint32_t adr = sec * SECTOR_SIZE + sft;

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

  flash.readBytes(adr, tmpArray, 256);

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

  printArray(tmpArray, 256);

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

void printArray(byte* data, int len) {
  for (int i = 0; i < len; i++) {
    Serial.print((char)data[i]);
  }
}

Далее мы заполняем нулевой сектор байтами со значением 65 (символы «A»).

  Serial.println(F("Write Block1 in Sector 0 (+shift 100) (A)"));
  writeBlock(0, SHIFT1, 'A'); Serial.println();

Запись сектора производится функцией writeBlock(), которой в качестве параметров передаются номер нужного нам сектора, смещение внутри него и само записываемое значение.

void writeBlock(byte sec, word sft, byte val) {
  uint32_t adr = sec * SECTOR_SIZE + sft;
  
  fillTmpArray(val);

  flash.writeBytes(adr, tmpArray, 256);
}

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

  uint32_t adr = sec * SECTOR_SIZE + sft;

Вызываем функцию fillTmpArray()

  fillTmpArray(val);

и заполняем с её помощью временный массив tmpArray нужными нам значениями (в данном случае байтами со значением 65).

void fillTmpArray(byte val) {
  for (int i = 0; i < 256; i++) {
    tmpArray[i] = val;
  }
}

И далее записываем блок из 256 байт в нужный нам сектор по нужному нам смещению.

  flash.writeBytes(adr, tmpArray, 256);

Далее производим контрольное чтение содержимого первого блока.

  Serial.println(F("Read Block1 in Sector 0 (+shift 100)"));
  readBlock(0, SHIFT1); Serial.println();

Затем читаем содержимое второго блока для контроля корректности его стирания вначале работы скетча.

  Serial.println(F("Read Block2 in Sector 1 (+shift 200)"));
  readBlock(1, SHIFT2); Serial.println();

И производим процедуру копирования содержимого первого блока во второй.

  Serial.println(F("Copy Block 1->2"));
  copyBlock(0, SHIFT1, 1, SHIFT2); Serial.println();

Копирование производится функцией copyBlock(), которой в качестве параметров передаются номера исходного сектора и сектора назначения.

void copyBlock(byte s1, word sft1, byte s2, word sft2) {
  byte tmpArray[256];

  uint32_t adr1 = s1 * SECTOR_SIZE + sft1;
  uint32_t adr2 = s2 * SECTOR_SIZE + sft2;

  flash.readBytes (adr1, tmpArray, 256);
  flash.writeBytes(adr2, tmpArray, 256);
}

Здесь снова вычисляются нужные нам адреса

  uint32_t adr1 = s1 * SECTOR_SIZE + sft1;
  uint32_t adr2 = s2 * SECTOR_SIZE + sft2;

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

  flash.readBytes (adr1, tmpArray, 256);
  flash.writeBytes(adr2, tmpArray, 256);

Далее снова производим контрольное чтение содержимого второго блока.

  Serial.println(F("Read Block2 in Sector 1 (+shift 200)"));
  readBlock(1, SHIFT2); Serial.println();

И в завершение выводим сообщение об окончании работы скетча.

  Serial.println(F("DONE"));

Вот результат работы нашего скетча (за пределы экрана выходят блоки данных в 256 байт):

Всё произошло в точности так как мы и планировали — блок из нулевого сектора был заполнен символами «A» и затем скопирован в первый сектор — тестовое задание успешно выполнено.

Заключение

На этом уроке мы научились работать с блоками памяти микросхемы M25P40VP — это важный навык, который пригодиться вам в практической работе с чипом M25P40VP и контроллерами 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

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