Как перестать бояться и полюбить 1-wire

  • Tutorial
Мне очень нравится протокол 1-wire своей простотой и удобством для применения в системах «умный дом». Недавно я писал программную эмуляцию одной микросхемы и погрузился во внутренности этого протокола. Чтобы накопленные знания могли принести пользу не только мне, я решил написать данную статью. Но в статье я хочу рассказать не про абстрактные диаграммы сигналов и кодирование данных — перепечаток такого материала есть достаточно, а хочу рассказать про более практические вещи. А именно: рассмотрю проверенные лично схемы адаптеров, собранные из простых и доступных деталей, и расскажу, как из Linux получить доступ к устройствам 1-wire. Попутно расскажу про сам протокол, будет и пример низкоуровневой работы, и пример доступа из JavaScript, а также рекомендации владельцам Raspberry Pi. Эта статья в первую очередь для тех, кто хочет разобраться с протоколом практически с нуля, чтобы начать его использовать в своих проектах. Возможно те, кто уже хорошо знаком с протоколом, также найдут что-то новое для себя.

Протокол 1-wire является проводным протоколом (в отличие от беспроводного), для кого-то это преимущество, а для кого-то — недостаток. Сеть 1-wire состоит из шины, к которой подключаются устройства 1-wire. На шине может присутствовать множество «подчиненных» устройств и только один «мастер». Подчиненные устройства могут общаться только с мастером и только по его запросу. Шина 1-wire состоит только из одной линии «данных» (отсюда и название — «однопроводный»), но для работы также необходима линия «земля», а для питания устройств еще и линия «питания». При небольшом количестве подчиненных и их небольшом энергопотреблении возможно также так называемое «паразитное» питание от линии данных. Таким образом, для подключения устройства по 1-wire Вам понадобится 3 (или 2) провода и мастер сети. Мастером сети может быть, например, микроконтроллер или ПК + адаптер 1-wire.

Физический уровень


Как же по одной линии происходит взаимодействие с множеством устройств? На физическом уровне линия устроена по принципу «монтажного И», это значит, что линия будет в состоянии логической «1» (уровень около 5V), если все устройства на линии перевели ее в состояние логической «1», а в состоянии логического «0» (около 0V), если хотя бы одно из устройств перевело ее в состояение логического «0». На практике это реализовывается подключением линии через резистор (называется «резистором подтяжки») к напряжению питания, а каждое из устройств может только замыкать линию с землей при помощи встроенного транзистора. Такой тип выхода устройств называется «открытый коллектор» или «открытый сток», англ. «open-drain». Резистор подтяжки обычно расположен в мастере сети (адаптере).
image

Очевидно, что при паразитном питании ток поступает через резистор подтяжки, и чем больше ток потребления устройств, тем ниже будет проседать напряжение линии. Стандартом регламентировано напряжение лиии 4.5-5.5V, хотя многие устройства и могут работать при значительно более низких напряжениях, но не все. Для снижения проседания напряжения можно уменьшить сопротивление резистора подтяжки, но согласно стандарту, сопротивление не должно быть менее 500 Ом. На практике сопротивление резистора подтяжки можно взять 1кОм, если будет использоваться паразитное питание, и, например, 4.7кОм, если питание будет по отдельной линии.

GPIO адаптер


Простейший адаптер 1-wire можно построить с помощью GPIO:


По такой схеме устроен «адаптер» при использовании GPIO микроконтроллера, работающего от напряжения 5V, соответсвенно на его портах допустимо напряжение до 5V. Но на портах Raspberry Pi допустимо напряжение до 3.3V, поэтому резистор подтяжки можно подключать только к 3.3V. Такой адаптер будет работать с некоторыми устройствами, но, как уже говорилось, не все устройства могут работать с пониженным напряжением. Эту проблему легко решить с помощью двунаправленого преобразователя логических уровней:


Диод между истоком и стоком на схемах иногда не обозначают, но он всегда есть в реальных транзисторах.

Схема адаптера с использованием GPIO для Raspberry Pi будет выглядеть следующим образом:


В этой схеме убран резистор подтяжки, подключенный к истоку, с этим «хаком» схема продолжит работать. Т.к. напряжение на GPIO (и на истоке) станет меньше чем 3.3V (напряжение на затворе) на величину напряжения затвор-исток, при которой открывается транзистор. Напряжения на GPIO будет достаточно, чтобы GPIO его воспринимал как логическую «1».

Канальный уровень


Для понимания принципа работы следующего типа адаптера, разберемся, как работает мастер 1-wire на канальном уровне. В 1-wire есть всего четыре примитива канального уровня: отправка ресет, чтение присутствия, отправка бита 0, отправка бита 1 и она же по совместительству чтение бита. Для понимания принципа будет достаточно рассмотреть только два последних: отправка бита 0 реализуется путем установки на линии логического «0» в течение 60-120мкс, отправка бита 1 — путем установки «0» в течение около 8мкс, чтение бита — путем последующего чтения состояния линии, если линия вернулась в «1», значит подчиненное устройство передало бит 1, если задержалась в «0» не менее, чем на 15мкс — значит подчиненное таким способом передало бит 0.

Пассивный адаптер


Следующий типа адаптера построен на использовании UART. Если по UART на скорости 115200 BAUD передать байт FFh, то на TX появится логический «0» только во время передачи стартового бита, это около 8мкс, подчиненные устройства это воспримут как отправку бита 1, а если передать байт 00h — появится логический «0» примерно на 78мкс, подчиненные устройства это воспримут как отправку бита 0. А что если TX соединить с RX? Мы будем получать из UART ровно то, что передали: передали FFh — получили FFh. А если эту линию принудительно задержать в логическом «0» чуть дольше, то получим уже не FFh, а, например, FEh, FCh, F8h, F0h и т.д. Таким простым способом реализуется чтение бита. Схема адаптера, построенного на использовании UART, он еще называется «пассивным» адаптером, будет такой:



