Поиск устройств на шине 1-Wire (на ассемблере для микроконтроллеров AVR фирмы Atmel)

Случилось тут мне собирать одно устройство неопределенного назначения, в котором используются датчики температуры DS1820. Поскольку датчиков было больше одного, то пришлось думать: как к ним обращаться. Можно было, конечно, заранее вбить в программу коды ROM каждого датчика, но это ведь не наш метод. Хотелось универсальности, чтобы раз и навсегда.  Пришлось обратить внимание на процедуру поиска устройств, т.е. проштудировать Application Note 187. 1-Wire Search Algorithm фирмы Dallas (Maxim). В аппноуте расписан принцип поиска и приведен алгоритм с примером на языке С. Поскольку мне необходимо было реализовать свою идею на ассемблере, да и люблю я его больше, пошел я поиском по сайтам и ничего толком не нашел. Все ссылаются на аппноут, а примера на ассемблере нет. В результате, я перевел Application Note 187. 1-Wire Search Algorithm на русский язык и портировал функцию поиска с языка С на AVR-ассемблер, и предлагаю ее вам, дабы заполнить этот пробел в Интернете.

Начну с теории. Если вникать в принципы поиска не хочется — милости прошу в конец статьи, где находятся ссылка на скачивание готовых модулей, их описание и пример использования.

Теоретическая часть

Везде в Интернете в описаниях шины 1-Wire и поддерживающих ее устройств написано, что процедура поиска устройств SEARCH_ROM (0xF0) сложна и непонятна. На самом деле, не так уж она и сложна, если понять принцип. А принцип я сейчас попытаюсь доступно изложить.

Подробно устройство шины 1-Wire я описывать не буду. Желающих узнать подробности могу отослать к хорошей статье, поясняющей ее работу как на низком, так и на высоком уровне: http://radiokot.ru/articles/13/

Итак, имеем шину 1-Wire, на которой висит одно ведущее устройство (обычно — микроконтроллер) и неизвестное количество ведомых устройств (не обязательно одинаковых). Согласно схеме включения, шина подтягивается к плюсу питания резистором, так что на ней при простое всегда присутствует логическая «1».

Соответственно то, что получает микроконтроллер из шины, является логическим «И» ответов всех устройств. Т.е. если все устройства отвечают в линию логической «1», то 1 AND 1 AND 1 и т.д. дает в результате ту же «1». Если же одно или несколько устройств отвечают «0», то вся шина просаживается в «0» даже если другие устройства ответили «1». Собственно, с точки зрения шины, для передачи «1» устройству вообще ничего не нужно делать, т.к. она итак подтянута к «1» резистором.

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

Весь обмен с устройствами начинается с того, что ведущее устройство (в большинстве случаев — микроконтроллер) отсылает в шину импульс сброса RESET. Ведомые устройства, получив импульс сброса, отвечают в шину импульсом присутствия PRESENCE. Поскольку все устройства висят на одной шине, они все отвечают в шину одновременно. Но на данном этапе их ответ (PRESENCE) совпадает.

После того, как PRESENCE получен, ведущее устройство начинает поиск, отправляя в шину либо команду 0xF0 (SEARCH ROM) либо 0xEC (ALARM SEARCH). Команду 0xEC мы рассматривать не будем, т.к. она является частным случаем команды SEARCH ROM и предназначена для поиска устройств в состоянии ALARM. Подробнее об этом можно почитать в даташитах к устройствам.

Получив команду SEARCH ROM, каждое устройство передает первый бит своего кода ПЗУ и дополнение этого бита (грубо говоря — его инверсию). Приняв от устройства первый бит и его дополнение, мы должны в подтверждение отправить этот первый бит назад, в шину. Но! Все устройства делают это одновременно, так что контроллер получает логическое «И» первых битов кодов и логическое «И» их дополнений. Что именно передавать в шину в качестве подтверждения? А вот что: на основании бита и его дополнения производится анализ:

  • Если бит = дополнение = «1», это значит, что на линии нет готовых устройств, т.е. никто не ответил. В этом случае поиск прекращается.
  • Если биты не равны, т.е. бит = 0, а дополнение = 1 или наоборот — это значит, что у всех устройств этот бит совпадает. Этот случай простой — мы просто записываем к себе этот бит и переходим к следующим операциям
  • Если бит = дополнение = «0» — это значит, что у устройств первый бит кода ПЗУ не совпадает, т.е. у кого-то «1» а у кого-то «0», это сложный случай, в аппноуте он называется «Discrepancy», что можно перевести как «Различие»

