Пример использования ESP PSRAM64(H) с Ардуино

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

Там в папке Docs есть описание всех возможностей.

Если не использовать PinOps (а это можно - см. документацию), то, по идее, должна работать с любыми ардуинами, т.к. используется только штатная библиотека SPI и "никаких фокусов". PinOps же имеет ограничения (например, с Мегой не работает).

Примеры использования (всё это и еще несколько есть на гитхабе).

1. Самый простой пример. Тупо пишем по 6 байтов в случайный адрес, а потом читаем оттуда:

#include <Printing.h>
#define	USE_PINOPS true
#include <ESP_PSRAM64.h>

ESP_PSRAM64 psram;

void setup(void) {
	Serial.begin(115200);
	Serial.println("Простой тест - пишем по 6 байтов в случайный адрес и читаем оттуда");
	delayMicroseconds(150);
	randomSeed(analogRead(0));
	psram.reset();
}

void loop(void) {
	// Буффер со случайным содержимым
	byte buffer[6];
	constexpr TSize size = sizeof(buffer);
	for (TSize i=0; i < size; i++) buffer[i] = random(0, 255);
	
	// Случайный адрес
	const TAddress address = random(0, psram.totalBytes - size);
	Serial << "Адрес: " << address << "\r\n";

	Serial << " Пишем: ";
	psram.write(address, buffer, size);
	for (TSize i=0; i < size; i++) Serial << (unsigned) buffer[i] << ' ';
	Serial.println();

	Serial << "Читаем: ";
	psram.read(address, buffer, size);
	for (TSize i=0; i < size; i++) Serial << (unsigned) buffer[i] << ' ';
	Serial.println();

	delay(2000);
}

Результат работы:

Простой тест - пишем по 6 байтов в случайный адрес и читаем оттуда
Адрес: 5642841
 Пишем: 74 237 72 4 188 26 
Читаем: 74 237 72 4 188 26 
Адрес: 1152200
 Пишем: 19 154 183 213 153 118 
Читаем: 19 154 183 213 153 118 
Адрес: 5862110
 Пишем: 87 41 173 152 173 140 
Читаем: 87 41 173 152 173 140 
Адрес: 1044770
 Пишем: 245 235 208 102 38 59 
Читаем: 245 235 208 102 38 59 
Адрес: 7078519
 Пишем: 249 80 244 252 87 31 
Читаем: 249 80 244 252 87 31 
........ и т.д.

2. Запись и чтение сложных типов данных, включая массивы и структуры

#include <Printing.h>
#define USE_PINOPS	true
#include <ESP_PSRAM64.h>

class CAnekdot : public Printable {
	char m_origin[16];
	char m_body[100];
	int m_id;

public:
	CAnekdot(void) {}
	CAnekdot(const int id, const char * origin, const char * body) {
		strcpy(m_origin, origin);
		strcpy(m_body, body);
		m_id = id;
	}
	size_t printTo(Print& p) const {
		return 
			p.print(m_id) 
			+ p.print(", (")
			+ p.print(m_origin)
			+ p.println(')')
			+ p.println(m_body);
	}
};

ESP_PSRAM64 psram;

void setup(void) {
	Serial.begin(115200);
	Serial.println("ESP PSRAM64H тест методов put и get\r\n");
	delayMicroseconds(150);
	psram.reset();

	//
	// Число с плавающей точкой
	float pi = M_PI;
	Serial << "Исходное число (float): " << pi << "\r\n";
	psram.put(1234, pi); // запишем
	pi = 0; // загадим
	psram.get(1234, pi); // прочитаем
	Serial << "Прочитанное число (float): " << pi << "\r\n\r\n";

	//
	// Число uint32_t
	uint32_t f_cpu = F_CPU;
	Serial << "Исходное число (uint32_t): " << f_cpu << "\r\n";
	psram.put(1234, f_cpu); // запишем
	f_cpu = 0; // загадим
	psram.get(1234, f_cpu); // прочитаем
	Serial << "Прочитанное число (uint32_t): " << f_cpu << "\r\n\r\n";

	//
	// Массив char[]
	char str[] = "\"Жил один еврей, так он сказал, что всё проходит.\"";
	Serial << "Исходный массив: " << str << "\r\n";
	psram.put(1234, str); // запишем
	str[0] = '\0'; // загадим
	psram.get(1234, str); // прочитаем
	Serial << "Прочитанный массив: " << str << "\r\n\r\n";

	// Исходная структура (запишем её по адресу 1234)
	CAnekdot source(321, "Одесса", "- Сёма, Вы таки куда?\r\n- Да, нет, я домой.");
	psram.put(1234, source);
	Serial.println("::: Исходная структура :::");
	Serial.println(source);

	// Прочитанная из PSRAM структура
	CAnekdot dest;
	psram.get(1234, dest);
	Serial.println("::: Прочитанная структура :::");
	Serial.println(dest);

	Serial.println("=== Тест завершён ===");
}

