Выбрать главу

Эти устройства (верхний ряд на рис. 7.1) используются вместе с библио­теками, имеющими исчерпывающий набор функций для отображения графики и текста на светодиодных дисплеях компании Adafruit. Больше информации об этих красочных и интересных устройствах можно найти на странице www.adafruit.com/products/902.

Библиотеки скрывают все взаимодействия через интерфейс I2C за своим фасадом, давая возможность пользоваться высокоуровневыми командами, как демонстрирует следующий фрагмент, взятый из примера, входящего в состав библиотеки:

#include <Wire.h>

#include "Adafruit_LEDBackpack.h"

#include "Adafruit_GFX.h"

Adafruit_8x8matrix matrix = Adafruit_8x8matrix();

void setup()

{

  matrix.begin(0x70);

  matrix.clear();

  matrix.drawLine(0, 0, 7, 7, LED_RED);

  matrix.writeDisplay();

}

Часы реального времени DS1307

Еще одно распространенное устройство I2C — модуль часов реального времени DS1307. Для этого модуля также имеется удобная и надежная библиотека, упрощающая взаимодействие с модулем и избавляющая от необходимости иметь дело с фактическими сообщениями I2C. Библиотека называется RTClib и доступна по адресу https://github.com/adafruit/RTClib.

Следующие фрагменты кода тоже взяты из примеров, поставляемых с библиотекой:

#include <Wire.h>

#include "RTClib.h"

RTC_DS1307 RTC;

void setup ()

{

  Serial.begin(9600);

  Wire.begin();

  RTC.begin()

  if (! RTC.isrunning()) {

    Serial.println("RTC is NOT running!");

    // записать в модуль дату и время компиляции скетча

    RTC.adjust(DateTime(__DATE__, __TIME__));

  }

}

void loop () {

    DateTime now = RTC.now();

    Serial.print(now.year(), DEC);

    Serial.print('/');

    Serial.print(now.month(), DEC);

    Serial.print('/');

    Serial.print(now.day(), DEC);

    Serial.print(" (");

    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);

    Serial.print(") ");

    Serial.print(now.hour(), DEC);

    Serial.print(':');

    Serial.print(now.minute(), DEC);

    Serial.print(':');

    Serial.print(now.second(), DEC);

    Serial.println();

    delay(1000);

}

Если вам интересно увидеть, как в действительности выполняются взаимодействия через интерфейс I2C, просто загляните в файлы библиотеки. Например, исходный код библиотеки RTClib хранится в файлах RTClib.h и RTClib.cpp. Эти файлы находятся в папке libraries/RTClib.

Например, в файле RTClib.cpp можно найти определение функции now:

DateTime RTC_DS1307::now() {

  Wire.beginTransmission(DS1307_ADDRESS);

  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 или относится к какому-то другому типу.