Posted by lex232 on

Протокол iButton. Разбираемся с 1-wire в Arduino

Протокол iButton. Разбираемся с 1-wire в Arduino

Помимо управления кнопкой, и ввода данных с клавиатура, неплохо было бы добавить электронные идентификаторы, которые упрощают процедуру входа. Один из самых популярных методов — touch memory или iButton. Как уж только не называют его у нас — таблетка, магнитный ключ, брелок и т.д. Изначально, конечно планировался, как тип контактной памяти (внутри, по сути микросхема ПЗУ), но его можно эмулировать, и работать с бесконтактными картами, клавиатурами и т.д.

Протокол iButton. Разбираемся с 1-wire в Arduino

Попробуем разобраться во всём этом разнообразии. Семейство iButton насчитывает много разных микросхем производства Dallas Semiconductors, вида DS19xx. Чаще всего используется простая DS1990A, которая упакована внутри ключа. Очень много заблуждений ходит в народе об этих ключах. Нет, они не размагничиваются, там нечему размагничиваться, и магнита никакого там нет, но статики могут и испугаться. Код туда не прописывается, он уже идёт с завода, кроме конечно перезаписываемых ключей «болванок» семейства RW. Универсальных ключей тоже нет, если конечно устройство считывания не программировал глупый инженер, как было с домофонами МЕТАКОМ, которые открывались ключом с кодом FF. В целом, можно найти много информации об этом интересном семействе ключей, но давайте рассмотрим, как они работают. Сам считыватель, это просто два контакта, в виде чашки, куда и прислоняется наш ключ.

Протокол iButton. Разбираемся с 1-wire в Arduino

Для этого возьмём arduino, и соберём небольшой скетч для чтения. Потом подключим логический анализатор и разберёмся, как он передаёт код в устройство. Работает он по простому протоколу передачи данных 1-wire, т.е. для передачи информации нужен всего 1 провод и земля. Подключим библиотеку 1-wire

#include <OneWire.h>

Наверное можно задуматься, а как эту штука получает питание? Да всё просто, оно паразитное. Маленький конденсатор накапливает достаточное количество энергии, чтобы потом быстро передать код. В момент передачи и получения информации, высокий уровень данных также подзаряжает его. Справедливости ради, есть модели микросхем, типа DS1992 до DS1996, которые питаются от внутреннего литиевого источника 3В, но также могут получать энергию от 1-wire, если батарея близка к разряду. Сообщим программе, что мы будем использовать для считывания 10 пин ардуино.

OneWire ds1990(10); // Выход считывателя на 10 пин

Посылки мы будем принимать тайм-слотами по 1 биту. Так как вся посылка, которую нам передаст формат iButton равна 64 битам, значит нам нужно создать массив на 8 байтовых значений.

byte address[8];    // Буфер приема

Теперь воспользуемся командами библиотеки 1-wire. Мы сканируем линию, следующей командой, чтобы понять, есть на линии устройство 1-wire или нет

  if (ds1990.reset()) // Проверяем, есть ли на линии устройство iButton

Мы видим, что мы проверяем некий сброс линии, а что же это такое? Reset это импульс низкого уровня, длительность которого составляет 480мкс, после которого следует импульс высокого уровня, тоже длительностью 480мкс. Всё это необходимо, чтобы зафиксировать, есть устройство на линии или нет. В спокойном режиме, череда этих импульсов выглядит так

Смотрите также:  Продолжаем подготовку к походу
Протокол iButton. Разбираемся с 1-wire в Arduino

Во время высокого уровня, когда Arduino делает запрос на наличие устройства на линии, мы ожидаем увидеть ответ, в виде низкого уровня от устройства iButton, которое называется в этом протоколе presence ( Присутствие). Если в этот момент мы поднесли чип к контактной чашке, то изменения будут выглядеть так.

Протокол iButton. Разбираемся с 1-wire в Arduino

Как только iButton заметил высокий уровень на линии данных, он генерирует импульс низкого уровня presence. Надо сделать небольшое отступление, что на 1-wire шину можно подключить несколько устройств, но в простом случае, у нас их два — Arduino и DS1990A, первый из которым является Master устройством, а сам iButton — Slave. Приведённые выше скриншоты сделаны с логического анализатора, для разнообразия приведу фото с документации, где можно увидеть аналогичную картину. tPDL — это и есть время импульса presence

Протокол iButton. Разбираемся с 1-wire в Arduino

После того, как мы инициализировали устройство, можно сделать ему запрос. Для этого мы генерируем тайм слот записи. Так выглядит запись логической 1

Протокол iButton. Разбираемся с 1-wire в Arduino

А так выглядит запись логического 0

Протокол iButton. Разбираемся с 1-wire в Arduino

Можем здесь увидеть, что длительность тайм-слота составляет 60мкс, но обычно устройство делает анализ в районе 30мкс, что попадает в окно выборки сигнала (от 15 до 60мкс). Первые 15 мкс — это время открытия транзистора, чтобы притянуть линию данных на низкий уровень.

Итак, после того, как мы обнаружили устройство на линии, самое время что-нибудь запросить у этого устройства. Если у нас на линии всего одно Slave устройство, то запросить его код мы можем простой командой Read ROM [33h]. Выше мы посмотрели на графиках, как отправлять тайм-слоты с высоким и низким уровнем, поэтому с помощью arduino Отправим код 33h на устройство