void loop(void) {}

Результат работы

ESP PSRAM64H тест методов put и get

Исходное число (float): 3.14
Прочитанное число (float): 3.14

Исходное число (uint32_t): 16000000
Прочитанное число (uint32_t): 16000000

Исходный массив: "Жил один еврей, так он сказал, что всё проходит."
Прочитанный массив: "Жил один еврей, так он сказал, что всё проходит."

::: Исходная структура :::
321, (Одесса)
- Сёма, Вы таки куда?
- Да, нет, я домой.

::: Прочитанная структура :::
321, (Одесса)
- Сёма, Вы таки куда?
- Да, нет, я домой.

=== Тест завершён ===

3. Измерение скорости записи и чтения

#include <Printing.h>
#define USE_PINOPS	true
#include <ESP_PSRAM64.h>

ESP_PSRAM64 psram;

void printReport(const char * title, const uint32_t duration) {
	uint32_t bytesPerSecond = (static_cast<uint64_t>(psram.totalBytes) * 1000 + duration / 2) / duration;
	const uint32_t spiBytesPerSecond = F_CPU / 16;
	uint32_t percent = (bytesPerSecond * 100 + spiBytesPerSecond / 2) / spiBytesPerSecond;
	Serial << title << "\r\n   " << (duration / 1000) << "с " 
		<< (duration % 1000) << "мс; " << bytesPerSecond 
		<< " байтов в секунду - " << percent << "% от скорости SPI\r\n";
}

void setup(void) {
	Serial.begin(115200);
	Serial.println("=== Тест на скорость чтения и записи ===");
	delayMicroseconds(150);
	psram.reset();
	//
	//	Тест на скорость заполнения (заполняем 8МБ нулями)
	uint32_t start = millis();
	psram.fill(0, psram.totalBytes);
	printReport("Заполнение 8МБ", millis() - start);
	Serial.flush();
	//
	//	Тест на скорость записи (пишем 8МБ блоками по 1кБ)
	uint8_t buffer[1024];
	const uint16_t sz = sizeof(buffer);
	start = millis();
	for (TAddress addr = 0; addr < psram.totalBytes; addr += sz) {
		psram.write(addr, buffer, sz);
	}
	printReport("Запись 8МБ блоками по 1кБ", millis() - start);
	Serial.flush();
	//
	//	Тест на скорость чтения (читаем 8МБ блоками по 1кБ)
	start = millis();
	for (TAddress addr = 0; addr < psram.totalBytes; addr += sz) {
		psram.read(addr, buffer, sz);
	}
	printReport("Чтение 8МБ блоками по 1кБ", millis() - start);
	//
	Serial.println("=== Тест завершён ===");
}

void loop(void) {}

Результат работы

=== Тест на скорость чтения и записи ===
Заполнение 8МБ
   23с 270мс; 360490 байтов в секунду - 36% от скорости SPI
Запись 8МБ блоками по 1кБ
   28с 657мс; 292725 байтов в секунду - 29% от скорости SPI
Чтение 8МБ блоками по 1кБ
   29с 186мс; 287419 байтов в секунду - 29% от скорости SPI
=== Тест завершён ===

Результат работы без использования PinOps (с удалённой из кода примера второй строкой)

Заполнение 8МБ
   26с 330мс; 318595 байтов в секунду - 32% от скорости SPI
Запись 8МБ блоками по 1кБ
   31с 724мс; 264425 байтов в секунду - 26% от скорости SPI
Чтение 8МБ блоками по 1кБ
   32с 254мс; 260080 байтов в секунду - 26% от скорости SPI
=== Тест завершён ===

Как видите, скорость не ахти, но что есть, то есть.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

В догонку!

Запустил на своей второй микросхеме PSRAM - на микрочиповской 23LC1024. Заработало всё и сразу с минимальными изменениями - буквально пару строк удалил и всё также работает. На той неделе объединю библиотеки в одну и выложу в тот же репозиторий. Кому надо - возьмёте.

Хочу поделиться впечатлениями.

