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

Работа с памятью M25P40. Часть 5. Работа со знаковыми типами данных

На прошлом уроке мы научились записывать и читать из памяти микросхемы M25P40VP данные беззнаковых типов word и unsigned long, теперь мы разберём примеры и научимся работать со знаковыми типами данных int и long.

Read & Write int

Int — это знаковый тип данных (int16_t), состоящий из двух байтов и принимающий значения от -32768 до 32767. Исходя из самого определения типа данных int, видно, что он позволяет работать с отрицательными числами и сейчас мы подробно разберём все особенности работы с подобными числами при записи их во Flash память M25P40VP.

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

/*
  M25P40 Read & Write int
*/

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

#define FLASH_SS 8

#define TEST_ADR 0
#define TEST_VAL  32000
//#define TEST_VAL -32000

union {
  int i;
  byte b[2];
} uib;

SPIFlash flash(FLASH_SS);

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

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

  erase();
  readInt(TEST_ADR);
  writeInt(TEST_ADR, TEST_VAL);
  readInt(TEST_ADR);
}

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 invert() {
  byte bytes[2];
  bytes[0] = uib.b[0];
  bytes[1] = uib.b[1];
  
  uib.b[0] = bytes[1];
  uib.b[1] = bytes[0];
}

void readInt(uint32_t adr) {
  flash.readBytes(adr, uib.b, 2);
  invert();
  
  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(uib.i);
}

