Библиотека для работы с i2c lcd. Библиотека Wire для Arduino для работы с шиной I2C. Описание библиотеки Wire

Библиотека для работы с i2c lcd. Библиотека Wire для Arduino для работы с шиной I2C. Описание библиотеки Wire

Описание библиотеки Wire

Данная библиотека позволяет вам взаимодействовать с I2C / TWI устройствами. На платах Arduino с компоновкой R3 (распиновка 1.0) SDA (линия данных) и SCL (линия тактового сигнала) находятся на выводах около вывода AREF. Arduino Due имеет два I2C / TWI интерфейса: SDA1 и SCL1 находятся около вывода AREF, а дополнительные линии находятся на выводах 20 и 21.

В таблице ниже показано, где расположены TWI выводы на разных платах Arduino.

Начиная с Arduino 1.0, данная библиотека наследует функции Stream , что делает ее совместимой с другими библиотеками чтения/записи. Из-за этого send() и receive() были заменены на read() и write() .

Примечание

Существуют 7- и 8-битные версии адресов I2C. 7 битов идентифицируют устройство, а восьмой бит определяет, идет запись или чтение. Библиотека Wire использует 7 битные адреса. Если у вас есть техническое описание или пример кода, где используется 8-битный адрес, вам нужно откинуть младший бит (т.е. сдвинуть значение на один бит вправо), получив адрес от 0 до 127. Однако адреса от 0 до 7 не используются, так как зарезервированы, поэтому первым адресом, который может быть использован, является 8. Обратите внимание, что при подключении выводов SDA/SCL необходимы подтягивающие резисторы. Для более подробной информации смотрите примеры. На плате MEGA 2560 есть подтягивающие резисторы на выводах 20 и 21.

Описание методов

Wire.begin()

Описание

Инициализирует библиотеку Wire и подключается к шине I2C как ведущий (мастер) или ведомый. Как правило, должен вызываться только один раз.

Синтаксис

Wire.begin(address)

Параметры

address: 7-битный адрес ведомого устройства (необязательно); если не задан, плата подключается к шине как мастер.

Возвращаемое значение

Пример

Примеры для ведомого устройства смотрите в примерах к методам onReceive() и onRequest() . Примеры для ведущего устройства смотрите в примерах к остальным методам. .

Wire.requestFrom()

Описание

Используется мастером для запроса байтов от ведомого устройства. Эти байты могут быть получены с помощью методов available() и read() .

Если этот аргумент равен true , то requestFrom() после запроса посылает сообщение STOP, освобождая шину I2C.

Если этот аргумент равен false , то requestFrom() после запроса посылает сообщение RESTART. Шина не освобождается, что мешает другому устройству-мастеру влезть между сообщениями. Это позволяет одному ведущему устройству посылать несколько запросов, пока оно контролирует шину.

Синтаксис

Wire.requestFrom(address, quantity)

Wire.requestFrom(address, quantity, stop)

Параметры

  • address: 7-битный адрес устройства, у которого запрашиваются байты;
  • quantity: количество запрашиваемых байтов;
  • stop: boolean . true посылает сообщение STOP после запроса. false посылает сообщение RESTART после запроса, сохраняя соединение активным.
Возвращаемое значение

byte: количество байтов, возвращенных от ведомого устройства.

Пример

Wire.beginTransmission()

Описание

Начинает передачу на ведомое I2C устройство с заданным адресом. После него последовательность байтов для передачи ставится в очередь с помощью функции write() , и их передача с помощью вызова endTransmission() .

Синтаксис

Wire.beginTransmission(address)

Параметры

address: 7-битный адрес устройства, на которое необходимо передать данные.

Возвращаемое значение

Пример

Wire.endTransmission()

Описание

Завершает передачу на ведомое устройство, которая была начата методом beginTransmission() и передает байты, которые были поставлены в очередь методом write() .

Для совместимости с определенными I2C устройствами, начиная с Arduino 1.0.1, requestFrom() принимает аргумент логического типа данных, меняющий его поведение.

Если этот аргумент равен true , то requestFrom() после передачи посылает сообщение STOP, освобождая шину I2C.

Если этот аргумент равен false , то requestFrom() после передачи посылает сообщение RESTART. Шина не освобождается, что мешает другому устройству-мастеру влезть между сообщениями. Это позволяет одному ведущему устройству посылать несколько передач, пока оно контролирует шину.

