Как быстро я должен иметь возможность записывать во флэш-память 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?
@Raketenolli, 👍1
Обсуждение1 ответ
Лучший ответ:
"Данные будут загружаться в буфер до тех пор, пока не переход обнаружен на выводе 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
- Какие контакты можно использовать для выбора микросхемы (CS, CC) на Arduino Nano Every?
- Более 4 MCP23S17 на 1 шине SPI
- Проблема с NRF24L01
- Аппаратный SPI Arduino NANO не работает
- Считыватель Rfid и экран SD-карты не работают вместе
- Nano не может получить ответ от ENC28J60 Ethernet Shield
- Проблема с SPI-коммуникациями с использованием NodeMCU v 3, Nano и SD
- Проблемы nRF24l01+. Данные принимаются как 0
Самая быстрая скорость, с которой может работать SPI, — это F_CPU/2, поэтому для Arduino с частотой 16 МГц это 8 МГц., @Majenko
Я бы прикрепил осциллограф и измерил; это лучший способ проверить, верны ли мои предположения о тактовой частоте и времени., @the busybee