Работа с внешней EEPROM (24Cxx)

Тема в разделе "Глядите, что я сделал", создана пользователем CryNET, 28 янв 2016.

  1. CryNET

    CryNET Гик

    Всем привет!

    Появилась необходимость в работе с внешней EEPROM, но в инете для Arduino дефицит рабочих примеров (я не нашел, а те, что находил просто не работали)
    Но путем проб и ошибок получилось найти рабочее решение.

    Накидаю сюда что-то типо методички, возможно кому-то поможет.
    upload_2016-1-28_15-16-30.png

    Тут все просто: A0, A1 и A2 - отвечают за адрес устройства на шине I2C (Об этом чуть позже)
    WP - защита от записи (Если подключить к "плюсу" - нельзя записывать, к "минусу" - можно записывать)
    VCC - "плюс" (+5V)
    GND - "минус"
    SDA и SCL - шины I2C

    Адрес устройства.
    upload_2016-1-28_15-16-2.png
    Тут так же все просто.
    В двоичной системе адрес устройства выглядит так:
    1010xxx
    Пины A0, A1 и A2 (P2, P1 и P0 тоже самое, что и A2, A1 и A0) отвечают за этот адрес. Если один из пинов подключить к минусу (GND) - это "0", а к плюсу - "1".
    На платах часто встречаются EEPROM, у которых все три пина (A0, A1, A2) подключены к минусу (GND), следовательно адрес будет таким: 1010000, что в шестнадцатеричной системе будет 0x50.
    Это обосновано тем, что на шине чаще всего одна EEPROM (В теории может быть до 8 шт.).

    Таким образом, если я подключу пин A0 к "плюсу" а остальные (A1 и A2) к "минусу" адрес такого устройства будет 1010001 -> 0x51. И т.д.

    Подключение.
    upload_2016-1-28_15-57-4.png
    Подключаем 2 EEPROM'ки (не забываем про адреса), SDA - шина данных, SCK(SCL) - синхронизация.
    У Arduino UNO A5 - SCL, A4 - SDA.

    Код.
    Функция записи в EEPROM:
    Код (C++):
    void EEPROM_WriteByte(byte dev, byte Address, byte data)
    {
      Wire.beginTransmission(dev);
      Wire.write(Address);
      Wire.write(data);
      delay(5); //Не знаю точно, но в Datasheet описана задержка записи в 5мс, поправьте меня, если я не прав.
      Wire.endTransmission();
    }
    Функция чтения EEPROM:
    Код (C++):
    //Вроде задержки при чтении не нужно
    byte EEPROM_ReadByte(int dev, byte Address) {
      byte rdata = 0xFF;
      Wire.beginTransmission(dev);
      Wire.write(Address);
      Wire.endTransmission();
      Wire.requestFrom(dev, 1);
      if (Wire.available()) rdata = Wire.read();
      return rdata;
    }
    Пример использования:
    Запись:
    Код (C++):
    #define DEVICE_1 0x50 //Адрес устройства

    Serial.print("Write to 24C04 . . . ");
    for (int i = 0; i < 512; i++) { //У 24C04 512 байт памяти
      EEPROM_WriteByte(DEVICE_1, i, 0xAB);
    }
    Serial.println("Done.");
    //Считать легко:
    //24C04 - 04 означает 4 килобита (24C08 - 8 килобит и т.д.)
    //килобиты - не килобайты, не путать!
    //4 килобита = 4096 бит
    //из бита в байт переводится путем деления на 8:
    //4096/8=512 байт
    Чтение:
    Код (C++):
    #define DEVICE_1 0x50 //Адрес устройства

    int val=0;
    Serial.println("Read 24C04: ");
    for (int i = 0; i < 512; i++) {
      Serial.print(EEPROM_ReadByte(DEVICE_1, i), HEX);
      Serial.print(" ");
      val++;
      if(val >= 8){ val=0; Serial.println(); } //Для удобства, в строчку по 8 байт
    }
    Вроде все, буду рад, если кому-то пригодится для быстрого старта :)
    Если есть вариант на чистом Си или вариант более лучше предложенного, буду рад лицезреть :)

    UPD: Datasheet http://www.atmel.com/images/doc0180.pdf
     
    Последнее редактирование: 28 янв 2016
    BGreen, DetSimen, Максим B и 7 другим нравится это.
  2. Alex19

    Alex19 Гуру

    У меня нет, только для AT24C256 (хотя, думаю они похожи, не вникал), если интересно, можете посмотреть тут - http://forum.amperka.ru/threads/Биб...-pcf8574-pcf8575-ms5803-30ba.5973/#post-49598.

    Но если погуглите, наверняка найдется, к примеру 24LC16 - http://avrdevices.ru/podkluchaem_pamyat_24lc16_k_avr/.
     
  3. CryNET

    CryNET Гик

    Спасибо, я посмотрю.

    Начал разбираться и переходить с Wiring на чистый Си, сейчас разбираюсь с понятиями и программаторами.
    Заодно спрошу: прогера нету, все что смог это спаять LPT "5 проводков" и вычитал о ArduinoISP. И интересно мнение - что тут лучше?
    Я полагаю что ответ будет AVRISP и если так, то является ли он полноценным программатором?
    Так же хочу у знающих спросить хорошую литературу где по-большей части не само программирование на Си, а теоретическая часть о контроллерах (avr).
     
  4. Alex19

    Alex19 Гуру

    Сам просто пишу на Си в Arduino IDE, не используя программаторы. Пока не требовалось работать с чипа отличными от тех, что установлены на Ардуинах.

    Поэтому не подскажу, как и по книгам.
    Удачи в изучении!
     
    CryNET нравится это.
  5. RA4PVJ

    RA4PVJ Нуб

    Я ещё только начинаю заниматься программированием, возникла такая задача как скопировать всё содержимое микросхемы 24С16. По вашему примеру мне не понятно какие библиотеки arduino использовать. Не могли бы Вы мне прислать какой-нибудь готовый пример. Спасибо!
     
  6. CryNET

    CryNET Гик

    Извините, что очень долго отвечал - на почту не заходил месяца 2 :)
    Если вам только скопировать, то я вам советую посмотреть в сторону PonyProg и приблуды для него. Она вроде легко паяется.
    Библиотеку я использовал стандартную, встроенную в среду Arduino IDE:
    https://www.arduino.cc/en/Reference/Wire
     
  7. elarm

    elarm Нуб

    При попытке использовать 24LC02B, обнаружено, что она занимает одним корпусом сразу по восемь адресов. Она игнорирует мои установки A0, A1 и A2. Если я создаю гирлянду из 2-х корпусов с адресами 0х50 и 0х51, то данные пишутся сразу в оба корпуса. При чтении так же есть ответ, похоже, что от обоих корпусов.

    Вот результаты сканирования шины I2C, при установке на неё всего одного корпуса 24LC02B:

    Scanning...
    I2C device found at address 0x50 !
    I2C device found at address 0x51 !
    I2C device found at address 0x52 !
    I2C device found at address 0x53 !
    I2C device found at address 0x54 !
    I2C device found at address 0x55 !
    I2C device found at address 0x56 !
    I2C device found at address 0x57 !
    done

    Сканер обнаруживает ВОСЕМЬ устройств!!! Что это за стрём такой? И как тут быть?
     
  8. Igor68

    Igor68 Гуру

    А Вы точно не перепутали - это точно 24C02B, а не 24C16. У меня трудно читаемо название... может повнимательнее посмотрите
    В моём случае 24С02:
    24c02.jpg
    для 24с04:
    24c04.jpg
    и для 24с16:
    24c16.jpg
    вырезка из документации
    .............
    DEVICE/PAGE ADDRESSES (A2, A1, A0): The A2, A1 and A0 pins are device
    address inputs that are hard wired for the AT24C01A and the AT24C02. As many as
    eight 1K/2K devices may be addressed on a single bus system (device addressing is
    discussed in detail under the Device Addressing section).
    The AT24C04 uses the A2 and A1 inputs for hard wire addressing and a total of four 4K
    devices may be addressed on a single bus system. The A0 pin is a no connect and can
    be connected to ground.
    The AT24C08A only uses the A2 input for hardwire addressing and a total of two 8K
    devices may be addressed on a single bus system. The A0 and A1 pins are no connects
    and can be connected to ground.
    The AT24C16A does not use the device address pins, which limits the number of
    devices on a single bus to one. The A0, A1 and A2 pins are no connects and can be
    connected to ground.
    .............
    AT24C01A, 1K SERIAL EEPROM: Internally organized with 16 pages of 8 bytes each,
    the 1K requires a 7-bit data word address for random word addressing.
    AT24C02, 2K SERIAL EEPROM: Internally organized with 32 pages of 8 bytes each,
    the 2K requires an 8-bit data word address for random word addressing.
    AT24C04, 4K SERIAL EEPROM: Internally organized with 32 pages of 16 bytes each,
    the 4K requires a 9-bit data word address for random word addressing.
    AT24C08A, 8K SERIAL EEPROM: Internally organized with 64 pages of 16 bytes
    each, the 8K requires a 10-bit data word address for random word addressing.
    AT24C16A, 16K SERIAL EEPROM: Internally organized with 128 pages of 16 bytes
    each, the 16K requires an 11-bit data word address for random word addressing.
    ...........
    The 1K, 2K, 4K, 8K and 16K EEPROM devices all require an 8-bit device address word
    following a start condition to enable the chip for a read or write operation (refer to Figure
    7).
    The device address word consists of a mandatory one, zero sequence for the first four
    most significant bits as shown. This is common to all the EEPROM devices.
    The next 3 bits are the A2, A1 and A0 device address bits for the 1K/2K EEPROM.
    These 3 bits must compare to their corresponding hard-wired input pins.
    The 4K EEPROM only uses the A2 and A1 device address bits with the third bit being a
    memory page address bit. The two device address bits must compare to their corre-
    sponding hard-wired input pins. The A0 pin is no connect.
    The 8K EEPROM only uses the A2 device address bit with the next 2 bits being for
    memory page addressing. The A2 bit must compare to its corresponding hard-wired
    input pin. The A1 and A0 pins are no connect.
    The 16K does not use any device address bits but instead the 3 bits are used for mem-
    ory page addressing. These page addressing bits on the 4K, 8K and 16K devices
    should be considered the most significant bits of the data word address which follows.
    The A0, A1 and A2 pins are no connect.
    The eighth bit of the device address is the read/write operation select bit. A read opera-
    tion is initiated if this bit is high and a write operation is initiated if this bit is low.
    Upon a compare of the device address, the EEPROM will output a zero. If a compare is
    not made, the chip will return to a standby state.

    Простите! Если я не прав.
     
    Последнее редактирование: 16 ноя 2016
  9. elarm

    elarm Нуб

    Однако! Интересный момент! Мы тоже так думали, даже пример подключения их нашли. upload_2016-11-17_13-38-55.png upload_2016-11-17_13-40-3.png upload_2016-11-17_13-38-55.png
    Вот эта память, а вот то, что про неё в даташите:
    Пины А0, А1 и А2 болтаются там в воздухе. Мы их купили 10 штук, в расчёте, повесить гирляндой. Как же тогда найти нормальную, с реально действующими адресами!!?? В смысле, по каким признакам в названии микросхемки.
     
  10. Igor68

    Igor68 Гуру

    Извините за "подозрения".... в прочтении названия на корпусе - сам потратил не менее 2х минут(еле видно). Все А0...А2 у меня на посажены корпус. (ATMEL 24C02 в моём случае). Вам похоже (если Пины А0, А1 и А2 болтаются там в воздухе) придётся либо купить другие, либо мультиплексировать обмен с каждой микросхемой 24Cxx из этих. Возьмите лучше одну 24С04 вместо гирлянды из двух
     
  11. elarm

    elarm Нуб

    24С04 конечно хорошая штучка, однако для поставленной задачи требуется некое "магическое" сочетание.
    -- Количество адресуемых ячеек в корпусе должно быть равно 2 в степени количества регистров.
    Например: количество байтов в 24С02 равно 256, таким образом, любая ячейка одного корпуса может хранить адрес ячейки другого корпуса. Ибо, 2**8 = 256.
    Так, было бы лучше, если бы в природе существовали EEPROM с такими характеристиками:
    512х9 или,
    1024х10 или,
    2048х12 и т.п.
    .......
    65536х16!

    Саму идею такой задачи решено промоделировать на Ардуине, взяв её в качестве мастера шины I2C.
     
  12. Igor68

    Igor68 Гуру

    Про задачу ничего не скажу... про память сказал
     
  13. K_V_B

    K_V_B Нуб

    Отличный пример, сколько бился с разными примерами что бы запустить DS1847-050 http://www.alldatasheet.com/datasheet-pdf/pdf/77318/DALLAS/DS1847-050.html, с этим наконец получилось, единственно может подскажет кто - при записи по адресам E0-FF, например значения A1, получаю:
    A1 A1 A1 A1 A1 A1 A1 A1
    0 A1 A1 A1 A1 A1 A1 A1
    A1 A1 A1 A1 A1 A1 A1 A1
    A1 A1 A1 A1 A1 A1 A1 A1
    хотя по адресам это User Memory - битая ячейка получается?
     
  14. интересно
     
  15. CryNET

    CryNET Гик

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

    З.Ы. В datasheet упоминается про задержку записи - вы учитываете её? Она примерно 5 мс.
    Выше я писал функцию записи. Если вы циклом записываете, то проверьте, что задержки достаточно для вашей EEPROM.
     
    Последнее редактирование: 26 янв 2017
  16. K_V_B

    K_V_B Нуб

    Да скорее всего время в задержке, т.к. при добавлении в цикл записи delay(100) значение в ячейку пишет. Однако возник другой вопрос - при выключении, а потом опять включении Arduino nano, если цикл записи в сетапе пробегал по всем ячейкам, выборочные пишет корректно, если прогоняется только пользовательский диапазон E0h-FFh, то писать иногда перестает, сталкивался кто с таким?
     
  17. CryNET

    CryNET Гик

    покажите ваш код, будет проще найти в чем ошибка
     
  18. K_V_B

    K_V_B Нуб

    Добрый день, код прилагаю:
    Код (C++):
    #include <Wire.h>     // for I2C
    #define DEVICE_1 0x50// device address for left-hand chip on our breadboard
    byte d=0; // data to store in or read from the EEPROM
    void setup()
    {
      Serial.begin(9600); // Initialize the serial line
      Wire.begin();         // wake up the I2C
      Serial.println("Writing data...");
    for (int i = 240; i < 242; i++) { //Запись в ячейки памяти
       EEPROM_WriteByte(DEVICE_1, i, 0x00);
    }
    Serial.println("Done.");
    int val=0;
    Serial.println("Read DS1847: ");
    for (int i = 0; i < 256; i++) {
       Serial.print(EEPROM_ReadByte(DEVICE_1, i), HEX);
       Serial.print(" ");
       val++;
       if(val >= 8){ val=0; Serial.println(); } //Для удобства, в строчку по 8 байт
    }
      Serial.println("DONE");

        Serial.print("Pre setup : ");
        Serial.println(EEPROM_ReadByte(DEVICE_1, 241), HEX);
        Serial.println();
        Serial.println("Writing data to F1h");
    for (int i = 0; i < 256; i++) { //256 позиций потенциометра
       EEPROM_WriteByte(DEVICE_1, 241, i);
       Serial.print("Read DS1847: ");
       Serial.println(EEPROM_ReadByte(DEVICE_1, 241), HEX);
    }
       Serial.print("DONE");
    }
    // writes a byte of data in memory location addr
    void EEPROM_WriteByte(byte dev, byte Address, byte data)
    {
       Wire.beginTransmission(dev);
       Wire.write(Address);
       Wire.write(data);
       delay(5); //Не знаю точно, но в Datasheet описана задержка записи в 5мс, поправьте меня, если я не прав.
       Wire.endTransmission();
    }

    //Вроде задержки при чтении не нужно
    byte EEPROM_ReadByte(int dev, byte Address) {
       byte rdata = 0xFF;
       Wire.beginTransmission(dev);
       Wire.write(Address);
       Wire.endTransmission();
       Wire.requestFrom(dev, 1);
       if (Wire.available()) rdata = Wire.read();
       return rdata;
    }

    void loop()
    {

    }
     
    т.е. если в первом цикле указаны только адреса потенциометров, то иногда после включения они устанавливаются в FFh и запись в память в последующем не происходит
     
  19. BGreen

    BGreen Нерд

    Спасибо за код!
    Вот только я запутался.
    У вас адрес ячейки памяти задан как байт, т.е. 0...255.
    EEPROM_WriteByte(byte dev, byte Address
    Но в примере вы инкрементируете адрес ячейки до 512.

    А еще у меня чип AT24C32, у него 32 килобита памяти, значит, 4096 байт. Значит, адреса ячеек должны быть не byte, а uint16_t, т.е. 2-байтные, наверное.
     
  20. parovoZZ

    parovoZZ Гуру

    Не проще в даташит заглянуть, чем гадать?