В этой статье мы продолжим изучать работу с секторами памяти микросхемы M25P40VP, установленной на плате uniSensors nRF24, и разберём приёмы выборочного стирания её отдельных секторов. Это важный аспект работы с 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, в этой статье мы продолжим эту тему и познакомимся с выборочным стиранием отдельных секторов и приёмами записи и сохранения данных при выборочном стирании секторов M25P40VP.
Итак, в этом скетче мы:- Научимся стирать не всю память M25P40VP сразу, а по секторам и посмотрим отличия этих двух типов стирания.
- Отработаем приёмы работы с данными в режиме выборочного стирания секторов и научимся выборочно сохранять информацию в чипе M25P40VP после процедуры стирания.
Скетч будет похож на скетч из предыдущей статьи, а все различия мы подробно разберём и проанализируем чуть ниже. А пока код скетча M25P40 Random Erase, Read & Write sector byte:
/* M25P40 Random Erase, 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 Random Erase, Read & Write sector byte start...")); Serial.print(F("Init ")); if (flash.initialize()) {Serial.println(F("OK"));} else {Serial.println(F("FAIL"));} eraseSectors(); Serial.println(); readSectors(); Serial.println(); writeSectors(); Serial.println(); erase64K(1); erase64K(3); erase64K(5); erase64K(7); Serial.println(); readSectors(); Serial.println(); } void eraseSectors() { for (byte i = 0; i < 8; i++) { erase64K(i); } } void readSectors() { for (byte i = 0; i < 8; i++) { read_Byte(i); } } void writeSectors() { for (byte i = 0; i < 8; i++) { write_Byte(i); } } 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 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, но не всё сразу, как делали в предыдущей статье, а отдельно по каждому сектору
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(); }
Далее проводим операцию выборочного чтения нужных нам байт из изо всех 8-и секторов.
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; }
Далее следует блок в котором мы сначала записываем тестовые данные во все сектора памяти, затем выборочно стираем 1-й, 3-й, 5-й и 7-й сектора и снова производим тестовое чтение для контроля корректности работы скетча.
writeSectors(); Serial.println(); erase64K(1); erase64K(3); erase64K(5); erase64K(7); Serial.println(); readSectors(); Serial.println();
Мультисекторная запись осуществляется функцией 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 (о задержках выполнения операций читайте в предыдущих статьях этого цикла).
Следующий блок выборочно стирает нужные нам сектора.
erase64K(1); erase64K(3); erase64K(5); erase64K(7); Serial.println();
И далее мы снова проводим тестовое чтение контрольных данных.
readSectors(); Serial.println();
Вот результат работы нашего скетча:
Разберём подробнее этот скриншот. В первом блоке мы видим вывод о посекторном стирании памяти чипа M25P40VP, которое происходит за 8 приёмов по одному сектору и занимает примерно 0,5 секунды на сектор. То есть полное стирание происходит примерно за 4,5 секунды.
Обратите внимание на следующий момент: полное стирание чипа одной командой происходит за 3,5 сек., а посекторное суммарно — за 4,5 сек. Это значит, что если вам нужно полностью стиреть чип, то лучше это делать не посекторно, а одной командой.
Одновременно, если вам нужно стереть только какой-то отдельный участок памяти M25P40VP, то разумнее пользоваться именно посекторным стиранием с задержкой примерно 0,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
И, наконец, выборочно стираем 1-й, 3-й, 5-й и 7-й сектора памяти микросхемы M25P40VP. Здесь мы подразумеваем, что выборочно стерев некоторые сектора памяти, мы при проверке обнаружим нетронутыми остальные данные, а на месте данных в стёртых секторах мы обнаружим ожидаемые значения байтов 0xFF.
Что полностью подтверждается чтением после всех наших манипуляций — тестовые ячейки в стёртых секторах содержат ожидаемые значения 0xFF, а остальные тестовые ячейки содержат записанные в них данные (номера содержащих их секторов). Отличный и полностью корректный результат!
Заключение
На этом уроке мы научились выборочно посекторно стирать информацию в памяти микросхемы M25P40VP и сохранять нужные нам данные в других секторах памяти. На следующем уроке мы научимся копировать и сохранять информацию внутри чипа M25P40VP и разберём соответствующие примеры.
Ссылки по теме
Обзор контроллера 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