
В этой статье расскажу, как подключить EEPROM память AT24C256 к Arduino через интерфейс I2C. Разберем, как записывать и читать обычные байты, хранить целые числа, дробные значения ( float), а также запишем в память 16 и 32-битные данные.
Введение
Сначала посмотрим на характеристики самой микросхемы и ее распиновку, затем соберем схему на макетной плате и загрузим рабочий скетч. В итоге у вас будет готовое решение для создания даталоггеров, хранения таблиц калибровки или пользовательских профилей в ваших устройствах.
Технические характеристики
- Объем памяти: 256 Кбит (общая память 32 КБ);
- Интерфейс: I2C;
- Рабочее напряжение: От 1,7 В до 5,5 В;
- Размер страницы для записи: 64 байта на страницу;
- Хранение данных: До 100 лет;
- Долговечность: До 1 миллиона циклов записи / стирания на байт;
- Скорость: Частота до 1 МГц (в зависимости от напряжения и режима);
- Рабочая температура: От -40 °C до +85 °C;
Описание
Внутренняя память любого микроконтроллера имеет существенные ограничения: встроенная Flash-память не рассчитана на интенсивную перезапись в процессе работы из-за ограниченного ресурса ячеек (в некоторых микроконтроллерах вообще нет внутренней энергонезависимой памяти), а SRAM является энергозависимой и теряет все данные при отключении питания. Использование внешней микросхемы EEPROM устраняет эти недостатки, обеспечивая сохранность информации без резервного источника тока и гарантируя до миллиона циклов записи
Принципа работы I2C-связи с AT24C256
Протокол I2C — это последовательная шина обмена данными, которая позволяет подключать к микроконтроллеру Arduino множество периферийных устройств (например, датчики, дисплеи или память AT24C256), используя всего две линии: SDA для передачи данных и SCL для синхронизации.

Главное преимущество шины заключается в адресации: каждому ведомому устройству присваивается уникальный идентификатор. Это даёт возможность управлять всей цепочкой компонентов по общим проводам, не расходуя лишние контакты микроконтроллера. Процесс обмена инициирует ведущий узел (Arduino): он формирует стартовое условие на линиях и отправляет адрес конкретного устройства — EEPROM-памяти. Получив свой адрес, ведомый модуль подтверждает готовность сигналом ACK и переходит в режим ожидания команд
Адрес I2C и организация памяти
I2C-адрес микросхемы AT24C256 конфигурируется аппаратно с помощью трех выводов — A0, A1 и A2. Комбинируя их логические уровни (подключая к земле или VCC), можно задать один из восьми уникальных 7-битных адресов в диапазоне от 0x50 до 0x57. Это позволяет подключать до восьми таких чипов на одной шине I2C, увеличивая общий объем доступной энергонезависимой памяти.
Внутренняя архитектура самого кристалла представляет собой массив объемом 32 768 байт (32 КБ). Для управления записью память организована в виде страниц по 64 байта каждая. Ключевое ограничение при работе с ней заключается в том, что операция записи не должна пересекать границу страницы. Если попытаться записать данные за пределы текущего 64-байтового блока, контроллер не перейдет на следующую страницу, а начнет циклически перезаписывать данные с ее начала.
- Общий объем памяти: 256 Кбит (32 КБ)
- Размер страницы: 64 байта
- Количество страниц: 512
- Диапазон адресов: от 0x0000 до 0x7FFF
Тайминг и размер страницы AT24C256
Эффективность работы с AT25C256 по шине I2C напрямую зависит от соблюдения временных параметров. Микросхема поддерживает тактовую частоту до 1 МГц, однако типичные библиотеки Arduino ограничивают скорость стандартным режимом в 100 кГц или быстрым — в 400 кГц.
Критически важным аспектом является соблюдение времени цикла записи, которое составляет около 5 миллисекунд. После завершения приема страницы данных микросхема переходит во внутренний цикл стирания-записи, на протяжении которого она перестает отвечать на любые запросы по шине.
Кроме того, необходимо строго соблюдать страничный алгоритм: максимальный объем непрерывной передачи ограничен размером физической страницы в 64 байта. Попытка передать больше приведет к циклическому возврату указателя адреса в начало текущей страницы и потере данных. Поэтому массивы информации следует предварительно сегментировать на блоки по 64 байта.
Необходимые детали
| № | Компонент | Количество | Купить |
|---|---|---|---|
| 1 | Arduino UNO R3 | 1 шт | Авито | AliExpress | RobotChip |
| 2 | Провода DuPont | 4 шт | Авито | AliExpress | RobotChip |
| 3 | Модуль памяти AT2C256 | 1 шт | Авито | AliExpress | RobotChip |
Подключение
Процесс подключения EEPROM к плате не требует дополнительных компонентов благодаря двухпроводному протоколу. Вам достаточно соединить выводы SDA и SCL микросхемы с соответствующими пинами Arduino A4 и A5, а также подать питание VCC и землю GND.

