Автор: Александр Бондаренко
Увлечение платформой Arduino привело меня к устройствам, работающим по шине I2C (сокращение от английских слов Inter-Integrated Circuit) также называемые как "Two-Wire" устройства. Выпускается большое количество микросхем, аппаратно поддерживающих I2C шину. Это и всевозможные датчики, часы реального времени, память, расширители портов и много чего другого. В статье ниже представлена модернизация проекта сканера устройств с шиной I2C на базе Arduino, который описан на странице http://playground.arduino.cc/Main/I2cScanner и пример практической работы с автономным от компьютера прототипом устройства.

Помимо вешеперечисленных датчиков и модуей, по I2C шине управляются и тюнеры, которые используют в телевизорах. Как раз один из экземпляров такого устройства попал в руки. Родилась идея поэкспериментировать с ним. Но как узнать, что тюнер хотя бы работает? Начал поиски по интернету. Может кто-нибудь использовал Arduino для управления такими устройствами? К сожалению, таких материалов не нашел, но заинтересовала одна интересная разработка на странице http://playground.arduino.cc/Main/I2cScanner . Это сканер I2C устройств на базе Arduino. Идея этого сканера очень проста - подцепить I2C устройство к пинам аппаратной реализации I2C (для Arduino Uno это A4 A5). Далее запускается сканирование диапазона адресов и при обнаружении устройств в мониторе порта Arduino IDE будет отображены их адреса.
Данный проект отвечал поставленной задаче. Но для меня существенным минусом показалось зависимость от персонального компьютера. Нужно запустить монитор среды разработки Arduino IDE, подключить Arduino через COM порт к ней. Немного поразмыслив, родилась идея сделать автономное от компьютера устройство, способное сканировать и отображать на экране LCD адреса I2C устройств подключенных к шине. Для такой задачи как нельзя лучше подходит LCD Keypad Shield, который был у меня в наличии и с которым я уже успел поработать. Он оборудован HD4478 совместимым LCD дисплеем с обвязкой и шестью кнопками, которые пригодятся для управления процессом сканирования. Его можно напрямую подключить к Arduino Uno и подобным, и получить автономный, законченный модуль для работы с I2C устройствами.
Представленный в статье прототип выполнен на самодельном Arduino-подобном модуле с контроллером ATmega328 (ATmega8).
Для обнаружения устройства на текущем адресе, при сканировании, используется возвращаемое значение функции
Wire.endTransmisstion
После подключения тестируемой платы к шине I2C и подачи питания, на экране отображается начальная заставка,

Затем предлагается начать сканирование нажатием на кнопку "RIGHT".

Ход и результаты сканирования отображаются на LCD дисплее.

При обнаружении устройства I2C выводится его адрес.
Вот на адресе 30 (0x1E) обнаружен трехосевой цифровой компас GY-271 на базе микросхемы HMC5883L

На адресе 119 (0x77) датчик давления и температуры BMP-180

На адресе 39 (0x27) I2C расширитель портов на PCF8574T, который используется для подключения LCD дисплея по шине I2C

На адресах 80 (0x50) и 104 (0x68) соответсвенно память 24C32 и часы на DS1307


А вот и тюнер KS-H-140EA на базе микросхемы TUA6020 откликнулся!

По окончании сканирования на экран выводится количество обнаруженных устройств.

