Сегодня получилось (третий день уже над этой проблемой корпел) сделать управление N термодатчиками, сидящими на шине 1-wire. Вот -
архив с прошивкой.
Естественно, т.к. я очень ленив, сначала было нагуглено некоторое количество примеров для работы с 1-wire на STM32 (прежде всего, с
easyelectronics.ru). После этого я целый день ковырялся в коде примеров, чтобы сделать что-то, пригодное для использования у меня. Весь вчерашний день я убил на отладку функции получения температуры, а сегодня потратил еще приличное количество времени на отладку функции опроса адресов датчиков, сидящих на шине.
Итак, вкратце, как это все работает
Стандартная схема подключения 1-wire подразумевает наличие диода Шоттки (коего у меня, естественно, не оказалось), однако, я нагуглил, что USART_TX можно сконфигурировать как выход с открытым коллектором (GPIO_Mode_AF_OD), в этом случае диод не потребуется.
Подключение 1-wire (даю ссылку на easyelectronics.ru, т.к. не хочу закидывать картинку на imageshack, а в ЖЖшке до сих пор постинг картинок не работает)
1-wire работает с использованием USART3 (можно было бы и вручную "дергать ногами", но это же неинтересно, тем паче, мне USART3 все равно не нужен будет). Работа с портом осуществляется посредством DMA. В общем, на easyelectronics на этот счет расписано подробней.
В принципе, все украденное работало прилично, кроме функции OW_Scan, которая искала устройства на шине. В алгоритм вникать мне не хотелось (в принципе, он несложный, но реализаций может быть уйма, а любой промах сразу отправит коту под хвост все старания), поэтому где-то я утащил готовый, подрихтовал его и воткнул в код прошивки.
Остальной код был взят из предыдущих опытов (поэтому управление светодиодом до сих пор работает). Я сделал небольшие изменения: чтобы не вызывать тяжелые функции в обработчиках прерываний, завел флаги. Команда пользователя просто устанавливает флаг, а в main все флаги проверяются, обрабатываются и сбрасываются.
Пока что функционал скудный: помимо "блымкания" светодиодом теперь прошивка умеет опрашивать 1-wire для поиска адресов висящих там термометров, выдавать эти адреса, а также выплевывать девятибайтовый буфер термометров (первый байт - искомая температура, второй - знак, но есть еще и вспомогательные байты, которые могут помочь измерять температуру более точно). Еще в термометрах есть два буфера для критических температур, их я пока не использую (но надо будет, наверное).
На стадии опытов оказалось, что при смене термометров "на горячую", если их больше одного, происходили всякие глюки, поэтому лучше всего "жамкать" reset при смене датчиков.
Термометров этих у меня 10 штук, для начала я решил посмотреть их коды. Распаял на макетке небольшую панельку, куда можно втыкать датчики и подпаял проводки для подключения двух термометров. Вот "Выхлоп" при работе с двумя термометрами:
com /dev/ttyACM0
C-a exit, C-x modem lines status
[STATUS]: RTS CTS DTR
// жму 'c' :
find devices
Found 2 devices
// жму 'p' :
Print devices
device 0:
0x10 0x7c 0xee 0x8f 0x02 0x08 0x00 0x1c
device 1:
0x10 0xad 0xbc 0x8f 0x02 0x08 0x00 0xf9
// жму 't' :
Read T
Device 0: 0x3b 0x00 0x4b 0x46 0xff 0xff 0x08 0x10 0x39
Device 1: 0x3a 0x00 0x4b 0x46 0xff 0xff 0x0c 0x10 0x41
Уже видно, что на полградуса показания отличаются.
Потом, попарно втыкая в панельку термометры и нажимая 'c'+'p', я получил список адресов всех датчиков:
0x10 0x7c 0xee 0x8f 0x02 0x08 0x00 0x1c
0x10 0xad 0xbc 0x8f 0x02 0x08 0x00 0xf9
0x10 0x68 0xd6 0x8f 0x02 0x08 0x00 0x21
0x10 0x12 0xf3 0x8f 0x02 0x08 0x00 0x5c
0x10 0x46 0x0a 0x90 0x02 0x08 0x00 0x59
0x10 0x5f 0x02 0x90 0x02 0x08 0x00 0xaa
0x10 0x78 0xe4 0x8f 0x02 0x08 0x00 0x7d
0x10 0xf7 0x02 0x90 0x02 0x08 0x00 0x57
0x10 0x92 0xf1 0x8f 0x02 0x08 0x00 0x35
0x10 0x3a 0xf8 0x8f 0x02 0x08 0x00 0x3b
(кстати, на сей раз глюков не было: после смены датчиков поиск работал нормально).
Первый байт адреса (или кода) датчика (0x10) - тип устройства (термометр). Далее следуют 6 байт собственно серийного номера датчика (судя по цифрам, little-endian, как и порядок бит в байтах), а завершает это - контрольная сумма.
Еще я столкнулся с такой штукой: иногда датчик писал "+85°C" (0xaa, 0x00):
Read T
Device 0: 0x3b 0x00 0x4b 0x46 0xff 0xff 0x03 0x10 0x1a
Device 1: 0xaa 0x00 0x4b 0x46 0xff 0xff 0x0c 0x10 0x87
если его пошевелить, показания менялись:
Device 0: 0x3d 0x00 0x4b 0x46 0xff 0xff 0x07 0x10 0xb2
Device 1: 0x40 0x00 0x4b 0x46 0xff 0xff 0x09 0x10 0xa7
Похоже, виноват плохой контакт (хотя, как ни странно, свои адреса эти штуки отдают).
Конкретно определить, какой датчик какой номер имеет, можно довольно просто: подключаем всю гроздь датчиков, определяем их адреса, а затем отключаем интересующий датчик. При опросе вместо температуры получим для него все "единицы" (понятное дело: шина-то подтянута на +3.3В):
Device 0: 0x3c 0x00 0x4b 0x46 0xff 0xff 0x0b 0x10 0xbc
Device 1: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
Так как в этом буфере есть константы (байты 2 и 3 мы можем изменить, байты 4 и 5 всегда равны 0xff, в байте 7 всегда лежит 0x10), легко найти, какой датчик отключен.
Для указанной пары датчиков, пока я писал это, температуры довольно-таки прилично выровнялись:
Device 0: 0x3b 0x00 0x4b 0x46 0xff 0xff 0x03 0x10 0x1a
Device 1: 0x3b 0x00 0x4b 0x46 0xff 0xff 0x02 0x10 0xde
(в байте 6 лежит остаток, полученный после вычисления температуры): 29.5°C.
Формула для вычисления точного значения температуры (в отсчетах, 1 отсчет == 0.5°C), предложенная в "даташите" термометра, мне не внушила доверия:
T = Tr - 0.25 + (B7-B6)/B7
(Tr - считанная температура, имеющая погрешность ±0.5 отсчета; B7,B6 - седьмой и шестой байты полученных данных).
Дело в том, что т.к. число B6 всегда меньше B7 (по крайней мере, сколько я раз ни запускал для разных датчиков измерения температуры, всегда было так), получается, что поправка всегда лежит в диапазоне [-0.125, 0.375]°C, но надо будет это проверить. Для более точного вычисления температур стоит, на мой взгляд, просто запустить длительный мониторинг показаний термометров при изменении их температур в широком диапазоне, медианную температуру считать истинной, получая таким образом градуировочные таблицы.
Вот пример показаний другой пары:
Device 0: 0x3b 0x00 0x4b 0x46 0xff 0xff 0x02 0x10 0xde
Device 1: 0x3b 0x00 0x4b 0x46 0xff 0xff 0x06 0x10 0xe5
Воспользовавшись формулой, получаем для первого: T = 59 - 0.25 + (16-2)/16 = 59.625 отсчетов (29.81°C), а второй дает температуру на 4/16=0.25 отсчетов (0.125°C) ниже. Вообще же, флуктуации показаний этих термометров довольно-таки приличные, так что вряд ли можно сказать, что точность их превышает заявленные 0.5°C.
В принципе, нужно, наверное "причесать" функции для работы с 1-wire (чтобы не нужно было писать простыней вида OW_Send(0, (uint8_t*)"\xbe\xff\xff\xff\xff\xff\xff\xff\xff\xff", 10, buf, 9, 1), а просто OW_SendFN + OW_Read. Ну и конечно же добавить проверку CRC.