По умолчанию этот аргумент равен true .

Синтаксис

Wire.endTransmission()

Wire.endTransmission(stop)

Параметры

stop: boolean . true посылает сообщение STOP после передачи. false посылает сообщение RESTART после передачи, сохраняя соединение активным.

Возвращаемое значение

byte , который указывает на состояние передачи:

  • 0: успех;
  • 1: данные слишком длинны для заполнения буфера передачи;
  • 2: принят NACK при передаче адреса;
  • 3: принят NACK при передаче данных;
  • 4: остальные ошибки.
Пример

Смотрите пример к методу write() .

Wire.write()

Описание

Записывает данные от ведомого устройства в отклик на запрос от ведущего устройства, или ставит в очередь байты для передачи от мастера к ведомому устройству (между вызовами beginTransmission() и endTransmission()).

Синтаксис

Wire.write(value)

Wire.write(string)

Wire.write(data, length)

Параметры

  • value: значение для передачи, один байт.
  • string: строка для передачи, последовательность байтов.
  • data: массив данных для передачи, байты.
  • length: количество байтов для передачи.
Возвращаемое значение

byte: write() возвращает количество записанных байтов, хотя чтение этого количества не обязательно.

Пример #include byte val = 0; void setup() { Wire.begin(); // подключиться к шине i2c } void loop() { Wire.beginTransmission(44); // передача на устройство #44 (0x2c) // адрес устройства задан в техническом описании Wire.write(val); // отправить байт значения Wire.endTransmission(); // остановить передачу val++; // увеличить значение if(val == 64) // если дошли до 64-го значения (max) { val = 0; // начать с начала } delay(500); }

Wire.available()

Описание

Возвращает количество байтов, доступных для получения с помощью read() . Этот метод должен вызываться на ведущем устройстве после вызова requestFrom() или на ведомом устройстве внутри обработчика onReceive() .

Синтаксис

Wire.available()

Параметры

Возвращаемое значение

Количество байтов, доступных для чтения.

Пример

Смотрите пример к методу read() .

Wire.read()

Описание

Считывает байт, который был передан от ведомого устройства к ведущему после вызова requestFrom() , или который был передан от ведущего устройства к ведомому.

Синтаксис

Параметры

Возвращаемое значение

byte: очередной принятый байт.

Пример #include byte val = 0; void setup() { Wire.begin(); // подключиться к шине i2c (адрес для мастера не обязателен) Serial.begin(9600); // настроить последовательный порт для вывода } void loop() { Wire.requestFrom(2, 6); // запросить 6 байтов от ведомого устройства #2 while(Wire.available()) // ведомое устройство может послать меньше, чем запрошено { char c = Wire.read(); // принять байт как символ Serial.print(c); // напечатать символ } delay(500); }

Wire.setClock()

Описание

Изменяет тактовую частоту для связи по шине I2C. У ведомых I2C устройств нет минимальной рабочей тактовой частоты, однако обычно используется 100 кГц.

Синтаксис

Wire.setClock(clockFrequency)

Параметры

clockFrequency: значение частоты (в герцах) тактового сигнала. Принимаются значения 100000 (стандартный режим) и 400000 (быстрый режим). Некоторые процессоры также поддерживают 10000 (низкоскоростной режим), 1000000 (быстрый режим плюс) и 3400000 (высокоскоростной режим). Чтобы убедиться, что необходимый режим поддерживается, обращайтесь к технической документации на конкретный процессор.

Возвращаемое значение

Wire.onReceive()

Описание

Регистрирует функцию, которая будет вызываться, когда ведомое устройство принимает передачу от мастера.

Синтаксис

Wire.onReceive(handler)

Параметры

handler: функция, которая должна будет вызываться, когда ведомое устройство принимает данные; она должна принимать один параметр int (количество байтов, прочитанных от мастера) и ничего не возвращать, т.е.:

void myHandler(int numBytes)

Возвращаемое значение

Пример

