Запись в eeprom через spi

Тема в разделе "ESP8266, ESP32", создана пользователем in_text, 16 фев 2020.

  1. in_text

    in_text Нуб

    Есть некая задача: записать в 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()
    {

    }
     
  2. parovoZZ

    parovoZZ Гуру

    а в даташите на микросхему разве нет алгоритма записи и чтения?
     
  3. in_text

    in_text Нуб

    Либо я слепой, либо там описаны только функции WREN, WRDI и т.д.
     
  4. parovoZZ

    parovoZZ Гуру

  5. in_text

    in_text Нуб

    Проблема точно в микре, может быть в коде какие-то косяки?
     
  6. Daniil

    Daniil Гуру

    1. Не видно, чтобы вы ждали когда запись завершиться.
    2. Не видно проверки готовности микросхемы к работе. Вы ей пуляете данные, а она от них очухаться не может.
    Попробуйте запрашивать и работать со статусным регистром и ввести задержку после записи данных и CS=HIGH
     
  7. in_text

    in_text Нуб

    Не знаю как работать со статусным регистром, но если разъясните - буду благодарен.
    Если вводить задержку после записи, а по даташиту сказано что это происходит после того, как на выводе CS установится логическая единица, данные по прежнему не запишутся (проверял задержку до 5с).
     
  8. in_text

    in_text Нуб

    Прочитал в даташите как работает RDSR, там говорится что если микра отвечает 0 - значит она готова к записи. Собственно вставил проверку на готовность после того, как даю команду на разрешение записи и в мониторе порта получил закономерный 0, что означает что микра готова к записи.
     
  9. Daniil

    Daniil Гуру

    Я правильно понимаю, что 1-й код работает на запись, а 2-й код на чтение?
    Можете, пожалуйста, привести схему? - есть ли у вас подтяжки на вывода ОК?
    Второй код мне нравится тем, что у него все операции проходят за 1 такт дёрганья CS.
    Игру с пином WP не понимаю, я бы для тестов зафиксировал бы его в одно состояние - в лог. "1". Кстати во 2-м коде проблема может быть в том, что когда вы начинаете записывать вы отправляете 2 команды в статусный регистр, хотя уже 2-я команда будет считаться старшим байтом адресом...и всё поехало, т.е. вы пишете не по тому адресу, по которому потом считываете.
    Держите задержку после записи не менее 5 мс. Лучше, по-началу, 10. Вы не заметите, а еепром понравится.
     
  10. in_text

    in_text Нуб

    Уже разобрался, задержка и правда нужна была, вот только это самое дерганье 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}
    Если есть что подсказать, то я жду предложений:)
     
  11. in_text

    in_text Нуб

    С этим тоже разобрался - всё оказалось проще.
    Код (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
    *комменты к коду ничего не проясняют*
     
  12. asam

    asam Гик

    Указываешь адрес следующей страницы и пишешь туда.
    А это и есть адрес по которому будет осуществлена запись.
     
  13. in_text

    in_text Нуб

    Т е если размер страницы 64 байта, то при записи на вторую страницу мне нужно указать такие параметры?
    Код (C++):
    SPI2.transfer(64);  // First address byte
    SPI2.transfer(64);  // Second address byte
     
  14. in_text

    in_text Нуб

    Не смог найти данные по адресу след. страницы, а тупое подставление чисел в (вместо 64)
    Код (C++):
    SPI2.transfer(64);  // First address byte
    SPI2.transfer(64);  // Second address byte
    Не дало никаких результатов, точнее как - результаты были, но данные записывались очень криво.
     
  15. asam

    asam Гик

    Нет. Адрес cостоит из 16 бит. А в байте 8 бит. Поэтому сначала передается старшая половина адреса, а потом младшая.
    И адрес начала следующей страницы будет
    SPI2.transfer(0);// First address byte
    SPI2.transfer(64);// Second address byte
     
    parovoZZ нравится это.
  16. in_text

    in_text Нуб

    Не получается записать, код выглядит так:
    Код (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 байта.
     
  17. Daniil

    Daniil Гуру

    Если вы пишете N байт подряд, то указываете адрес как показали выше, старший байт, потом младший.
    Вы, как программист, должны следить, чтобы начальный и конечный адреса не выползали за страницу.
    Страница 64 байта. 0х0000 - 0х003F - 1-я страница, 0x0040-0x007F - 2-я страница и т.д. Если вы будете писать по адресу 0x003F 2 байта подряд, то адрес 2-го байта должен быть (по плану) 0x0040, но микросхема этого не поймёт и запишет 2-й байт по адресу 0х0000. Поэтому если вы видите, что следующий байт переходит через страницу (остаток от деления адреса на 64 стал ноль И целочисленное деление начального адреса на 64 != целочисленному делению конечного адреса на 64), то должны прекратить запись и инициировать новую, с новым адресом на новой странице и продолжить писать незаписанные байты.
     
  18. in_text

    in_text Нуб

    Видимо я чего-то не понимаю в страницах, либо не правильно посылаю команды в 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);
     
  19. Daniil

    Daniil Гуру

    Попробуйте использовать
    delay(X);// где X>5
    По коду логической ошибки не видно. Гипотеза - пока запись не произошла (а через 2 мс запись НЕ завершилась) он игнорит новые поступающие данные.
     
  20. asam

    asam Гик

    Перед записью каждой страницы надо давать команду WREN.
    Приведите весь код, а не огрызки.
     
    Daniil нравится это.