ds1990.write(0x33); // Отправляем команду "считать ROM"

Теперь снова прогоним всё логическим анализатором, и посмотрим, что же произошло, после идентификации устройства iButton. После того, как прошёл высокий уровень reset, Master устройство, в лице Ардуино отправило команду запроса 0x33 в HEX, что в двоичном коде выглядит так. В данном случае временные интервалы задаёт Arduino, опуская линию в 0, перед каждый тайм-слотом. Затем либо оставляет её в низком уровне, либо поднимает до высокого, что соответствует 1. После сообщения кода, линия остаётся в высоком уровне, до запроса ответа от iButton.

Смотрите также:  Демоплата на AVR
Протокол iButton. Разбираемся с 1-wire в Arduino

Затем нам необходимо прочитать ответ от устройства iButton. Для этого также воспользуемся командами библиотеки 1-wire в arduino. В этом цикле, в течении 8 раз мы получаем байт информации командой *.read(); и записываем его в массив address[].

 delay(1);                        // Задержка, для подготовки приёма данных
    for (int i = 0; i < 8; i++)
    {
      address[i] = ds1990.read(); // Считываем
    }

Далее в диаграммах, мы разберёмся, как происходит получение ответа, а пока для теста я буду использовать считыватель Matrix 2. Он работает с картами формата Em-Marine, но на выходе у него эмуляция стандарта DS1990, что нам и нужно. Подключим его к Arduino на 10 ногу.

Протокол iButton. Разбираемся с 1-wire в Arduino

Полученное значение нужно куда-то вывести. Для этого отправим в терминал массив address, который мы получили во время считывания контроллера. Здесь мы без переноса строки, записываем с пробелом каждый считанный байт из массива address[]. Для удобства выводим его в терминал, в шестнадцатеричном коде, добавляя в команду Serial.write окончание HEX

Serial.print("HEX card =");         
    for (int i = 0; i < 8; i++)      // Цикл для вывода 8 байт в терминал
    {
      Serial.write(' ');             // Выводим пробел для визуального удобства
      Serial.print(address[i], HEX); // Выводим данные из массива в формате HEX
    }

Прочитаем карту, значение будет выведено в терминал

HEX card = 1 4C F6 6C 0 5E 0 26

Ещё раз проверим, как выглядит полный обмен данных с DS1990. Сначала мы проверили линию reset, потом запросили данные сообщением 0x33, ну а затем получили ответ.

Смотрите также:  WG12864B + ds18b20
Протокол iButton. Разбираемся с 1-wire в Arduino

Для наглядности опять же, картинка документации, всё сходится

Протокол iButton. Разбираемся с 1-wire в Arduino

Рассмотрим более внимательно ответную посылку с кодом карты. Как и написано выше, начало тайм-слотов приёма инициирует Master устройство, подтягивая уровень к земле. Дальше iButton начинает пересылать код. Т.е. перед каждым тайм-слотом (1 бит информации), Master притягивает линию данных к земле, а iButton, либо оставляет её, что соответствует 0, или поднимает до уровня логической 1. Перед началом получения 64 битной ответной посылки, мы немного удерживаем линию в высоком уровне, чтобы пополнить заряд энергии нашего iButton устройства. Делаем это командой delay(1); перед процедурой считывания.

Протокол iButton. Разбираемся с 1-wire в Arduino

Как мы и разобрали выше, тайм слот каждого передаваемого бита немного больше, чем 60 микросекунд. Также хорошо различимы 0 и 1. Попробуем преобразовать код карты, который мы получили в терминале, в последовательность 0 и 1. Получим следующую строку

0000 0001 | 0100 1100 | 1111 0110 | 0110 1100 | 0000 0000 | 0101 1110 |0000 0000 ‭|0010 0110‬ | 

Посмотрим из документации, как должна выглядеть ответная посылка

Протокол iButton. Разбираемся с 1-wire в Arduino

Теперь для наглядности разобьём 0 и 1 на байты, и графически поделим их. Сначала мы видим код семейства, равный 01, затем уникальный номер из 6 байт, а затем проверочную сумму CRC.

Протокол iButton. Разбираемся с 1-wire в Arduino

Это делается, чтобы, из первых 7 байт информации, Master устройство могло быстро посчитать CRC, и сравнить с последним принятым байтом. Если величины совпали, значит чип прочитан верно. Про CRC коды можно написать огромную статью, для них бывают разные полиномы, но так как, у нас 8 битная архитектура, то здесь используется CRC8 Dallas. Сама формула расчёта CRC представляет собой полином вида CRC = x8 + x5 + x4 + 1, с применением операций Исключающее ИЛИ.

Протокол iButton. Разбираемся с 1-wire в Arduino

Возьмём ещё раз наши значения в виде 7, а не 8 байт

1 4C F6 6C 0 5E 0

И попытаемся посчитать CRC на онлайн калькуляторе

Протокол iButton. Разбираемся с 1-wire в Arduino

Как видим — значение совпало с тем, что передала нам карта в 8 байте. Вводить проверку CRC в коде или нет — личное дело каждого. Конечно, она тратит время микроконтроллера на подсчёт, но и есть большой бонус в виде контроля целостности данных. Код считывания карты по которому мы сегодня работали,доступен по ссылке