Таблица подключения
| Вывод | Arduino UNO | AT24C256 |
|---|---|---|
| 1 | VCC | 3V3 |
| 2 | SDA | A4 |
| 3 | SCL | A5 |
| 4 | GND | GND |
Программа 1 — Запись одного байта
В первой программе рассмотрим запись одного байта данных в память AT24C256.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <Wire.h> // Подключаем библиотеку для работы с интерфейсом I2C #define EEPROM_I2C_ADDRESS 0x50 // Задаем 7-битный I2C-адрес микросхемы. void setup() { Wire.begin(); // Инициализируем шину I2C в роли Master Serial.begin(9600); // Запускаем последовательный порт для отладки на скорости 9600 бод Serial.println("Writing single byte to AT24C256..."); writeEEPROM(0x000A, 0x55); // Записываем байт данных 0x55 по адресу памяти 0x000A delay(10); // Задержка для завершения цикла записи страницы во внутреннюю память Serial.println("Write complete."); } void loop() {} // Основной цикл пуст, так как запись выполняется один раз при включении // Функция записи одного байта данных void writeEEPROM(unsigned int memAddress, byte data) { Wire.beginTransmission(EEPROM_I2C_ADDRESS); // Открываем передачу к устройству по адресу 0x50 Wire.write((memAddress >> 8) & 0xFF); // Отправляем старший байт адреса памяти (биты 15-8) Wire.write(memAddress & 0xFF); // Отправляем младший байт адреса памяти (биты 7-0) Wire.write(data); // Отправляем сам байт данных для записи Wire.endTransmission(); // Завершаем передачу и отправляем накопленные данные слейву delay(5); // Ожидаем окончания внутреннего цикла записи } |
Если все правильно сделали при открытия мониторинга порта увидим результат работы программы.

Описание кода:
Подключаем библиотеку I²C Arduino для связи с EEPROM, затем задает 7-битный I²C-адрес EEPROM (базовый адрес).
1 2 |
#include <Wire.h> #define EEPROM_I2C_ADDRESS 0x50 |
В функции setup() инициализируем шину I2C командой Wire.begin(), запускаем последовательный порт командой Serial.begin(9600) и вызываем функцию writeEEPROM(0x000A, 0x55) для записи значения 0x55 в ячейку памяти EEPROM с адресом 0x000A. Добавляем небольшую задержку (например, delay(10)), чтобы цикл записи завершился
1 2 3 4 5 6 7 8 9 10 |
void setup() { Wire.begin(); Serial.begin(9600); Serial.println("Writing single byte to AT24C256..."); writeEEPROM(0x000A, 0x55); delay(10); Serial.println("Write complete."); } |
В функции loop() зацикливаем программу. Далее, у нас идет функция writeEEPROM(). Она начинает передачу данных по шине I²C в EEPROM, отправляет 16-битный адрес памяти двумя частями (сначала старший байт, затем младший), отправляет байт данных для записи и завершает передачу. После этого программа ожидает завершения записи в течение 5 мс.
1 2 3 4 5 6 7 8 9 10 11 |
void loop() {} void writeEEPROM(unsigned int memAddress, byte data) { Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.write(data); Wire.endTransmission(); delay(5); } |
Программа 2 — Чтение одного байта
Чтобы получить данные из памяти, необходимо сначала указать где они лежат. Для этого вы передаете ему нужный адрес (два байта). Сразу после этого вы меняете режим шины на чтение и запрашиваете у устройства один байт — EEPROM автоматически отдаст значение из указанной ячейки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <Wire.h> // Подключаем библиотеку для работы с интерфейсом I2C #define EEPROM_I2C_ADDRESS 0x50 // Задаем 7-битный I2C-адрес микросхемы. void setup() { Wire.begin(); // Инициализируем шину I2C в роли Master Serial.begin(9600); // Запускаем последовательный порт для отладки на скорости 9600 бод byte data = readEEPROM(0x000A); // Читаем байт из ячейки памяти по адресу 0x000A (10 в десятичной системе) Serial.print("Data read from EEPROM: 0x"); // Выводим текстовую подсказку Serial.println(data, HEX); // Печатаем прочитанное значение в шестнадцатеричном формате и переводим строку } void loop() {} // Основной цикл пуст, так как запись выполняется один раз при включении // Функция чтения одного байта из внешней памяти byte readEEPROM(unsigned int memAddress) { byte data = 0; // Переменная для хранения результата // Этап 1: Установка указателя адреса внутри EEPROM Wire.beginTransmission(EEPROM_I2C_ADDRESS); // Открываем передачу к устройству по его I2C-адресу Wire.write((memAddress >> 8) & 0xFF); // Отправляем старший байт адреса памяти (биты 15-8) Wire.write(memAddress & 0xFF); // Отправляем младший байт адреса памяти (биты 7-0) Wire.endTransmission(); // Завершаем передачу // Этап 2: Запрос и чтение данных Wire.requestFrom(EEPROM_I2C_ADDRESS, 1); // Запрашиваем у ведомого устройства 1 байт данных if (Wire.available()) // Проверяем, поступили ли данные в буфер Arduino data = Wire.read(); // Если да — считываем байт из буфера return data; // Возвращаем полученное значение вызвавшему коду } |
Если все правильно сделали при открытия мониторинга порта увидим результат работы программы.

