Датчик температуры 18B20 представляет собой микросхему в корпусе TO-92. На первый взгляд его не отличить от обычного транзистора. У датчика всего три вывода. Вывод с номером 1 — это «земля», питание подключается к выводу 3, а цифровой сигнал температуры снимается с ножки 2. Микросхема соединяется с ведущим устройством (у нас это будет компьютер) посредством довольно простого протокола, который называется 1-Wire, что переводится с английского как «один провод». Таким образом информация передается по одному сигнальному проводу. Но конечно, нам необходимо 3 провода для того, чтобы подключить датчик.
Весь протокол связи через порт компьютера реализован в маленькой управляющей программе, которую необходимо запустить на компьютере, после подключения к нему нашей приставки. Программу можно скачать по ссылке в конце статьи.
Кроме всего прочего, приставку можно использовать для проверки работоспособности датчиков 18B20 перед использованием из в ваших самоделках и для определения уникального кода датчика (его серийного номера). Это иногда бывает нужно, чтобы подключить к микроконтроллеру несколько таких датчиков по одной линии 1-Wire.
Схема представляет собой устройство согласования шины 1-Wire датчика с портом RS232 компьютера. В схеме всего несколько деталей. Функции ключей выполняют два транзистора типа 2N7002. Это очень распространенные и исключительно дешевые MOSFET транзисторы в SMD корпусе. Эти транзисторы в мире полевиков являются чем-то вроде народного КТ315 в мире советских биполярных транзисторов. Они используются в миллионах радиолюбительских конструкций и в огромном количестве различной промышленной радиоэлектронной техники. Если вы захотите собрать эту схему на макетной плате из выводных компонентов, то транзисторы 2N7002 можно заменить аналогом в корпусе TO-92, не менее распространенным и легендарным 2N7000. Ссылки на детали я помещу в конце статьи. Стабилитрон применен выводной, любого типа, на напряжение в районе 5 вольт.
Как я уже писал, вы можете использовать все компоненты выводного типа и собрать эту приставку на кусочке макетной платы, как это сделал я с моим первым устройством. Оно до сих пор работает, я применяю его для проверки датчиков 18B20.
Специально для данной статьи я развел печатную плату в программе DipTrace.
musbench.com
Сегодня мы рассмотрим возможность использования UART-to-I2C/SPI/1W шлюза для подключения к компьютеру датчиков температуры DS18B20, то есть фактически будем делать USB-термометр. Причём термометр мы будем делать не простой, а с возможностью передачи данных по сети.
Нам понадобятся: шлюз UART-to-I2C/SPI/1W, USB-to-UART конвертер, датчик температуры DS18B20 фирмы Dallas, монтажные провода и специальное ПО. Конвертер USB-to-UART можно взять любой, однако лучше взять вот такой (с нашего сайта). Во-первых, его разъём UART — это ответная часть разъёма UART шлюза, поэтому для их соединения вам не придётся ничего изобретать. Во-вторых, наш конвертер позволяет не только организовать обмен данными между шлюзом и компьютером, но и запитать шлюз (причём любым напряжением: +3,3В или +5В, оба они присутствуют на разъёме конвертера как раз в нужных местах). Вариант со шлюзом и нашим USB-to-UART показан на фотографии справа.
Cоединив вместе USB-to-UART и UART-to-I2C/SPI/1W шлюз, мы, фактически, получаем уже USB-to-I2C/SPI/1W шлюз. Теперь остаётся только подключить к ниму датчик температуры. Датчик DS18B20 нужно подключать к разъёму XT3 шлюза. Это делается следующим образом: первую ногу датчика подключаем к выводу COM (минус питания), третью ногу — к выводу Supply (плюс питания) и, наконец, вторую ногу датчика — к выводу MOSI/1W/DATA (линия передачи данных). Всё, собранную схему можно втыкать в USB-порт компьютера.
Чтобы считывать с помощью собранной схемы измеряемую датчиком температуру нам осталось сделать всего две вещи:
Во-первых, нужно установить на компьютер драйвера для USB-to-UART преобразователя. Преобразователь, предлагаемый на сайте, сделан на базе чипа cp2102 фирмы silabs, дрова для него можно скачать на их официальном сайте, вот по этой ссылке.
Когда драйвера на USB-to-UART конвертер установлены — при его подключении к USB-порту у вас в диспетчере устройств будет появляться виртуальный com-порт. Именно через этот виртуальный com-порт специально написанная программа будет общаться со шлюзом.
Во-вторых, нужно запустить специальную программу, которая при подключении с виртуальному com-порту автоматически настраивает шлюз на работу с однопроводной линией и далее периодически опрашивает подключенный к шлюзу датчик и отображает считанное с него значение температуры (ссылки для скачивания программы и её исходников можно найти в конце статьи).
Эта программа позволяет не только считывать данные с датчика температуры, но и передавать их по сети. Для этого в программе реализован http-сервер, который рассылает считанные с датчика данные через TCP-порт. Причём, сгенерированную HTML-страницу он посылает удалённому хосту только один раз, а далее пересылает только сами данные, что позволяет значительно экономить трафик. Установленная частота обновления данных — 1 раз в секунду.
Главное окно программы показано на картинке слева. В верхней части этого окна выбирается нужный com-порт. Кнопки «Connect» и «Disconnect» позволяют установить или разорвать связь с датчиком температуры.
В средней части окна настраивается http-сервер. Собственно говоря, тут нужно только выбрать порт, через который удалённые хосты будут подключаться к нашему серверу. Кнопки «Start» / «Stop» служат для включения / выключения сервера.
По умолчанию сервер использует порт 80, то есть тот, который используется для http по умолчанию всеми браузерами. Если выбрать другой порт, то его нужно будет указывать в браузере в явном виде через двоеточие после адреса (адрес:порт, например 127.0.0.1:5454).
Скачать программу ( exe ), скачать исходники ( проект под C++ Builder 6 в zip-архиве )
Программа специально выложена с исходниками, всяческие перепиливания / допиливания — приветствуются, ниже даны полезные ссылки, которые могут вам в этом помочь:
Описание протокола 1-Wire.
Список функций и регистров шлюза.
Небольшое видео, демонстрирующее работу USB-термометра.
radiohlam.ru
Электронный термометр позволит вам вести наблюдение за температурой различных объектов или сред, в которые будут помещены датчики, и визуально наблюдать за происходящими изменениями, а так же контролировать нахождение температуры в норме, предупреждая Вас звуковым сигналом, если потребуется.
Инструкция по сборке термометра
Рис 1. Установка диода U2 (1N5817)
Рис 2. Установка диода U4 (1N5817)
Рис 3. Установка стабилитрона U1 (BZX55C3V9)
Рис 4. Установка стабилитрона U3 (BZX55C6V2)
Рис 5. Установка резистора R1 (CF-0,125 1,5кОм) и пропайка общего провода
Рис 5. Установка разъема для подключения датчика *
Рис 7. Установка конструкции в корпус разъема DB9-F
Программное обеспечение
meandr.org
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include «ds18b20.h»
/*
* ds18b20 — инициализация
*/
uint8_t resetDS18B20() {
uint8_t i;
// импульс сброса, минимум 480?s
DS18B20_PORT &= ~(1 << DS18B20_DQ);
DS18B20_DDR |= (1 << DS18B20_DQ); // выход
_delay_us(480);
// Когда DS18B20 обнаруживает положительный перепад, он ждет от 15?s до 60?s
DS18B20_DDR &= ~(1 << DS18B20_DQ); // вход
_delay_us(60);
// и затем передает импульс присутствия, перемещая шину в логический «0» на длительность от 60?s до 240?s.
i = (DS18B20_PIN & (1 << DS18B20_DQ));
_delay_us(420);
//return the read value, 0=ok, 1=error
return i;
}
/*
* написать один бит
*/
inline void writeBitDS18B20(uint8_t bit) {
// логический «0» на 1?s
DS18B20_PORT &= ~(1 << DS18B20_DQ);
DS18B20_DDR |= (1 << DS18B20_DQ); // выход
_delay_us(1);
// если нужно написать 1, нужно освободить линию (если не — держать низкий уровень)
if (bit) {
DS18B20_DDR &= ~(1 << DS18B20_DQ); // вход
}
// ждать 60?s и освободить линию
_delay_us(60);
DS18B20_DDR &= ~(1 << DS18B20_DQ); // вход
}
/*
* прочитать один бит
*/
uint8_t readBitDS18B20(void) {
uint8_t bit = 0;
// логический «0» на 1?s
DS18B20_PORT &= ~(1 << DS18B20_DQ);
DS18B20_DDR |= (1 << DS18B20_DQ); // вход
_delay_us(1);
// освободить линию и ждать 14?s
DS18B20_DDR &= ~(1 << DS18B20_DQ); // вход
_delay_us(14);
// прочитать значение
if (DS18B20_PIN & (1 << DS18B20_DQ)) {
bit = 1;
}
// ждать 45?s и вернуть значение
_delay_us(45);
return bit;
}
/*
* написать один байт
*/
void writeByteDS18B20(uint8_t byte) {
uint8_t i = 8;
while (i—) {
writeBitDS18B20(byte & 1);
byte >>= 1;
}
}
/*
* прочитать один байт
*/
uint8_t readByteDS18B20(void) {
uint8_t i = 8, n = 0;
while (i—) {
n >>= 1;
n |= (readBitDS18B20() << 7);
}
return n;
}
/*
* получить температуру
*/
double getTempDS18B20() {
uint8_t temperatureL;
uint8_t temperatureH;
double retd = 0;
if (DS18B20_STOPINTERRUPTONREAD == 1) {
cli();
}
resetDS18B20(); // сброс
writeByteDS18B20(DS18B20_CMD_SKIPROM); // пропуск ПЗУ(ROM)
writeByteDS18B20(DS18B20_CMD_CONVERTTEMP); // начать преобразование показаний температуры
while (!readBitDS18B20()); // ждать, пока преобразование не завершится
resetDS18B20(); // сброс
writeByteDS18B20(DS18B20_CMD_SKIPROM); // пропуск ПЗУ(ROM)
writeByteDS18B20(DS18B20_CMD_RSCRATCHPAD); // читать scratchpad
// прочитать 2 байта из scratchpad
temperatureL = readByteDS18B20();
temperatureH = readByteDS18B20();
if (DS18B20_STOPINTERRUPTONREAD == 1) {
sei();
}
// преобразовать полученное 12 битное значение
retd = ((temperatureH << 8) + temperatureL) * 0.0625;
return retd;
}
micro-pi.ru
#define DEVICES_ERROR 1
#include «config.h»
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include «OneWire.h»
uint8_t ONE_WIRE_DQ = PINB0;
void oneWireInit(uint8_t pin) {
ONE_WIRE_DQ = pin;
ONE_WIRE_PORT |= (1 << ONE_WIRE_DQ);
ONE_WIRE_DDR |= (1 << ONE_WIRE_DQ); // выход
}
/*
* сброс
*/
uint8_t reset() {
uint8_t response;
// импульс сброса, минимум 480us
ONE_WIRE_PORT &= ~(1 << ONE_WIRE_DQ);
ONE_WIRE_DDR |= (1 << ONE_WIRE_DQ); // выход
_delay_us(480);
// Когда ONE WIRE устройство обнаруживает положительный перепад, он ждет от 15us до 60us
ONE_WIRE_DDR &= ~(1 << ONE_WIRE_DQ); // вход
_delay_us(60);
// и затем передает импульс присутствия, перемещая шину в логический «0» на длительность от 60us до 240us.
response = (ONE_WIRE_PIN & (1 << ONE_WIRE_DQ));
_delay_us(410);
// если 0, значит есть ответ от датчика, если 1 — нет
return response;
}
/*
* отправить один бит
*/
void writeBit(uint8_t bit) {
if (bit & 1) {
cli();
// логический «0» на 1us
ONE_WIRE_PORT &= ~(1 << ONE_WIRE_DQ);
ONE_WIRE_DDR |= (1 << ONE_WIRE_DQ); // выход
_delay_us(10);
sei();
ONE_WIRE_DDR &= ~(1 << ONE_WIRE_DQ); // вход
_delay_us(55);
} else {
cli();
// логический «0» на 1us
ONE_WIRE_PORT &= ~(1 << ONE_WIRE_DQ);
ONE_WIRE_DDR |= (1 << ONE_WIRE_DQ); // выход
_delay_us(65);
ONE_WIRE_DDR &= ~(1 << ONE_WIRE_DQ); // вход
sei();
_delay_us(5);
}
}
/*
* отправить один байт
*/
void writeByte(uint8_t byte) {
uint8_t i = 8;
while (i—) {
writeBit(byte & 1);
byte >>= 1;
}
}
/*
* получить один байт
*/
uint8_t readByte() {
uint8_t i = 8, byte = 0;
while (i—) {
byte >>= 1;
byte |= (readBit() << 7);
}
return byte;
}
/*
* получить один бит
*/
uint8_t readBit(void) {
uint8_t bit = 0;
cli();
// логический «0» на 1us
ONE_WIRE_PORT &= ~(1 << ONE_WIRE_DQ);
ONE_WIRE_DDR |= (1 << ONE_WIRE_DQ); // вход
_delay_us(3);
// освободить линию и ждать 14us
ONE_WIRE_DDR &= ~(1 << ONE_WIRE_DQ); // вход
_delay_us(10);
// прочитать значение
if (ONE_WIRE_PIN & (1 << ONE_WIRE_DQ)) {
bit = 1;
}
// ждать 45us и вернуть значение
sei();
_delay_us(45);
return bit;
}
/*
* читать ROM подчиненного устройства (код 64 бита)
*/
uint64_t readRoom(void) {
uint64_t oneWireDevice;
if(reset() == 0) {
writeByte(CMD_READROM);
// код семейства
oneWireDevice = readByte();
// серийный номер
oneWireDevice |= (uint16_t)readByte()<<8 | (uint32_t)readByte()<<16 | (uint32_t)readByte()<<24 | (uint64_t)readByte()<<32 | (uint64_t)readByte()<<40 | (uint64_t)readByte()<<48;
// CRC
oneWireDevice |= (uint64_t)readByte()<<56;
} else {
return 1;
}
return oneWireDevice;
}
/*
* Команда соответствия ROM, сопровождаемая последовательностью
* кода ROM на 64 бита позволяет устройству управления шиной
* обращаться к определенному подчиненному устройству на шине.
*/
void setDevice(uint64_t rom) {
uint8_t i = 64;
reset();
writeByte(CMD_MATCHROM);
while (i—) {
writeBit(rom & 1);
rom >>= 1;
}
}
/*
* провеска CRC, возвращает «0», если нет ошибок
* и не «0», если есть ошибки
*/
uint8_t crcCheck(uint64_t data8x8bit, uint8_t len) {
uint8_t dat, crc = 0, fb, stByte = 0;
do {
dat = (uint8_t) (data8x8bit >> (stByte * 8));
for (int i = 0; i < 8; i++) { // счетчик битов в байте
fb = crc ^ dat;
fb &= 1;
crc >>= 1;
dat >>= 1;
if (fb == 1) {
crc ^= 0x8c; // полином
}
}
stByte++;
} while (stByte < len); // счетчик байтов в массиве
return crc;
}
/*
* поиск устройств
*/
void searchRom(uint64_t * roms, uint8_t & n) {
uint64_t lastAddress = 0;
uint8_t lastDiscrepancy = 0;
uint8_t err = 0;
uint8_t i = 0;
do {
do {
lastAddress = searchNextAddress(lastAddress, lastDiscrepancy);
if(lastAddress != DEVICES_ERROR) {
uint8_t crc = crcCheck(lastAddress, 8);
if (crc == 0) {
roms[i++] = lastAddress;
err = 0;
} else {
err++;
}
} else {
err++;
}
if (err > 3) {
return;
}
} while (err != 0);
} while (lastDiscrepancy != 0 && i < n);
n = i;
}
/*
* поиск следующего подключенного устройства
*/
uint64_t searchNextAddress(uint64_t lastAddress, uint8_t & lastDiscrepancy) {
uint8_t searchDirection = 0;
uint64_t newAddress = 0;
uint8_t idBitNumber = 1;
uint8_t lastZero = 0;
reset();
writeByte(CMD_SEARCHROM);
while (idBitNumber < 65) {
uint8_t idBit = readBit();
uint8_t cmpIdBit = readBit();
// id_bit = cmp_id_bit = 1
if (idBit == 1 && cmpIdBit == 1) {
return DEVICES_ERROR;
} else if (idBit == 0 && cmpIdBit == 0) {
// id_bit = cmp_id_bit = 0
if (idBitNumber == lastDiscrepancy) {
searchDirection = 1;
} else if (idBitNumber > lastDiscrepancy) {
searchDirection = 0;
} else {
if ((uint8_t) (lastAddress >> (idBitNumber — 1)) & 1) {
searchDirection = 1;
} else {
searchDirection = 0;
}
}
if (searchDirection == 0) {
lastZero = idBitNumber;
}
} else {
// id_bit != cmp_id_bit
searchDirection = idBit;
}
newAddress |= ((uint64_t) searchDirection) << (idBitNumber — 1);
writeBit(searchDirection);
idBitNumber++;
}
lastDiscrepancy = lastZero;
return newAddress;
}
/*
* пропустить ROM
*/
void skipRom() {
reset();
writeByte(CMD_SKIPROM);
}
micro-pi.ru