Микросхема от микрочипа понравилась гораздо больше. Она, конечно, проигрывает по объёму (128к против 8М) (и по частоте - 20М против 33, но нам это неважно - у нас всё равно столько нет в ардуине).

В остальном же она намного приятнее.

Во-первых в сто раз выигрывает по потреблению в режиме standby - а это значит можно присобачить на её питание таблетку CR2032 через диодную развязку и использовать как еепром - на 4+ года хватит. А если присобачить два холдера под батарею, то можно и горячую замену использовать.

Во-вторых, она стабильнее. Например и та и другая позволяет писать за один присест хоть всю память. Но микрочиповская реально это позволяет, а у ЕСП-шной при долгом удержании CE начинают проскакивать сбои (нечастые, но ...) - надо периодически останавливать запись и начинать снова (что и я делаю в библиотеке, а для микрочиповской это не нужно) . Может тут виной то, что у меня схема собрана на соплях макетке - не знаю, но она же для обеих микросхем одинаково собрана.

Наконец, микрочиповская имеет более широкий диапазон питания.

См. сравнительную таблицу:

  ESP PSRAM64 23LC1024
Память 128к
Частота SCK 33/133/144МHz 20МHz
Питание 2,7V - 3,6V 2,5V - 5,5V
Минимум при котором память ещё сохраняется нет инф. 1V
Потребление    
   при записи 40mA 1-3mA
   в ожидании 200µA 1-4µA
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Мне кажется, "первые впечатления" несколько поверхностные.

Чтобы претендовать на объективность, микросхемы должны были бы иметь сопоставимый объем памяти.

А так получается:

1. - в 64 раза меньший объем,

2. - в 100 раз меньше потребление в режиме standby,

3. - в 20 раз меньше потребление при записи,

4. - в полтора раза ниже максимальная частота,

5. - в полтора раза выше максимальное напряжение,

6. - зафиксирована более стабильная работа.

Собственно, 2 и 3 являются очевидными следствиями 1.

Очевидно, что и 5 также является следствием 1.

Ну а 6 в свою очередь, весьма вероятно, является следствием 3, т.е. тоже следствием 1. Я бы еще поэкспериментировал, установив керамику в непосредственной близости выводов питания.

Другое дело, что для Atmel328 тяжело придумать задачу, где бы требовалось более 128к памяти, следовательно, 8М практически не имеет преимуществ, а недостатки остаются.

В этом смысле интересно попробовать ее с stm32f103, где баланс между реальными плюсами и реальными минусами будет совсем другой (отсутствие 5В - уже не недостаток, а частота - явное преимущество).

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

А что - мило! Два замечания.

1. А что все-таки не сделал шаблон для вектора? put/get для массивов народу будут неудобны, в памяти копию держать надо ;)

2. Комменты и pdf-ка на русском. Гит - не сильно русский ресурс. Сперва на аглицком, а для души - можно и на родном! ;)))

----

ессно, что оба номера - просто шутка и придирки... ну шоп не промолчать! ;)))

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

До вектора и вообще высокоуровневых дел пока руки не дошли. Это то, о чём я изначально писал как о "низкоуровнеовм драйвере". put с get'ом затесались просто потому, что "бесплатно".

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Евгений Петрович, у Вас в pdf-ке в таблице на первой странице перепутаны MOSI и MISO.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да? Щас гляну.

Спасибо, поправил.

b707
Offline
Зарегистрирован: 26.05.2017

ЕвгенийП пишет:

значит можно присобачить на её питание таблетку CR2032 через диодную развязку и использовать как еепром - на 4+ года хватит.

вот эту фразу не понял - а без внешнего питания она разве данные не сохраняет?

Добавка - почитал даташит, про сохранение данных ничего не сказано... мда, не знал. что такое бывает :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

b707,

а в даташите на микрочиповскую PSRAM 23LC1024 (топик же теперь уже про две сразу) , так там прямо сказано: "RAM Data Retention Voltage: 1V". Так что ни о каком сохранении и речи нет.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Сегодня (точнее, уже вчера) провел тесты устойчивости работы PSRAM64H на частоте 36 МГц. Из более миллиарда итераций записи/чтения шестибайтового фрагмента по случайным адресам не зафиксировано ни одной ошибки. Конденсатор по питанию емкостью 0.1 в плату не впаивал - стоял рядом на беспаечной макетке.

В общем, в процессе записи/чтения сбоев не зафиксировано. Можно, конечно, попытаться еще проверить на надежность хранения. Например, заполнить всю память по ДПСЧ, вернуть "затравку" в исходное состояние и через некоторое время проверить записанное на совпадение с вновь сгенерированной ППСЧ.

Но скорость - разочаровывает. Раз в 5 меньше, чем можно было ожидать. Логанализатор показал на огромные паузы между однобайтовыми посылками. Думаю, это проблема реализации SPI.h для пилюли.

b707
Offline
Зарегистрирован: 26.05.2017

andriano пишет:

Но скорость - разочаровывает. Раз в 5 меньше, чем можно было ожидать. Логанализатор показал на огромные паузы между однобайтовыми посылками. Думаю, это проблема реализации SPI.h для пилюли.

а писали/читали побайтно или сериями? - вроде как микросхема поддерживает оба варианта (и еще целыми страницами)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

andriano пишет:

записи/чтения шестибайтового фрагмента по случайным адресам

Попробуйте, пожалуйста, записывать всю память одним разом (за одну #CE) или хотя бы по 10-100кб за раз. Я наблюдал проблемы именно при таком режиме).

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Писал кусочками по 6 байт и другие варианты не пробовал.

Собственно, и за эти тесты взялся потому, что возникла идея использовать ее в одном проекте. Там по специфике запись/чтение будут осуществляться исключительно по 8 байт. Естественно, с таким же выравниванием. Поэтому заведомо без пересечения границы страницы. 

Дойдут руки - посмотрю, что получается при записи больших фрагментов. Правда, не совсем понятно, откуда брать такое количество данных: ДПСЧ - это довольно долго, т.е. темп записи получится невысоким. А для высокого - только писать либо константу, либо буфер небольшого объема (хотя бы те же 8-16к) в цикле.

Евгений, опишите подробнее, в каких именно условиях появлялись проблемы.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

andriano пишет:
опишите подробнее, в каких именно условиях появлялись проблемы.

1. Берём пример ValidationTest.ino из моей библиотеки. В нём ничего не меняем. Он пишет пол-памяти кусками по килобайту случайных значений, потом копирует эти пол-памяти во вторую половину (также блоками по килобайту) а потом читает из двух половин и сравнивает на равенство.

2. Берём собственно библиотеку и в ней находим макрос

#define MULTIBYTE_OPERATION(_operation, _dataOp) \
	for (TSize i = 0; i < size; ) { \
		opStart(_operation, address + i); \
		for (uint8_t j = 0; j < dataBlockSize && i < size; j++, i++) _dataOp; \
		highCE(); \
	}

и заменяем цикл в строке №4 (именно от отвечает за резание операции на куски) на просто _dataOp;, а строки №№3 и 5 выносим из цикла (при этом в строке №3 убираем "+i"). По идее должно быть как у меня было изначально - пишет "сколько дали" одним куском (с одним опусканием CE).

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Петрович, почту проверь

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ответил

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Собственно, добавил в setup последовательную запись. Публикую непричесанный исходник (в частности, комментарии не всегда соответствуют реальности)

static int do_rand (unsigned long *ctx)
{
/*
 * Вычисляет x = (7^5 * x) mod (2^31 - 1)
 * с переполнением по 31 битам:
 *      (2^31 - 1) = 127773 * (7^5) + 2836
 * Из публикации "Генераторы случайных чисел: хорошие найти трудно"
 * ("Random number generators: good ones are hard to find")
 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
 * October 1988, p. 1195.
 */
   long hi, lo, x;
   /* Нельзя инициализировать нулем, поэтому будем использовать
      другое значение. */
   if (*ctx == 0)
      *ctx = 123459876;
   hi = *ctx / 127773;
   lo = *ctx % 127773;
   x = 16807 * lo - 2836 * hi;
   if (x < 0)
      x += 0x7fffffff;
   return ((*ctx = x) % ((u_long)RAND_MAX + 1));
}
int rand_r(unsigned int *ctx)
{
   u_long val = (u_long) *ctx;
   int r = do_rand(&val);
   *ctx = (unsigned int) val;
   return (r);
}

static u_long next = 1;

int rand (void)
{
   return (do_rand(&next));
}

void srand(u_int seed)
{
   next = seed;
}

//#include <Printing.h>
//#define	USE_PINOPS true
#define  PSRAM64_CE_PIN PA4
#include <SPI.h>
#include <ESP_PSRAM64.h>

ESP_PSRAM64 psram;

const int size = 16384;
byte bufOut[size];

void setup(void) {
	Serial.begin(115200);
  while(!Serial);
	Serial.println("Simple test - write 6 bytes to random address and read from here");
	delayMicroseconds(150);
//	randomSeed(analogRead(0));
  srand(analogRead(0));
	psram.reset();
  srand(1234567890);
  
  int t0 = millis();
  for(int i = 0; i < size; i++) bufOut[i] = rand()&0xFF;
  int t1 = millis();
  Serial.print("fill buffer time(ms): ");
  Serial.println(t1-t0);

  t0 = millis();
  for(int i = 0; i < (8192*1024); i += size)
    psram.write(i, bufOut, size);
  t1 = millis();
  Serial.print("write to PSRAM time(ms): ");
  Serial.println(t1-t0);

  int numErrors = 0;
  t0 = millis();
  for(int i = 0; i < (8192*1024); i++) {
    if((i % size) == 0)
      srand(1234567890);
    byte b0, b1;
    psram.read(i, &b0, 1);
    b1 = rand()&0xFF;
    if(b0 != b1) {
      Serial.print("   !!! PSRAM ERROR !!!, address: ");
      Serial.print(i);
      Serial.print(", written: ");
      Serial.print(b1);
      Serial.print(", read: ");
      Serial.println(b0);
      numErrors++;
    }
  }
  t1 = millis();
  Serial.print("read from PSRAM time(ms): ");
  Serial.println(t1-t0);
  Serial.print(numErrors);
  Serial.println(" errors found");
  Serial.println();
  
}

const bool DEBUG = false;
const byte bb[2][6] = {{0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA},{0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55}};

void loop(void) {
	// Буфер со случайным содержимым
  const int size = 6;
  static int counter = 0;
	byte buffer_out[size], buffer_in[size];
//	constexpr TSize size = sizeof(buffer);
//	for (TSize i=0; i < size; i++) buffer_out[i] = rand()&0xFF;  // random(0, 255);
//  for (TSize i=0; i < size; i++) buffer[i] = i+48;  // random(0, 255);
  for (TSize i=0; i < size; i++) buffer_out[i] = bb[counter & 1][i];  // random(0, 255);
	
	// Случайный адрес
	const TAddress address = rand()%(psram.totalBytes - size); // random(0, psram.totalBytes - size);
  if(DEBUG) {
    Serial.print("Address: ");
    Serial.println(address);
  	Serial.print(" Writing: ");
  }
	psram.write(address, buffer_out, size);
  if(DEBUG) {
  	for (TSize i=0; i < size; i++) {
	    Serial.print((unsigned) buffer_out[i]);
	    Serial.print(' ');
	  }
  	Serial.println();
  	Serial.print("Reading: ");
  }
  psram.read(address, buffer_in, size);
  int j = 0; // счетчик ошибок
  static int j0 = 0;
	for (TSize i=0; i < size; i++) {
    if(DEBUG) {
	    Serial.print((unsigned) buffer_in[i]);
	    Serial.print(' ');
    }
    j += (buffer_in[i] != buffer_out[i]);
	}
  if(DEBUG) {
  	Serial.println();
  }
 if(j) Serial.println("   !!!   ERROR   !!!");
 j0 += j;
  counter++;
  if(((counter%100000) == 0) || DEBUG) {
    Serial.print("Iterations: ");
    Serial.print(counter);
    Serial.print(", Errors: ");
    Serial.print(j0);
    Serial.print(", Elapsed time: ");
    Serial.print(millis()/1000.0);
    Serial.println(" s");
    if(DEBUG)  delay(2000);
  }
}

Тест проходит однократно, после чего переходим к непрерывному 6-байтовому тесту. Результат:

Simple test - write 6 bytes to random address and read from here
fill buffer time(ms): 17
write to PSRAM time(ms): 11412
read from PSRAM time(ms): 61164
0 errors found

Iterations: 100000, Errors: 0, Elapsed time: 78.72 s
Iterations: 200000, Errors: 0, Elapsed time: 81.48 s
Iterations: 300000, Errors: 0, Elapsed time: 84.24 s
Iterations: 400000, Errors: 0, Elapsed time: 87.00 s
Iterations: 500000, Errors: 0, Elapsed time: 89.76 s
Iterations: 600000, Errors: 0, Elapsed time: 92.52 s

Это, естественно, в том варианте, который был, т.е. с принудительной разбивкой на 32-байтовые блоки.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Сейчас сделаю в соответствии с рекомендациями, но должен заметить, что сравнение прочитанного с прочитанным же - не очень надежный тест.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Що маємо, то маємо :-)

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Да уж...

"Ничего не меняем"- это очень оптимистично. Там и pinOps и Printing... Я как-то раньше просматривал Ваши исходники глазами и не пытался их откомпилировать, а тут...

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

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

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Что-то не могу я пропихнуть эту конструкцию. Может, чего-то недопонял.

//#define OLD_VARIANT

#ifdef OLD_VARIANT

#define MULTIBYTE_OPERATION(_operation, _dataOp) \
	opStart(_operation, address); \
	for (TSize i = 0; i < size; ) { \
		_dataOp; \
	}; \
	highCE()

#undef  OLD_VARIANT
#else

#define MULTIBYTE_OPERATION(_operation, _dataOp) \
	for (TSize i = 0; i < size; ) { \
		opStart(_operation, address + i); \
		for (uint8_t j = 0; j < dataBlockSize && i < size; j++, i++) _dataOp; \
		highCE(); \
	}
#endif

Вот в таком варианте работает. А стоит раскомментировать первую строку - глухо. Даже строки-заставки не выводит.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

В седьмой строке не хватает i++ в цикле.

Необходимость Pinops убирается константой - она во второй строке примера.

yuhenotix@2p-ma...
Offline
Зарегистрирован: 09.06.2019

Нормально!

А есть что-то подобное для SPI EEPROM ? Я  не тороплюсь, пусть медленнее записывало бы но  при выключении не стиралось. Или изображения хранить или  звуки.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

