STM Урок 94. DS18B20. Подключаем несколько датчиков на провод. Часть 1

 

 

 

Продолжаем работу с датчиком температуры DS18B20, который подключается посредством однопроводной шины 1-WIRE. Вернее не с датчиком, а с несколькими датчиками, подключенными на один провод. А точнее не с несколькими, а с двумя, потому что больше у меня нет.

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

Первый датчик у нас уже был подключен на макетной плате, правда я её заменил на меньшего размера, а второй датчик у меня несколько в другом исполнении — в экране и на проводе. Такие варианты также существуют в широкой продаже и используются в более тяжёлых условиях (незащищённых от атмосферных и иных воздействий).

Теперь наша схема выглядит следующим образом (нажмите на картинку для увеличения изображения):

image00_0500

Второй датчик подключен полностью параллельно первому (все три вывода).

Начнём теперь потихоньку разбираться, как же можно на одном проводе давать команды и получать данные одновременно из двух датчиков. Как известно, для этого существует уникальный серийный 64-битный код, который нам и предстоит сегодня считать с обоих датчиков для того, чтобы по этим кодам раздельно к ним и обращаться.

Проект создадим полностью на основе проекта DS18B20 урока 92 и присвоем ему имя DS18B20_MORE_SENSORS.

Откроем наш проект в Cube MX и, ничего в нём не трогая, сгенерируем проект для Keil и также откроем его там. Настроим программатор на автоперезагрузку, включим уровень оптимизации 1, подключим файл ds18b20.c и будем думать, как нам считать этот серийный идентефикатор. Оказывается, это не совсем просто. Есть команда Read ROM с кодом 0x33, которая читает серийный код, она не сложная. Мы отправляем её датчику и датчик нам выдаёт 8 заветных байтов. Но есть одна небольшая проблемка: данная команда работает только при условии, что у нас будет подключен только один датчик. Можно подключить датчики по очереди, считать с них данные коды и потом подключить их вместе и по этим кодам обращаться, но, согласитесь, для рядового пользователя, для которого мы разрабатываем устройство, это будет, мягко говоря, трудновато. Поэтому есть другая команда, которая считывает идентификаторы со всех датчиков, подключенных одновременно. Это команда Search Rom с кодом 0xF0. Но тут тоже не всё так просто, Придётся написать очень нелёгкий алгоритм, так как с первого раза данная команда не работает. Вот этим мы и займёмся сегодня. Поэтому и был для этого написан отдельный урок.

Также, чтобы узнать подробно, как работает команда поиска идентификатора, существует уже другая документация, разработанная не отдельно для нашего датчика, а для интерфейса 1-WIRE по технологии iButton. Application Note 937. При желании узнать ещё больше, чем вам здесь дам я, вы можете данную книгу найти без труда.

Разбираться с этим поиском мы будем постепенно. А пока создадим для этого функцию в файле ds18b20.c над функцией инициализации датчика

//-----------------------------------------------

uint8_t ds18b20_SearhRom(uint8_t *Addr)

{

  uint8_t id_bit_number;

  uint8_t last_zero, rom_byte_number, search_result;

  uint8_t id_bit, cmp_id_bit;

  uint8_t rom_byte_mask, search_direction;

  //проинициализируем переменные

  id_bit_number = 1;

  last_zero = 0;

  rom_byte_number = 0;

  rom_byte_mask = 1;

  search_result = 0;

  return search_result;

}

//-----------------------------------------------

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

Добавим две глобальные переменные в файл main.c

char str1[60];

uint8_t Dev_ID[8][8]={0};

uint8_t Dev_Cnt;

Одна переменная будет для хранения нескольких серийных номеров (я думаю больше восьми датчиков мы не будем подключать, а если будем, то изменим первую размерность), а вторая — для количества датчиков на шине, которое мы затем определим программно.

Также добавим несколько глобальных переменных в файл ds18b20.c, а также подключим две переменные, добавленные выше

#include "ds18b20.h"

//--------------------------------------------------

uint8_t LastDeviceFlag;

uint8_t LastDiscrepancy;

uint8_t LastFamilyDiscrepancy;

uint8_t ROM_NO[8];

extern uint8_t Dev_ID[8][8];

extern uint8_t Dev_Cnt;

//--------------------------------------------------

Вернёмся в нашу функцию ds18b20_SearhRom и напишем следующее условие

search_result = 0;

if (!LastDeviceFlag)

{

  ds18b20_Reset();

  ds18b20_WriteByte(0xF0);

}

В тело данного условия мы попадём если флаг последнего устройства у нас не будет установлен. Мы здесь перезагрузим наши датчики и отправим в них во все команду Search Rom.

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