Описание кода:
Подключаем библиотеку I²C Arduino для связи с EEPROM, затем задает 7-битный I²C-адрес EEPROM (базовый адрес).
1 2 |
#include <Wire.h> #define EEPROM_I2C_ADDRESS 0x50 |
В функции setup() инициализируем шину I2C командой Wire.begin(), запускаем последовательный порт командой Serial.begin(9600) и вызывается пользовательская функция чтения byte data = readEEPROM(0x000A). Ей передается 16-битный адрес ячейки 0x000A (10 в десятичной системе). Функция возвращает прочитанный байт, который сохраняется в переменную data.
1 2 3 4 5 6 7 8 9 |
void setup() { Wire.begin(); Serial.begin(9600); byte data = readEEPROM(0x000A); Serial.print("Data read from EEPROM: 0x"); Serial.println(data, HEX); } |
В функции
loop() зацикливаем программу. Далее, у нас идет функция
readEEPROM(), которая читает одни байта из внешней памяти. Она принимает 16-битный адрес ячейки и возвращает считанное значение.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void loop() {} byte readEEPROM(unsigned int memAddress) { byte data = 0; Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.endTransmission(); Wire.requestFrom(EEPROM_I2C_ADDRESS, 1); if (Wire.available()) data = Wire.read(); return data; } |
Программа 3 — Запись и чтение нескольких байтов.
Вы можете записать в память сразу пачку из 64 байт. Это предел одной страницы. Если вы попытаетесь отправить 65-й байт или больше за тот же раз, память не пойдет дальше по списку. Она вернется в самое начало этой же страницы и начнет записывать новую информацию поверх старой.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#include <Wire.h> // Подключаем библиотеку для работы с интерфейсом I2C #define EEPROM_I2C_ADDRESS 0x50 // Задаем 7-битный I2C-адрес микросхемы. void setup() { Wire.begin(); // Инициализируем шину I2C в роли Master Serial.begin(9600); // Запускаем последовательный порт для отладки на скорости 9600 бод byte writeData[] = {10, 20, 30, 40, 50}; // Массив данных, который будет записан в память writeMultiple(0x0010, writeData, 5); // Вызываем функцию записи: стартуем с адреса 0x0010 и пишем 5 байт delay(10); // Ждем 10 мс. byte readData[5]; // Буфер для чтения пяти байтов из памяти readMultiple(0x0010, readData, 5); // Читаем данные по тому же адресу в созданный буфер Serial.println("Data read from EEPROM:"); // Выводим заголовок в монитор порта for (int i = 0; i < 5; i++) { // Проходим циклом по массиву прочитанных данных Serial.print(readData[i]); // Печатаем значение текущего байта в десятичном виде Serial.print(" "); // Добавляем пробел для разделения чисел } Serial.println(); // Перевод строки после вывода всего массива } void loop() {} // Основной цикл пуст, так как задача выполняется один раз при старте // Функция постраничной записи нескольких байтов void writeMultiple(unsigned int memAddress, byte *data, int length) { Wire.beginTransmission(EEPROM_I2C_ADDRESS); // Открываем сессию передачи ведомому устройству Wire.write((memAddress >> 8) & 0xFF); // Отправляем старший байт адреса памяти (биты 15-8) Wire.write(memAddress & 0xFF); // Отправляем младший байт адреса памяти (биты 7-0) // Передаем сами данные. Важно: если длина блока + текущий адрес пересекают границу страницы (например, 64 байта), // запись "зациклится" внутри страницы, и данные будут испорчены. for (int i = 0; i < length; i++) { Wire.write(data[i]); } Wire.endTransmission(); // Завершаем передачу и отправляем стоп-условие delay(5); // Даем микросхеме время на завершение цикла записи (T_write). // Без этой паузы следующая операция чтения вернет старые данные или ошибку NACK. } // Функция пакетного чтения нескольких байтов void readMultiple(unsigned int memAddress, byte *buffer, int length) { // Этап 1: Установка указателя (аналогично функции записи) Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); // Старший байт адреса Wire.write(memAddress & 0xFF); // Младший байт адреса Wire.endTransmission(); // После этого вызова внутренний счетчик адреса EEPROM установлен на нужную позицию // Этап 2: Чтение запрошенного количества байтов Wire.requestFrom(EEPROM_I2C_ADDRESS, length); // Запрашиваем 'length' байт у устройства for (int i = 0; i < length && Wire.available(); i++) { // Читаем, пока не достигли лимита и есть данные в буфере buffer[i] = Wire.read(); // Сохраняем каждый байт в переданный пользователем массив } } |
Если все правильно сделали при открытия мониторинга порта увидим результат работы программы.