По такой схеме также часто строится «адаптер» для микроконтроллера, имеющего аппаратный UART. В схеме необходимо использовать только диод Шоттки, т.к. важно малое падение напряжение на диоде. Диод в этой схеме — это «хак» для преобразования выхода «пуш-пулл» (англ. push-pull) в подобие выхода «открытый коллектор». В выходе пуш-пулл к выходу коммутируется транзисторами либо земля, либо напряжение питания, а в выходе открытый коллектор коммутируется только земля, а для «обозначения» логической «1», выход переводится в высокоимпедансное состояние, в этом состоянии выход можно подтянуть резистором к любому напряжению и он беспрепятственно приобретает это напряжение.

Здесь стоит упомянуть адаптер для RS232, хоть этот интерфейс уже практически и не встречается. На канальном уровне UART и RS232 это один и тот же протокол, но на физическом они отличаются: в UART используется напряжение 0V и 5V (или 3.3V, такой адаптер будет немного позже), а в RS232 — +12V и -12V соответственно. Вот схема адаптера 1-wire для RS232, взятая из Dallas Application Note 74:



Как мы помним, в Raspberry Pi на всех портах допустимо напряжение до 3.3V, поэтому нужен вот такой «пассивный» адаптер для UART 3.3V:



В этой схеме также необходимо использовать только диод Шоттки.

Аппаратные адаптеры


Существует еще и третий тип адаптеров — аппаратные, это микросхемы-конвертеры в 1-wire, выпускаются они для трех протоколов: USB, UART, i2c. У них есть преимущества по сравнению с предыдущими типами, например, в «пассивном» адаптере используется прием/передача целого байта для приема/передачи одного бита, а в аппаратном на этом получаем выигрыш в 8 раз (на самом деле аппаратно реализованы и другие функции: ресет, алгоритм поиска), кроме скорости это и упрощение, и сокращение кода, что очень актуально для микроконтроллеров.

Но главное преимущество аппаратных адаптеров это «активная» подтяжка. Разберемся с этим подробнее, для наглядности рассмотрим случай с паразитным питанием. При кратковременном нахождении линии в логическом «0», устройства питаются от своих встроенных конденсаторов, разряжая их, а при возвращении линии в логическую «1», конденсаторы начинают заряжаться, потребляя повышенный ток от линии. Как мы помним, ток идет через резистор подтяжки, имеем RC-цепь с переходным процессом с постоянной времени t=R*C, что проявляется в медленном нарастании напряжения на линии. Очевидным выходом в этой ситуации является кратковременное уменьшение сопротивления резистора подтяжки для сокращения постоянной времени переходного процесса. Эта техника и называется активной подтяжкой, реализовывается путем коммутации линии с напряжением питания на несколько микросекунд (вспоминаем тип выхода пуш-пулл). Это очень помогает на длинных или не очень качественных линиях, т.к. сами проводники также имеют сопротивление.

Конверторы из USB и i2c мне не доводилось использовать, а вот схему с применением микросхемы-конвертера из UART (DS2480B) я приведу, но позже и в чуть более продвинутом исполнении.

Гальваническая развязка


В предыдущих рассмотренных схемах линии земля, питание и данные прямо или косвенно соединяли внутренние схемы всех устройств. Такой вариант подойдет, если устройства будут под Вашим контролем и в безопасной среде, скажем в пределах квартиры или дома. Но если, например, датчик, работающий по 1-wire необходимо установить на крышу (метеостанция), то необходимо задуматься о защите. В данном случае речь идет о защите внутренних схем дорогостоящего оборудования от повышенного напряжения или статических разрядов. Защитить ПК или Raspberry Pi можно, отвязав его гальванически от всех линий 1-wire.

Развязывать необходимо две вещи: питание (и вместе с ним землю) и сигналы. Питание адаптера можно обеспечить от отдельного БП или использовав DC-DC преобразователь с гальванической развязкой (например B0505S). Сигналы можно развязать с помощью оптронов, это пара светодиод-фототранзистор в закрытом общем корпусе. Оптрон позволяет передавать сигнал только в одном направлении, а линия 1-wire является двунаправленной и, к сожалению, нет простых схем сделать двунаправленную гальваническую развязку. Это же касается GPIO, USB и i2c. А вот в UART обе линии RX и TX являются однонаправленными, поэтому получится сделать адаптер как пассивный, так и на аппаратном 1-wire-UART конвертере. Необходимо учесть, что в пассивном адаптере используется скорость UART 115200 BAUD, а аппаратный производит обмен данными на скорости 9600 BAUD. При выборе оптронов необходимо обращать внимание на параметры tON, tOFF, они должны быть существенно меньше 1/BAUD.

К сожалению у меня не нашлось быстрых оптронов, а PC817C, которые были в наличии, уверенно работали только до 19200 BAUD. Примерно таким образом я бы реализовал пассивный адаптер с гальванической развязкой, но, в отличие от остальных схем, эту схему я не проверял:



Полевой транзистор в этой схеме использован для создания большого входного сопротивления «приемной» части. Бонусом гальванической развязки является то, что схема одинаково будет работать и при напряжении UART 5V, и при 3.3V.

И, как было обещано, схема адаптера с применением 1-wire-UART конвертера (микросхема DS2480B) с гальванической развязкой:



Это самый совершенный из всех перечисленных адаптеров.

Приведу также свой вариант его реализации на макетной плате:



Две перемычки используются для подачи GND и +5V на DC-DC в случае UART 5V, а при подключении к UART 3.3V одна убирается и контакт подключается к +5V, второй перемычкой землю также можно сделать отдельной от земли UART.

Эмуляторы адаптеров


За неимением микросхемы DS2480B можно воспользоваться ее программной эмуляцией на любом микроконтроллере AVR, который имеет аппаратный UART. Такой МК установлен практически во всех Arduino. Здесь приведу схему, как из популярного программатора USBasp на микроконтроллере ATmega8A сделать 1-wire адаптер:



Для прошивки Вам, естественно, понадобится второй программатор. Код эмулятора и готовую прошивку можно скачать отсюда, код взят из проекта tinyowd, в эмуляторе не реализована активная подтяжка и некоторые функции.

Сетевой уровень


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