После отправки команды Search Rom (0xF0) все устройства будут последовательно формировать на шине состояние 0 и 1, соответствующее значению их фактического бита в ROM. Процесс чтения начинается с самого младшего бита кода и проходит в два такта времени. Если все устройства на шине в очередном считываемом бите содержат 0, то будет последовательно присутствовать на шине 01. Если оба устройства будут содержать 1, то результат будет 10. Всё это имеет место, если у всех устройств одинаковые биты в данной позиции. А если будет хотя бы у одного устройства результат другой, то это приведёт к результату 00, указывая на конфликт. Далее в следующем (в третьем) такте контроллер передаёт 0 или 1, чтобы отобрать устройства, у которых совпадёт данный бит с битом содержащимся в его идентификаторе. Устройства, у которых бит не совпадёт, перейдут в состояние ожидания и будут в нём находиться до тех пор, пока не получат импульс сброса (перезагрузки). Затем идёт следующий аналогичный цикл из трёх тактов для следующего бита. Итого таких циклов будет 64, а тактов получается 192, в течении которых контроллер определит код одного из устройств, остальные будут обязательно находиться в режиме ожидания, на то он и уникальный — этот код. По этому коду контроллер сможет спокойно затем обращаться к данному устройству. То есть оставшиеся циклы уже будут проходить без конфликтов и будут уже иметь определённый результат.

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

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

И таким образом мы поступаем, повторяя процедуры до тех пор, пока конфликтов не будет вообще.

Конечно без практики, я понимаю, данный процесс не понять. Поэтому в документации приведён был конкретный пример, в котором идентифицировались 4 устройства на шине 1-WIRE

image00

Для простоты все биты идентификаторов не показываются и старшие биты обозначены символами x.

Процесс чтения, как я уже и говорил, происходит с самого малдшего байта. Поторим вышенаписанное на данном примере.

1. Контроллер формирует импульс сброса, на что все устройства отвечают импульсом присутствия.

2. Контроллер отправляет команду 0xF0 (Search ROM).

3. Контроллер читает один бит с шины, на что устройства ответят 00, что позволяет говорить о разрядном конфликте, означающем, что устройства в данном разряде имеют разное состояние.

4. Контроллер отправляет бит 0 в шину, что приведёт устройства 2 и 3 в состояние ожидания и также к тому, что при всех следующих операциях до сброса данные устройства уже не будут откликаться.

5. Контроллер читает следующий бит, получая 01, так как результат у 1 и 4 устройства, которые остались способными откликаться на команды биты одинаковые и равны 0.

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

7. Контроллер снова читает следующий бит, используя два такта чтения и получает 00, что соответствует разрядному конфликту, опять же позволяющему судить о том, что биты у устройств разные.

8. Контроллер снова отправляет бит 0 в шину, что приведёт устройство 1 к переходу в режим ожидания и в результате этого у нас способным откликаться останется только одно устройство. Но то, что оно одно, мы просто знаем заведомо. А на практике нам это ещё не известно. Поэтому продолжаем чтение до самого старшего 63 бита.

9. Остальные все циклы чтения не приведут к конфликтам, так как у нас уже не будет разных битов на шине, так как устройство у нас только одно, тем самым мы считаем один идентификатор до конца.

10. Теперь мы должны прочитать идентификатор следующего устройства, для чего должны повторить шаги от 1 до 7, соответственно активируя заново все устройства.

11. В самой старшей позиции, в которой контроллер после обнаружения последнего конфликта писал 0, контроллер уже пишет 1. Это уже приведёт к переходу в режим ожидания уже устройство 4, что даст нам дочитать код ROM устройства 1.

12. Читаем код устройства 1 до конца.

13. Далее нам требуется считать ROM следующего устройства (одного из устройств 2 или 3). Для этого мы уже повторяем шаги только с 1 до 3.

14. В самой старшей позиции, в которой контроллер после обнаружения последнего конфликта в проходе втором писал 0, контроллер уже пишет 1. Это приведёт к переходу в режим ожидания устройства 1 и 4.

15. Дальше читаем следующий бит, получая 00, что означает, что биты у устройств 2 и 3 разные.

16. Контроллер посывает в шину 0, что приведёт к отключению устройство 3, оставляя устроство 2, как единственное активное.

17. Читаем код устройства 2 до конца.

18. Далее мы должны считать код идентификации последнего устройства 3, выполняя шаги 13 — 15.

19. В самой старшей позиции, в которой контроллер после обнаружения последнего конфликта в проходе третьем писал 0, контроллер уже пишет 1. Это приведёт к переходу в режим ожидания устройство 2, оставляя устройство 3 активным.

20. Читаем код ROM устройства 3 до конца.

Теперь у нас считаны коды всех 4 устройств. Если устройств на шине будет больше, то проходов тоже будет больше и шагов, следовательно, тоже.

Надеюсь, что теперь вам процесс считывания кодов стал более понятен.

Также у нас будет возможность это ещё раз закрепить, когда мы будем выдумывать алгоритм при написании кода функции поиска кодов, также ещё закрепляя визуально, поглядывая на результаты логического анализа на шине.

В следующей части урока мы напишем полностью функцию поиска идентификаторов датчиков и считаем уникальные коды со всех датчиков температуры.

 

Предыдущий урок Программирование МК STM32 Следующая часть

 

 

Отладочную плату можно приобрести здесь STM32F103C8T6

Логический анализатор можно приобрести здесь Логический анализатор на 8 каналов

Датчик температуры в экране с проводом можно приобрести здесь DS18B20 в экране с проводом

Переходник USB to TTL можно приобрести здесь ftdi ft232rl

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

STM DS18B20. Подключаем несколько датчиков на провод