Описание кода:
Подключаем библиотеку I²C Arduino для связи с EEPROM, затем задает 7-битный I²C-адрес EEPROM (базовый адрес).
1 2 |
#include <Wire.h> #define EEPROM_I2C_ADDRESS 0x50 |
В функции setup() инициализируем шину I2C командой Wire.begin(), запускаем последовательный порт командой Serial.begin(9600). Далее создается массив writeData со значениями {10, 20, 30, 40, 50}, и вызывается функция writeMultiple(0x0010, writeData, 5) для отправки данных. Затем программа ожидает 10 мс, чтобы микросхема EEPROM успела завершить внутренний цикл записи. После этого создается массив readData для приема пяти байт, и вызывается функция readMultiple(0x0010, readData, 5) для получения данных из памяти по тому же адресу.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void setup() { Wire.begin(); Serial.begin(9600); byte writeData[] = {10, 20, 30, 40, 50}; writeMultiple(0x0010, writeData, 5); delay(10); byte readData[5]; readMultiple(0x0010, readData, 5); Serial.println("Data read from EEPROM:"); for (int i = 0; i < 5; i++) { Serial.print(readData[i]); Serial.print(" "); } Serial.println(); } |
В функции loop() программа зацикливается. Далее вызывается функция Wire.beginTransmission() для инициализации передачи данных и функция readMultiple() для чтения данных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
void loop() {} void writeMultiple(unsigned int memAddress, byte *data, int length) { Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); for (int i = 0; i < length; i++) { Wire.write(data[i]); } Wire.endTransmission(); delay(5); } void readMultiple(unsigned int memAddress, byte *buffer, int length) { Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.endTransmission(); Wire.requestFrom(EEPROM_I2C_ADDRESS, length); for (int i = 0; i < length && Wire.available(); i++) { buffer[i] = Wire.read(); } } |
Программа 4 — Сохранение и чтение целочисленных значений.
Представьте, что ваше число — это двузначное число, где каждая цифра может быть от 0 до 255. Одна ячейка памяти слишком мала, чтобы вместить всё целиком, поэтому мы используем две. В первую мы кладем «десятки» (старший байт), а во вторую — «единицы» (младший байт). Чтобы получить исходное число назад, мы просто достаем обе цифры и ставим их рядом в правильном порядке.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <Wire.h> // Подключаем библиотеку для работы с интерфейсом I2C #define EEPROM_I2C_ADDRESS 0x50 // Задаем 7-битный I2C-адрес микросхемы. void setup() { Wire.begin(); // Инициализируем шину I2C в роли Master Serial.begin(9600); // Запускаем последовательный порт для отладки на скорости 9600 бод int number = 12345; // Исходное целое число, которое нужно сохранить writeInt(0x0020, number); // Записываем его в память начиная с адреса 0x0020 delay(10); // Ждем завершения цикла записи внутри чипа (T_write) int readNumber = readInt(0x0020); // Считываем 2-байтное число обратно из того же адреса Serial.print("Stored Integer: "); Serial.println(readNumber); // Выводим результат. Если все верно, напечатает "12345" } void loop() {} // Код выполняется однократно // Функция записи целочисленного значения (2 байта) в EEPROM void writeInt(unsigned int memAddress, int value) { byte highByte = (value >> 8) & 0xFF; // Извлекаем старший байт числа (биты 15-8) сдвигом вправо byte lowByte = value & 0xFF; // Извлекаем младший байт числа (биты 7-0) через маску Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); // Старший байт адреса памяти в чипе Wire.write(memAddress & 0xFF); // Младший байт адреса памяти в чипе Wire.write(highByte); // Передаем старший байт данных Wire.write(lowByte); // Передаем младший байт данных Wire.endTransmission(); delay(5); // Пауза на физическую запись ячеек } // Функция чтения целочисленного значения (2 байта) из EEPROM int readInt(unsigned int memAddress) { // Указываем чипу, откуда начинать отдачу данных Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.endTransmission(); // Запрашиваем ровно два байта Wire.requestFrom(EEPROM_I2C_ADDRESS, 2); byte highByte = Wire.read(); // Читаем первый пришедший байт (старший) byte lowByte = Wire.read(); // Читаем второй пришедший байт (младший) // Собираем целое число обратно: сдвигаем старший байт на его место и объединяем младшим через OR return (highByte << 8) | lowByte; } |
Если все правильно сделали при открытия мониторинга порта увидим результат работы программы.