Первый случай, когда бит/дополнение = «1» рассматривать нет смысла — активных устройств больше нет, поиск прекращаем.

Когда бит/дополнение не равны — тут тоже все просто, этот бит одинаков у всех устройств, так что просто запоминаем его в качестве первого бита кода ПЗУ.

Если же есть различие, т.е. бит/дополнение равны «0», тогда и начинается самое интересное. Смысл поиска в том, что приняв от устройства бит, мы должны в подтверждение отправить его назад, в шину. Поскольку в данном случае текущий бит у устройств не совпадает, мы отправляем в шину «0». Это дает нам вот что: все устройства, у которых этот первый бит был равен «1» отключаются от шины и больше не реагируют ни на что до следующего сброса! Т.е. не участвуют больше в поиске. Остаются те, у которых первый бит был равен «0». Они продолжают участвовать в поиске и готовы выслать нам второй бит своего кода, а мы записываем «0» в качестве первого бита кода ПЗУ.

Позицию, где случилось последнее различие, мы запоминаем. Она понадобится нам при следующем поиске, когда мы дойдем до этого места. Т.е. при следующем поиске в этом месте мы увидим, что направление «0» мы уже выбирали и вместо нуля пойдем другим путем: выдадим на шину «1», тем самым, отметая предыдущие устройства, которые мы уже сканировали. Направление меняется именно на последнем различии, в предыдущие различия выбираем тот же направление что и раньше. Это нужно, для того,  чтобы не упустить ни одного устройства.

Итак, весь поиск сводится к последовательности:

  • 1) Принять бит кода ПЗУ и его дополнение
  • 2) На основании этих битов и последнего различия предыдущего поиска принять решение и выдать в шину направление поиска
  • 3) Запомнить полученный бит или выбранное направление в коде ПЗУ
  • 4) Если было различие — запомнить его позицию
  • 5) Перейти к приему следующего бита

Повторяется эта последовательность для каждого из 64 бит. В результате мы получим код ПЗУ одного из устройств. Затем, начинаем поиск еще раз, выбрав другое направление, в случае если встретилось последнее различие. Получим еще один номер ПЗУ. И так до тех пор, пока на шине не останется неизвестных нам устройств.

Чтобы было понятно привожу переведенную блок-схему алгоритма:
Блок-схема алгоритма

Вот в общем виде и весь принцип поиска. Здесь описан именно принцип, а не детальный план. Главное знать, как это делается. Тем, кто решит написать программу поиска самостоятельно я рекомендую изучить Application Note 187. 1-Wire Search Algorithm в первоисточнике. Если с английским туго — могу предложить свой вольный перевод этого аппноута. А теперь, перейдем от теории к практике.

Практическая часть

 

Модуль, содержащий процедуру поиска можно скачать по этой ссылке: Скачать

 

Архив содержит следующие модули:

1-Wire.asm — Замечательный модуль программной реализации 1-Wire. Автор — Radoslaw Kwiecien, http://avr-mcu.dxp.pl

wait.asm — модуль временных задержек. Необходим в модуле 1-Wire.asm. Автор тот же.

1-Wire Search.asm — модуль, содержащий непосредственно процедуру поиска устройств на шине 1-Wire на AVR-ассемблере. Автор — ваш покорный слуга.

Зачем столько? Во-первых не могу не поделиться очень удобными модулями реализации протокола 1-Wire от Radoslaw Kwiecien. Я их перевел, там все понятно. Если кто-то не желает их использовать — милости прошу. Но тогда прочитайте внимательно вступление в модуле 1-Wire Search.asm — там написано, какие внешние подпрограммы используются и обмен между ними.

Если же вы решите воспользоваться моими библиотеками целиком — то достаточно подключить к вашему проекту только модуль 1-Wire.asm. Остальные  подключаются внутри него.

Ну, и напоследок — пример использования поиска:

; ...

#define F_CPU (4000000) ; Определяем константу с тактовой частотой кристалла - она необходима в модуле 1-Wire.asm

; ...

.include "1-wire.asm" ; Подключаем модули 1-Wire

; ...