yuhenotix@2p-mail.com пишет:

А есть что-то подобное для SPI EEPROM ? 

Кто-то выкладывал, поищите.

А вообще, смотря что за епром, а то для некоторых на гитхабе полно библиотек.

yuhenotix@2p-ma...
Offline
Зарегистрирован: 09.06.2019

Там чтение   в основном, работы с  записью с предварительным стиранием страниц  не видел я. А EEPROM  w25q32 64 128 ... такие и аналоги.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ЕвгенийП пишет:

В седьмой строке не хватает i++ в цикле.

Ну да, когда мысли сосредоточены уже на следующем устройстве для проекта, проявляется такая рассеянность.

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, и слава Богу, значит источником ошибок была моя макетка с её сопливой сборкой.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Все может быть.

Кстати, по коду данные перемежаются командами раз в 32 байта, но происходит это не на границе страниц (что было бы логично), а просто через каждые 32 байта от начала указанного блока. Почему так?

И еще. Я, наверное, не слишком внимательно читал дэйташит, но что-то не смог там обнаружить данные, какой максимальный ток допустим через выводы данных. А то хочется сопрячь MISO с 5-вольтовыми узлами обычным резистивным делителем, вот и выбираю номиналы. Пока ориентируюсь на 470/1к или 330/680.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

andriano пишет:
раз в 32 байта, но происходит это не на границе страниц (что было бы логично), а просто через каждые 32 байта от начала указанного блока. Почему так?

Потому, что в режиме "Linear Burst", который я использую, при частотах до 84МГц границы страниц абсолютно пофигу (раздел 4, стр. 8 даташита).

hans-zemmer
Offline
Зарегистрирован: 30.06.2021

Евгений, есть вопрос по питанию ESP-шного чипа. 
Пробовал запускать простой пример simpletest. Верно читает из памяти с вероятностью примерно 70% только если руками держу преобразователь уровней . Как только отпускаю - все. Либо нули, либо 255. 3,3 вольта подаю на преобразователь с импульсного понижающего dc/dc. Хотел с ардуины (нано) запитать, но она чего-то 3.3 не выдает, что-то там сломалось. Насколько требователен этот чип к питанию? Есть нюансы?

Задача вообще такая: целый день собираюсь писать данные с датчиков пачками по 32 байта в эту память с интервалом в 1 секунду, потом по нажатию кнопки нужно все данные аккуратно перенести на флешку. 

hans-zemmer
Offline
Зарегистрирован: 30.06.2021

Разобрался. Заработало. На преобразователе уровней контакт OE надо подключить к питанию. 

b707
Offline
Зарегистрирован: 26.05.2017

"Если не работает - прочтите, наконец, инструкцию!" (С)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

hans-zemmer пишет:

Разобрался. Заработало. На преобразователе уровней контакт OE надо подключить к питанию. 

Блин, ну как же вы все задолбали-то! Ну, написано же в приклеенной теме для новичков, что при вопросе обязательно должны быть скетч и схема. Почему вы никогда их не приводите? Вот как Вам можно помочь, если Вы в секрете держите чего там намудрили?