Есть некая задача: записать в EEPROM память 3 числа, любых. Сделать это нужно через SPI. Облазив множество ссылок нашёл только один более-менее подходящий пример записи в память данных, но и он не работает на ESP32 толком - либо я что-то не умею, либо библиотека SPI.h работает как-то иначе. Однако я разобрался с тем как задействовать hspi и как записать через него данные, но появилась другая проблема, я могу записать данные только однократно: 1) Опустив уровень CS подаем команду WREN, поднимаем уровень CS 2) Опустив уровень CS подаем команду WRITE, поднимаем уровень CS Если делать так, то при чтении в порт выводятся одни нули, но если после этого изменить код: 1) Опустив уровень CS подаем команду WREN 2) Подаем команду WRITE, поднимаем уровень CS То данные запишутся, и всё бы хорошо, но если изменить цифры которые я только что записал для того, что бы записать новые значение - ничего не выйдет, они просто не запишутся и исправить это можно изменяя код как описано выше. Микросхема которую я использую AT25256B Собственно вопрос в том, как избавится от этого косяка и спокойно писать данные? Код в первом случае: Код (C++): #include <SPI.h> SPIClass SPI2(HSPI); const byte COMMAND_WRITE_ENABLE = 0x06; const byte COMMAND_WRITE_DATA = 0x02; const byte COMMAND_READ_DATA = 0x03; const byte chipSelectPin = 15; const int WP = 26; void setup() { Serial.begin(115200); pinMode(WP, OUTPUT); pinMode(chipSelectPin, OUTPUT); SPI2.begin(); SPI2.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE0)); pinMode(chipSelectPin, OUTPUT); digitalWrite(chipSelectPin, HIGH); Serial.println("Write enable"); digitalWrite(chipSelectPin, LOW); digitalWrite(WP, HIGH); SPI2.transfer(COMMAND_WRITE_ENABLE); digitalWrite(chipSelectPin, HIGH); Serial.println("Writing data"); SPI2.transfer(COMMAND_WRITE_DATA); digitalWrite(chipSelectPin, LOW); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte SPI2.transfer(1); SPI2.transfer(8); SPI2.transfer(9); digitalWrite(chipSelectPin, HIGH); digitalWrite(WP, LOW); Serial.println("Reading data"); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_READ_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte Serial.println(SPI2.transfer(0), DEC); Serial.println(SPI2.transfer(0), DEC); Serial.println(SPI2.transfer(0), DEC); digitalWrite(chipSelectPin, HIGH); SPI2.endTransaction(); } void loop() { } Код во втором случае: Код (C++): #include <SPI.h> SPIClass SPI2(HSPI); const byte COMMAND_WRITE_ENABLE = 0x06; const byte COMMAND_WRITE_DATA = 0x02; const byte COMMAND_READ_DATA = 0x03; const byte chipSelectPin = 15; const int WP = 26; void setup() { Serial.begin(115200); pinMode(WP, OUTPUT); pinMode(chipSelectPin, OUTPUT); SPI2.begin(); SPI2.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE0)); pinMode(chipSelectPin, OUTPUT); digitalWrite(chipSelectPin, HIGH); Serial.println("Write enable"); digitalWrite(chipSelectPin, LOW); digitalWrite(WP, HIGH); SPI2.transfer(COMMAND_WRITE_ENABLE); Serial.println("Writing data"); SPI2.transfer(COMMAND_WRITE_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte SPI2.transfer(1); SPI2.transfer(8); SPI2.transfer(9); digitalWrite(chipSelectPin, HIGH); digitalWrite(WP, LOW); Serial.println("Reading data"); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_READ_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte Serial.println(SPI2.transfer(0), DEC); Serial.println(SPI2.transfer(0), DEC); Serial.println(SPI2.transfer(0), DEC); digitalWrite(chipSelectPin, HIGH); SPI2.endTransaction(); } void loop() { }
Значит, что-то должно быть здесь: https://www.microchip.com/wwwproducts/en/AT25256B Я с этой микрой не сталкивался(
1. Не видно, чтобы вы ждали когда запись завершиться. 2. Не видно проверки готовности микросхемы к работе. Вы ей пуляете данные, а она от них очухаться не может. Попробуйте запрашивать и работать со статусным регистром и ввести задержку после записи данных и CS=HIGH
Не знаю как работать со статусным регистром, но если разъясните - буду благодарен. Если вводить задержку после записи, а по даташиту сказано что это происходит после того, как на выводе CS установится логическая единица, данные по прежнему не запишутся (проверял задержку до 5с).
Прочитал в даташите как работает RDSR, там говорится что если микра отвечает 0 - значит она готова к записи. Собственно вставил проверку на готовность после того, как даю команду на разрешение записи и в мониторе порта получил закономерный 0, что означает что микра готова к записи.
Я правильно понимаю, что 1-й код работает на запись, а 2-й код на чтение? Можете, пожалуйста, привести схему? - есть ли у вас подтяжки на вывода ОК? Второй код мне нравится тем, что у него все операции проходят за 1 такт дёрганья CS. Игру с пином WP не понимаю, я бы для тестов зафиксировал бы его в одно состояние - в лог. "1". Кстати во 2-м коде проблема может быть в том, что когда вы начинаете записывать вы отправляете 2 команды в статусный регистр, хотя уже 2-я команда будет считаться старшим байтом адресом...и всё поехало, т.е. вы пишете не по тому адресу, по которому потом считываете. Держите задержку после записи не менее 5 мс. Лучше, по-началу, 10. Вы не заметите, а еепром понравится.
Уже разобрался, задержка и правда нужна была, вот только это самое дерганье CS тоже нужно было. Сейчас всё выглядит вот так: Код (C++): #include <SPI.h> SPIClass SPI2(HSPI); const byte COMMAND_WRITE_ENABLE = 0x06; const byte COMMAND_WRITE_DATA = 0x02; const byte COMMAND_READ_DATA = 0x03; const byte RDSR = 0x05; const byte chipSelectPin = 15; const int WP = 26; void setup() { Serial.begin(115200); pinMode(WP, OUTPUT); pinMode(chipSelectPin, OUTPUT); SPI2.begin(); SPI2.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE0)); pinMode(chipSelectPin, OUTPUT); digitalWrite(chipSelectPin, HIGH); Serial.println("Write enable"); digitalWrite(chipSelectPin, LOW); digitalWrite(WP, HIGH); SPI2.transfer(COMMAND_WRITE_ENABLE); digitalWrite(chipSelectPin, HIGH); digitalWrite(chipSelectPin, LOW); int chkWriteStatus = SPI2.transfer(RDSR); digitalWrite(chipSelectPin, HIGH); if (chkWriteStatus == 0) { Serial.println("EEPROM ready for Writing!"); Serial.println("Writing data"); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_WRITE_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte for (int i = 0; i < 8; i++) { SPI2.transfer(i); // Write byte 1 } digitalWrite(chipSelectPin, HIGH); delay(2); } else { Serial.println("EEPROM not ready for Writing!"); } Serial.println("Reading data"); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_READ_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte for (int i = 0; i < 8; i++) { Serial.println(SPI2.transfer(i), DEC); } digitalWrite(chipSelectPin, HIGH); digitalWrite(WP, LOW); SPI2.endTransaction(); } void loop() { } По поводу WP - настроен он в соответствии с даташитом, вот только я не заметил как он влияет на работу. Сейчас, когда запись настроена, есть новая цель - записать массив, вот с этим тоже есть проблемы, поскольку библиотека как-то по особому, или это я особенный, записывает массив. Сам массив будет выглядеть как-то так, только значений больше будет: Код (C++): uint8_t data[] {0x01, 0x00, 0x05, 0x00, 0x08, 0x1C, 0x00, 0x58} Если есть что подсказать, то я жду предложений
С этим тоже разобрался - всё оказалось проще. Код (C++): uint8_t data[] {0x01, 0x00, 0x05, 0x00, 0x08, 0x1C, 0x00, 0x58}; if (chkWriteStatus == 0) { Serial.println("EEPROM ready for Writing!"); Serial.println("Writing data"); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_WRITE_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte for (int i = 0; i < 8; i++) { SPI2.transfer(data[i]); } digitalWrite(chipSelectPin, HIGH); delay(2); } Но остается два неясных момента: 1) Как работают страницы с памятью и как, если одна страница заполнена, перейти на вторую? 2) Для чего нужны вот эти две строчки и как ими грамотно, в случае надобности, пользоваться? Код (C++): SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte *комменты к коду ничего не проясняют*
Указываешь адрес следующей страницы и пишешь туда. А это и есть адрес по которому будет осуществлена запись.
Т е если размер страницы 64 байта, то при записи на вторую страницу мне нужно указать такие параметры? Код (C++): SPI2.transfer(64); // First address byte SPI2.transfer(64); // Second address byte
Не смог найти данные по адресу след. страницы, а тупое подставление чисел в (вместо 64) Код (C++): SPI2.transfer(64); // First address byte SPI2.transfer(64); // Second address byte Не дало никаких результатов, точнее как - результаты были, но данные записывались очень криво.
Нет. Адрес cостоит из 16 бит. А в байте 8 бит. Поэтому сначала передается старшая половина адреса, а потом младшая. И адрес начала следующей страницы будет SPI2.transfer(0);// First address byte SPI2.transfer(64);// Second address byte
Не получается записать, код выглядит так: Код (C++): uint8_t data[] {0x01, 0x00, 0x05, 0x00, 0x08, 0x1C, 0x00, 0x58, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x23, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x43, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE8, 0x01, 0x00, 0x01, 0x08, 0x20, 0x01, 0xFF, 0x68, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; Serial.println("Writing data"); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_WRITE_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte for (int i = 0; i < 64; i++) { SPI2.transfer(data[i]); } digitalWrite(chipSelectPin, HIGH); delay(2); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_WRITE_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(64); // Second address byte for (int i = 64; i < 128; i++) { SPI2.transfer(data[i]); } digitalWrite(chipSelectPin, HIGH); delay(2); Serial.println("Reading data"); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_READ_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(0); // Second address byte for (int i = 0; i < 128; i++) { Serial.println(SPI2.transfer(i), HEX); } digitalWrite(chipSelectPin, HIGH); digitalWrite(WP, LOW); SPI2.endTransaction(); Первые 64 байта записываются нормально, а дальше какой-то мусор, не похожий на следующие 64 байта.
Если вы пишете N байт подряд, то указываете адрес как показали выше, старший байт, потом младший. Вы, как программист, должны следить, чтобы начальный и конечный адреса не выползали за страницу. Страница 64 байта. 0х0000 - 0х003F - 1-я страница, 0x0040-0x007F - 2-я страница и т.д. Если вы будете писать по адресу 0x003F 2 байта подряд, то адрес 2-го байта должен быть (по плану) 0x0040, но микросхема этого не поймёт и запишет 2-й байт по адресу 0х0000. Поэтому если вы видите, что следующий байт переходит через страницу (остаток от деления адреса на 64 стал ноль И целочисленное деление начального адреса на 64 != целочисленному делению конечного адреса на 64), то должны прекратить запись и инициировать новую, с новым адресом на новой странице и продолжить писать незаписанные байты.
Видимо я чего-то не понимаю в страницах, либо не правильно посылаю команды в EEPROM. Сделал вот так и вторая страница просто не записывается: Код (C++): Serial.println("Writing data"); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_WRITE_DATA); SPI2.transfer(0); // First address byte SPI2.transfer(64); // Second address byte for (int i = 0; i < 64; i++) { SPI2.transfer(data[i]); } digitalWrite(chipSelectPin, HIGH); delay(2); digitalWrite(chipSelectPin, LOW); SPI2.transfer(COMMAND_WRITE_DATA); SPI2.transfer(64); // First address byte SPI2.transfer(128); // Second address byte for (int i = 64; i < 128; i++) { SPI2.transfer(data[i]); } digitalWrite(chipSelectPin, HIGH); delay(2);
Попробуйте использовать delay(X);// где X>5 По коду логической ошибки не видно. Гипотеза - пока запись не произошла (а через 2 мс запись НЕ завершилась) он игнорит новые поступающие данные.