Все подчиненные устройства «слышат» мастера одновременно, но отвечать должно только одно. Для этого после начальной процедуры «отправка ресет, чтение присутствия» осуществляется процедура «адресации подчиненного», после которой весь последующий обмен данными осуществляется только с выбранным подчиненным, а все остальные устройства эти данные игнорируют до следующего цикла «отправки ресет».

Существует всего четыре команды сетевого уровня, которые понимают все без исключения подчиненные: CCh (SKIPROM), 33h (READROM), 55h (MATCHROM), F0h (SEARCHROM). Но есть и некоторые другие, например, ECh (ALARM), A5h (RESUME) и Overdrive-варианты предыдущих команд, которые понимают не все устройства. На самом деле две первые команды адресуют не одно а все устройства, первая (SKIPROM) используется для широковещательной рассылки команд, например «запустить на всех устройствах измерение температуры», а сама температура будет прочитана позже по отдельности с каждого устройства. Вторая (READROM) используется только когда известно, что устройство на шине может быть только одно. Но для проверки на сетевом уровне есть еще контрольная сумма адреса, так что, если ответят несколько устройств, контрольная сумма это позволит выявить.

Адреса 1-wire, их еще называют идентификаторами, имеются только у подчиненных устройств. Адреса имеют длину 8 байт, из которых независимые только 7, а восьмой байт содержит контрольную сумму этих семи байтов адреса. Адреса всех устройств уникальны, они прошиваются при изготовлении и изменить их уже нельзя. Первый байт адреса всегда одинаков для одного типа микросхем, т.е. по нему можно однозначно определить название микросхемы. Этот байт носит название «адрес семейства».

После процедуры «адресации подчиненного» последующий обмен данными уже относится к сеансовому и более высоким уровням модели ISO OSI и может отличаться для разных семейств устройств.

Низкоуровневая работа


Продемонстрирую, насколько просто происходит работа с DS2480B. Для этого в одном терминале из командной строки bash запускаем:

while IFS= read -rd "" -n1 c;do printf "%02x\n" "'$c";done </dev/ttyAMA0

Эта команда будет читать из UART и выводить на экран в шестнадцатеричном формате каждый прочитанный байт. В другом терминале будем отправлять байты в UART.

Первым делом отпрявляем Break для сброса DS2480B, для этого снижаем скорость UART ниже 9600 BAUD и предаем 00h, DS2480B при чтении в стоп-бите обнаружит логический «0», воспримет это как команду сброса и приведет свое состояние в исходное (окажется в режиме команд):

stty -F /dev/ttyAMA0 raw pass8 4800
echo -ne '\x00' > /dev/ttyAMA0

После чего возвращаем скорость на 9600 BAUD:

stty -F /dev/ttyAMA0 9600

Отправляем любую команду (в данном случае это команды DS2480B) для калибровки скорости DS2480B, на которую DS2480B никак не отреагирует, например, безобидную команду чтения параметра конфигурации 01h:

echo -ne '\x01' > /dev/ttyAMA0

Отправляем команду C1h, которая генерирует «отправку ресет» на шине 1-wire, в ответ получим CDh если на 1-wire последовал импульс «присутствия», или EDh если «присутствия» не последовало (ответ наблюдаем в другом терминале):

echo -ne '\xc1' > /dev/ttyAMA0

Переключаем DS2480B в режим данных, в ответ ничего не получаем:

echo -ne '\xe1' > /dev/ttyAMA0

В режиме данных все байты, которые передаются в UART транслируются конвертером побитно в 1-wire и при передаче битов 1 автоматически происходит чтение бита, которое может обнулять биты в исходном байте. Модифицированный таким образом байт конвертер отправляет обратно в UART. Отправляем команду 33h, теперь это уже команда READROM сетевого уровня протокола 1-wire, в ответ получим 33h без изменений:

echo -ne '\x33' > /dev/ttyAMA0

В ответ на READROM устройство (для простоты подключаем на шину только одно устройство) отправит свой ID, для этого ему необходимо послать 64 единичных бита (идентификаторы состоят из 8 байт), которые в ответе будут модифицированы:

echo -ne '\xff\xff\xff\xff\xff\xff\xff\xff' > /dev/ttyAMA0


Вот что будет в другом терминале после выполнения всех вышеперечисленых команд:

cd
33
28
8d
f4
44
02
00
00
a6

Идентификатор подчиненного устройства здесь будет 288df444020000a6, где 28 — адрес семейства (датчики температуры DS18B20), a6 — контрольная сумма адреса, проверять ее должны мы, а не DS2480B.

Естественно, в Linux так никто не работает с 1-wire. Но при программировании микроконтроллеров или драйверов, все происходит именно подобным образом.

Высокоуровневая работа


В ОС Linux есть два подхода к высокоуровневой работе с устройствами 1-wire:

  • с помощью приложений в пространстве пользователя,
  • с помощью драйверов ядра.

Первый способ исторически появился раньше и на сегодняшний день все еще является более предпочтительным. Самой полной реализацией является пакет owfs, в котором есть поддержка большого числа адаптеров и подчиненных устройств. Приложение, как правило, получает доступ к адаптеру через файл устройства в /dev/ и всю низкоуровневую работу с ним выполняет самостоятельно.

При работе с драйверами ядра, вся низкоуровневая работа с устройствами выполняется в ядре, которое предоставляет доступ приложениям к 1-wire через интерфейс netlink и дополнительно через специальную файловую систему /sys/.

Драйверы ядра


Драйверы ядра делятся на драйверы для адаптеров и драйверы для подчиненных устройств. Драйверы адаптеров создают уровень абстракции W1, объединяя шины 1-wire от всех доступных адаптеров. Далее есть два варианта использования уровня абстракции W1: использование его в приложениях, например, в owfs как еще один вид «адаптера», или использование поверх него драйверов подчиненных устройств (тоже в ядре).

Для работы с 1-wire с помощью драйверов ядра достаточно загрузить драйвер адаптера. Например, для DS2490 (это аппаратный 1-wire-USB конвертер):

sudo modprobe ds2490

Для DS2482 (это аппаратный 1-wire-i2c конвертер) предварительно необходимо загрузить драйвер для конкретного i2c контроллера и затем сообщить ему адрес адаптера на шине i2c:

sudo modprobe i2c-<контроллер>
sudo modprobe ds2482
sudo echo 'ds2482 0x18' > /sys/bus/i2c/devices/i2c-0/new_device

Для GPIO:

sudo modprobe w1-gpio

В случае Raspberry Pi в файле /boot/config.txt добавить строку, или можно даже несколько:

dtoverlay=w1-gpio,gpiopin=4
dtoverlay=w1-gpio,gpiopin=17
dtoverlay=w1-gpio,gpiopin=27

Тогда при загрузке ядра модуль w1-gpio загрузится автоматически. К сожалению, в ядре (на момент написания статьи это linux-rpi-5.4.y) драйверы для DS2480B и пассивного адаптера не реализованы.

При загрузке любого драйвера адаптера автоматически загрузится модуль wire, который запустит периодический поиск подчиненных устройств на шине адаптера и автоматически загрузит модули для найденных подчиненных устройств. В каталоге /sys/bus/w1/devices/ появятся подкаталоги с именами, равными идентификаторам найденных устройств, в подкаталогах единственным полезным является файл w1_slave.

Несмотря на простоту этого способа, я его не рекомендую по причине того, что драйверы все еще сырые, для некоторых устройств реализован не весь функционал, а для многих устройств драйверов вообще нет.

OWFS


Центральной частью пакета owfs является демон owserver, который работает с адаптерами через файлы устройств в /dev/ или через другие интерфейсы ядра и предоставляет доступ ко всем устройствам 1-wire для других приложений по собственному протоколу поверх TCP, мультиплексируя запросы от клиентов и кешируя ответы от устройств.

Для установки owfs необходимо выполнить аналоги следующих команд для своего дистрибутива:

sudo apt-get update -y
sudo apt-get install -y owfs ow-shell

Или скомпилировать из исходных кодов отсюда: github.com/owfs/owfs/releases

Следующим шагом необходимо добавить адаптеры в файл конфигурации. Пример файла конфигурации /etc/owfs.conf:

## USB адаптер на микросхеме DS2490
#server: usb
## i2c адаптер на микросхеме DS2482
#server: device /dev/i2c-0
## UART адаптер на микросхеме DS2480B
server: device /dev/ttyAMA0
## Пассивный адаптер
server: passive /dev/ttyUSB0
## доступ к адаптерам через драйверы ядра
#server: w1
## [адрес:]порт, на котором owserver принимает соединения от клиентов
server: port localhost:4304
## [адрес:]порт, на котором owhttpd принимает соединения от клиентов
http: port localhost:2121

В owfs нет поддержки адаптера, использующего GPIO, поэтому единственным способом получить доступ к GPIO из owfs является использование драйвера ядра w1-gpio и получение к нему доступа через уровень абстракции W1. Такой способ потребует запуска owserver от имени root. В любом случае, я не рекомендую адаптер на GPIO для серьезного использования, т.к. он реализован программно (техника bitbang) и грузит процессор.

После запуска owserver, к нему можно обращаться командами, например:

owdir /
owwrite /1d.40420f484e59/pages/page.6 'Электроэнергия кв.1'

Как можно заметить, контрольная сумма адреса не указывается, owserver сам ее вычисляет. К owserver также можно обращаться и с другого хоста:

owget -s 10.0.0.1:4304 /1d.40420f484e59/counter.A

Естественно, для этого в /etc/owfs.conf должно быть:

server: port 10.0.0.1:4304

Следует предостеречь, что в owserver нет никаких разграничений прав доступа, поэтому любой, кто может подключиться к порту owserver может как читать, так отправлять команды и записывать на подчиненные устройства.

Для языков программирования C, python, perl, php есть библиотеки, из которых тем же способом через TCP можно получить доступ к owserver. Протокол у owserver очень простой, поэтому можно легко написать реализацию для любого другого языка.

Доступ из JavaScript


Еще один демон из пакета owfs, который может быть полезен — owhttpd, он также обращается к owserver по TCP, а с другой стороны предоставляет доступ на порту 2121 по http. Но самое полезное находится в нем по адресу http://сервер:2121/json/, что открывает возможности для обращения к устройствам 1-wire из веб-страницы на JavaScript с помощью XMLHttpRequest.

var r = new XMLHttpRequest();
r.open("GET", "http://127.0.0.1:2121/json/uncached/", true);
r.responseType = "json";
r.onload = function () {
  var i, h = "";
  if (this.status == 200 && typeof this.response === "object")
    for (i in this.response)
      h += i + "<br>";
  document.getElementById("owlist").innerHTML = h;
};
r.send();

Здесь то же самое предостережение: доступ к owhttpd также позволяет как читать, так и записывать на подчиненные устройства всем, кто может подключиться к порту owhttpd. Поэтому приведенный выше способ годится только, например, для вэб-интерфейсов администратора для внутреннего использования.

Полезные советы


Т.к. owserver обращается к адаптерам через файлы устройств в /dev/, то можно с помощью udev передать права на устройства другому пользователю, например, создать пользователя owfs и от его имени запускать owserver. Для этого в файле /etc/udev/rules.d/85-onewire.rules добавить строки примерно с таким содержимым:

KERNEL=="ttyAMA0", SUBSYSTEM=="tty", SYMLINK+="owbus0", OWNER="owfs"
KERNEL=="ttyUSB[0-9]*", SUBSYSTEM=="tty", SUBSYSTEMS=="usb",  ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="FTVNG262", SYMLINK+="owbus1", OWNER="owfs"

Если необходимо организовать несколько 1-wire шин, то потребуется несколько UART интерфейсов. Для этого можно восопользоваться несколькими USB-UART конвертерами. А в Raspberry Pi 4 можно активировать до пяти аппаратных UART, для этого в /boot/config.txt нужно добавить:

dtoverlay=disable-bt
dtoverlay=uart2
dtoverlay=uart3
dtoverlay=uart4
dtoverlay=uart5

Файлы устройств после перезагрузки будут называться /dev/ttyAMA[0..4], эти UARTы будут работать на выводах:

AMA0 TX=GPIO14(пин  8) RX=GPIO15(пин 10)
AMA1 TX=GPIO0 (пин 27) RX=GPIO1 (пин 28)
AMA2 TX=GPIO4 (пин  7) RX=GPIO5 (пин 29)
AMA3 TX=GPIO8 (пин 24) RX=GPIO9 (пин 21)
AMA4 TX=GPIO12(пин 32) RX=GPIO13(пин 33)

Напомню, что так же, как и в случае с GPIO, если мы используем встроенный UART на Raspberry Pi, необходимо применение преобразователя логических уровней или гальванической развязки, т.к. все GPIO на Raspberry Pi работают с напряжением до 3.3V.

Сравнение с ModBus


Для понимания, какое положение занимает 1-wire в сравнении с другими подобными протоколами для автоматизации, я провел сравнение с популярным протоколом ModBus. ModBus также использует модель мастер-подчиненные и в связке с RS485 также может работать используя всего два провода.

+-----------------------+------------------+--------------------------+
|                       | 1-wire           | ModBus                   |
+-----------------------+------------------+--------------------------+
| Проприетарный         | Да               | Нет                      |
| Документация          | Открыта          | Открыта                  |
| Физический уровень    | 1-wire           | RS485, RS232, TCP/IP,... |
| Канальный уровень     | 1-wire           | UART                     |
| Сетевой уровень       | 1-wire           | ModBus                   |
| Уровень приложения    | 1-wire           | ModBus                   |
| Только два провода    | да               | возможно в RS485         |
| Паразитное питание    | да               | нет                      |
| Напряжения            | 0..5 V           | до +/-12V в RS485        |
| Дифференциальный сигн.| нет              | да, в RS485              |
| Скорость обмена       | фикс. ~15 kbit/s | выбирается, до 10 Mbit/s |
| Фронты сигналов       | < 8 us           | < 1/BAUD                 |
| Точность тактирования | +/-50%           | +/-5%                    |
| Длина линии           | до 300м          | до 1200м (10^8/BAUD)     |
| Размер пакета         | не ограничен     | до 253 байт              |
| Размер адреса         | 56 бит           | 8 бит                    |
| Контрольная сумма     | 16 бит           | 16 бит                   |
+-----------------------+------------------+--------------------------+

Как можно заметить, 1-wire более медленный, не такой гибкий и не такой помехозащищенный, что не дает ему права называться «промышленным». Но его характеристик более чем достаточно для домашней автоматизации в системах «умный дом». А используемые напряжения и простота «адаптера» для микроконтроллера делают его идеальным для разработки собственных устройств на МК.

Выводы


Протокол 1-wire на самом деле является очень легким в освоении. Я надеюсь, что статья поможет пролить свет на многие ранее не понятные моменты, которые могли быть препятствием на пути к использованию данного протокола.

Для знакомства с протоколом владельцам Raspberry Pi я рекомендую начать со сборки адаптера на GPIO. В файле /boot/config.txt добавить:

dtoverlay=w1-gpio,gpiopin=4

Установить owfs и записать в /etc/owfs.conf всего три строки:

server: w1
server: port localhost:4304
http: port localhost:2121

Владельцам ПК или ноутбука рекомендую приобрести USB-UART конвертер (он Вам еще не раз пригодится, они бывают на микросхемах FT232, PL2303, CP210X и др. — подойдет любой) и собрать пассивный адаптер. Установить owfs и записать в /etc/owfs.conf тоже всего три строки:

server: passive /dev/ttyUSB0
server: port localhost:4304
http: port localhost:2121

И в том и в другом случае после перезагрузки выполнить:

owdir /

Следующим шагом рекомендую сборку адаптера на DS2480B с гальванической развязкой.

Приведу несколько полезных 1-wire подчиненных устройств, которые стоит попробовать:

  • DS18B20 — самый популярный датчик температуры, диапазон измеряемых температур: -55C… +125C, точность ±0.5C, возвращает температуру уже в градусах цельсия: 8 бит на целую часть и 4 бит — на дробную;
  • DS2450 — четырехканальный АЦП, можно использовать для аналоговых датчиков, выводы также могут работать на выход, тип выхода открытый коллектор, можно использовать для управления реле;
  • DS2423 — двухканальный счетчик импульсов (уже не производится, но есть программные эмуляторы), можно использовать, например, для дистанционного снятия показаний с бытовых приборов учета;
  • DS2438 — контроллер аккумулятора, но он идеально подходит для подключения датчиков влажности, имеет АЦП и датчик температуры, еще два вывода можно использовать для датчика освещенности, т.е. почти мини-метеостанция.

