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

Работа с памятью M25P40. Часть 6. Read и Write Float

Мы уже узнали довольно много об организации и работе микросхемы памяти M25P40VP, установленной на плате беспроводных контроллеров uniSensors nRF24, но мы не станем останавливаться на достигнутом и продолжим наши исследования. На этом уроке мы разберём работу (чтение и запись) данных типа float. Это очень важный и востребованный тип данных и знание принципов работы с ним очень пригодится вам в создании ваших реальных проектов.

Read & Write float

В среде программирования Ардуино числа типа float занимают 4 байта (32 бита) и могут содержать значения с «плавающей» точкой от -3.4028235E+38 до 3.4028235E+38.

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

Теперь давайте создадим скетч, который позволит записывать числа с плавающей точкой типа float в память микросхемы M25P40VP и считывать их обратно.

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

/*
  M25P40 Read & Write Float
*/

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

#define FLASH_SS 8

#define TEST_ADR 0

#define TEST_VAL 3.1415926535
//#define TEST_VAL 0.0
//#define TEST_VAL -3.1415926535

#define DEPTH 2
//#define DEPTH 7

union {
  float fl;
  byte b[4];
} flb;

SPIFlash flash(FLASH_SS);

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

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

  erase();
  readFloat(TEST_ADR);
  
  writeFloat(TEST_ADR, TEST_VAL);
  readFloat(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] = flb.b[0];
  bytes[1] = flb.b[1];
  bytes[2] = flb.b[2];
  bytes[3] = flb.b[3];
  
  flb.b[0] = bytes[3];
  flb.b[1] = bytes[2];
  flb.b[2] = bytes[1];
  flb.b[3] = bytes[0];
}

void readFloat(uint32_t adr) {
  flash.readBytes(adr, flb.b, 4);
  invert();
  
  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(flb.fl, DEPTH);
  Serial.println();
}

void writeFloat(uint32_t adr, float fl) {
  flb.fl = fl;
  invert();
  flash.writeBytes(adr, flb.b, 4);
  
  Serial.print(F("Write #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(fl, DEPTH);
}

void loop() {
  
}

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

#define TEST_ADR 0

Затем мы определяем тестовое число из диапазона, поддерживаемого типом float. Для примера мы возьмём число pi равное 3.1415926535 с точностью 10 знаков после точки. Эта точность взята с нескольким запасом и об этом мы поговорим чуть ниже.

Здесь же мы задаём нулевое значение тестового числа и отрицательное значение — эти данные понадобятся нам в дальнейшем для тестирования корректности алгоритмов записи и считывания информации из M25P40VP. Пока два последних числа оставляем закомментированными.

#define TEST_VAL 3.1415926535
//#define TEST_VAL 0.0
//#define TEST_VAL -3.1415926535

Далее мы определяем также «дефайн» DEPTH — это количество знаков после точки с которым будет работать наш скетч. Пока оставляем значение 2 (знака после точки).

#define DEPTH 2
//#define DEPTH 7

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

union {
  float fl;
  byte b[4];
} flb;

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

  erase();
  readFloat(TEST_ADR);

Затем записываем в неё новое значение типа float и снова читаем и проверяем результат.

  erase();
  readFloat(TEST_ADR);
  
  writeFloat(TEST_ADR, TEST_VAL);
  readFloat(TEST_ADR);

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

void readFloat(uint32_t adr) {
  flash.readBytes(adr, flb.b, 4);
  invert();
  
  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(flb.fl, DEPTH);
  Serial.println();
}

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

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

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

  byte bytes[4];
  bytes[0] = flb.b[0];
  bytes[1] = flb.b[1];
  bytes[2] = flb.b[2];
  bytes[3] = flb.b[3];
  
  flb.b[0] = bytes[3];
  flb.b[1] = bytes[2];
  flb.b[2] = bytes[1];
  flb.b[3] = bytes[0];

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

  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(flb.fl, DEPTH);
  Serial.println();

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

void writeFloat(uint32_t adr, float fl) {
  flb.fl = fl;
  invert();
  flash.writeBytes(adr, flb.b, 4);
  
  Serial.print(F("Write #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(fl, DEPTH);
}

Далее мы присваиваем объединению flb.fl тестовое значение.

  flb.fl = fl;

Инвертируем его.

  invert();

И записываем в память M25P40VP.

  flash.writeBytes(adr, flb.b, 4);

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

  Serial.print(F("Write #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(fl, DEPTH);

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

Обратите внимание, что чтение числа типа float из тестовой ячейки сразу после стирания памяти приводит к некорректному результату типа nan — это обусловлено тем, что после стирания все ячейки заняты числами 255 (b11111111) и система воспринимает их как некорректные для чисел типа float.

Обратите внимание также, что числа выводятся с точностью 2 знака после точки — это стандартное представление float чисел в среде Ардуино.

Проверка корректности работы с нулевым значением

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

//#define TEST_VAL 3.1415926535
#define TEST_VAL 0.0
//#define TEST_VAL -3.1415926535

Компилируем и загружаем исправленный вариант скетча и убеждаемся в корректности работы нашего алгоритма с нулевыми значениями float чисел.

Проверка корректности работы с отрицательными числами

Комментируем две первые строки блока тестовых значений и оставляем раскомментированной последнюю.

//#define TEST_VAL 3.1415926535
//#define TEST_VAL 0.0
#define TEST_VAL -3.1415926535

После компиляции и загрузки нового варианта скетча убеждаемся в корректности его работы с отрицательными числами типа float.

Количество знаков после точки

Теперь проведём эксперимент с выводом чисел типа float с различной точностью (различным количеством знаков после точки). Для этого в блоке

#define DEPTH 2
//#define DEPTH 7

меняем комментирование на противоположное.

//#define DEPTH 2
#define DEPTH 7

После этого в фрагментах кода

  Serial.print(F("Read #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(flb.fl, DEPTH);

и

  Serial.print(F("Write #")); Serial.print(adr); Serial.print(F(": ")); Serial.println(fl, DEPTH);

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

Также тут стоит обратить внимание на то, что исходное число с точностью 10 знаков после точки

#define TEST_VAL 3.1415926535

выводится системой Ардуино с округлением и точностью только 7 (6) знаков после точки.

Write #0: 3.1415927
Read #0: 3.1415927

Заключение

На этом мы заканчиваем изучение работы с числами типа float при записи и чтении их из микросхемы 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

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