void writeInt(uint32_t adr, int i) {
  uib.i = i;
  invert();
  flash.writeBytes(adr, uib.b, 2);
  
  Serial.print(F("Write #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(i);
}

void loop() {
  
}

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

#define TEST_ADR 0

Затем мы определяем тестовое число из диапазона, поддерживаемого типом int. Здесь же мы задаём второе (отрицательное) число для проверки работы нашего кода с отрицательными числами. Пока отрицательное число оставляем закомментированным.

#define TEST_VAL  32000
//#define TEST_VAL -32000

Также мы создаём объединение (union) для совместного хранения в памяти различных типов данных — 2-байтового числа i типа int и 2-байтового массива b[2]. Это нам понадобится в дальнейшем для записи, чтения и приведения различных типов данных.

union {
  int i;
  byte b[2];
} uib;

Далее следует блок, определяющий функционал нашего скетча. Мы стираем память чипа, читаем содержимое тестовой ячейки, записываем в неё новое значение и снова читаем и проверяем результат.

  erase();
  readInt(TEST_ADR);
  writeInt(TEST_ADR, TEST_VAL);
  readInt(TEST_ADR);

Чтение производится функцией readInt(), которой мы передаём адрес тестовой ячейки в качестве параметра.

void readInt(uint32_t adr) {
  flash.readBytes(adr, uib.b, 2);
  invert();
  
  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(uib.i);
}

Здесь мы используем функцию flash.readBytes() чтения массива (из 2-х байт) из памяти M25P40VP по заданному адресу и «массивное» представление объединённого 2-байтового числа uib.b[].

  flash.readBytes(adr, uib.b, 2);

Поскольку порядок байтов, выдаваемых функцией flash.readBytes() противоположен порядку байтов в представлении чисел типа int, мы дополнительно инвертируем этот порядок в объединении uib при помощи функции invert().

void invert() {
  byte bytes[2];
  bytes[0] = uib.b[0];
  bytes[1] = uib.b[1];
  
  uib.b[0] = bytes[1];
  uib.b[1] = bytes[0];
}

Далее выводим на печать полученный результат при помощи объединённой версии ulb.ul типа unsigned long.

  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(uib.i);

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

Значение -1, прочитанное сразу после стирания памяти микросхемы M25P40VP, определяется числами 0xFF, которыми заполняются ячейки памяти при стирании. Далее мы видим успешные запись и считывание тестового числа 32000 типа int.

Теперь проведём тест на корректную работу нашего кода при оперировании отрицательными числами из диапазона, поддерживаемого типом int. Для это поменяем комментирование

#define TEST_VAL  32000
//#define TEST_VAL -32000

на противоположное

//#define TEST_VAL  32000
#define TEST_VAL -32000

Запускаем скетч и видим корректную работу нашего кода с отрицательными числами.

Напомним, поскольку тип int занимает 2 байта, то и запись нашего числа занимает 2 ячейки Flash памяти, в нашем случае #0 и #1.

Read & Write long

Работа по записи и чтению данных типа long похожа на работу с данными типа int, стой разницей, что первые занимают 4 байта в памяти вместо 2-х. Long — это знаковый тип данных (int32_t), состоящий из четырёх байтов и принимающий значения от -2147483648 до 2147483647.

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

/*
  M25P40 Read & Write long
*/

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

#define FLASH_SS 8

#define TEST_ADR 0
#define TEST_VAL  2111000111
//#define TEST_VAL -2111000111

union {
  long lg;
  byte b[4];
} ulb;

SPIFlash flash(FLASH_SS);

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

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

  erase();
  readLong(TEST_ADR);
  writeLong(TEST_ADR, TEST_VAL);
  readLong(TEST_ADR);
}

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 invert() {
  byte bytes[4];
  bytes[0] = ulb.b[0];
  bytes[1] = ulb.b[1];
  bytes[2] = ulb.b[2];
  bytes[3] = ulb.b[3];
  
  ulb.b[0] = bytes[3];
  ulb.b[1] = bytes[2];
  ulb.b[2] = bytes[1];
  ulb.b[3] = bytes[0];
}

void readLong(uint32_t adr) {
  flash.readBytes(adr, ulb.b, 4);
  invert();
  
  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(ulb.lg);
}

void writeLong(uint32_t adr, long lg) {
  ulb.lg = lg;
  invert();
  flash.writeBytes(adr, ulb.b, 4);
  
  Serial.print(F("Write #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(lg);
}

void loop() {
  
}

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

#define TEST_ADR 0

Затем мы определяем тестовое число из диапазона, поддерживаемого типом long (от -2147483648 до 2147483647). Здесь же создаём второе тестовое число для проверки корректности работы нашего кода с отрицательными числами.

#define TEST_VAL  2111000111
//#define TEST_VAL -2111000111

Также мы создаём объединение (union) для совместного хранения в памяти различных типов данных — 4-байтового числа lg типа long и 4-байтового массива b[4]. Это нам понадобится в дальнейшем для записи, чтения и приведения различных типов данных.

union {
  long lg;
  byte b[4];
} ulb;

Далее следует блок, определяющий функционал нашего скетча. Мы стираем память чипа, читаем содержимое тестовой ячейки, записываем в неё новое значение и снова читаем и проверяем результат.

  erase();
  readLong(TEST_ADR);
  writeLong(TEST_ADR, TEST_VAL);
  readLong(TEST_ADR);

Чтение производится функцией readLong(), которой мы передаём адрес тестовой ячейки в качестве параметра.

void readLong(uint32_t adr) {
  flash.readBytes(adr, ulb.b, 4);
  invert();
  
  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(ulb.lg);
}

Здесь мы используем функцию flash.readBytes() чтения массива (из 4-х байт) из памяти M25P40VP по заданному адресу и «массивное» представление объединённого 4-байтового числа ulb.b[].

  flash.readBytes(adr, ulb.b, 4);

Поскольку порядок байтов, выдаваемых функцией flash.readBytes() противоположен порядку байтов в представлении чисел типа unsigned long, мы дополнительно инвертируем этот порядок в объединении ulb при помощи функции invert().

void invert() {
  byte bytes[4];
  bytes[0] = ulb.b[0];
  bytes[1] = ulb.b[1];
  bytes[2] = ulb.b[2];
  bytes[3] = ulb.b[3];
  
  ulb.b[0] = bytes[3];
  ulb.b[1] = bytes[2];
  ulb.b[2] = bytes[1];
  ulb.b[3] = bytes[0];
}

Далее выводим на печать полученный результат при помощи объединённой версии ulb.lg типа long.

  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(ulb.lg);

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

Сразу после операции стирания первые 4 байта (#0, #1, #2, #3) содержат значения 0xFF, которые интерпретируются типом long как -1, а затем мы успешно записали и прочитали из первых четырёх ячеек памяти M25P40VP тестовое число 2111000111.

Теперь проведём тест на корректную работу нашего кода при оперировании отрицательными числами из диапазона, поддерживаемого типом long. Для это поменяем комментирование

#define TEST_VAL  2111000111
//#define TEST_VAL -2111000111

на противоположное

//#define TEST_VAL  2111000111
#define TEST_VAL -2111000111

Запускаем скетч и видим корректную работу кода с отрицательными числами.

Заключение

В этой статье мы научились работать (читать и записывать) знаковые типы данных int и long в память микросхемы M25P40VP, установленной на беспроводном контроллере uniSensors nRF24. В следующей статье мы продолжим знакомство с чипом 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

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