Общие сведения:
Все модули серии «Flash» позволяют назначать себе адрес для шины I2C, который сохраняется в энергонезависимую память и действует даже после отключения питания. По умолчанию все модули серии «Flash» поставляются с адресом 0x09. Допускается указывать адреса в диапазоне: 7 < адрес < 127. Протокол шины I2C не допускает наличие устройств с одинаковым адресом.
Для работы с модулями серии «Flash» используются библиотеки. В каждой библиотеке предусмотрен метод смены адреса changeAddress(), но он будет работать только с тем модулем, для которого написана библиотека.
Изменить адрес любого I2C модуля серии «Flash» можно аппаратно, используя установщик адресов FLASH-I2C, или программно, используя библиотеку смены адресов iarduino_I2C_Address.
Примеры:
В данном разделе раскрыты примеры смены адреса при использовании библиотеки iarduino_Address. Сама библиотека содержит больше примеров, доступных из меню Arduino IDE: Файл / Примеры / iarduino_I2C_Address.
Смена адреса одного модуля на шине I2C:
Пример меняет текущий адрес модуля шины I2C на указанный в скетче и сохраняет его в энергонезависимую память, значит адрес сохранится и после отключения питания. Для работы скетча не требуется знать какой сейчас адрес у модуля.
uint8_t newAddress = 0x09; // Назначаемый модулю адрес (0x07 < адрес < 0x7F). // #include <iarduino_I2C_Address.h> // Подключаем библиотеку для работы с адресами модулей линейки I2C-flash. iarduino_I2C_Address j; // Объявляем объект j для работы с модулем I2C-flash. Адрес модуля будет определен автоматически. // Если адрес модуля известен, то его можно указать при создании объекта, например, iarduino_I2C_Address j(0xA0); void setup(){ // Serial.begin(9600); // Инициируем передачу данных по шине UART на скорости 9600 бит/сек. j.begin(); // Начинаем работу с модулем. while(!j){;} // Запрещаем дальнейшую работу если модуль не обнаружен. Serial.print("Найден модуль 0x"); // Serial.println( j, HEX ); // Выводим текущий адрес модуля. j=newAddress; // Меняем адрес модуля на newAddress. if(j==newAddress){ // Проверяем новый адрес модуля. Serial.println("Адрес модуля изменён"); // Успех записи нового адреса можно проверить по результату присвоения: if( j=newAddress ){/*успешно*/;}else{/*провал*/;} } // Serial.print("Текущий адрес модуля 0x"); // Serial.println( j, HEX ); // Выводим текущий адрес модуля. } // // void loop(){ // } //
Для работы данного примера, на шине I2C должен быть только один модуль.
Данный скетч демонстрирует не только возможность смены адреса на указанный в переменной newAddress
, но и обнаружение, и вывод текущего адреса модуля на шине I2C.
В примере проверка нового адреса выполнена после его присвоения, но эту проверку можно выполнить непосредственно в момент присвоения if( j=newAddress ){ /* Успех */; }
.
Смена адресов нескольких модулей:
Пример меняет адреса нескольких модулей сразу. Модулям с адресами 0xAB и 0xCD назначаются адреса 0x10 и 0x11 соответственно. На шине могут присутствовать и иные модули, адреса которых отличаются от указанных.
#include <iarduino_I2C_Address.h> // Подключаем библиотеку для работы с адресами модулей линейки I2C-flash. iarduino_I2C_Address j[]={0xAB, 0xCD}; // Определяем массив объектов, указав текущие адреса модулей I2C-flash на шине I2C. // void setup(){ // Serial.begin(9600); // Инициируем передачу данных по шине UART на скорости 9600 бит/сек. // Начинаем работу с модулями: // Указанные действия можно выполнить в цикле (см. пример ниже). j[0].begin(); // Начало работы можно выполнять с проверкой наличия модуля: if( j[0].begin() ){/*успешно*/;}else{/*провал*/;} j[1].begin(); // // Выводим старые адреса модулей на шине I2C: // Указанные действия можно выполнить в цикле. Serial.println("Найдены модули:"); // if( j[0] ){ Serial.println( j[0] ); } // Если модуль с адресом 0xAB не обнаружен, то условие if(j[0]) не будет выполнено. if( j[1] ){ Serial.println( j[1] ); } // Если модуль с адресом 0xCD не обнаружен, то условие if(j[1]) не будет выполнено. // Указываем новые адреса модулям: // Указанные действия можно выполнить в цикле. Serial.println("Меняем адреса модулей..."); // Важно назначать адреса, которых ещё нет на шине I2C!!! j[0]=0x10; // Успех записи нового адреса можно проверить по результату присвоения: if( j[0]= 0x10 ){/*успешно*/;}else{/*провал*/;} j[1]=0x11; // Успех записи нового адреса можно проверить после присвоения: j[1]=0x11; if( j[1]==0x11 ){/*успешно*/;}else{/*провал*/;} // Выводим адреса модулей на шине I2C: // Указанные действия можно выполнить в цикле. Serial.println("Новые адреса модулей:"); // if( j[0] ){ Serial.println( j[0] ); } // if( j[1] ){ Serial.println( j[1] ); } // } // // void loop(){ // } //
Для работы данного примера, на шине I2C должны присутствовать модули с адресами указанными при определении массива объектов j
.
В примере не используются циклы работы с массивом, для простоты его понимания.
Если объявить массив объектов без указания адресов, например, iarduino_I2C_Address j[2];
то адреса будут определены автоматически. Нулевой элемент массива объектов будет работать с модулем имеющем наименьший адрес и далее по возрастанию.
Если количество устройств на шине меньше чем количество элементов массива объектов, то «лишние» элементы массива не пройдут инициализацию и не пройдут проверку if( j[n] )
, но при этом все устройства будут обнаружены, а их адреса изменены.
Если количество устройств на шине больше чем количество элементов массива объектов, то не все модули будут обнаружены и адреса «лишних» модулей не будут изменены.
Автоматическая сортировка адресов всех модулей:
Пример находит все модули на шине и автоматически меняет адреса устройств Flash I2C. Новые адреса устройств будут идти по порядку начиная с адреса в константе start
.
Важной особенностью этого примера является то, что функция sorting()
способна обнаружить все устройства Flash I2C, в т.ч. и с одинаковыми адресами, и назначить каждому устройству новый уникальный адрес. Предположим, если на шине находится 5 устройств Flash I2C, неважно какие у них адреса, допустим у всех устройств адрес 0x09, то после выполнения скетча, на шине будут 5 устройств с адресами: 0x60, 0x61, 0x62, 0x63, 0x64.
const uint8_t start = 0x60; // Первый адрес создаваемого списка новых адресов для модулей Flash I2C. // #include <iarduino_I2C_Address.h> // Подключаем библиотеку для работы с адресами модулей линейки I2C-flash. iarduino_I2C_SortAddress k; // Объявляем объект (k) для сортировки адресов. // void setup(){ // // Подготовка: // Serial.begin(9600); // Инициируем передачу данных по шине UART на скорости 9600 бит/сек. uint8_t i; // // Выполняем сортировку адресов: // Serial.print("Сортировка... "); // i = k.sorting(start); // Если адреса уже отсортированы, то функция вернёт false. if(i){ Serial.println("выполнена!" ); } // else { Serial.println("не требуется."); } // } // // void loop(){ // } //
Обратите внимание, что объект k
создан как экземпляр класса iarduino_I2C_SortAddress
, в отличии от предыдущих примеров, где объект j
создан как экземпляр iarduino_I2C_Address
.
Объект k
имеет только одну функцию - sorting()
предназначенную для автоматической сортировки адресов устройств Flash I2C и принимает адрес который станет первым в списке новых адресов. Но объект k
не умеет работать с устройствами и выводить их адреса, как мы делали в предыдущих примерах, по этому в следующем примере создадим объекты обоих классов и не только отсортируем устройства, но и выведем их адреса...
Автоматическая сортировка и вывод адресов:
Значение константы sum
определяет количество элементов массива j
, а следовательно, количество занимаемой памяти. Значение sum
должно быть равно или больше чем реальное количество модулей на шине I2C.
const uint8_t sum = 10; // Количество объектов. Должно быть равно или больше реального количества модулей на шине I2C. const uint8_t start = 0x60; // Первый адрес создаваемого списка новых адресов для модулей Flash I2C. // #include <iarduino_I2C_Address.h> // Подключаем библиотеку для работы с адресами модулей линейки I2C-flash. iarduino_I2C_Address j[sum]; // Объявляем массив объектов (j), указав их количество (sum). Адреса модулей будут определены автоматически. iarduino_I2C_SortAddress k; // Объявляем объект (k) для сортировки адресов. // void setup(){ // // Подготовка: // Serial.begin(9600); // Инициируем передачу данных по шине UART на скорости 9600 бит/сек. uint8_t i; // // Начинаем работу с модулями: // for(i=0; i<sum; i++){ j[i].begin(); } // Если очередной модуль обнаружен, то begin() вернёт true. // Выводим адреса найденных модулей: // Serial.println("Найдены модули:"); // for(i=0; i<sum; i++){ // Вместо цикла for() можно воспользоваться циклом while( j[i] ){ ... i++; } if( j[i] ){ // Если модуль не существует, то j[i] вернёт false. Serial.print((String)(i+1)+".) 0x"); // Выводим порядковый номер модуля. Serial.print( j[i], HEX ); // Выводим адрес модуля на шине I2C. Serial.println('.'); // Переходим к следующей строке. } // } // // Выполняем сортировку адресов: // Serial.print("Сортировка... "); // i = k.sorting(start); // Если адреса уже отсортированы, то функция sorting() вернёт false. if(i){ Serial.println("выполнена!" ); } // else { Serial.println("не требуется."); } // // Повторно начинаем работать с модулями: // for(i=0; i<sum; i++){ j[i].begin(); } // Начинаем работу с каждым модулем. // Выводим адреса найденных модулей: // Serial.println("Найдены модули:"); // for(i=0; i<sum; i++){ // Вместо цикла for() можно воспользоваться циклом while( j[i] ){ ... i++; } if( j[i] ){ // Если модуль не существует, то j[i] вернёт false. Serial.print((String)(i+1)+".) 0x"); // Выводим порядковый номер модуля. Serial.print( j[i], HEX ); // Выводим адрес модуля на шине I2C. Serial.println('.'); // Переходим к следующей строке. } // } // } // // void loop(){ // } //
В этом примере массив объектов j
создан от класса iarduino_I2C_Address
, каждый элемент этого массива работает с одним модулем, адрес которого был обнаружен функцией begin()
. В то время как объект k
создан как экземпляр класса iarduino_I2C_SortAddress
, он предназначен только для автоматической сортировки всех адресов.
Код функции setup()
разделён на 4 части: подготовка, начало работы с модулями и вывод их изначальных адресов на экран, сортировка, повторное начало работы с модулями и вывод их адресов на экран уже после сортировки.
В этом примере выведены только адреса устройств Flash I2C, но есть возможность вывести и более подробную информацию о устройствах, как в следующем примере...
Вывод подробной информации о всех модулях:
Значение константы sum
определяет количество элементов массива j
, а следовательно, количество занимаемой памяти. Значение sum
должно быть равно или больше чем реальное количество модулей на шине I2C.
const uint8_t sum = 10; // Количество объектов. Должно быть равно или больше реального количества модулей на шине I2C. // #include <iarduino_I2C_Address.h> // Подключаем библиотеку для работы с адресами модулей линейки I2C-flash. iarduino_I2C_Address j[sum]; // Объявляем массив объектов (j), указав их количество (sum). Адреса модулей будут определены автоматически. // void setup(){ // // Подготовка: // Serial.begin(9600); // Инициируем передачу данных по шине UART на скорости 9600 бит/сек. uint8_t i; // // Проходим по всем элементам массива j: // for(i=0; i<sum; i++){ // // Начинаем работу с модулем: // if(j[i].begin()){ // Если очередной модуль обнаружен, то begin() вернёт true. // Выводим информацию о модуле: // Serial.print((String)(i+1)+".) 0x"); // Выводим порядковый номер модуля. Serial.print( j[i], HEX ); // Выводим адрес модуля на шине I2C. if(j[i].getDevice()==DEVICE_I2C_FLASH || j[i].getDevice()==DEVICE_I2C_FLASH_OLD){ Serial.print(F(" Модуль Flash I2C «")); // Serial.print(j[i].getName()); // Выводим название модуля. Будет пустым если модуль DEVICE_I2C_FLASH_OLD. Serial.print(F("»")); // Serial.print(F(", модель 0x")); // Serial.print(j[i].getModel(),HEX); // Выводим номер модели (идентификатор устройства). Serial.print(F(", версия ")); // Serial.print(j[i].getVersion()); // Выводим версию прошивки модуля. Serial.print(F(", подтяжка линий ") ); // Serial.print(j[i].getPullI2C()?"в":"от");// Выводим состояние внутрисхемной подтяжки линий шины I2C. Serial.print(F("ключена")); // }else // if(j[i].getDevice()==DEVICE_I2C_UNKNOWN){ // Serial.print(F("Неизвестный модуль")); // }else // if(j[i].getDevice()==DEVICE_I2C_DOUBLE){ // Serial.print(F(" адрес принадлежит")); // Serial.print(F(" двум и более модулям"));// } // Serial.println('.'); // } // } // } // // void loop(){ // } //
В этом примере выводятся адреса всех устройств обнаруженных на шине I2C, но не более чем указано в константе sum
. Если найденное устройство принадлежит семейству Flash I2C то для него выводится название, номер модели, версия прошивки и состояние внутрисхемной подтяжки линий шины I2C. Для остальных устройств будет выведена строка "Неизвестный модуль"
. Если на одном адресе обнаружено несколько устройств из линейки Flash I2C, то для этого адреса будет выведена строка "адрес принадлежит двум и более модулям"
.
Описание функций библиотеки:
В данном разделе описаны функции библиотеки iarduino_I2C_Address для работы с модулями серии Flash-I2C.
Данная библиотека может использовать как аппаратную, так и программную реализацию шины I2C. О том как выбрать тип шины I2C рассказано в статье Wiki - расширенные возможности библиотек iarduino для шины I2C.
Подключение библиотеки:
- Если адрес модуля известен (в примере используется адрес 0x09):
#include <iarduino_I2C_Address.h> // Подключаем библиотеку. iarduino_Address obj(0x09); // Создаём объект obj указав адрес модуля на шине I2C.
- Если адрес модуля неизвестен (адрес будет найден автоматически):
#include <iarduino_I2C_Address.h> // Подключаем библиотеку. iarduino_Address obj; // Создаём объект obj без указания адреса.
- Если адреса модулей известны (в примере используются адреса 0x0A, 0x0B, 0x0C):
#include <iarduino_I2C_Address.h> // Подключаем библиотеку. iarduino_Address obj[]={0x0A,0x0B,0x0C}; // Создаём массив объектов указав текущие адреса модулей на шине I2C.
- Если адреса модулей неизвестны (адреса будут найдены автоматически):
#include <iarduino_I2C_Address.h> // Подключаем библиотеку. iarduino_Address obj[3]; // Создаём массив объектов obj указав количество модулей.
- Если требуется выполнить автоматическую сортировку адресов модулей Flash I2C:
#include <iarduino_I2C_Address.h> // Подключаем библиотеку. iarduino_I2C_SortAddress all; // Объявляем объект all для сортировки адресов.
- Если требуется выполнить сортировку и работать с модулями:
#include <iarduino_I2C_Address.h> // Подключаем библиотеку. iarduino_I2C_SortAddress all; // Объявляем объект all для сортировки адресов. iarduino_Address obj[3]; // Создаём массив объектов obj указав количество модулей.
Сознание массива объектов как экземпляров класса iarduino_Address
позволяет работать с каждым модулем по отдельности (получать и менять его адрес, получать название, версию, тип модели, получать и менять состояние внутрисхемной подтяжки линий шины I2C).
Создание объекта как экземпляра класса iarduino_I2C_SortAddress
позволяет использовать только одну функцию - sorting()
предназначенную для автоматической сортировки адресов стразу всех устройств Flash I2C.
Функция begin();
- Назначение: Начало работы с модулем.
- Класс: iarduino_Address.
- Синтаксис: begin();
- Параметры: Нет.
- Возвращаемое значение: bool - результат обнаружения модуля (true или false).
- Примечание:
- По результату обнаружения модуля можно определить наличие модуля на шине.
- Функцию достаточно вызвать однократно, но обязательно до обращения к любым другим функциям объекта библиотеки.
- Пример:
if( obj.begin() ){ Serial.print( "Модуль обнаружен!" ); } else { Serial.print( "Модуль не найден!" ); }
Оператор присвоения адреса
- Назначение: Смена адреса модуля на шине I2C.
- Класс: iarduino_Address.
- Синтаксис: ОБЪЕКТ = АДРЕС.
- Параметры: Нет.
- Возвращаемое значение: bool - результат сохранения нового адреса (true или false).
- Примечание: Вместо оператора «=» можно использовать функцию changeAddress().
- Пример:
obj = 50; // Присвоение модулю адреса 50 без проверки сохранения адреса в модуль. if( obj = 25 ){ Serial.print( "Присвоение адреса 25 с проверкой сохранения" ); }
Оператор получения адреса
- Назначение: Запрос текущего адреса модуля на шине I2C.
- Класс: iarduino_Address.
- Синтаксис: ПЕРЕМЕННАЯ = ОБЪЕКТ.
- Параметры: Нет.
- Возвращаемое значение: uint8_t - текущий адрес модуля.
- Примечание: Вместо оператора «=» можно использовать функцию getAddress().
- Пример:
uint8_t i = obj; // Получение текущего адреса модуля в переменную i. if( obj == 25 ){ Serial.println( "Адрес модуля = 25" ); } Serial.print( "Адрес модуля = " ); Serial.print( obj );
Функция sorting();
- Назначение: Автоматическая сортировка адресов всех устройств Flash I2C.
- Класс: iarduino_I2C_SortAddress.
- Синтаксис: sorting( АДРЕС );
- Параметры:
- uint8_t АДРЕС - первый адрес из списка создаваемых адресов.
- Возвращаемое значение: bool - результат изменения адресов (true или false).
- Примечание:
- Функция меняет адреса всех устройств Flash I2C.
- Новые адреса устройств будут идти по порядку начиная с указанного.
- Если текущие адреса устройств не требуют сортировки (уже идут по порядку начиная с указанного), то функция не будет менять адреса и вернёт false.
- Если на шине есть устройства Flash I2C с одинаковыми адресами, функция найдет каждое устройство и назначит им новый уникальный адрес.
- Допускается наличие на шине устройств не из линейки Flash I2C (их адрес не меняется).
- Устройства DEVICE_I2C_FLASH_OLD не поддерживают автоматическую сортировку.
- Пример:
if( all.sorting() ){ Serial.print( "Сортировка выполнена!" ); } else { Serial.print( "Сортировка не требуется." ); }
Дополнительные функции библиотеки:
Дополнительные функции позволяют получать информацию о модулях Flash-I2C.
Функция getDevice();
- Назначение: Запрос принадлежности устройства к линейке Flash I2C.
- Класс: iarduino_Address.
- Синтаксис: getDevice();
- Параметры: Нет.
- Возвращаемое значение: uint8_t ТИП - 1 из 5 значений:
- DEVICE_I2C_ABSENT - устройство отсутствует на шине (не найдено).
- DEVICE_I2C_UNKNOWN - неизвестное устройство (стороннее).
- DEVICE_I2C_DOUBLE - адрес занят двумя и более устройствами.
- DEVICE_I2C_FLASH - устройство принадлежит линейке Flash I2C.
- DEVICE_I2C_FLASH_OLD - устройство Flash I2C без поддержки вывода названия и сортировки адреса.
- Пример:
if( obj.getDevice()==DEVICE_I2C_DOUBLE ){ Serial.println("На шине есть устройства с одинаковыми адресами."); }
Функция reset();
- Назначение: Перезагрузка модуля.
- Класс: iarduino_Address.
- Синтаксис: reset();
- Параметры: Нет.
- Возвращаемое значение: bool - результат перезагрузки (true или false).
- Пример:
if( obj.reset() ){ Serial.print( "Модуль перезагружен" ); } else { Serial.print( "Модуль не перезагружен" ); }
Функция changeAddress();
- Назначение: Смена адреса модуля на шине I2C.
- Класс: iarduino_Address.
- Синтаксис: changeAddress( АДРЕС );
- Параметры:
- uint8_t АДРЕС - новый адрес модуля на шине I2C (целое число от 0x08 до 0x7E)
- Возвращаемое значение: bool - результат смены адреса (true или false).
- Примечание: Вместо функции changeAddress() можно использовать оператор «=».
- Пример:
if( obj.changeAddress(0x12) ){ Serial.print( "Адрес модуля изменён на 0x12" ); } else { Serial.print( "Не удалось изменить адрес" ); }
Функция getAddress();
- Назначение: Запрос текущего адреса модуля на шине I2C.
- Класс: iarduino_Address.
- Синтаксис: getAddress();
- Параметры: Нет.
- Возвращаемое значение: uint8_t АДРЕС - текущий адрес модуля на шине I2C (от 0x08 до 0x7E)
- Примечание: Вместо функции getAddress() можно использовать оператор «=».
- Пример:
Serial.print( "Адрес модуля на шине I2C = 0x"); Serial.println( obj.getAddress(), HEX );
Функция getVersion();
- Назначение: Запрос версии прошивки модуля.
- Класс: iarduino_Address.
- Синтаксис: getVersion();
- Параметры: Нет
- Возвращаемое значение: uint8_t ВЕРСИЯ - номер версии прошивки от 0 до 255.
- Пример:
Serial.print( "Версия прошивки модуля "); Serial.println( obj.getVersion(), HEX );
Функция getName();
- Назначение: Запрос названия модуля.
- Класс: iarduino_Address.
- Синтаксис: getName();
- Параметры: Нет
- Возвращаемое значение: String НАЗВАНИЕ - от 0 до 255 символов.
- Примечание:
- Название возвращается в кодировке UTF-8.
- В названии модулей допускается наличие символов Кириллицы.
- Устройства DEVICE_I2C_FLASH_OLD не поддерживают вывод названия.
- Пример:
Serial.print( "Название модуля: "); Serial.println( obj.getName() );
Функция setPullI2C();
- Назначение: Управление внутрисхемной подтяжкой линий шины I2C.
- Класс: iarduino_Address.
- Синтаксис: setPullI2C( [ФЛАГ] );
- Параметры:
- bool ФЛАГ требующий установить внутрисхемную подтяжку линий шины I2C (true или false).
- Возвращаемое значение:
- bool - результат включения / отключения внутрисхемной подтяжки (true или false).
- Примечание:
- Вызов функции без параметра равносилен вызову функции с параметром true - установить.
- Флаг установки внутрисхемной подтяжки сохраняется в энергонезависимую память модуля, а значит будет действовать и после отключения питания.
- Внутрисхемная подтяжка линий шины I2C осуществляется до уровня 3,3 В, но допускает устанавливать внешние подтягивающие резисторы и иные модули с подтяжкой до уровня 3,3 В или 5 В, вне зависимости от состояния внутрисхемной подтяжки модуля.
- Пример:
if( obj.setPullI2C(true ) ){ Serial.print( "Внутрисхемная подтяжка установлена." ); } if( obj.setPullI2C(false) ){ Serial.print( "Внутрисхемная подтяжка отключена." ); }
Функция getPullI2C();
- Назначение: Запрос состояния внутрисхемной подтяжки линий шины I2C.
- Класс: iarduino_Address.
- Синтаксис: getPullI2C();
- Параметры: Нет.
- Возвращаемое значение: bool - ФЛАГ включения внутрисхемной подтяжки (true или false).
- Пример:
if( obj.getPullI2C() ){ Serial.print( "Внутрисхемная подтяжка включена." ); } else { Serial.print( "Внутрисхемная подтяжка отключена." ); }
Обсуждение