Как быстро я должен иметь возможность записывать во флэш-память AT45DB321D?

Я проверил техническое описание AT45DB321D, и там сказано, что он работает на частоте 66 или 33 МГц. . Обе эти скорости выше, чем то, что поддерживает Arduino Nano (здесь мой гугл-фу меня не подводит, но, согласно документу Arduino SPI это, вероятно, порядка 4 МГц).

Согласно техническому описанию

Данные могут синхронизироваться с входного контакта (SI) либо в буфер 1, либо в буфер 2. Чтобы загрузить данные в стандартный буфер DataFlash (528 байт), используйте 1-байтовый код операции, 84H для буфера 1 или 87H для буфера. 2, должны быть синхронизированы с устройством, за которыми следуют три байта адреса, состоящие из 14 битов безразличия и 10 битов адреса буфера (BFA9-BFA0). 10 бит адреса буфера определяют первый записываемый байт в буфере. [...] Данные будут продолжать загружаться в буфер до тех пор, пока на выводе CS не будет обнаружен переход с низкого уровня на высокий.

Что означает написать один байт, который мне понадобится

  • 1 байт кода операции
  • 3 байта адреса
  • 1 байт данных

всего 5 байт или 40 бит, что на частоте 4 МГц дает 10 мкс.

Мой код, как показано ниже, дает примерно 48 мкс на байт (выводит продолжительность ~ 12380 мкс для передачи 256 байтов). Я использую библиотеку DataFlash.

#include <SPI.h>
#include "DataFlash.h"

static const int csPin    = 10;
static const int resetPin = 8;
static const int wpPin    = 7;

DataFlash dataflash;

void setup()
{
  Serial.begin(115200);

  SPI.begin();

  dataflash.setup(csPin, resetPin, wpPin);
  dataflash.begin();

  long start = micros();
  dataflash.bufferWrite(0, 0);
  for(int i = 0; i <= 255; i++) {
    SPI.transfer(i);
  }
  long afterBuffer = micros();

  Serial.print("Writing 256 values to buffer: ");
  Serial.println(afterBuffer - start);
}

void loop()
{
  // ничего
}

Вполне вероятно, что я что-то упускаю, но особенно если я хочу передавать большие куски данных одновременно, я хотел бы немного ускорить этот процесс. Создание 256-байтного буфера и запись всего сразу

// вне измерения времени
uint8_t buffer256[256];
for(int i = 0; i <= 255; i++) {
  buffer256[i] = i;
}
// внутреннее измерение времени
SPI.transfer(buffer256, 256);

занимает примерно столько же времени.

tl;dr: Все ли работает правильно/что я делаю не так? Как ускорить запись на флэш-чип с помощью Arduino Nano?

, 👍1

Обсуждение

Самая быстрая скорость, с которой может работать SPI, — это F_CPU/2, поэтому для Arduino с частотой 16 МГц это 8 МГц., @Majenko

Я бы прикрепил осциллограф и измерил; это лучший способ проверить, верны ли мои предположения о тактовой частоте и времени., @the busybee


1 ответ


Лучший ответ:

2

"Данные будут загружаться в буфер до тех пор, пока не переход обнаружен на выводе CS."

Команда dataflash.bufferWrite записывает байты кода операции и адреса, а затем в цикле for вы записываете 256 байтов данных. Байты данных записываются сразу, без указания кода операции и адреса перед каждым из них. (Так что он уже делает то, что вы хотите сделать.)

Неважно, записываете ли вы байт сразу или каждый с помощью одной записи SPI. SPI может отправлять их только последовательно. И исходный код операции и адрес отправляются только один раз с помощью команды dataflash.bufferWrite.

Кстати: есть два буфера для записи в 1 и 2. Поэтому мне любопытно, почему работает команда dataflash.bufferWrite(0, 0);. ИМХО, это должно быть dataflash.bufferWrite(1, 0); или dataflash.bufferWrite(2, 0);.

Вы также не запускаете внутреннюю запись на страницы. Команды нет, например dataflash.bufferToPage(1, 7);. Данные передаются только в буфер флэш-памяти.

Поскольку данные не записываются на страницу памяти, вы потеряете их, если отключите питание.

ОБНОВЛЕНИЕ

