Wire.write(i);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 7);
uint8_t ss = bcd2bin(Wire.read() & 0x7F);
uint8_t mm = bcd2bin(Wire.read());
uint8_t hh = bcd2bin(Wire.read());
Wire.read();
uint8_t d = bcd2bin(Wire.read());
uint8_t m = bcd2bin(Wire.read());
uint16_t y = bcd2bin(Wire.read()) + 2000;
return DateTime (y, m, d, hh, mm, ss);
}
Функция Wire.read возвращает значения в двоично-десятичном формате (Binary-Coded Decimal, BCD), поэтому они преобразуются в байты с помощью библиотечной функции bcd2bin.
В формате BCD байт делится на два 4-битных полубайта. Каждый полубайт представляет одну цифру двузначного десятичного числа. Так, число 37 в формате BCD будет представлено как 0011 0111. Первые четыре бита соответствуют десятичному значению 3, а вторые четыре бита — значению 7.
В заключение
В этой главе вы познакомились с интерфейсом I2C и приемами его использования для организации взаимодействий плат Arduino с периферийными устройствами и другими платами Arduino.
В следующей главе мы исследуем еще одну разновидность последовательного интерфейса, используемого для взаимодействий с периферией. Он называется 1-Wire. Этот интерфейс не получил такого широкого распространения, как I2C, но он используется в популярном датчике температуры DS18B20.
8. Взаимодействие с устройствами 1-Wire
Шина 1-Wire служит целям, похожим на цели шины I2C (глава 7), то есть она обеспечивает возможность взаимодействий микроконтроллеров с периферийными устройствами посредством минимального количества линий передачи данных. Стандарт 1-Wire, разработанный в компании Dallas Semiconductor, свел потребность в линиях до логического минимума — всего одной. Шина имеет более низкое быстродействие, чем I2C, но обладает интересной особенностью — паразитным питанием (parasitic power), позволяющее подключать периферийные устройства к микроконтроллеру всего двумя проводами: GND (ground — земля) и комбинированным проводом питания и передачи данных.
Шина 1-Wire поддерживается более узким диапазоном устройств, чем I2C. Большинство из них производят компании Dallas Semiconductor и Maxim. К их числу относятся устройства идентификации картриджей для принтеров, флеш-память и ЭСППЗУ, а также АЦП. Однако наибольшую популярность среди устройств 1-Wire у радиолюбителей завоевал температурный датчик DS18B20 компании Dallas Semiconductor.
Аппаратная часть 1-Wire
На рис. 8.1 показано, как подключить датчик DS18B20 к плате Arduino, используя всего два контакта и режим паразитного питания DS18B20.
Рис. 8.1. Подключение устройства 1-Wire к плате Arduino
1-Wire — это именно шина, а не соединение «точка–точка». К ней можно подключить до 255 устройств, взяв за основу схему, изображенную на рис. 8.1. Если вы пожелаете использовать устройство в режиме нормального питания, то сопротивление 4,7 кОм можно убрать, а вывод Vdd датчика DS18B20 вместо GND соединить непосредственно с контактом 5 В на плате Arduino.
Протокол 1-Wire
Так же как I2C, интерфейс 1-Wire использует понятия ведущего и ведомого устройств. Микроконтроллер играет роль ведущего, а периферийные устройства — ведомых. Каждое ведомое устройство еще на заводе получает уникальный идентификационный номер, который часто называют адресом, чтобы его можно было идентифицировать на шине, к которой подключено множество ведомых. Адрес имеет размер 64 бита, что позволяет иметь примерно 1,8 × 1019 разных идентификационных номеров.
Подобно I2C, протокол 1-Wire предусматривает переключение режима работы шины ведущим устройством на ввод и вывод, чтобы иметь возможность двусторонних взаимодействий. Однако в шине 1-Wire отсутствует отдельная линия передачи тактовых сигналов, поэтому нули и единицы передаются длинными и короткими импульсами. Импульс длительностью 60 мкс обозначает 0, а длительностью 15 мкс — 1.
Обычно линия данных находится под напряжением с уровнем HIGH, но, когда микроконтроллеру (ведущему) требуется послать команду устройству, он генерирует специальный импульс сброса с уровнем LOW длительностью не менее 480 мкс. Вслед за ним следует последовательность импульсов 1 и 0.
Библиотека OneWire
Работу с интерфейсом 1-Wire здорово упрощает библиотека OneWire, которая доступна по адресу http://playground.arduino.cc/Learning/OneWire.
Инициализация 1-Wire
Чтобы инициализировать Arduino как ведущее устройство на шине 1-Wire, сначала нужно подключить библиотеку OneWire:
#include <OneWire.h>
Затем создать экземпляр OneWire и указать, какой контакт Arduino будет использоваться как линия данных на шине 1-Wire. Эти два действия можно объединить в одну команду, а в роли линии данных использовать любой контакт на плате Arduino — достаточно просто передать номер контакта в виде параметра:
OneWire bus(10);
В данном случае роль линии данных шины будет играть контакт D10.
Сканирование шины
Поскольку каждое ведомое устройство, подключенное к шине, имеет уникальный идентификационный номер, присвоенный на заводе, нужен какой-то способ определить адреса устройств, подключенных к шине. Было бы неблагоразумно «зашивать» адреса устройств в скетч, потому что в случае замены новое ведомое устройство будет иметь уже другой адрес и скетч не сможет обращаться к нему. Поэтому ведущее устройство (Arduino) должно создать своеобразную опись устройств на шине. Здесь следует отметить, что первые 8 бит в адресе определяют «семейство», которому принадлежит устройство, то есть по ним можно определить, является ли устройство, например, датчиком DS18B20 или относится к какому-то другому типу.
В табл. 8.1 перечислены некоторые из наиболее известных кодов семейств для шины 1-Wire. Полный список можно найти на странице http://owfs.sourceforge.net/family.html.
Таблица 8.1. Коды семейств устройств для шины 1-Wire
Код семейства (шестнадцатеричный)
Семейство
Описание
06
iButton 1993
Идентификационный ключ
10
DS18S20
Высокоточный температурный датчик с разрешающей способностью 9 бит
28
DS18B20
Высокоточный температурный датчик с разрешающей способностью 12 бит
1C
DS28E04-100
ЭСППЗУ емкостью 4 Кбайт
В библиотеке OneWire имеется функция search, которую можно использовать для поиска всех ведомых устройств на шине. Следующий пример выводит адреса всех устройств на шине в монитор последовательного порта:
// sketch_08_01_OneWire_List
#include <OneWire.h>
OneWire bus(10);
void setup()
{
Serial.begin(9600);
byte address[8]; // 64 бита
while (bus.search(address))
{
for(int i = 0; i < 7; i++)
{
Serial.print(address[i], HEX);
Serial.print(" ");
}
// проверить контрольную сумму
if (OneWire::crc8(address, 7) == address[7])
{
Serial.println(" CRC OK");
}
else
{
Serial.println(" CRC FAIL");
}
}
}
void loop()
{
}
На рис. 8.2 показан результат выполнения этого скетча при наличии двух температурных датчиков DS18B20, подключенных к Arduino. Обратите внимание на то, что оба устройства имеют один и тот же код семейства в первом байте, равный 28 (в шестнадцатеричном формате).