P.S.: Если некоторые очевидные моменты в статье расписаны излишне подробно, прошу отнестись к этому с пониманием, т.к. статья рассчитана на читателей с разным уровнем подготовки. И наоборот, если какие-то моменты освещены слишком скомкано или не понятно, прошу написать об этом в комментариях.
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 56

    0

    Спасибо.


    Хотя хак


    В этой схеме убран резистор подтяжки, подключенный к истоку, с этим «хаком» схема продолжит работать. Т.к. напряжение на GPIO (и на истоке) станет меньше чем 3.3V (напряжение на затворе) на величину напряжения затвор-исток, при которой открывается транзистор. Напряжения на GPIO будет достаточно, чтобы GPIO его воспринимал как логическую «1».

    выглядит сомнительным и "производитель_и_партия_транзисторов зависимым".

      0
      полуофф Вопрос практический: Есть герметичный датчик температуры. Трехпроводной кабель, на конце которого металлический стержень. Знаю, что внутри DS18B20. Как найти VCC DQ GND?
        +2
        1. Мультиметром находите GND: между GND и DQ и между GND и VCC есть диоды
        2. Подключением к 1-wire определяете, где VCC, а где DQ
          +1

          To check the DS18B20 with a multimeter, choose the "diode testing" mode, and put the positive testing lead to the white wire (GND) of the sensor. The voltage between white wire (GND) and the red wire (VDD) should be around 0.59V ~ 0.63V; the voltage between white wire (GND) and the yellow wire (DQ) should slightly lower, around 0.49V ~ 0.55V.
          https://www.auberins.com/index.php?main_page=product_info&products_id=322

            0
            В данной ситуации ни один из проводов неизвестен. По методу honechko три провода определены.
          0
          Отличная статья. Спасибо. Подскажите есть ли возможность подключить в качестве подчиненного устройства микроконтроллер STM32f0xxx (или аналогичный) в режиме паразитного питания по двум проводам. Не подводя питания отдельно.
            +1
            Скорее всего получится, все зависит от тока потребления МК.
            Считаете примерно так: [напряжение шины DQ просядет на] = [ток потребления МК] * [сопротивление резистора подтяжки] и смотрите, достаточно ли оставшегося напряжения (VCC — «просадка») для штатной работы МК (и других устройств на той же шине, если они есть).
            Для снижения тока МК используйте, где это только возможно, режимы сна.
            Для примера у меня ATtiny13A на 4.8MHz с использованием сна потребляет 400-500мкА.
            Можно также снизить частоту МК, это также снижает почти линейно ток потребления.
              0
              Для примера у меня ATtiny13A с использованием сна потребляет 400-500мкА.

              неприлично много, например stm8l или stm32l в сне потребляют около 1 мкА
                0
                Я имел в виду не самый глубокий сон (power-down), а idle, в котором МК будет успевать просыпаться и обрабатывать протокол. И ток указал для реального устройства, работающего по 1-wire в режиме обработки протокола.
                Давайте не превращать тему в холивар avr vs stm32)
                  0

                  Да нет никакого спора, просто нет смысла МК работать, можно спать и потреблять единицы мкА. Тем более One Wire поддерживает работу от наведенного тока: https://www.maximintegrated.com/en/design/technical-documents/app-notes/6/6755.html

                    +1
                    Ну если МК спит и ему снится, что он работает, то да.
                  0

                  Со включенной оперативкой будет уже 3-4

                    0
                    всё равно не 500
                      +1
                      Давайте внесу ясность:
                      * есть ток потребления МК в активном режиме, когда выполняются инструкции,
                      * есть ток потребления МК в неглубоком сне, когда он может проснуться всего за пару микросекунд, чтобы обработать протокол,
                      * есть ток потребления МК в глубоком сне (power-down), из которого он просыпается неприлично долго.
                      Для обработки протокола (скажем для 1-wire нужно время реакции <8мкс), МК будет находиться x% времени в активном режиме и (100-x)% в неглубоком сне.
                      Вот посчитав токи в этих режимах в реальном процентном соотношении и получается 400-500мкА.
                        0
                        вы так и хотите avr vs stm32 )
                        вообще сам протокол 1w не энергоэффективный.
                          0
                          Вообще сами микроконтроллеры не эффективны, нужно использовать только узкоспециализированные микросхемы, аналоговые и обязательно ламповые
                            0

                            Да, причем плис — это тоже неэффективно, только асики!

                    0
                    неприлично много, например stm8l или stm32l в сне потребляют около 1 мкА
                    1 мкА — это про глубокую спячку. При включенном тактовом генераторе и работающей периферии (то, что в AVR называется «режим Idle») ваш стм32л потребляет от 80 мкА до 2.5 мА, а никакие не 1 мкА.
                    В глубоком сне AVR-ки потребляют 200 нА. При этом, кстати, модуль TWI остаётся способен распознавать собственный адрес и будить МК при обращении.
                      0

                      А в чем смысл того, что вы сравниваете stm32 в рабочем режиме со включенным генератором и выключенную аврку без оперативки и генератора? Любой контроллер можно так отключить, это почти равноценно отключению по питанию.


                      А стмке так же есть асинхронная периферия, которая умеет генерировать прерывания из стопа (тот же LPUART, LPTIER и другое). А есть еще PSoC от сайпресса, где асинхронную периферию можно программировать самому на верилоге и она еще в тысячу раз эффективнее архаичных stm32 и avr.

                        0
                        Э-эм… где это я их сравниваю? Это dernuss зачем-то сравнил AVR в комбинированном активном режиме и выключенный стм без оперативки. Я только привёл немного циферок для разных «спячек».
                        и выключенную аврку без оперативки
                        Оперативка в авр никогда не отключается, но тем не менее 0.2 мкА.
                        тот же LPUART, LPTIER и другое
                        Но не I2C. Может быть кому-то и есть польза от асинхронного уарта, вот только I2C — вполне себе системная шина и возможность просыпаться по событию на системной шине я считаю уберполезной абилкой.
                        А есть еще PSoC от сайпресса, где асинхронную периферию можно программировать самому на верилоге
                        Насколько помню, весь верилог там ограничивается несколькими LUT-табличками, на которых можно слепить немного glue logic между выводами периферии и I/O ножками. До полноценного набортного плис, на котором можно было бы накатать модуль I2C с фичами не хуже чем в авр там как до луны на карачках.
                          +1
                          Оперативка в авр никогда не отключается, но тем не менее 0.2 мкА.

                          Название МК в студию.
                          Сами лично пробовали? Чем микротоки измеряете?


                          Насколько помню, весь верилог там ограничивается несколькими LUT-табличками, на которых можно слепить немного glue logic между выводами периферии и I/O ножками. До полноценного набортного плис, на котором можно было бы накатать модуль I2C с фичами не хуже чем в авр там как до луны на карачках.

                          Проблемы тех, то не осилил. Я осилил и все работает прекрасно. Смеюсь теперь над любителями архаичных контроллеров с прибитой гвоздями периферией.

                            0
                            Название МК в студию.
                            В принципе, любые межки с picoPower (буква P на конце), тиньки из не самых древних, в т.ч. 13A.
                            Сами лично пробовали?
                            Пробовал конечно. Главное плату хорошо отмывать, утечки по остаткам всяких ЛТИ-120 могут на порядок превышать потребление схемы.
                            Чем микротоки измеряете?
                            А в чём вообще проблема? Обычный UT-61E в режиме мкА. Можно и пА померить микровольтметром, используя входные 10М в качестве шунта.
                            Проблемы тех, то не осилил. Я осилил и все работает прекрасно.
                            "Всё работает" — это крайне подозрительная формулировка, обычно намекающая на то что не работает ничего. Конкретно I2C может выводить МК из пауэр-даун? Мне эта фича на архаичных атмежках очень нравится.
                              +1

                              Открываем даташит на attiny13a, видим что она вообще не умеет работать в режиме остановки, с сохранением оперативки и PC, то есть в ней нет режима, аналогичного STOP в STM32:


                              Заголовок спойлера


                              When the SM[1:0] bits are written to 10, the SLEEP instruction makes the MCU enter Powerdown mode. In this mode, the Oscillator is stopped, while the external interrupts, and the Watchdog continue operating (if enabled). Only an External Reset, a Watchdog Reset, a Brown-out Reset, an external level interrupt on INT0, or a pin change interrupt can wake up the MCU. This sleep mode halts all generated clocks, allowing operation of asynchronous modules only.

                              То есть, как я написал выше, это банальное отключение по питанию с возможностью генерировать RESET по паре событий. Ни о какой работе оперативки с потреблением 0,2 мкА тут речи не идет.


                              Обычный архаичный контроллер.


                              А в чём вообще проблема? Обычный UT-61E в режиме мкА. Можно и пА померить микровольтметром, используя входные 10М в качестве шунта.

                              Этот батареечный мультиметр будет показывать погоду на марсе в таком режиме, потому что он делает измерения не непрерывно, а всего лишь пару раз в секунду, показывая вам случайную величину, которая попала в квант измерения времени.


                              Для измерения микротоков нужен настолный мультиметр, к примеру Rigol DM3058E который измеряет непрерывно и показывает среднее.

                                0
                                То есть, как я написал выше, это банальное отключение по питанию с возможностью генерировать RESET по паре событий. Ни о какой работе оперативки с потреблением 0,2 мкА тут речи не идет.
                                Полнейшую чушь написали.
                                Этот батареечный мультиметр будет показывать погоду на марсе в таком режиме, потому что он делает измерения не непрерывно, а всего лишь пару раз в секунду, показывая вам случайную величину, которая попала в квант измерения времени.
                                Полнейшую чушь написали.
                                  0
                                  Оперативка в авр никогда не отключается, но тем не менее 0.2 мкА.

                                  В принципе, любые межки с picoPower (буква P на конце), тиньки из не самых древних, в т.ч. 13A.

                                  Так как называется режим у AtTiny13a, в котором сохраняется оперативка, но общее потребление составляет 0,2 мкА, уважаемый не порящий чушь?

                                    0

                                    Можете не верить, но "Power-down". Всё сохраняется, выход прерыванием по уровню или сбросом WDT при котором, внезапно, ОЗУ (в т.ч. регистры) остаются неизменны.

                                      0

                                      А вы точно понимаете разницу между регистрами периферии и SRAM? А то они в общем адресном пространстве, можно и перепутать...

                                        0

                                        Если мы просыпаемся из PowerDown по прерыванию, то всё состояние будет сохранено.


                                        Прошу прощения, что не идентифицировал регистры, как R0:R31.
                                        А те, другие, частенько портами называют.

                                      0
                                      Power-down
                                    0
                                    … даташит на attiny13a, видим что она вообще не умеет работать в режиме остановки, с сохранением оперативки и PC .....

                                    Не порите чушь, ей больно.

                                      0

                                      А, да, всегда забываю про бесполезный Idle Mode, в котором неработаюшая аврка потребляет столько же, сколько stm32 на полном ходу со всей включенной периферией и без использования функций энергосбережения вообще)))))))))))


                                      Заголовок спойлера



                                      На 4 МГц (архаичная архитектура даже не умеет отлючать тактовый генератор) 1 мА у AVR с неработающим ядром, против 0,63 мА у STM32, которая выполняет бенчмарк.


                                      Удачи в разработке)))

                                        0
                                        Вы бы свою энергию лучше направили на написание статьи «Сравнение энергопотребления МК». Я серьезно, было бы очень интересно. В даташитах кроме всего прочего есть графики зависимости потребления от частоты и потребления от напряжения, было бы очень полезно сравнить на равных частотах и на равных напряжениях. А то маркетологи любят запудрить мозги.
                                          0
                                          Как правило сравнивать 8 и 32 битные микроконтроллеры бесполезно, задачи как правило разные. 8 битные обычно и озу и пзу обделяют, ни шифрования нормального не сделать, ни чего. У меня например не убиралось в stm8l AES128 + BME280 + RF трансмиттер. Пришлось заменить на stm32l и не мучаться. После этого везде ставлю 32битные с побольше памятью
                                            +1
                                            Вы бы свою энергию лучше направили на написание статьи «Сравнение энергопотребления МК».

                                            Я бы написал одну строчку: если нужна максималная энергоэффективность — делайте на PSoC, у них самый лучший техпроцесс (10-20 мкА на МГЦ) и программируемая периферия, которая позволяет не будить ядро на простых задачах.


                                            А что может быть интересно в этой теме? По сути ведь все есть в даташитах и в 99% там правда. Просто у разных производителей оно по-разному написано, кто-то не дает таблички на все возможные варианты конфигураций.


                                            В даташитах кроме всего прочего есть графики зависимости потребления от частоты и потребления от напряжения, было бы очень полезно сравнить на равных частотах и на равных напряжениях.

                                            Но самую большую подставу обычно составляет зависимость от температуры. Мало какой контроллер спроектирован так, что он при нагреве не начинает потреблять в 10-100 раз больше нормы:



                                            Казалось бы редкая ситуация, а вы попробуйте сделать счетчик горячей воды на батарейках)))

                                            0

                                            Да, насчёт PC я немного погорячился (он может не сохраниться, если использовать WDT). А насчёт ОЗУ — я полностью прав.


                                            Про квадратичную зависимость тока от напряжения — упомянем?

                                              0

                                              В даташите ясно написано что в power-down (в котором 0,2 мкА) оперативка отключается, вы выглядите очень глупо, затеяв спор с даташитом.


                                              The Idle mode stops the CPU while allowing the SRAM, Timer/Counter, ADC, Analog Comparator, and Interrupt system to continue functioning. The Power-down mode saves the register contents, disabling all chip functions until the next Interrupt or Hardware Reset. The ADC Noise Reduction mode stops the CPU and all I/O modules except ADC, to minimize switching noise during ADC conversions.

                                              Единственные контроллеры, которые умеют достигать такого потребления с сохранением оперативки — msp430 на fram.

                                                0

                                                Там написано, что основной тактовый генератор выключается и активны только асинхронно работающие блоки или WDT:
                                                Power-down Mode
                                                … the SLEEP instruction makes the MCU enter Powerdown mode. In this mode, the Oscillator is stopped, while the external interrupts, and the Watchdog continue operating (if enabled). Only an External Reset, a Watchdog Reset, a Brown-out Reset, an external level interrupt on INT0, or a pin change interrupt can wake up the MCU. This sleep mode halts all generated clocks, allowing operation of asynchronous modules only.


                                                Из сравнения рисунка "Clock Distribution" и таблицы "Active Clock Domains and Wake-up Sources in the Different Sleep Modes" (clkCPU) нетрудно заключить, что содержимое ОЗУ (включая R0:R31) остаётся неизменным и это действительно так.

                                                  –1

                                                  А, лол, точно, я совсем забыл что мы говорим о "контроллере" с 64 байтами оперативки, то есть меньше чем в RTC_BACKUP регистрах у типичного современного МК.


                                                  Опять аврщики опустили меня на свой уровень архаичного железа и подавили опытом!


                                                  Удачи там в разработке на 64 байтах оперативки. Они и правда сохраняются в power-down. Жаль, правда, что в этом нет никакого практического смысла.

                                                +3
                                                Да, насчёт PC я немного погорячился (он может не сохраниться, если использовать WDT).
                                                Будить ресетом — это немного странно. Можно же попросить собаку дёрнуть сперва прерывание.
                                                Из сравнения рисунка «Clock Distribution» и таблицы «Active Clock Domains and Wake-up Sources in the Different Sleep Modes» нетрудно заключить, что содержимое ОЗУ остаётся неизменным и это действительно так.
                                                В общем-то, в разделе про слип моды явно написано «The contents of the Register File and SRAM are unaltered when the device wakes up from sleep».
                              0
                              А не будут ли импульсы при передачи данных сильно опускать напряжение на входе МК? Нужна ли какая-то схема на конденсаторах? Или и так будет работать, если просто подтянуть шину данных к питанию резистором 4.7 кОм?
                                +2
                                Естественно нужен конденсатор, и между ним и DQ обязательно нужен диод (Шоттки), чтобы конденсатор не мешал линии данных. Я применяю следующий «хак»: ставлю конденсатор между GND и VCC микроконтроллера, а DQ и VCC 1-wire подключаю к двум пинам МК, таким образом используя встроенные диоды в пинах (между выводом и VCC МК есть диоды):

                                В схеме использую еще и супрессоры, рекомендую для защиты.
                              0
                              гадаю конечно, но что то сомнительно, что заведется. Все таки стмики32 даже младших серий достаточно привередливы к питанию.
                              –1
                              Лично я использовал готовый преобразователь, микросхему на i2c и не знал горя.
                                0
                                Есть две модификации этой микросхемы: DS2482-100 — с одним выходом 1-wire и DS2482-800 — с аж 8 выходами 1-wire.
                                Т.к. с этими микросхемами я не работал, то упомянул вскользь. Если у Вас есть чем дополнить, поделитесь, пожалуйста опытом.
                                +2
                                Спасибо за заголовок) как же я ненавижу 1-wire.
                                  0
                                  Открытый сток или коллектор никак не подключены внутрь микросхемы, то есть его можно подключить к нагрузке, питаемой от другого источника питания, имеющий лишь общую землю с управляющей схемой.
                                    0
                                    Если это вопрос, то отвечу. К открытому коллектору/стоку в чистом виде — да можно. Но если это пин МК или RPi, который работает в режиме открытый коллектор/сток, то напряжение на нем не должно превышать напряжения питания МК/RPi. Причина в диоде, который есть во внутреннем устройстве пина, он включен между пином и Vcc МК/RPi. Если на пин подать напрежение больше, чем Vcc+0.6V, то ток от внешнего источника пойдет через этот диод и будет «питать» МК/RPi, т.е. МК/RPi будет запитан от повышеного напряжения. Выход — подключить к пину внешний транзистор.
                                      0
                                      Да нет, не вопрос. Не более чем уточнение, относящееся к термину «открытый коллектор/сток».
                                    0

                                    Спасибо за статью, 1-wire становится понятнее.

                                      0
                                      А можете привести пример как опросить шину на предмет наличия устройств?
                                      То есть изначально неизвестно сколько чего на шине висит. Как узнать их адреса?
                                        0
                                        Используя высокоуровневый доступ — очень просто, для вараианта с owfs:
                                        owdir /
                                        а при использовании драйверов ядра:
                                        ls /sys/bus/w1/devices/
                                          0
                                          А на сетевом уровне? Насколько я понимаю на команду поиска устройства должны ответить своим идентификатором. Если эту команду послать широковещательно, то отвечать будут все вместе, в результате в лучшем случае услышим только то устройство, которое имеет выше приоритет. А остальных как услышать?
                                            0
                                            Для этого используется уже не READROM, а команда SEARCHROM и алгоритм поиска, о нем подробнее почитать можно, например, здесь radiohlam.ru/1-wire_scan
                                        0
                                        Немного поправлю: UART — это любой Universal Asynchronous Receiver/Transmitter, общее понятие, у которого нет единого физического стандарта. Как Вы правильно заметили, в разных реализациях используются разные напряжения логических сигналов, но еще сигналы бывают и разной полярности (в паузе может быть как 0, так и 1). А вот RS232 — это уже стандарт.
                                          0
                                          очень хотелось понять, но уже абзаца «Пассивный адаптер» заблудился, следующий так же, далее поплыл совсем. написано знатоком для знатоков
                                            0
                                            Для перехода от «Канальный уровень» до «Пассивный адаптер» нужно всего-лишь разобраться с UART.

                                          Only users with full accounts can post comments. Log in, please.