Описание кода:
Подключаем библиотеку I²C Arduino для связи с EEPROM, затем задает 7-битный I²C-адрес EEPROM (базовый адрес).
1 2 |
#include <Wire.h> #define EEPROM_I2C_ADDRESS 0x50 |
В функции setup() инициализируем шину I2C командой Wire.begin(), запускаем последовательный порт командой Serial.begin(9600). Далее создается переменную nuber со значениями 12345 и вызывается функция writeInt(0x0020, number) для отправки данных. Затем программа ожидает 10 мс, чтобы микросхема EEPROM успела завершить внутренний цикл записи. После этого создается переменную readNumber для хранения адреса.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void setup() { Wire.begin(); Serial.begin(9600); int number = 12345; writeInt(0x0020, number); delay(10); int readNumber = readInt(0x0020); Serial.print("Stored Integer: "); Serial.println(readNumber); } |
В функции loop() программа зацикливается. Далее вызывается функция vold writeInt()для инициализации передачи данных и функция readIntдля чтения данных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
void loop() {} void writeInt(unsigned int memAddress, int value) { byte highByte = (value >> 8) & 0xFF; byte lowByte = value & 0xFF; Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.write(highByte); Wire.write(lowByte); Wire.endTransmission(); delay(5); } int readInt(unsigned int memAddress) { Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.endTransmission(); Wire.requestFrom(EEPROM_I2C_ADDRESS, 2); byte highByte = Wire.read(); byte lowByte = Wire.read(); return (highByte << 8) | lowByte; } |
Программа 5 — Сохранение и считывание чисел с плавающей запятой
Число с плавающей запятой ( float) в четыре раза больше обычного целого числа — целых 4 байта. Чтобы не записывать каждый байт вручную, можно использовать адресную арифметику. Вы берете физический адрес вашей переменной float, сообщаете компилятору, что теперь это адрес массива байтов, и копируете все 4 ячейки памяти в EEPROM одну за другой. При чтении необходимо выполнить обратное действие: прочитать 4 байта из последовательных ячеек во временный буфер типа float.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#include <Wire.h> // Подключаем библиотеку для работы с интерфейсом I2C #define EEPROM_I2C_ADDRESS 0x50 // Задаем 7-битный I2C-адрес микросхемы. void setup() { Wire.begin(); // Инициализируем шину I2C в роли Master Serial.begin(9600); // Запускаем последовательный порт для отладки на скорости 9600 бод float temperature = 36.78; // Исходное число с плавающей точкой writeFloat(0x0030, temperature); // Записываем его в память начиная с адреса 0x0030 delay(10); // Пауза на физическое сохранение данных в кристалле float readTemp = readFloat(0x0030); // Считываем 4-байтный float обратно Serial.print("Stored Float: "); Serial.println(readTemp, 2); // Печатаем с точностью до 2 знаков после запятой ("36.78") } void loop() {} // Задача выполняется один раз // Функция записи float (4 байта) в EEPROM void writeFloat(unsigned int memAddress, float value) { byte *data = (byte*)&value; // Берем адрес переменной value и "обманываем" компилятор, // говоря что это не float, а указатель на начало массива байт. Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); // Старший байт адреса ячейки Wire.write(memAddress & 0xFF); // Младший байт адреса ячейки // Циклом передаем все 4 байта представления числа напрямую из памяти for (int i = 0; i < 4; i++) Wire.write(data[i]); // data[0] — самый младший байт (Little-Endian) Wire.endTransmission(); delay(5); // Пауза на запись страницы } // Функция чтения float (4 байта) из EEPROM float readFloat(unsigned int memAddress) { float value; // Создаем пустую переменную, которая займет 4 байта в RAM byte *data = (byte*)&value; // Получаем прямой доступ к её ячейкам памяти как к массиву Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.endTransmission(); Wire.requestFrom(EEPROM_I2C_ADDRESS, 4); // Запрашиваем ровно 4 байта for (int i = 0; i < 4; i++) data[i] = Wire.read(); // Побитово заполняем ячейки памяти нашей переменной value // Порядок должен строго совпадать с порядком при записи! return value; // Возвращаем переменную. Теперь биты в ней сложились обратно в число } |
Если все правильно сделали при открытия мониторинга порта увидим результат работы программы.