Боюсь, я не совсем понял ваш вопрос. Как я теперь вижу, вы задавались вопросом, почему вам нужно в среднем 48 мкс для 256 байт, когда шина SPI работает на частоте 4 МГц.

Я немного посмотрел коды и документы. И вот что я нашел:

Библиотека DataFlash выбирает другой делитель часов:

/* Setup SPI */
SPI.setDataMode(SPI_MODE3);
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV2);

SPI_CLOCK_DIV2 после некоторых вычислений приводит к частоте Fosc/2. За вычислениями очень сложно следить, потому что они используют 8-битное число, которое разделено на 2 полубайта, каждый из которых определяет часть содержимого регистра. Но я совершенно уверен, что результат Fosc/2. Но это приведет к тактовой частоте 8 МГц. Это не может объяснить наблюдаемые вами тайминги.

Кстати: есть также интересное примечание об устаревании о такой установке тактовой частоты. ;-)

  // Эта функция устарела. Новые приложения должны использовать
  // beginTransaction() для настройки параметров SPI.
  inline static void setClockDivider(uint8_t clockDiv) {
    SPCR = (SPCR & ~SPI_CLOCK_MASK) | (clockDiv & SPI_CLOCK_MASK);
    SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((clockDiv >> 2) & SPI_2XCLOCK_MASK);
  }

Но потом я увидел еще одну вещь, которая может быть проблемой. Существуют передаточные функции с одним значением. Один для байтов (uint8_t) и один для целых чисел (uint16_t). Ваша переменная count является целым числом, поэтому используется двухбайтовый метод передачи, в результате чего получается два байта на передачу и, следовательно, 512 байтов. Но это также не объяснение наблюдениям. А запись буферов работает только с uint8_t[], так что ??.

Что еще могло произойти?

Микроконтроллер может работать на более низкой частоте, чем 16 МГц. Я думаю, что есть предохранители, которые можно настроить для снижения основной тактовой частоты для использования MCU с более низким напряжением (например, 3,6 В).

Отсутствие beginTransaction / endTransaction (DataFlash не использует это) шины SPI может повлиять на прерывания, что может исказить синхронизацию. (Но это очень дикая догадка ;-))

Наблюдение за тем, что передача вдвое большего объема данных с помощью единственного метода передачи int занимает примерно столько же времени, сколько метод с буферизацией, дает нам подсказку, что, возможно, проблема не в скорости SPI.

В функции dataflash.bufferWrite(0, 0); есть, например, функция ожидания waitUntilReady();, что может привести к длительной задержке ~12 миллисекунд. Вы можете легко попробовать это, убрав dataflash.bufferWrite(0, 0); из тайминга.

void DataFlash::waitUntilReady()
{
    while(!isReady()) {};
}

uint8_t DataFlash::isReady()
{
    return status() & AT45_READY;
}

uint8_t DataFlash::status()
{
    uint8_t status;
    reEnable();     // Сброс декодера команд.
    SPI.transfer(DATAFLASH_STATUS_REGISTER_READ);
    status = SPI.transfer(0);
    disable();
    return status;
}
,

Вы правы, bufferWrite(0, 0) работает, потому что внутри это (среди прочего) SPI.transfer(bufferNum ? DATAFLASH_BUFFER_2_WRITE : DATAFLASH_BUFFER_1_WRITE);. Кроме того, я знаю о переносе буфера на страницу памяти, но это выходит за рамки этого вопроса (и также занимает около 50 мкс ;-)., @Raketenolli

Спасибо. Я примерно так и предполагал, но не был уверен. Я прочитал вопрос во второй раз и теперь, надеюсь, вижу проблему. Я обновлю ответ., @Peter Paul Kiefer

@Raketenolli Я обновил ответ., @Peter Paul Kiefer

Извините, что не вернулся к этому раньше, и теперь мне интересно, изменил ли я что-нибудь тогда, о чем сейчас забыл. Теперь я постоянно наблюдаю от 660 до 670 мкс для всей операции записи, что означает примерно 2,6 мкс на байт. Параметр SpeedMaximum в SPISettings действует, только если установлено значение менее 8000000, при этом продолжительность увеличивается до 940 мкс (а затем с шагом до 1520, 2500 и 4560)., @Raketenolli