#include void setup() { Wire.begin(8); // подключиться к i2c шине с адресом #8 Wire.onReceive(receiveEvent); // зарегистрировать обработчик события Serial.begin(9600); // настроить последовательный порт для вывода } void loop() { delay(100); } // функция, которая будет выполняться всякий раз, когда от мастера принимаются данные // данная функция регистрируется как обработчик события, смотрите setup() void receiveEvent(int howMany) { while (1 < Wire.available()) // пройтись по всем до последнего { char c = Wire.read(); // принять байт как символ Serial.print(c); // напечатать символ } int x = Wire.read(); // принять байт как целое число Serial.println(x); // напечатать число }

Wire.onRequest()

Описание

Регистрирует функцию, которая будет вызываться, когда мастер запрашивает данные от ведомого устройства.

Синтаксис

Wire.onRequest(handler)

Параметры

handler: функция, которая должна будет вызываться, она не принимает параметров и ничего не возвращает, т.е.:

void myHandler()

Возвращаемое значение

Пример

Код для платы Arduino, работающей в качестве ведомого устройства:

#include void setup() { Wire.begin(8); // подключиться к i2c шине с адресом #8 Wire.onRequest(requestEvent); // зарегистрировать обработчик события } void loop() { delay(100); } // функция, которая будет выполняться всякий раз, когда мастером будут // запрошены данные // данная функция регистрируется как обработчик события, смотрите setup() void requestEvent() { Wire.write("hello "); // ответить сообщением }