rcall OWClearROM_NO ; Обнуляем буфер кода ПЗУ
rcall OWFirst ; Ищем первое устройство на шине

sbrs search_flags, search_result ; Если ошибка - то поиск прерываем
rjmp  EndOfExample

loop1:
; ... Здесь делаем с найденным кодом ПЗУ все что нам угодно ...

rcall OWNext ; ищем следующее устройство на шине. БУФЕР ОЧИЩАТЬ НЕЛЬЗЯ!
sbrs search_flags, search_result ; Если ошибка - то поиск прерываем
rjmp EndOfExample

rjmp loop1 ; и так до тех пор, пока все коды ПЗУ не вычислим.

EndOfExample:

; ... идем дальше ...

Вот и весь пример. Засим  — прощаюсь. Будут вопросы — милости просим. Отвечу.

Поделиться с друзьями:
  • Добавить ВКонтакте заметку об этой странице
  • Одноклассники
  • Facebook
  • В закладки Google
  • Мой Мир
  • Twitter
  • LiveJournal
  • Яндекс.Закладки
  • LinkedIn
  • Reddit
  • StumbleUpon
  • БобрДобр
  • Memori.ru
  • МоёМесто.ru

Комментарии

  1. >>>Все устройства делают это одновременно, так что контроллер получает логическое «И»…

    Я бы сказал не И, а ИЛИ. Т.к. 0 на выходе хотя бы одного устройства даёт неизменяемый 0 на шине, что эквивалентно оцерации ИЛИ — «хотя бы один».

    А вообще спасибо! Читал статью т.к. незнал алгоритма поиска. Всё прояснилось:)

    1. Нет, в данном случае все правильно, т.к. операция «ИЛИ» применима не для нулей, а для единиц.
      Вот описание логического «ИЛИ»: результат равен 0, если все операнды равны 0; во всех остальных случаях результат равен 1.
      Вот описание логического «И»: результат равен 1, если все операнды равны 1; во всех остальных случаях результат равен 0.

      Покажу на примере:
      Имеется 3 устройства на шине. первое выдает «1», второе «0», третье «1»:

      Вот ваше «ИЛИ»: 1 OR 0 OR 1 = 1
      Вот мое «И»: 1 AND 0 AND 1 = 0

      Так что, контроллер получит именно логическое «И» всех сигналов.

    2. А зачем нужен поиск всех датчиков не зная в каком месте они установлены? Не лучше просто перед установкой в конкретном месте прочитать его id и присвоить ему порядковый номер? Вот тогда мы будем знать в каком месте установлен датчик.

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

    1. И рад бы, да не могу… Пол-года назад у компа накрылся жесткий диск, выцарапывать с него инфу пришлось быстро и без разбору… Видимо перевод аппноута был утерян, потому что среди спасенного найти его не могу … Сожалею…

  2. Давно это было, не помню точно в чем рисовал, но либо это был Corel Draw, либо Microsoft Visio, ибо других векторных редакторов не юзал. Скорее всего, это был Visio

  3. Программа для рисования блок схем WIZFLOW , весит много меньше чем MV, функциональности для рисования блок схем хватает.
    А статья отличная много стало сразу понятно.

  4. Большое спасибо!!! Очень познавательно. Программы правда пишу на Алгоритм-Билдере, и портировал выложенный Вами код на него (если интересно дайте адрес поделюсь). Вопрос вот в чем: а у Вас случайно нету процедуры расчета контр.суммы побитно (чтоб не использовать табличный метод), уж больно он прожорлив :-(, при том что приём все равно идет побитно и скорости контроллера хватает чтоб вычеслять ее на лету. Когда то где то видел, но сейчас почему то не могу найти???

  5. Вот, нашел тут у себя:

    .def wiretemp = r22 ; регистр под нужды 1-wire
    .def wireres = r2 ; обязательно из числа r0...r15

    ;*********************************************************************
    crc8:
    ; выполняет подсчет CRC по алгоритму 1-Wire
    ; вход: r0 - считанный байт
    ; выход: CRC - содержит подсчитанную сумму
    ; портит: регистр Z
    ; примечание: перед первым вызовом CRC необходимо обнулить

    push r0
    ldi wiretemp, 8
    mov wireres,wiretemp
    r1w_loop_crc:
    lds wiretemp, CRC
    eor r0, wiretemp
    ror r0
    mov r0, wiretemp
    brcc zero
    ldi wiretemp, 0x18
    eor r0, wiretemp
    zero:
    ror r0
    sts CRC, r0
    pop r0
    ; 4 следующие команды делают циклический сдвиг r0
    push r0
    ror r0
    pop r0
    ror r0
    ; сдвиг закончен - очень удобно, не правда ли?!
    push r0
    dec wireres
    brne r1w_loop_crc
    pop r0
    lds r0, CRC

    ret

    ...
    .dseg
    CRC: .byte 1 ; расчитываемая контр.сумма

  6. Хотел бы получить перевод APPLICATION NOTE 187
    1-Wire Search Algorithm по версии автора. Заранее спасибо.

    1. Я же уже писал в комментариях: «Пол-года назад у компа накрылся жесткий диск, выцарапывать с него инфу пришлось быстро и без разбору… Видимо перевод аппноута был утерян, потому что среди спасенного найти его не могу … Сожалею…»

  7. Спасибо за статью =) почитал её, почитал ссылки внутри, и теперь 1-wire понимаю практически полностью =)
    Статья очень полезна для желающего работать с технологией 1-wire и знающего о ней на уровне «ну это когда они все как-то на одном проводе 0_о» =)

  8. Спасибо за статью. Как обычно собираю мозаику из разных источников. Где то понял одну часть, где то другую. Эта статья меня можно сказать ввела в курс дела в алгоритме поиска. (работать с одним устройством на шине умел давно). Но есть здесь и слабое место в описании приема битов и дополнений. Не хватает таблицы битов и дополнений, не понятно было слово «дополнение». И по поводу блок схемы алгоритма. С помощью нее я наконец полностью разобрался в теме. А потом уже с пониманием нашел в
    Application note — AVR318: Dallas 1-Wire master таблицу алгоритм поиска и понял что она проще. Она на стр 13. Её тоже можно добавить в статью.

    1. Ну вроде все нормально, понятно написано: «Получив команду SEARCH ROM, каждое устройство передает первый бит своего кода ПЗУ и дополнение этого бита (грубо говоря — его инверсию).»

      Слово «инверсия» здесь ключевое …

      За аппноут — спасибо, гляну на досуге.

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

  10. Пара вопросов:

    >каждое устройство передает первый бит своего кода ПЗУ

    Точнее говоря, младший бит (LSB, bit 0)? Например, для DS18B20 это получится 0x28 => 00101000 => бит 0.

    >Остаются те, у которых первый бит был равен «0». Они продолжают участвовать в поиске и готовы выслать нам второй бит своего кода, а мы записываем «0» в качестве первого бита кода ПЗУ.

    Т.е. в результате получим список сер. номеров упорядоченный сначала по одинаковым младшим битам, затем по длине последовательностей нулей.
    т.е примерно так (несколько db18b20):
    ..10000 00101000
    …1000 00101000
    ….100 00101000
    ….100 00101000
    …..10 00101000

  11. Здравствуйте, не подскажите куда копать….
    Сделал на асме реализацию обмена по 1-wire. считываю серийник с iButton и термодатчика без проблем командой 33h. Но другие команды не распознает. в частности 0CCh и 0BEh. просто не отвечает после них. В чем может быть дело

    1. Сложно сказать. Попробуйте воспользоваться не своей реализацией обмена, а какой-нить другой. Если ошибка исчезнет, значит проблема в вашем коде

      1. Спасибо разобрался. Скачал данную реализацию — а она почти один в один. Просто криво Rom — команды слал)))

  12. Подскажите пожалуйста

    в строке
    ; … Здесь делаем с найденным кодом ПЗУ все что нам угодно …

    необходимо эти действия или какие-то не нужны?
    1)0хСС послать устройству
    2)0х44 послать устройству
    задержка 740 мс
    3)0хСС послать устройству
    4)0хВЕ послать устройству
    5)считать с устройства
    6) преобразовать
    7)вывести на экран

    И как вывести на экран одновременно 2 и больше показаний а не в одной строчке

        1. Да нету проекта. Делал контроллер. Предназначался он для контроля аквариума с экзотическими рыбами. С двумя датчиками температуры. В процессе и родилась мысль исследовать поиск. Обвесил всеми 1-wire присосками какие нашел. Датчики, домофонные i-button и все такое. Потом когда статью закончил — все разобрал и доделал аквариум 🙂 Так что проект, как таковой, отсутствует.

  13. Ладненько спрошу по-другому)
    После метки loop1: в ROM_NO будет вычисленный RoM сначала 1 устройства, потом 2, потом n. Правильно?

    1. Вставил после loop1 код. Гляньте плиз по метке DS нужно какие-то команды удалить или добавить что-то?

      rcall OWClearROM_NO
      rcall OWFirst
      sbrs search_flags, search_result
      rjmp EndOfExample
      loop1:

      ; Код
      main:
      rcall ds
      rcall bcd_convert ;преобразование
      ldi lcd_command,$01
      rcall lcdcmd
      ldi wreg, 3
      rcall ds_longdelay
      rcall display; вывод на экран

      rcall OWNext
      sbrs search_flags, search_result
      rjmp EndOfExample
      rjmp loop1
      EndOfExample:
      rjmp main

      Ds:
      rcall ds_reset

      ldi ds_command, 0xCC
      rcall ds_write

      ldi ds_command, 0x44
      rcall ds_write

      ldi wreg, 255
      rcall ds_longdelay ; 178ms
      ldi wreg, 255
      rcall ds_longdelay ; 178ms
      ldi wreg, 255
      rcall ds_longdelay ; 178ms
      ldi wreg, 255
      rcall ds_longdelay ; 178ms
      ldi wreg, 60
      rcall ds_longdelay ; 40ms

      rcall ds_reset

      ldi ds_command, 0xCC
      rcall ds_write

      ldi ds_command, 0xBE
      rcall ds_write

      ldi wreg, 5
      rcall ds_delay

      rcall ds_read

      ret

      ds_reset:

      cbi PORTB, PB0
      ldi wreg, 0xFF
      out DDRB, wreg

      ldi wreg, 220

      ldi wreg, 0xFE
      out DDRB, wreg

      ldi wreg, 220
      rcall ds_delay
      ret

  14. недавно асмой увлекся..
    а на СИ имел дело с этой шиной.. только криво получалось
    за статейку и файлы спс, пойду переводить Application Note 187. 1-Wire Search Algorithm

  15. Отличная стать! Хорошо написана, вот только для меня есть один скользкий момент исходя из блоксхема алгоритма содержит блок где направление поиска берется равным биту из пзу в текущей позиции как это может быть веди мы ищем этот бит? Или может там просто надо написать текущий бит? Прошу если можно подробнее прокомментировать этот момент . Заранее спасибо!

  16. Возникла проблема с подсчетом CRC для DS18B20. Использую известный алгоритм CRC8 (с полиномом 31h). Датчик выдает совершенно другое значение (при чтении памяти и кодов). По какому алгоритму он работает ?

  17. В переводе алгоритма поиска от автора есть одно несоответствие с оригиналом.
    В авторском варианте алгоритма присутствует блок проверки
    «LastDiscrepancy = LastFamilyDiscrepancy ?», а в английском варианте этого блока нет.
    Я реализовал этот алгоритм с среде Algorithm Builder и столкнулся с проблемой некорректной работы алгоритма для числа подключенных устройств больше двух.
    Возникает вопрос: почему у авторского варианта алгоритма присутствует этот блок?
    Может быть, это специально сделано?

    Ссылка на на использованный оригинал документа «Application Note 187»:
    http://www.sal.wisc.edu/PFIS/docs/rss-nir/archive/public/Product%20Manuals/maxim-ic/AN187.pdf

    1. Как показывает практика, оригинал оригиналу — рознь. Вот здесь, например: http://www.elin.ru/files/pdf/1-Wire/app187.pdf лежит именно тот «оригинал», по которому я работал. И там блок проверки «LastDiscrepancy = LastFamilyDiscrepancy ? » вполне себе присутствует.
      Возможно у вас более старый релиз аппноута?
      Я не совсем понял из Вашего текста, у какого именно алгоритма возникает ошибка, у моего или у того что в Вашем аппноуте?

  18. Спасибо за статью!
    Благодаря ей и обсуждению здесь полностью понял алгоритм поиска, спасибо ещё раз!
    В апноте : AVR318 Dallas 1-Wire® master.pdf , приведён более простой алгоритм, без определения кода семейства (Family). Тоже очень полезный материал. В сети есть его перевод на великий и могучий. Особенно полезен для первоначального ознакомления.

Добавить комментарий для abrakadobr Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *