Arduino Nano 33 BLE - использование flash для хранения данных; как расширить фрагмент примера кода

programming flash arduino-nano-ble

Я изучал, как хранить данные между перезапусками. С Arduino UNO существует множество библиотек EEPROM, но я не нахожу что-то "вне стойки" для Arduino Nano 33 BLE.

Мне удалось найти хорошо документированный пример Пита Уордена, который недавно поставил на github (https://github.com/petewarden/arduino_nano_ble_write_flash), и он может быть скомпилирован и загружен, но мне неясно, как можно было бы развернуть его, чтобы написать простую функцию, которая может хранить и читать несколько значений (целое число, float, string, массивы).

Код ниже, я немного заржавел на вещах, но я думаю, что ключ находится в части:

  // Считайте первые четыре байта копии нашего флэш-массива 32-битными
  // целое число.
  int32_t* counter_address = reinterpret_cast<int32_t*>(ram_buffer);

Как можно было бы добавить дополнительные переменные (и разной длины) в этот код, чтобы вы могли писать и читать более одной переменной? Почему-то это казалось бы командой вроде:

int32_t* another_variable = [modified version of:] reinterpret_cast<int32_t*>(ram_buffer);

... это было бы неплохо, но я не совсем уверен, как это делается.

   * Copyright 2021 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// Этот скетч демонстрирует, как инициализировать, читать и обновлять флэш-память
// на Arduino Nano BLE Sense 33. Он содержит счетчик, который начинается с нуля
// когда скетч впервые загружается и увеличивается на единицу каждый раз,
когда // плата сбрасывается без повторной flash. Вы можете проверить это, загрузив
// скетч, открыв последовательный монитор и увидев вывод "Счетчик=0" изначально.
// Если вы затем нажмете кнопку сброса, вы увидите "Счетчик=1", и так далее, как
// плата перезапущена.

// Этот Mbed API позволяет нам получить доступ к флэш-памяти, поэтому мы включаем заголовок. Смотрите
// https://os.mbed.com/docs/mbed-os/v6.9/apis/flashiapblockdevice.html
#include "FlashIAPBlockDevice.h"

// Одна из проблем, с которыми я столкнулся с существующими примерами использования flash
// было ли то, что они не продемонстрировали, как установить начальное значение для памяти
// когда скетч был впервые загружен. Это было важно для большинства случаев использования,
// например, мы хотим, чтобы счетчик начинался с нуля, поэтому начальное содержимое нужно
// устанавливается при мигании программы. Я обычно пытаюсь использовать linker
// скрипты, чтобы выделить область памяти и пометить ее, чтобы она инициализировалась во время
// мигает, но это не очень легко с Arduino IDE. Вместо этого я использую немного
// a hack, by:
// - Объявление массива байтов как const, поэтому я знаю, что он будет в конечном итоге
// flash.
// - Заставляя его выравниваться с размером блока flash, чтобы он мог быть
// доступ к API, который работает с целыми страницами flash.
// - Добавление пустого списка инициализаторов ({}) говорит компилятору обнулить
// содержимое в двоичном файле, который мигнул на устройство.
// Последний пункт немного тонковат, потому что вы можете подумать, что выполнение этого в
// конструкторе или setup() будет работать, но это будет вызываться каждый раз
, когда // программа сбрасывается. Что мне было нужно, так это способ создать двоичный файл с этой областью
// уже инициализирован перед загрузкой, который предлагает этот подход.
// Некоторые другие примеры используют жестко закодированные адреса для flash, как правило, в сторону
// верхняя часть адресного пространства, чтобы они не конфликтовали с загруженной программой,
/ / но этот подход имеет то преимущество, что ваша область записи обрабатывается
// так же, как и другие переменные, поэтому вам не нужно беспокоиться о
// переписывать что угодно.


// Из экспериментов я знаю, что "размер страницы" или "размер блока" flash
// на плате находится 4KB и должен соответствовать сообщенному get_erase_size() из объекта
// block device. В теории мы должны быть в состоянии использовать
// MBED_CONF_FLASHIAP_BLOCK_DEVICE_SIZE макрос, но это, кажется, установлено в ноль. 
      
    #include "FlashIAPBlockDevice.h"
        
    constexpr int kFlashBlockSize = 4096;
        
        // Your flash area needs to be a multiple of the block size. In this case I want
        // 64KB (even though I'm only using the first four bytes). You should make this
        // the smallest multiple of the block size that will hold your data.
        #define ROUND_UP(val, block_size) ((((val) + ((block_size) - 1)) / (block_size)) * (block_size))
        constexpr int kFlashBufferSize = ROUND_UP(64 * 1024, kFlashBlockSize);
        
        // Как уже упоминалось выше, alignas гарантирует, что мы получим действительный адрес, выровненный по блоку,
        // const должен убедиться, что он находится во flash, а пустые скобки означают массив
        // заполняется нулями при первой загрузке программы.

        alignas(kFlashBlockSize) const uint8_t flash_buffer[kFlashBufferSize] = {};
        
        void setup() {
              Serial.begin(9600);
            
              // Осторожно! Если вы скопируете и вставите этот код в свой собственный пример и попытаетесь запустить его без подключенного последовательного монитора, эта строка будет висеть вечно.
              // Вам нужно будет удалить это в производстве (и да, я был пойман на этом
              // сам слишком часто).
              while (!Serial);
            
              // Адрес массива будет определен компилятором, и нам нужно
              // захватить его как 32-битное целое число для использования с Mbed API.
              const uint32_t flash_buffer_address = reinterpret_cast<uint32_t>(flash_buffer);
              Serial.println(String("flash_buffer_address=0x") + String(flash_buffer_address, 16));
            
              // Создайте флэш-объект, который мы будем использовать для доступа к флэш-памяти.
              static FlashIAPBlockDevice bd(flash_buffer_address, kFlashBufferSize);
              bd.init();
            
              // Нам нужно будет скопировать область флэш-памяти в оперативную память, чтобы изменить ее, поэтому выделите
              // массив нужного размера.  
              uint8_t* ram_buffer = (uint8_t*)(malloc(kFlashBufferSize));
            
              // Скопируйте существующее содержимое флэш - памяти в наш буфер оперативной памяти.
              bd.read(ram_buffer, 0, kFlashBufferSize);
            
              // Нам нужно стереть область flash, прежде чем мы сможем писать в нее снова.
              bd.erase(0, kFlashBufferSize);
            
              // Считайте первые четыре байта копии нашего флэш-массива 32-битными
              // целое число.
              int32_t* counter_address = reinterpret_cast<int32_t*>(ram_buffer);
            
              // Распечатайте значение до каких-либо изменений.
              Serial.println(String("Counter=") + String(*counter_address, 16));
            
              // Обновите нашу оперативную копию целого числа счетчика.
              *counter_address += 1;
            
              // Запишите всю копию нашего буфера оперативной памяти обратно во flash. 
              bd.program(ram_buffer, 0, kFlashBufferSize);
            
              // Выключите наш объект флэш-доступа.
              bd.deinit();  
            }

, 👍2

Обсуждение

поместите их в буфер, прочитайте их из буфера, @Juraj

ОК... но как именно поместить их в буфер? Синтаксис-это то, что не понято. Понятно, что если у вас есть буфер, вы можете поместить его в определенные места в буфере - но как это делается?, @asylumax

Прошивка будет удаляться каждый раз, когда вы загружаете новый скетч. Это то, чего ты хочешь?, @Fahad

Нет; просьба состоит в том, чтобы вы могли добавить N переменных. При выполнении этого с помощью EEPROM требовались смещения из области памяти. Что мне любопытно, так это то, как вы можете это сделать с помощью множества переменных разных типов., @asylumax


3 ответа


2

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

Платы Arduino, упомянутые выше, используют микроконтроллер nRF52840 от Nordic Semiconductor. Этот чип оснащен очень простой файловой системой для флэш-памяти. Это используется в библиотеке для хранения и извлечения произвольной структуры данных вашей программы.

Пожалуйста, взгляните на репозиторий NanoBLEFlashPrefs на GitHub.

,

В новой настройке платы Arduino я получаю сообщение "ПРЕДУПРЕЖДЕНИЕ: библиотека NanoBLEFlashPrefs утверждает, что работает на архитектуре mbed_nano и может быть несовместима с вашей текущей платой, которая работает на архитектуре mbed". , @asylumax

@asylumax Вы можете смело игнорировать это, если вы действительно используете Arduino Nano 33 BLE. Я тоже видел это, но теперь оно исчезло. Какую версию IDE и какую версию платы поддержки "Arduino Mbed OS Nano Boards" вы используете? С версией 2.6.1 поддержки этой платы и версией 1.8.15 IDE я больше не вижу этого предупреждения., @Dirk


2

Вы также можете попробовать мою библиотеку FS_Nano33BLE с помощью простых в использовании файлов LittleFS / FATFS.

Доступ к файловой системе использует обычные API POSIX или API файловой системы mbed

,

Отличная библиотека; похоже, нет функции "каталог", чтобы выяснить, какие файлы существуют. Прямо сейчас я пишу несколько оберток, чтобы вы могли использовать строки, которые могут использовать некоторые люди., @asylumax

Я смог создать команду "каталог", основываясь на предоставленной вами ссылке, (https://os.mbed.com/docs/mbed-os/v6.10/apis/file-system-apis.html). Спасибо., @asylumax


2

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

struct StoredData {
    int32_t some_integer_variable;
    float some_float;
    // etc...
};

Затем вы можете использовать тот же шаблон, что и исходный код:

StoredData* my_data = reinterpret_cast<StoredData*>(ram_buffer);

и доступ к отдельным переменным как my_data->some_integer_variable>, my_data->some_float>и т.д.

,