Описание кода:
Подключаем библиотеку I²C Arduino для связи с EEPROM, затем задает 7-битный I²C-адрес EEPROM (базовый адрес).
1 2 |
#include <Wire.h> #define EEPROM_I2C_ADDRESS 0x50 |
В функции setup() инициализируем шину I2C командой Wire.begin(), запускаем последовательный порт командой Serial.begin(9600). Далее создается переменную temperature значениям 36.78 и вызывается функция writeFloat(0x0030, temperature) для отправки данных. Затем программа ожидает 10 мс, чтобы микросхема EEPROM успела завершить внутренний цикл записи. После этого создается переменную readTemp для хранения адреса.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void setup() { Wire.begin(); Serial.begin(9600); float temperature = 36.78; writeFloat(0x0030, temperature); delay(10); float readTemp = readFloat(0x0030); Serial.print("Stored Float: "); Serial.println(readTemp, 2); } |
В функции loop() программа зацикливается. Далее функция writeFloat преобразует 4-байтное число float в массив байтов через указатель и записывает их в EEPROM по I2C, начиная с заданного адреса, а функция readFloat выполняет обратную операцию: запрашивает 4 байта из памяти по тому же адресу и побитово собирает их обратно в переменную float через указатель на байтовый массив, полагаясь на прямой побайтовый доступ к памяти и порядок Little-Endian.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
void loop() {} void writeFloat(unsigned int memAddress, float value) { byte *data = (byte*)&value; Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); for (int i = 0; i < 4; i++) Wire.write(data[i]); Wire.endTransmission(); delay(5); } float readFloat(unsigned int memAddress) { float value; byte *data = (byte*)&value; Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.endTransmission(); Wire.requestFrom(EEPROM_I2C_ADDRESS, 4); for (int i = 0; i < 4; i++) data[i] = Wire.read(); return value; } |
Программа 6 — Сохранение и чтение 16-битных и 32-битных шестнадцатеричных значений
Принцип записи ничем не отличается от работы с целыми числами или float. Если вам нужно сохранить, например, 32-битный MAC-адрес или код цвета, вы просто выделяете под него цепочку из нужного количества адресов (4 байта для 32-битного числа, 2 байта для 16-битного). Для упрощения кода можно использовать универсальную функцию-шаблон, которая принимает любое значение и записывает его по адресу через указатель:
Пример 16-битного HEX:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// Функция для записи 16-битного шестнадцатеричного значения void writeHex16(unsigned int memAddress, unsigned int hexValue) { Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); // Старший байт адреса ячейки памяти Wire.write(memAddress & 0xFF); // Младший байт адреса ячейки памяти Wire.write((hexValue >> 8) & 0xFF); // Старшие 8 бит записываемого числа Wire.write(hexValue & 0xFF); // Младшие 8 бит записываемого числа // В ячейках памяти будет сохранен Big-Endian: сначала старшая часть, затем младшая. Wire.endTransmission(); // Отправляем сформированный пакет данных ведомому устройству delay(5); // Ждем завершения внутреннего цикла записи микросхемы (t_WR). // Критически важная пауза: микросхеме требуется время (около 5 мс) для физического сохранения заряда в ячейках Flash/EEPROM. } // Функция для чтения 16-битного шестнадцатеричного значения unsigned int readHex16(unsigned int memAddress) { // Этап 1: Установка внутреннего указателя адреса внутри микросхемы памяти Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.endTransmission(); // После этого вызова внутренний счетчик адресов чипа установлен на нужную позицию // Этап 2: Запрос и чтение данных Wire.requestFrom(EEPROM_I2C_ADDRESS, 2); // Запрашиваем у устройства ровно два последовательных байта byte high = 0, low = 0; // Переменные для сборки итогового значения if (Wire.available()) high = Wire.read();// Читаем первый пришедший байт (старший разряд) if (Wire.available()) low = Wire.read(); // Читаем второй пришедший байт (младший разряд) // Проверка available() защищает от зависания при обрыве линии или отсутствии ответа. // Этап 3: Сборка целого числа из двух полученных байтов return (high << 8) | low; // Сдвигаем старший байт на 8 позиций влево и объединяем его с младшим через побитовое ИЛИ. // Происходит обратная сборка исходного 16-битного числа из переданных частей. } |
Пример 32-битного HEX:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// Функция для записи 32-битного значения void writeHex32(unsigned int memAddress, unsigned long hexValue) { Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); // Старший байт адреса ячейки памяти (биты 15-8) Wire.write(memAddress & 0xFF); // Младший байт адреса ячейки памяти (биты 7-0) // Разбиваем 32-битное число на 4 байта и отправляем их от самого старшего к младшему (Big-Endian) Wire.write((hexValue >> 24) & 0xFF); // Байт 3 (MSB - Most Significant Byte, биты 31-24) Wire.write((hexValue >> 16) & 0xFF); // Байт 2 (биты 23-16) Wire.write((hexValue >> 8) & 0xFF); // Байт 1 (биты 15-8) Wire.write(hexValue & 0xFF); // Байт 0 (LSB - Least Significant Byte, биты 7-0) // Исправлен комментарий: это младший байт (LSB), а не старший. Wire.endTransmission(); delay(5); // Allow EEPROM write cycle to complete (даем время t_WR на физическую запись данных) } // Функция для чтения 32-битного значения unsigned long readHex32(unsigned int memAddress) { // Этап 1: Установка внутреннего указателя адреса внутри микросхемы памяти Wire.beginTransmission(EEPROM_I2C_ADDRESS); Wire.write((memAddress >> 8) & 0xFF); Wire.write(memAddress & 0xFF); Wire.endTransmission(); // Этап 2: Запрос и побайтовое чтение данных Wire.requestFrom(EEPROM_I2C_ADDRESS, 4); // Запрашиваем у устройства 4 последовательных байта unsigned long value = 0; for (int i = 0; i < 4; i++) { // Сдвигаем текущее накопленное значение на 8 бит влево и добавляем новый прочитанный байт. // Это позволяет собрать 32-битное число из четырех полученных 8-битных частей. value = (value << 8) | Wire.read(); } return value; // Возвращаем собранное 32-битное число }value; } |
Эти инструменты позволяют без лишних усилий записывать в память любые HEX-числа (например, 0x1A2B или 0xFF00AA55), что идеально подходит для хранения настроек калибровки оборудования, ID устройств или адресов в протоколах связи.
В итоге микросхема AT24C256 способна работать абсолютно с любыми типами информации. Неважно, имеете ли вы дело с целыми числами, дробными ( float) или шестнадцатеричными кодами — принцип всегда один: правильно «нарезать» данные на байты перед отправкой и правильно «склеить» их обратно при получении. Благодаря таким простым функциям ваш Arduino получает возможность постоянно хранить не просто разрозненные цифры, а целые массивы и настройки, делая устройство гораздо более автономным и надежным.