В результате получился удобный сканер для быстрой проверки работоспособности I2C устройства и нахождения его адреса. Если все железо аккуратно поместить в корпус, то будет полноценный прибор.
Успехов всем увлеченным электроникой и платформой Arduino!
В заключении код проекта.
// --------------------------------------
// Данный скетч основан на идее
// http://playground.arduino.cc/Main/I2cScanner
// с приминением LCD Keypad Shield вместо присоединения к компьютеру
// и отображению результатов в COM порт
// задействованы кнопки для управления процессом сканирования
// Полезные материалы по данной теме расположены
// http://www.gammon.com.au/forum/?id=10896
// и
// http://www.gammon.com.au/i2c
// Подключаем Arduino UNO и подобные
// Analog port 4 (A4) = SDA (serial data)
// Analog port 5 (A5) = SCL (serial clock)
//Alexandr Bondarenko 23.01.2016 v1.0
#include <Wire.h>
#include <LiquidCrystal.h>
String deviceWasFound = "Device was found";
String unknownDevice = "Unknown device";
String atAddress = "at: ";
String scanning = "Scanning...";
String scanCompleted = "SCAN COMPLETED!";
String wasFound = "Was found: ";
String forStart = "For START SCAN";
String pressRight = "press RIGHT";
String nameProject = "I2C Scanner v1.0";
String author = "press RIGHT";
int analogKeyPin = 0;//A0 пин задейсвован для кнопок
LiquidCrystal lcd(8, 9, 4, 5, 6, 7 );
// Нажатые кнопки
int button;
const int BUTTON_NONE = 0;
const int BUTTON_RIGHT = 1;
const int BUTTON_UP = 2;
const int BUTTON_DOWN = 3;
const int BUTTON_LEFT = 4;
const int BUTTON_SELECT = 5;
// Состояние программы
int state;
const int DEFAULT_STATE = 0;
const int SCAN_STATE = 1;
const int FIND_STATE = 2;
const int ERROR_STATE = 3;
const int COMPLETE_STATE = 4;
boolean completeShow = false;// признак необходимости отображения окончания сканирования
boolean findUpdate = false;// признак необходимости обновления экрана при обнаружении устройства
int nDevices = 0;// количество найденных устройств
byte error, address;
byte min_address = 0;// минимальный адрес для сканирования
byte max_address = 127;// максимальный адрес для сканирования
byte foundAddress = 0;// адрес найденного устройства
void setup()
{
Wire.begin();
lcd.begin(16, 2);
lcd.print(nameProject);
delay(1000);
lcd.setCursor(0, 1);// Установить 0 символ 1 строки
lcd.print(author);
delay(1500);
lcd.clear();
lcd.print(forStart);
lcd.setCursor(0, 1);// Установить 0 символ 1 строки
lcd.print(pressRight);
}
void loop()
{
if(getPressedButton() == BUTTON_RIGHT)//запуск и продолжение сканирования только по нажатию кнопки RIGHT
{
state = SCAN_STATE;// переходим в состояние сканирования
findUpdate = true;// разрешаем обновление экрана при обнаружении устройства
lcd.clear();
lcd.print(scanning);
}
if(state == SCAN_STATE)// продолжаем сканирование
{
if(address < max_address)// еще имеются адреса для сканирования
{
scan();// выполним сканирование текущего адреса
}
else//сканирование завершено
{
state = COMPLETE_STATE;
completeShow = true;
}
}
if(state == COMPLETE_STATE)// сканирование закончено
{
if(completeShow)// сюда заходим только однократно
{
lcd.clear();
lcd.print(scanCompleted);
lcd.setCursor(0, 1); // установить 0 символ 1 строки
String nd = String(nDevices,DEC);
String s = wasFound + nd;
lcd.print(s);
}
completeShow = false;
}
if(state == FIND_STATE)
{
if(findUpdate)// сюда заходим только однократно при обнаружении устройства
{
nDevices = nDevices + 1;// увеличиваем счетчик найденных устройств
lcd.clear();
lcd.print(deviceWasFound);
lcd.setCursor(0, 1); // установить 0 символ 1 строки
String a = toHEX(foundAddress);
String ad = String(foundAddress,DEC);
String s = atAddress + ad + " 0x" + a;
lcd.print(s);
findUpdate = false;// запрещаем обновление экрана при обнаружении устройства
}
}
if(state == ERROR_STATE)//
{
// неопределенные устройства плюсуем к найденным
nDevices = nDevices + 1;// увеличиваем счетчик найденных устройств
lcd.clear();
lcd.print(unknownDevice);
lcd.setCursor(0, 1); // установить 0 символ 1 строки
String a = toHEX(foundAddress);
String ad = String(foundAddress,DEC);
String s = atAddress + ad + " 0x" + a;
lcd.print(s);
findUpdate = false;// запрещаем обновление экрана при обнаружении устройства
}
}
// метод сканирования текущего адреса
void scan()
{
if(address < max_address)
{
error = 1;// уберем ошибку 0 и 4 если была
// Сканирование использует возвращаемое значение функции
// Write.endTransmisstion для обнаружения устройства на текущем адресе сканирования
Wire.beginTransmission(address);
error = Wire.endTransmission();
lcd.setCursor(12, 1); // Установить 0 символ 1 строки
String hex = toHEX(address);
lcd.print(hex);
if(error == 0)//найдено устройство
{
state = FIND_STATE;
foundAddress = address;
}
if(error == 4)//найдено устройство
{
state = ERROR_STATE;
foundAddress = address;
}
address = address + 1;// увеличим текущий сканируемый адрес
delay(200);
}
}
// метод определения нажатой кнопки
int getPressedButton()
{
int buttonValue = analogRead(0); // считываем значения с аналогового входа(A0)
if (buttonValue < 100)
{
return BUTTON_RIGHT;
}
else if (buttonValue < 200)
{
return BUTTON_UP;
}
else if (buttonValue < 400)
{
return BUTTON_DOWN;
}
else if (buttonValue < 600)
{
return BUTTON_LEFT;
}
else if (buttonValue < 800)
{
return BUTTON_SELECT;
}
return BUTTON_NONE;
}
// метод преобразования числа типа byte в виде строки в HEX представлении
String toHEX(byte number)
{
String ret;
if(number < 16)
{
ret = "0" + String(number,HEX);
}
else
{
ret = String(number,HEX);
}
return ret;
}
В данный момент еще реализованы не все элементы нашего сообщества.
Мы активно работаем над ним и в ближайшее время возможность комментирования статей будет добавлена.