С номиналами от 10 Ом до 1 МОм);

  • 2 резистора по 4,7 кОм (из того же набора);
  • соединительные провода (например, вот хороший набор);
  • компьютер с Arduino IDE.
  • 1 Описание интерфейса I2C

    Последовательный протокол обмена данными IIC (также называемый I2C - Inter-Integrated Circuits, межмикросхемное соединение) использует для передачи данных две двунаправленные линии связи, которые называются шина последовательных данных SDA (Serial Data) и шина тактирования SCL (Serial Clock) . Также имеются две линии для питания. Шины SDA и SCL подтягиваются к шине питания через резисторы.

    В сети есть хотя бы одно ведущее устройство (Master) , которое инициализирует передачу данных и генерирует сигналы синхронизации. В сети также есть ведомые устройства (Slave) , которые передают данные по запросу ведущего. У каждого ведомого устройства есть уникальный адрес, по которому ведущий и обращается к нему. Адрес устройства указывается в паспорте (datasheet). К одной шине I2C может быть подключено до 127 устройств, в том числе несколько ведущих. К шине можно подключать устройства в процессе работы, т.е. она поддерживает «горячее подключение».

    Давайте рассмотрим временную диаграмму обмена по протоколу I2C. Есть несколько различающихся вариантов, рассмотрим один из распространённых. Воспользуемся логическим анализатором, подключённым к шинам SCL и SDA.

    Мастер инициирует обмен. Для этого он начинает генерировать тактовые импульсы и посылает их по линии SCL пачкой из 9-ти штук. Одновременно на линии данных SDA он выставляет адрес устройства , с которым необходимо установить связь, которые тактируются первыми 7-ми тактовыми импульсами (отсюда ограничение на диапазон адресов: 2 7 = 128 минус нулевой адрес). Следующий бит посылки - это код операции (чтение или запись) и ещё один бит - бит подтверждения (ACK), что ведомое устройство приняло запрос. Если бит подтверждения не пришёл, на этом обмен заканчивается. Или мастер продолжает посылать повторные запросы.

    Это проиллюстрировано на рисунке ниже.. В первом случае, для примера, отключим ведомое устройство от шины. Видно, что мастер пытается установить связь с устройством с адресом 0x27, но не получает подтверждения (NAK). Обмен заканчивается.


    Теперь подключим к шине I2C ведомое устройство и повторим операцию. Ситуация изменилась. На первый пакет с адресом пришло подтверждение (ACK) от ведомого. Обмен продолжился. Информация передаётся также 9-битовыми посылками, но теперь 8 битов занимают данные и 1 бит - бит подтверждения получения ведомым каждого байта данных. Если в какой-то момент связь оборвётся и бит подтверждения не придёт, мастер прекратит передачу.

    2 Реализация I2C в Arduino

    Arduino использует для работы по интерфейсу I2C два порта. Например, в Arduino UNO и Arduino Nano аналоговый порт A4 соответствует SDA, аналоговый порт A5 соответствует SCL.


    Для других моделей плат соответствие выводов такое:

    3 Библиотека "Wire" для работы с IIC

    Для облегчения обмена данными с устройствами по шине I2C для Arduino написана стандартная библиотека Wire . Она имеет следующие функции:

    Функция Назначение
    begin(address) инициализация библиотеки и подключение к шине I2C; если не указан адрес, то присоединённое устройство считается ведущим; используется 7-битная адресация;
    requestFrom() используется ведущим устройством для запроса определённого количества байтов от ведомого;
    beginTransmission(address) начало передачи данных к ведомому устройству по определённому адресу;
    endTransmission() прекращение передачи данных ведомому;
    write() запись данных от ведомого в ответ на запрос;
    available() возвращает количество байт информации, доступных для приёма от ведомого;
    read() чтение байта, переданного от ведомого ведущему или от ведущего ведомому;
    onReceive() указывает на функцию, которая должна быть вызвана, когда ведомое устройство получит передачу от ведущего;
    onRequest() указывает на функцию, которая должна быть вызвана, когда ведущее устройство получит передачу от ведомого.

    4 Подключение I2C устройства к Arduino

    Давайте посмотрим, как работать с шиной I2C с помощью Arduino.

    Сначала соберём схему, как на рисунке. Будем управлять яркостью светодиода, используя цифровой 64-позиционный потенциометр AD5171 (см. техническое описание), который подключается к шине I2C. Адрес, по которому мы будем обращаться к потенциометру - 0x2c (44 в десятичной системе).


    5 Управление устройством по шине IIC

    Рассмотрим диаграммы информационного обмена с цифровым потенциометром AD5171, представленные в техническом описании:


    Нас тут интересует диаграмма записи данных в регистр RDAC . Этот регистр используется для управления сопротивлением потенциометра.

    Откроем из примеров библиотеки "Wire" скетч: Файл Образцы Wire digital_potentiometer . Загрузим его в память Arduino.

    #include // подключаем библиотеку "Wire" byte val = 0; // значение для передачи потенциометру void setup() { Wire.begin(); // подключаемся к шине I2C как мастер } void loop() { Wire.beginTransmission(44); // начинаем обмен с устройством с I2C адресом "44" (0x2C) Wire.write(byte(0x00)); // посылаем инструкцию записи в регистр RDAC Wire.write(val); // задаём положение 64-позиционного потенциометра Wire.endTransmission(); // завершаем I2C передачу val++; // инкрементируем val на 1 if (val == 63) { // по достижении максимума потенциометра val = 0; // сбрасываем val } delay(500); }

    После включения вы видите, как яркость светодиода циклически нарастает, а потом гаснет. При этом мы управляем потенциометром с помощью Arduino по шине I2C.

    Пришла мне посылка из Китая в которой лежит микросхема EEPROM фирмы Atmel. Которую хочется подключить к Arduino. Но совершенно не хочется использовать готовую библиотека, а разобраться самому. По этому статья получиться немного объемной и скучной и разделем ее на три части:

    • Теория интерфейса I2C.
    • EEPROM, описания моей микросхемы(AT24C256) и подключение.
    • Написание библиотеки для работы с памятью.

    Часть первая, I2C и библиотека «Wire».

    Последовательный протокол обмена данными IIC (также называемый I2C — Inter-Integrated Circuits, межмикросхемное соединение). Разработана фирмой Philips Semiconductors в начале 1980-х как простая 8-битная шина внутренней связи для создания управляющей электроники. Так как право на использование его стоит денег фарма Atmel назвала его TWI , но смысл от этого не меняется.

    Как это работает?

    Для передачи данных используются две двунаправленные лини передачи данных. SDA (Serial Data) шина последовательных данных и SCL (Serial Clock) шина тактирования. Обе шины подтянуты резисторами к плюсовой шине питания. Передача/Прием сигналов осуществляется прижиманием линии в 0, в единичку устанавливается сама, за счет подтягивающих резисторов .

    В сети есть хотя бы одно ведущее устройство (Master ), которое инициализирует передачу данных и генерирует сигналы синхронизации и ведомые устройства (Slave ), которые передают данные по запросу ведущего. У каждого ведомого устройства есть уникальный адрес, по которому ведущий и обращается к нему. Конечно понятно что Ведущий это наш микроконтроллер, а ведомый наша память. Ведущее устройство начинает прижимать шину SCL к нулю с определенной чистотой, а шину SDA прижимать или отпускать на определенное число тактов передавая Единичку или Нолик. Передача данных начинается с сигнала START потом передается 8 бит данных и 9-тым битом Ведомое устройство подтверждает прием байт прижимая шину SDA к минусу. Заканчивается передача сигналом STOP .

    Библиотека «Wire».

    Для облегчения обмена данными с устройствами по шине I2C для Arduino написана стандартная библиотека Wire которая есть уже в комплекте IDE . Она имеет следующие основные функции:

    Wire.begin(Address) вызывается один раз для инициализации и подключения к шини как Ведущий или Ведомое устройство. Если Address не задан подключаемся как Мастер устройство.

    Wire.beginTransmission(address) начинает передачу на ведомое I2C устройство с заданным адресом.

    Wire.endTransmission() прекращает передачу данных ведомому. Функция возвращает значение типа byte :

    • 0 — успех.
    • 1- данные слишком длинны для заполнения буфера передачи.
    • 2 — принят NACK при передаче адреса.
    • 3 — принят NACK при передаче данных.
    • 4 — остальные ошибки.

    Wire.write() запись данных от ведомого устройства в отклик на запрос от ведущего устройства, или ставит в очередь байты для передачи от мастера к ведомому устройству.Фактически записывает данные в буфер. Размер буфера 32 байт а (минус 2 байта адрес, фактически 30 байт ), а передает буфер функция Wire.endTransmission().

    • Wire.write(value) — value : значение для передачи, один байт.
    • Wire.write(string) — string : строка для передачи, последовательность байтов.
    • Wire.write(data, length) — data : массив данных для передачи, байты. length : количество байтов для передачи.

    Wire.read() Считывает байт, который был передан от ведомого устройства к ведущему или который был передан от ведущего устройства к ведомому. Возвращаемое значение byte : очередной принятый байт.

    Это самые основные функции библиотеке, остальные мы рассмотрим по ходу пьесы))

    Часть вторая, EEPROM.

    EEPROM (англ. Electrically Erasable Programmable Read-Only Memory ) - электрически стираемое перепрограммируемое ПЗУ (ЭСППЗУ), один из видов энергонезависимой памяти (таких, как PROM и EPROM). Память такого типа может стираться и заполняться данными до миллиона раз.

    Мне прислали уже готовый модуль EEPROM с микросхемой AT24C256 фирмы Atmel объемом 32 кбайт. Что бы разобраться с этим чудом нам придется проштудировать datasheet который очень скучный и на английском. Так что я вам выдам уже готовый результат моих мук.

    Характеристики:

    • Низковольтные и стандартные питание. VCC = 1.7V to 5.5V.
    • 400kHz (1.7V) and 1MHz (2.5V, 2.7V, 5.0V) совместим с частотой синхронизации.
    • Выносливость: 1,000,000 Циклов Записи.
    • Внутренне организованный как 32 768 страниц x 8 бит.

    выводы:

    • WP — защита от записи. Если вывод подключен к GND то можно записывать данные в память.
    • A0…A2 — выводы задающие адрес устройства.
    • Vcc — питание плюс.
    • GND — питание минус.
    Адрес памяти:

    Задается тремя ногами A0..A2. Если нога прижата к Gnd то значение бита 0, если к Vcc то 1. Микросхема использует восьми битный адрес, последний бит отвечает за выбор операции . Если значение бита высокий то инициализируется операция чтения, если низкий(ноль) то операция записи.

    То есть если все три вывода прижаты к GND и мы хотим записать в память, адрес устройства будет выглядеть как 10100000 (в библиотеке «Wire» используется 7-ми битный адрес, сдвигаем все в право на один бит 01010000 0x50 ).

    Запись данных в память:

    Для записи мы с начало обращаемся к памяти с битом Записи в адресе . Потом посылаем два 8-ми битных адреса(то есть у нас 0x8000 адресов) , затем байт данных и сигнал STOP . После этого EEPROM входит во внутренне синхронизированный цикл записи tWR(Write Cycle Time 5 ms) в энергонезависимую память. Все входные сигналы
    отключено во время этого цикла записи, и EEPROM не ответит, пока запись не будет завершена .

    Копаемся дальше и находим в datasheet что память микросхемы организована как 512 страниц по 64 байта . То есть мы может записать сразу до 64 байт информации за одну команду. Для этого мы передаем все 64 байта информации и только после этого посылаем сигнал STOP .

    Чтение данных:

    С чтением данных все интересней. Память поддерживает три варианта чтения:

    • Читать текущий адрес;
    • Читать случайный адрес;
    • Последовательное чтение;

    Память запоминает последний адрес записи пока не отключено питание, по этому мы можем прочитать последний байт без указания адреса.

    Что бы прочитать случайный адрес нам нужно с начало отправить команду на запись и передать адрес который хотим прочитать(не забывайте что адрес состоит из двух 8-ми битных частей ). За тем отправить команду на чтение и получить искомый байт, завершив все командой STOP.

    Последовательное чтение может выполняться как с текущего адреса так и со случайного и будет продолжаться пока микроконтроллер не отправит сигнал СТОП. При переполнении адреса память ее сбросить и адресация начнется с начала.

    Ну что же пришло время попробовать что нибудь записать:
    #include // Подключаем библиотеку #define EEPROM_ADDRESS 0x53 // Задаем адрес памяти word address = 0; // Адрес куда будем записывать byte data_send = 170 ; // Даные void setup() { Wire.begin(); // Инициализируем I2C Serial.begin(9600); Serial.print("Write byte to EEPROM memory..."); Serial.println (data_send); Wire.beginTransmission(EEPROM_ADDRESS); // Начинаем передачу Wire.write(address >> 8); Wire.write(address & 0xFF); // Отправляем два байта адреса Wire.write(data_send); // Отправляем данные byte status= Wire.endTransmission(); // Заканчиваем передачу и проверяем статус передачи. if (status == 0)Serial.println ("Ок"); // delay(10); Serial.println("Read byte from EEPROM memory..."); Wire.beginTransmission(EEPROM_ADDRESS); // Что бы прочитать данные отравляем сначала адрес где они лежат. Wire.write(address >> 8); Wire.write(address & 0xFF); status= Wire.endTransmission(); if (status == 0)Serial.println ("Ок"); // stop transmitting Wire.requestFrom(EEPROM_ADDRESS, (byte)1); // отправляем команду на чтение одного байта данных byte data = 0; if (Wire.available()) // проверяем что есть данные для чтения. { data = Wire.read(); //читаем данные } Serial.println (data, DEC); } void loop() { }

    Wire.requestFrom(address, quantity) — Используется мастером для запроса байтов от ведомого устройства. Эти байты могут быть получены с помощью методов available() и read() . Размер буфера такой же 32 байта .

    • address : 7-битный адрес устройства, у которого запрашиваются байты;
    • quantity : количество запрашиваемых байтов;

    Wire.available() — Возвращает количество байтов, доступных для получения с помощью read() .

    Ну и пример с записью в память строки «Hello Word»:

    #include #define EEPROM_ADDRESS 0x53 word address = 0; char data_send = "Hello Word" ; void setup() { Wire.begin(); // Serial.begin(9600); Serial.print("Write byte to EEPROM memory..."); Serial.println (data_send); Wire.beginTransmission(EEPROM_ADDRESS); Wire.write(address >> 8); Wire.write(address & 0xFF); Wire.write(data_send); byte status= Wire.endTransmission(); if (status == 0)Serial.println ("Ок"); delay(10); Serial.println("Read byte from EEPROM memory..."); Wire.beginTransmission(EEPROM_ADDRESS); Wire.write(address >> 8); Wire.write(address & 0xFF); status= Wire.endTransmission(); if (status == 0)Serial.println ("Ок"); // stop transmitting Wire.requestFrom(EEPROM_ADDRESS, (byte)10); byte data = 0; for (int i=0 ; i<10 ;i++) { if (Wire.available()) { data = Wire.read(); } Serial.write (data); } } void loop() { }

    Организация памяти:

    Так как в datasheet об этом написано смутно. Я на практике пытался разобраться с этим. В datasheet написано что память микросхемы организована как 512 страниц по 64 байта . Что это значит? Если мы захотим записать больше 64 байт сразу,скажем по адресу 0x40(адрес начало второй страницы), при выходе адреса за границы страницы внутренний счетчик микросхемы сбросит адрес на начало страницы . А лишние байты будут записаны в начало страницы и сотрут данные которые были там записаны.

    Для чтения таких ограничений нету, в даташипе написано только то что когда вы достигнете конца адресов вас автоматически перекинет на начало (адрес 0х00).

    На этом я думаю все. Вы конечно можете скачать уже готовую библиотеку для EEPROM , дума сможете и написать свою. Главное мы разобрались в основном принципе работы.

    Мне нужно было сделать часы на основе микросхемы, имеющей I 2 C интерфейс. Микросхема RTC, т.н. "часы реального времени" PCF8583.

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

    Но была одна проблемка - писАть код жутко не хотелось, и я решил найти готовый код в интернете. Как позже выяснилось, найти «на свою голову». Скачал пример работы с I 2 C, подправил, прошил микроконтроллер. Не заработало. Стал ковырять код, искать причину неработоспособности… и ужаснулся!! Запись в некоторых случаях велась во весь порт сразу, а не в конкретные биты. Таким образом, если на порт повесить ещё что-то, например, дисплей, то скорее всего, оно работать не будет. Также неправильно было реализовано чтение данных по шине (без генераций условия окончания приёма, или просто без NACK). Но это пол-беды. Основная проблема в другом. Зачастую автор кода выставлял в порт логическую «1», а как мы знаем, шина I 2 C управляется «притягиванием» выводов SDA и SCL к общему проводу. А логическая «1» на шине, в свою очередь, формируется за счёт подтяжки к плюсу питания резисторами на 4,7 килоом. Таким образом, если на выходе микроконтроллера выставить логическую «1», а ведомое устройство «притянет» этот выход к общему проводу, то получится «ба-бах» короткое замыкание. Мне это очень не понравилось, и я решил изобрести свой велосипед написать свою библиотеку, а вернее даже 2 библиотеки: одна для работы с шиной I 2 C, а другая непосредственно для работы с часами реального времени PCF8583. Да, кстати, код написан в .

    Для того, чтобы подключить библиотеку I 2 C к проекту, нужно прописать её через include, как на картинке, а также скопировать библиотеку в папку с проектом.

    После чего, необходимо открыть файл "i2c.h", и указать ножки микроконтроллера, которые будут выступать в роли шины I 2 C. По умолчанию шина настроена на ножки PC0 (SCL) и PC1 (SDA). А настройка делается вот тут:

    Всё, библиотеку I2C мы подключили, ножки настроили, библиотека готова к работе. Пример использования:

    I2c_init (); // Инициализация шины I2C i2c_start_cond(); // старт шины i2c_send_byte (0xA0); // адрес устройства, которое висит на шине i2c_send_byte (0x10); // байт данных, который записываем в устройство i2c_send_byte (0x10); // ещё один байт данных, который записываем в устройство i2c_stop_cond(); // стоп шины

    После стоп-условия, мы можем проверить, всё ли у нас в порядке с шиной I 2 C. Для этого нужно прочитать переменную «i2c_frame_error». Если всё нормально, то в ней будет 0. Если один из выводов шины не «подтянулся» к питанию, и логическая «1» не установилась на шине, то библиотека генерирует ошибку, и записвает в переменную «i2c_frame_error» циферку 1. Читать переменную «i2c_frame_error» нужно после стоп-условия. На рисунке ниже продемонстрирую как работает контроль ошибки:

    Теперь займёмся подключением библиотеки часов реального времени PCF8583. Для этого нужно проделать те же самые действия. Скопируем в папку с проектом файл "PCF8583.h", и пропишем его в include, как на фото:

    Готово. Библиотека часов реального времени PCF8583 подключена. Она не требует каких-либо настроек, поэтому можно сразу приступать к чтению времени и даты с микросхемы. Обращаю внимание, что библиотека PCF8583 работает при помощи библиотеки I2C, поэтому если хотим работать с PCF8583, то нужно подключить обе библиотеки!

    Пример использования библиотеки (запись и чтение времени и даты):

    // Инициализация шины I2C i2c_init (); // Подготавливаем время и дату для записи в микросхему PCF8583 PCF_hour=23; // 23 часа PCF_min=59; // 59 минут PCF_day=31; // 31 число PCF_month=12; // 12 месяц - декабрь PCF_year=0; // год (0 - не високосный) PCF_weekday=6; // 6 день недели (воскресенье) // Записываем время и дату в микросхему PCF8583 PCF_write_hh_mm_ss(); // Считываем время и дату из микросхемы PCF8583 PCF_read_hh_mm_ss(); Пример работы с оперативной памятью (запись и чтение): // Подготавливаем 5 байт для записи в микросхему PCF8583 PCF_data_ram_1=255; // байт 1 PCF_data_ram_2=255; // байт 2 PCF_data_ram_3=255; // байт 3 PCF_data_ram_4=255; // байт 4 PCF_data_ram_5=255; // байт 5 // Записываем 5 байт в микросхему PCF8583 PCF_write_ram(); // Считываем 5 байт из микросхемы PCF8583 PCF_read_ram();

    Чтение из микросхемы ещё проще – достаточно вызвать функцию PCF _ read _ hh _ mm _ ss () после чего, время и дата появятся в переменных, откуда их только забирай. Для чтения оперативной памяти соответственно используем функцию PCF _ read _ ram () после чего данные забираем в переменных PCF _ data _ ram _ N

    Вот список переменных, где и что хранится:

    // время и дата PCF_hour=0; // время, часы (от 0 до 23, защита от переполнения при записи и чтении) PCF_min=0; // время, минуты (от 0 до 59, защита от переполнения при записи и чтении) PCF_sec=0; // время, секунды (только для чтения, при записи сбрасываются в 00) PCF_day=0; // день (от 1 до 31, защита от переполнения при записи и чтении) PCF_weekday=0 // день недели (0-понедельник; 6-воскресенье, защита от переполнения при записи и чтении) PCF_month=0; // месяц (от 1 до 12, защита от переполнения при записи и чтении) PCF_year=0; // год (0-високосный; 1,2,3-невисокосные, защита от переполнения при записи и чтении) // оперативная память PCF_data_ram_1; // Данные (ОЗУ PCF8583), байт 1 PCF_data_ram_2; // Данные (ОЗУ PCF8583), байт 2 PCF_data_ram_3; // Данные (ОЗУ PCF8583), байт 3 PCF_data_ram_4; // Данные (ОЗУ PCF8583), байт 4 PCF_data_ram_5; // Данные (ОЗУ PCF8583), байт 5

    Теперь расскажу про защиту от переполнения. Допустим, мы забыли подключить микросхему. Прочитаем данные с микросхемы, и… прочитается байт 11111111, или число 255. Всё дело в том, что в основе шины I 2 C лежат 2 подтягивающих резистора, вот они то и выдают нам логические «единички» если микросхема не подключена. Для защиты от подобных случаев, в библиотеке PCF8583 я сделал защиту от переполнений, которая следит за тем, чтобы часики не показывали вам 62 часа 81 минуту… Наличие переполнения можно проследить, прочитав переменную «PCF_overflow». Если в ней 0, значит ошибок переполнения не было. Если в ней 1 или более, значит ошибки переполнения имеются. Читать переменную «PCF_overflow» нужно после функции чтения даты и времени PCF _ read _ hh _ mm _ ss ()

    Для наглядности, проект AVR Studio 6 под ATmega32 прилагается. Перекомпилировать можно под любой AVR. В проекте я также подключил дисплей для визуального контроля. При подаче питания, микроконтроллер устанавливает 23 часа 59 минут, 31 декабря, Воскресенье. И через минуту становится 00 часов 00 минут, 1 января, Понедельник.

    Теперь расскажу, почему я говорил про «кривой» календарь этой микросхемы. Всё дело в том, что микросхема не умеет хранить текущий календарный год, а хранит лишь флаг високосного года. Короче говоря:
    0 – високосный год
    1 – не високосный год
    2 – не високосный год
    3 – не високосный год

    И так по циклу 0-1-2-3-0-1-2-3-0…

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

    Также прилагаю в конце статьи небольшой видеоотчёт. В программировании я можно сказать новичок, хоть и программирую уже 3 года (понемногу), за код строго не судите, если есть какие-либо дополнения и замечания, пишите, будем исправлять. Всем удачных самоделок!

    Список радиоэлементов

    Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
    МК AVR 8-бит

    ATmega32

    1 В блокнот
    Часы реального времени (RTC)

    PCF8583

    1 В блокнот
    LCD-дисплей WH1602 1


    top