Задача: Измерение температуры с помощью датчика DS18b20
Исходный материал: PIC16f628a, DS18b20, MAX232 level converter, devboard, proteus.
В данной статье рассмотрен пример работы в случае присутствия на линии одного датчика DS18b20, также здесь приводится текст стандартных функций для работы с протоколом 1-wire.
Наша цель – получить температуры с минимальными телодвижениями, поэтому читаем даташит и обращаем внимание лишь на существенные моменты. Первый из которых – организация питания. В данном случае я не испытывал проблем с подачей питания, поэтому будем использовать стандартную схему питания:
Линия данных должна быть обязательна подтянута к питанию через резистор (в даташите рекомендуют 4.7 кОм). Курим даташит дальше – смотрим на организацию памяти:
Всего 9 байт памяти:
- младший байт значения температуры
- старший байт значения температуры
- триггер срабатывания по максимуму
- триггер срабатывания по минимуму
- конфигурационный регистр
- внутренние нужды
- внутренние нужды
- внутренние нужды
- CRC
Из это кучи нас интересуют 0, 1 и 4 байт (и то 4 постольку поскольку).
4 байт – конфигурационный регистр:
В нем мы можем перезаписывать только 6 и 5 биты отвечающие за разрешение измерений. По дефолту датчик измеряет с 12 битным разрешением, что соответствует точности 0.0625 градуса. Возможны также следующие варианты:
- 9 бит – 0.5 градуса
- 10 бит – 0.25 градуса
- 11 бит – 0.125 градуса
Ниже представлена таблица значений битов и значений получаемого разрешения:
Рекомендую также обратить внимание на время преобразования температуры (такое время придется ждать, после подачи команды конвертации) если вы не хотите ловить левые косяки.
Посмотрим, что собой представляет непосредственно значение температуры в первых двух байтах:
В младшем байте 4 младших бита отвечают за дробную часть, целое значение берется из старших бит младшего и трех младших бит старшего байта. Биты S отвечают за знак значения (Обратите внимание, что для конвертации отрицательных значений температуры, нужно применять другие правила конвертации двоичного кода в десятичный) – если биты S = 1 — температура отрицательна, ну и соответственно наоборот.
Рассмотрим базовые процедуры для работы с one-wire, любая система команд по протоколу начинается с инициализации:
Для успешной процедуры, микроконтроллер должен провалить линию минимум на 480 мкс, затем отпустить и посмотреть провалит ли ее вслед за ним наш датчик, если провал есть – все нормально датчик присутствует. Напишем под это дело свою функцию, не забывая что в данном случае мы оперируем лишь состояниями порта (вход/выход) а не значениями.
предварительно объявим такое дело:
#define STATE TRISB4 #define PIN RB4
static bit INIT(void){ static bit b; STATE = 1; STATE = 0; //Проваливаем линию __delay_us(500); //Ждем 500 мкс STATE = 1; //Переключаемся на вход __delay_us(65); //Ждем 65 мкс b = PIN; //Смотрим чего там на линии __delay_us(450); //Дожидаемся до положенного временного интервала return b; //Возвращаем 0 или 1 }
Функция возвращает 0 если устройство присутствует на шине и 1 если устройства не обнаружено. Дальше нам надо сварганить функцию для записи в микросхему:
Чтобы передать устройству 0, нужно опустить линию минимум на 60 мкс, для передачи 0 – проваливаем шину минимум на 1 мкс, а затем отпускаем и выдерживаем нужное время. Учитываем то, что биты должны передаваться младшим вперед:
void TX(unsigned char cmd){ unsigned char temp = 0; unsigned char i = 0; temp = cmd; for (i=0;i<8;i++) { if (temp&0x01) { STATE = 0; //передаем 1 __delay_us(5); STATE = 1; __delay_us(70); } else { //передаем 0 STATE = 0; __delay_us(70); STATE = 1; __delay_us(5); } temp >>= 1; } }
Теперь по чтению:
Для успешного чтения микроконтроллер сначала должен опустить шину хотя бы на секунду, а дальше переключиться обратно на вход и смотреть чего там происходит. Чтобы снять корректное значение нужно выждать по крайней мере 15 мкс от начала (чтобы не попасть на значение во время перезарядки емкостей).
UPDATE: Предыдущее высказывание неверно, и виной этому моя невнимательность, нам нужно не выждать 15 мкс, а успеть за эти 15 мкс снять значение от датчика.
Поэтому я предлагаю из старой функции убрать 10 микросекундную задержку:
unsigned char RX() { unsigned char d = 0; unsigned char i = 0; for (i=0;i<8;i++){ STATE = 0; //прижимаем линию __delay_us(6); STATE = 1; //__delay_us(10); //дожидаемся больше чем 15 мкс --убираем нафиг, т.к. это идет в разрез с даташитом d>>=1; //освобождаем место под новый бит if (PIN == 1) d |= 0x80; //если 1 то записываем 1 __delay_us(70); //ждем до положенного времени } return d; }
Этих трех функций достаточно для работы с протоколом 1-wire. Для того чтобы снять показания температуры нам нужно выполнить ряд команд (для случая одного датчика на шине):
- инициализация
- 0xCC – пропускаем идентификацию
- 0х44 – запуск конвертации температуры
- ждем необходимое время(750 мс для 12 битного разрешения)
- повторная инициализация
- 0xCC – пропускаем идентификацию
- 0хBE – читаем регистры
Я создал для выполнения данной последовательности отдельную процедуру, включающую в себя конвертацию двоичного значения температуры в десятичное.
void get_temp() { static bit init; unsigned char temp1; unsigned char temp2; init = INIT(); if (!init) { //успешно инициализировались? TX(0xCC); TX(0x44); __delay_ms(150); //ждем 750 мс __delay_ms(150); __delay_ms(150); __delay_ms(150); __delay_ms(150); } init = INIT(); //повторная инициализация if (!init) { TX(0xCC); TX(0xBE); //команда на чтение temp1 = RX(); //читаем младший байт temp2 = RX(); //читаем старший байт } temp_drob = temp1 & 0b00001111; //Записываем дробную часть в отдельную переменную temp_drob = ((temp_drob*6)+2)/10; //Переводим в нужное дробное число temp1 >>= 4; sign = temp2 & 0x80; //определяем знак температуры temp2 <<= 4; temp2 &= 0b01110000; temp2 |= temp1; //помещаем все в одну переменную if (sign) { //если минус temperature = 127-temp2; //глобальная переменная temp_drob = 10 - temp_drob; //глобальная переменная } else temperature = temp2; }
Чтобы проверить работоспособность я собрал схему на макетке и вывел информацию через уарт:
void main() { unsigned char input = 0; init_comms(); get_temp(); printf("\ftemperatura -- "); if (sign) printf("-"); else printf("+"); printf("%d", temperature); printf(".%d", temp_drob); while(1){ input = getch(); if (input == 50) { get_temp(); printf("\r\ntemperatura -- "); if (sign) printf("-"); else printf("+"); printf("%d", temperature); printf(".%d", temp_drob); } } }
Вот что получилось:
Исходники обновленного проекта лежат на репе. Что и как обновилось если кому интересно может посмотреть на комменты мая 2014 года 🙂
Как я понял это функция задержки – __delay_us. А можно ее описание?
Это стандартный макрос компилятора Hi-Tech. Организует задержку на заданное количество микросекунд. По аналогии для милисекунд существует макрос __delay_ms(). Более подробно можно о этих макросах почитать в мануале компилятора.
PS. Для использования этих макросов в коде должно присутствовать определение частоты кварца
#define _XTAL_FREQ 4000000 //здесь указывается нужная частота
Хочу собрать термометр,прочитал статью начал собирать все до кучи.В функции void TX при компиляции пишет что переменная cmd не используется.Что тут делать?
Warning [1090] D:\Electronica\Project\termometr\termometr.c; 111. variable “_cmd” is not used
А можно код глянуть? Строка –
temp = cmd;
присутствует в функции?
на почту выслал
Странно, мне ничего не пришло.
адрес sargein@gmail.com?
В вашем коде есть несколько неоднозначных моментов:
1. Строки:
не имеют особого смысла, т.к. функции возвращают целочисленные значения + у вас нет глобальной переменной sign, которая отвечает за знак температуры, предлагаю такой вариант переменных:
2.
В частности строка TX(cmd); – я не понимаю как ее пропустил компилятор, такая переменная нигде в коде не объявлялась.
3. Чтобы проверить работоспособность достаточно вызвать функцию get_temp(), внутри нее используются все остальные, а потом в уарт выпихнуть значение переменной temperature и temp_drob
Доброго времени суток!
собрал схему согласно Вашей статьи, программку написал, но датчик не отвечает и не распознается, т.е.
…….
if (INIT())
то послать на через USART 1
else
послать ноль…
по этому поводу возникло два вопроса: 1 не мог ли датчик сгореть (включал его точно так как в даташите и у Вас в статье)
2 несоответствие временной задержки.
насчет второго уже голову сломал, т.к. по теории в расчете на 1мц получается приходится времени: (4 такта / 4000000 Гц) = 1 мкс или как в моём случае 4/20000000=0.2мкс
а! в программе Proteus получается, что совершенно по другому.
если не трудно то можете привести другой пример временных задержек?
ЗЫ: хорошие статьи в прочем как и сам сайт, РЕСПЕКТ!!!!!))))
Нашел решение!!!!
в программе выше изложенной, а точнее
”
static bit INIT(void){
static bit b;
STATE = 1;
STATE = 0; //Проваливаем линию
”
тут вместо STATE 1-го необходимо заменить на PIN = 0;
Доброе утро, Вы нашли неверное решение: для успешной процедуры инициализации необходимо, чтобы датчик сам провалил линию в 0, а в вашем случае этого произойти не может, так как вы железно подтянули линию микроконтроллером к земле. Дальше программа естественно тоже работать не будет. Если не работает мой код, то:
1. Надо проверить схему подключения.
2. Надо попробовать другой датчик (может этому уже конец пришел).
3. Попробовать поиграться с задержками.
Вообще, чтобы не гадать, лучше всего посмотреть вашу схемку и код.
sarge отправил на Ваш почтовый ящик текст программы.
заранее скажу что я не использовал задержку, которую Вы предложили – __delay_us() и __delay_ms() т.к. при компиляции показывает ошибку
”
Error[482] : symbol “_DelayMs” multiply defined in “C:\HTSOFT\project\DS18b20(2)\DS18b20.obj”
Error[482] : symbol “_DelayMs” multiply defined in “C:\HTSOFT\project\DS18b20(2)\DS18b20.obj”
Error[499] : undefined symbol:
___delay_us (C:\HTSOFT\project\DS18b20(2)\DS18b20.obj)
“
Ошибку показывает потому, что у вас нет макроса:
#define _XTAL_FREQ 4000000 //ну или другая ваша частота
Как вы в своем коде проверяли что формируется нужная задержка?
через счетчик MPLAB StopWatch
строчку “#define _XTAL_FREQ 4000000 //ну или другая ваша частота”
пробовал с ней, тоже самая ошибка :(((((
а можете пример написать работы с этим таймером, с шапкой в том числе?
Да не вопрос, вот вам весь проект с файлом симуляции для протеуса http://diymicro.ru/wp-content/uploads/2012/06/One-wire_DS18B20.rar
ага! а глупый вопрос – какая версия у Вас MPLAB?
тут скорее важна версия компилятора – у меня. hi-tech picc 9.71
)))) хм думаю Вы правы
тут еще одну встроенную задержку нашел, она находится в директории
C:\Program Files\HI-TECH Software\PICC\9.50\samples\delay
можете что ни будь про неё рассказать?
Ну это не встроенная задержка, это просто образец функции в папке с примерами. Вообще неважно каким образом будет делаться задержка, можете и на встроенных таймерах все это сделать, просто у меня сделано макросами, и чтобы что-то сказать мне надо заново все проделать, а мне этого совсем не хочется.
Да и таймеры у меня обычно заняты более важными делами, тут и макросы отлично справляются.
Работает!)))
дело в том, что у меня частота кварца 20МГц, а не 4, и по этому получалось, что должным образом задержка не обеспечивалась.
Не знаю по каким причинам но __delay_us() и __delay_ms() не распознавал компилятор, по этому воспользовался
C:\Program Files\HI-TECH Software\PICC\9.50\samples\delay
а временную задержку уже подгонкой подобрал..
меня еще одна строчка заинтересовала:
printf(“%d”, temperature); –это тоже является макросом MPLAB, я так понимаю.
Можете рассказать что нибудь про него по детальней, а то в интернете ссылаются на команды С++.
“printf(«%d», temperature); —это тоже является макросом MPLAB, я так понимаю.” ой! ошибся HI – TECH
не, это стандартная функция С библиотеки stdio.h, в данном случае она выводит строку через уарт, но в целом ничем не отличается от обычной printf, так что про нее читать можно в любом источнике
init_comms() – это что такое? мой PCWHD не понимает
Ну во-первых здесь все коды привидены под Hi-tech PICC Compiler, а не под CCS.
Во-вторых, init_coms() это пользовательский макрос для настроек работы с уартом, он в файле usart.h, описывалось вот в этой статье – http://diymicro.ru/pic-mk-eksperiment-14-modul-usart.html
здравствуйте уважаемый sarge. Помогите пожалуйста написать прошивку для устройства которое будет работать следующим образом. DS18B20 будет установлен на радиаторе, при температуре радиатора выше 40 гр на одном из портов ввода-вывода лог1 , а при понижении температуры до 30 лог0. Спасибо!
Добрый день,
Ваша задача выглядит довольно просто:
1. Заводим таймер с нужным интервалом проверки
2. При прерывании по таймеру берем значение температуры функцией (get_temp())
3. Если температура больше или равна 40 и статус = 0, то на выход 1
иначе, если температура меньше или равна 30 и статус = 1, то на выход 0.
Вот и все, функции для работы с датчиком можно взять прямо в этой статье, про таймеры на сайте также есть разделы.
Спасибо. При компиляции ругается на cmd. Датчик инициализировать я смог сам. Сейчас задача стоит получение от него значения температуры для дальнейшего сравнения.
Ну показывайте код. Так я не могу сказать почему он ругается 🙂
Куда мне его прислать? Может адрес электронной почты напишите? мой ub9obc@gmail.com
Да можно прямо сюда на самом деле. В теги засунуть только.
мой email – sargein@gmail.com
А можете, пожалуйста, закинуть весь проект в архив и отправить на почту? vhemaster@mail.ru Заранее благодарю!
Отправил на почту проект.
Добрый вечер! Стоит передо мной задача как у melekhov’a. Пока пытаюсь смоделировать в протеусе, камень 12F629. Все функции кроме main() слизаны отсюда, get_temp() крутится в основном цикле. Но температура всегда 0, в чем проблема?
[spoil]
int temperature;//целая часть
int temperature_fraction;//дробная часть
bit init;
bit sign;//знак
void init_uc();
void get_temp(void);
void main(){
init_uc();
while(1){
get_temp();
if(temperature > 50) start_funs = 1;
if(temperature < 40) start_funs = 0;
}
}
void init_uc(){
temperature = 25;
temperature_fraction = 0;
CMCON = 0xFF;
GPIO = 0;
TRIS0 = 0;
TRIS1 = 0;
OPTION=0b00001000; //Устанавливаем предделитель TMR0 1:1
WPU=0b00010100;
IOCB=0b00000000;
}[/spoil]
На линии 1-wire в протеусе изменения какие-либо происходят если осциллом посмотреть? Или еще лучше сразу снимите график и оцените все ли правильно происходит.
Посмотрел осциллографом. Первый цикл проходит как надо, но с датчика приходит не та температура (74 – установлено, а приходит 58). На последующих циклах осциллограф рисует все время прямую. Температура приходит так 47, 127, 0, 0..0…Я отправил Вам на почту свой проект, буду очень благодарен, если посмотрите. На макетке не заработало.
1. В архиве нет файла для протеуса.
2. Обычно макрос __delay_ms() работает до определенной максимальной цифры (255 вроде).
3. Зачем опрашивать датчик каждую секунду?
1. Файл-проект для протеуса *.pdsprj? Я отправил его повторно, но он был в архиве.
2. __delay_ms(2000) работал у меня в том числе и на железе. Поменял на пять __delay_ms(200), результат тот же.
3. Но ведь это не принципиально, все необходимые задержки вроде есть в get_temp(). МК питаться будет от сети.
Кстати, закомментировал вот эту часть кода
if (sign) { //если минус
temperature = 127-temp2; //глобальная переменная
temperature_fraction = 10 – temperature_fraction; //глобальная переменная
}
и температура стала приходить другая, это удивительно, ведь я всегда устанавливал положительную температуру и эти инструкции не должны выполняться. Старший бит от DS18B20 0b00000XXX.
Мой протеус 7 не открывает pdsprj файлы.
У меня тут сейчас со временем большой напряг, поэтому я хотел бы, чтобы вы перекинули свой код на 628 камень и проверили по уарту например, адекватно ли температура отображается.
Если надо могу прислать свой проект с файлами протеуса под 628а.
Да, оправьте, пожалуйста, на giper-utesov@yandex.ru.
Здравствуйте sarge. Очень нравится ваш сайт, в статьях всё очень подробно описано!
Разбираю ваш код, столкнулся с проблемой.
в Datasheet DS18B20 описано :DS18B20 будет формировать (ответ на слот времени чтения от
устройства управления) логический «0» когда происходит температурное преобразование. И
логическую «1», когда конвертирование выполнено.
у вас в коде:
TX(0xCC);
TX(0x44);
__delay_ms(150); //ждем 750 мс
__delay_ms(150);
__delay_ms(150);
__delay_ms(150);
__delay_ms(150); }
init = INIT(); //повторная инициализация
if (!init) {
TX(0xCC);
TX(0xBE); //команда на чтение
temp1 = RX(); //читаем младший байт
temp2 = RX(); //читаем старший байт
}
Объясните пожалуйста повторной инициализацией вы проверяете выполнено конвертирование или нет?
Если да то мне не понятно, ведь тогда получается что если “0” то
TX(0xCC);
TX(0xBE); //команда на чтение
а в Datasheet всё наоборот.
Фух, уже столько времени прошло, если я правильно вспомнил, то в даташите есть такая строчка
Оттуда же:
Под транзакциями понимается последовательность из двух команд. Так что я вроде как просто следовал даташиту и проверял не завис ли девайс.
Успешность конвертирования я не проверяю.
Понятно,так как не проверяете успешность конвертирования поэтому у вас стоит задержка в 750ms как описано вDatasheet чтобы датчик успешно конвертировал.
Просто меня смутило то, что когда я убрал задержку 750ms всё продолжало успешно работать.
Спасибо! Буду дальше разбираться.
Добрый день! Прошу рассказать или ткунть пальцем, где самому почитать. Про хитрое преобразование двоичного кода показаний с термодатчика в десятичное число. Несколько не понятны мне те семь строчек со здвигом и & (поразрядное и) кажется. Так если не напрягаться то просто бы слизал ваш код, но мне на будущее придется делать преобразование двоичного кода в шеснадцатеричный.
Ээ, в даташите 🙂
Добрый вечер . В описании функции приема байта есть коментарий , что после подсаживания шины ждем больше 15 микросекунд а затем смотрим что там у нас 1 или 0. Так вот по факту изучив даташит. Состояние шины нужно смотреть сразу, и не более чем через 15 мкс.
Из- за этого коментария потратил лишний час 🙁
Все правильно в комментарии, посмотрите на картинку внимательно, мы изначально проваливаем шину на некоторое время ( 6 мкс у меня ), переводим состояние пина на вход и ждем еще как минимум 10 мкс (в сумме 16 мкс) и проверяем что у нас там 0 или 1.
То же и на картинке.
Так в чем ваш вопрос или вам просто хотелось высказаться?
И это конечно тоже. Но пока не убрал задержку на экране были все нули.
На картинке master read 0 slot внизу показано время где нужно роверить состояние на входе Master samples. Там видно, что нужно проверить за время не более 15 мкс. А пишу для того, что наверняка найдетсся еще не один товарищ который будет повторять опыты с вашего сайта. Но не просто тупо копируя исходники, а читая коментарии и напрягая Мозг 🙂
Вернулся с выходных и нашел время перелопатить даташит.
Вы правы, невнимательно прочитал про чтение, с записью вроде все ок.
Хотя и с чтением у меня пока ни одной проблемы не было, а я уже не один девайс с этим кодом собрал.
А как реализовать опрос двух трех датчиков по одной линии внятных примеров я ненашел, если не трудно может раскажете ?Спасибо.
Я с несколькими датчиками на одной шине не работал, поэтому сказать с уверенностью ничего не могу. Но мне всегда казалось, что этот не особо сложно, посылаем нужный алдрес и все. Копать по ходу нужно в сторону команды Search ROM.
P.S. Вот если вы разберетесь и согласитесь выложить ваш пример работы и функций сюда с вашим авторством естественно, то статья будет более полной 🙂
В инете нашел некоторое непонятное описание, не могу понять ведь при команде сброса ответят все датчики, в чем прикол? то что нужно rom читать это я знаю, но получается что нужно подключить датчик, прочитать rom, запомнить, подключить следующий и т.д. и работать с этими rom-ами по очереди хотя официальное описалово гласит что интерфейс поддерживает так сказать горячее подключение без дополнительной инициализации всех датчиков. Я хочу попробовать сделать псевдо умный дом на этой линии как не как до 300 метров при определённых условиях можно вытянуть. Один ведущий и несколько ведомых, соединение с ПК, программы для ПК примитивную хочу попробовать написать на HIASM, может кто знаком? Может с помощью вашего сайта, Вас, здешних обитателей попробовать сделать проект псевдо народный открытый умный дом?
Есть некоторые идеи для реализации проекта но один Я наверное не потяну.
Я думал, что с серч ром делается один раз, чтобы узнать адреса всех девайсов, а дальше уже использовать хитрые команды, чтобы вызывать конкретный датчик (Match ROM вроде).
Алгоритм поиска датчиков есть в апнотах – http://www.maximintegrated.com/app-notes/index.mvp/id/187
Идею общего проекта лучше продвигать в сообществах (хабр или лучше easyelectronics), а здесь обитателей немного.
В первой функции инициализации вроде как ошибка:
static bit INIT(void){
static bit b;
//*************************************************************
STATE = 1;
STATE = 0; //Проваливаем линию
//*************************************************************
__delay_us(500); //Ждем 500 мкс
STATE = 1; //Переключаемся на вход
__delay_us(65); //Ждем 65 мкс
b = PIN; //Смотрим чего там на линии
__delay_us(450); //Дожидаемся до положенного временного интервала
return b; //Возвращаем 0 или 1
}
по идее тама должно быть:
STATE = 0; // направление ножки на выход
PIN = 0; //Проваливаем линию
???
Достаточно просто изменить IO, выход сам провалится в 0, если не оговорено иное.
Функция проверенная не один раз в железе, все работает 🙂
Но можно так как и я написал? Вроде ошибки не должно быть?
По идее можно.
А что означает эта функция?
input = getch(); // getch()???
if (input == 50) {
это просто для теста, здесь юзается уарт, эта функция посимвольно смотрит, что вы вводите с клавиатуры в терминале.
а в этой проге что она делает? И “50” это что? (заранее извините – только начинаю осиливать Си)
50 – ascii код клавиши 2
запускаете проект, в терминале высвечивается менюшка, можно нажимать на клавиши и тестировать температуру и перезапускать замер
… У меня один раз замер делает, а потом тишина…. (в Протеусе)
нажимайте на клавишу 2
Пасиб Заработало.
Может это только у меня так, но врядли, не думаю что может зависеть от версии компилятора или протеуса.
Заметил нюанс в алгоритме пересчета температуры. Если полученная с датчика температура отрицательна и при этом не является целым числом, т-е имеет какую-либо дробную часть, значение температуры считается верно, если же дробной части нет, занижается на единицу.
Т-е если на датчике -1.1 – на экране -1.1
если на датчике -1.0 – на экране уже 0. Причем именно 0, ноль после запятой в этом случае не отображался.
Если считаете нужным, могу поместить сюда свою версию исправленной функции, где все работает как надо.
Или вы специально разместили именно в том виде, чтобы копи-пейстом не обошлось и был повод включить голову? 🙂
Нет, не специально, но про косячок с отрицательными температурами знаю давно, лень было разбираться т.к. юзаю его в проектах где температура вряд ли опуститься ниже +10 градусов 🙂
По поводу исправленной функции – было бы замечательно, кидайте в комментарии, я закину в статью и помечу, что у функции ваше авторство.
Добавилась одна переменная и изменен алгоритм вычисления температуры.
Можно было изменить серьезней, брать из новой переменной и десятые и признак отрицательной температуры, но этого я делать не стал чтобы не менять структуру программы слишком уж сильно.
Ну и конечно огромное спасибо Вам за ваш труд и отдельное – за наличие обратной связи в форуме 🙂
Ща чутка занят, к вечеру постараюсь обновить статью и залью в реп исходники.
1. вы по ходу if потеряли.
2. Зачем temp3 если вы не используете temp2?
3. А что произойдет если мы в 0 в вашей функции, каким образом переменная temperature получит правильное значение?
Еще вопрос, как вы проверяли код, у меня в протеусе завышает на 0.9, то есть
датчик = -1.0, показывает -0.1
Эх заняли вы меня на вечер сегодня :), пошерстил по своим проектам и посмотрел как для меня логично исправить эту ситуацию.
Функцию получения температуры я переписал в следующем виде:
В итоге получаем на выходе интовский байт без ошибок, содержащий в старшей части целочисленное значение, в младшей – знак и дробную часть. Все работает.
По идее если ввести переменные с плавающей точкой, то все можно сделать гораздо красивее, но я оставлю это уже вам 🙂
Весь проект целиком можно глянуть здесь
Настрочил довольно много, потом присмотрелся – в фрагменте кода от моего имени все не совсем так, как я писал, видимо что-то сбилось во время отправки.
Могу снова попробовать прислать его комментом, но может лучше по почте? Мой адрес у вас полагаю есть, по крайней мере при отправке комментов я его ввожу в форму.
Могу сразу и проект в протеусе с исходником программы прислать, она простейшая, все видно глазами за минуту.
Ну а пока не списались, в порядке тренировки мозгов, возьмем случай когда с DS18B20 приходит значение температуры -1.
Тогда temp2=0b11111111, temp1=0b11110000
temp3=0b1111111111110000
поскольку значение отрицательно, проводим поразрядную инверсию (~), после которой temp3=0b0000000000001111
temp3+1=0b0000000000010000
0b0000000000010000/16.0 = 1
что является значением температуры без десятых и без знака 🙂
А вот случай
Упс, пардон, предыдущее самоотправилось, промазал.
А вот случай когда на датчике 0:
temp3=0
значение положительное, значит считаем по формуле temp3/16
0/16=0
В моей версии новой, что конкретно не устраивает вас? 🙂
На репе весь проект лежит, в том числе и под протеус, в виде отдельной библиотеки, которую можно подрубить к любому проекту. Все работает.
Вообще все эти левые формулы пересчета мне ни разу не нравятся, если хотим сделать что-то универсальное, то надо модифицировать примерно в таком виде:
1. Установить переменную, отвечающую за разрешение датчика
2. В зависимости от разрешения пересчитывать дробную часть в виде x = byte_value*разрешение датчика (по дефолту 0.0625 градуса) для плюсовых температур
x = 10 – byte_value*разрешение датчика (насчет 10 не уверен).
В случае 0 и отрицательного значения просто добавляем 1 к целочисленному значению.
Да, когда добавляете код, сразу берите его в теги (кв скобка)code(кв скобка)
Если честно, в новой версии не разобрался, ниасилил, многа букаф. То, кусок чего я пытался кинуть, моя первая в жизни прога на си, потому вам мою понять было бы намного проще чем мне вашу 🙂
Но что касается формулы пересчета, в том и дело, что не левая она. Как я внезапно осознал, Ds18b20 выдает результат в виде дробного числа с фиксированной точкой, отрицательного или положительного. Не мной этот формат вывода придуман и даже не далласом. А для перевода этого формата представления в другие есть стандартные формулы пересчета, придуманные думаю что задолго до моего рождения… Именно они и универсальны.
Дык если хватает памяти я про это и говорю, берем себе и умножаем полученное число на разрешение и добавляем к 0 или отнимаем.
Распишите ваш алгоритм действий словами по пунктам, и вместе оценим что проще и проанализируем, а то в коде вашем выше пробелов много.
temp_drob = temp1 & 0b00001111; //Записываем дробную часть в отдельную переменную
temp_drob = ((temp_drob*6)+2)/10; //Переводим в нужное дробное число
temp3=temp2; //добавляем старший байт в переменную для расчета температуры
temp3<=10) {temp_drob=0;}//чиним ноль
} else temperature = temp3/16;
[CODE]
temp_drob = temp1 & 0b00001111; //Записываем дробную часть в отдельную переменную
temp_drob = ((temp_drob*6)+2)/10; //Переводим в нужное дробное число
temp3=temp2; //добавляем старший байт в переменную для расчета температуры
temp3<=10) {temp_drob=0;}//чиним ноль
} else temperature = temp3/16;
[/CODE]
Да что ты будешь делать.
В общем, объявляем переменную unsigned int.
Загоняем в старший ее байт старший байт полученного с датчика значения, в младший – полученный младший байт.
Проверяем на минус, если его нет, целое значение температуры равно содержимому этой переменной деленному на 16.
Если минус есть, побитово инвертируем ее содержимое, прибавляем единицу и делим на 16.
а с плюсом, что делать, если есть такая хорошая простая формула для минуса, то надо и для плюса по человечески сделать же? 🙂
С плюсом, просто содержимое этой переменной делим на 16, без затей 🙂
Ок, опробую вечером.
Хотя стоп, а с дробным значением что делать? Остаются те же формулы пересчета полагаю? 🙂
Тогда ваш алгоритм мало чем от моего отличается (деление на 16 это фактически сдвиг вправо на 4 позиции), у меня просто функция возвращает инт переменную, причем она уже содержит бит с температурой. Просто пришлось добавить проверку 0, но у вас она так же есть.
Зато у меня нет проверки на корректность отрицательной температуры в зависимости от того, есть ли десятичный знак.
Дробная часть пока вычитается по вашей формуле, почитаю еще, может и ее получится украсить
добрый день!
изучаю листинг вернее то что в коментах
не понятно кусок кода
signloc = (temp2 & 0x80) >> 7;
в моем понимании это определение знака температуры если это так что это работать не будет по моему мнению
байт MS=11111XXX или MS=00000XXX
после строки signloc = (temp2 & 0x80) >> 7;
signloc будет всегда равна нулю!!!
Ну как же, берем байт 0b1xxxxxxx к примеру
после операции 0b1xxxxxxx & 0x80 у вас остается 0b10000000
теперь сдвиг вправо на 7 позиций, и ваша 1 перемещается на место младшего бита.
То что непонятно всегда ведь можно проверить в коде, и даже не обязательно с мк:
https://dl.dropboxusercontent.com/u/82053027/Capture1.PNG
да проглядел ….показалось что 0х08 а тогда вроде как можно
signloc=temp2&0x80 без сдвига даст true для if()
А вот так можно, да.
Здравствуйте sarge!
Изучая Ваши листинги неоднократно встречал использование Вами команды printf(). В книжках по Си говорится, что это команда вывода информации на экран монитора. Если я правильно понял, таким образом Вы выводите на экран некоторую отладочно-служебную информацию.
Великолепный прием! Но, как только я снимаю REM с любой команды в Вашем листинге, тут же получаю ошибку компиляции:
Warning [361] U:\Pic\Proj\Termometr\Exp16\onewire.c; 106.1 function declared implicit int
…….. …….. …..
Serial number: HCPICP-654321 (PRO)
Error [1098] ../../common/printf.c; 14. conflicting declarations for variable “_printf” (…\Termometr\Exp16\onewire.c:106)
Библиотека stdio.h в файле main.c подключена.
Идея очень интересна, но как ей воспользоваться? Подскажите, пожалуйста.
Доброго времени суток,
Два встречных вопроса:
1. Подключен ли у вас usart.h, usart.c ?
2. Пробовали ли вы код оригинал, который лежит на репе?
Очень похоже что вы пытаетесь пользоваться уартом без уарта.
Здравствуйте уважаемый sarge!
Проанализировал ситуацию с проектом. Файлы usart.h и usart.c к проекту подключены (в окне Project MPLAB). Файлы с кодом взяты с репы, несколько причесал на свой вкус отступы табуляции. На всякий случай заменил свежими без редактирования. Как только убираю REM со строк с printf() – сразу ошибка компиляции. Попробовал запустить Ваш проект (тот, что в архиве One-wire_DS18B20.rar), он несколько иной. Убрал REM там – все компилируется!!!
Кажется, я все понял. По наивности я считал, что если в файле main.c присутствует строка
#include , значит заголовочный файл подключен к проекту и его содержимым можно пользоваться. Как сказал персонаж из Простоквашино, “Фиг-вам, индейское жилище!”. Если указанная строка присутствует в main.c, то и использовать команду printf() можно только в этом файле, а в файле onewire.c нельзя! А если нужно, то следует разместить директиву препроцессора и в этом файле тоже! Наверное, типичная ошибка начинающего.
После исправления ошибки все заработало. А если я соберу реальную схему и подключу ее к ПК через RS-232, на реальном экране я увижу сообщения, аналогичные Proteus? Нужно ли при этом запускать терминальную программу на ПК?
И еще один вопрос, если можно. В Вашем коде использован весьма элегантный прием проваливания/отпускания шины – через TRISA. Вот только когда PIC выдает на шину 0, этот ноль должен быть на выводе RA. А специальной команды установки этого нуля в тексте я не увидел. Расчет на состояние по умолчанию? Везде говорят, что на это лучше не надеяться, а устанавливать уровни принудительно. Я прав или ошибаюсь?
Хорошо, что с принтом разобрались, да нужно инклюды ставить везде где пользуетесь командами.
Соберете, подключите через RS232 или через виртуальной ком порт на юсб, правильно настроите терминальную программу (да, ее обязательно нужно запускать и более того настроить предварительно, смотрите в моей лаборатории статьи про уарт, там вроде была инфа про все это дело).
Про момент с TRIS уже точно не помню, но мне почему то кажется что это в первую очередь связано с возможностью запитывать датчик от самой шины, если там будет жесткий ноль то все датчики будут обесточены.
Dobroe vremya sutok . proboval izuchit primer ds18b20 .pri proverke na proteuse pokazivaet temperaturu ,esli izmenite temperaturu pokazaniya ne izmenyaetsya (ne obnovlyaetsa pokazaniya ) ,esli vikluchaete i vkluchite proteus potom pokazivaet temperaturu, esli esho izmenite temeraturu izmeneniya net.
Вы по ссылке на полный проект ходили? Там лежат все файлы в том числе и для моделирования в протеусе.
Только что попробовал – все меняется и работает без перезапуска протеуса.
nikak ne poluchaetsya , posle izmeneniya temperaturi ne meneyaetsa pokazania , spustya 2-3 minut najimaete knopku 2 pokazivaet novuyu znacheniyu .
https://bitbucket.org/embedgs/diymicro.ru/src/95fd908f9d99/PicLab/?at=master programma ne kompiliruetsa ,pokazivaet soobsheniya :Error [499] ; 0. undefined symbol:
_get_temp(ds18b20.obj)
Во-первых, ссылка на проект:
https://bitbucket.org/embedgs/diymicro.ru/src/95fd908f9d994ccc8b029f040e54a90ed93bf95b/PicLab/E16_OneWire/?at=master
Во-вторых: подключите проект целиком в mplabX, похоже что вы не подключаете заголовочные файлы.
P.S. Только что попробовал, добавил в код пару строк, теперь еще надпись Starting вываливается сначала – все откомпилилось и запускается, и показания меняются по каждому нажатию кнопки 2.
P.P.S. Проблема с вашей стороны, код уже неоднократно тестировался, причем не только на симуляторах, но и в железе.
vkluchil vse fayli .otkompilirovalas bez oshibok ,proveril rabotaet . hotel sprosit pochemu pokozaniya posle izmenenii temperaturi ne obnovlyaetsya avtomaticheski , a tolko posle najatii knopki 2 ? SPASIBO
Все просто – так написано в коде 🙂
добрый день!
в своем коде за основу работы с датчиком взял ваш код…..в принципе все за исключением организации индикации. Так вот у меня слдующая проблема при моделировании в протеусе первый раз датчик инициализируется затеем считываю с него показания и отдаю на индикацию затем при входжении в следующий цикл вернее в фукции INIT (); при переходе от строчки STATE 0; к строке __delay_us (500); на шине DQ появляется 5 вольт и все и никакой дальнейщей реакции не происходит при последующих циклах . я правильно понимаю что перед каждым опросом датчика надо проходить процедуру инициализации
Добрый день,
Вы видели что все исходники доступны на битбакете и все можно пощупать?
Я не уверен, что по каждому опросу нужно делать инициализацию, надо смотреть даташит, но я делаю в каждом цикле, и протеус прекрасно отрабатывает каждый новый запрос в рамках одной симуляции.
Что-то не так с вашим кодом, но так как я его не вижу мало что могу подсказать.
Добрый день!
Так и не получилось снять информацию с датчика. Предыдущий пост был не совсем корректным у меня pic16f676 . Собрал схемку в протеусе ну как ранее писал при первом опросе датчик отдает
temp1 =0x80;
temp2 =0x80;
а при повторной инициализации при переходе от строчки STATE 0; к строке __delay_us (500); на шине DQ появляется 5 вольт.
А ваш проект у меня в mplabx не хочет собираться. Говорит
error: Configuration “default” builds with “hi-tech-picc”, but no toolchains of that type are installed.
Errors have occurred while loading one or more configurations.
If a specific error is not shown above, this may happen when you import a project from another computer.
+ You can add language tools in Tools->Options embedded tab.
+ You can change which language tool to use in the project properties dialog.
я новичок и только начинаю разбираться и не совсем понял что от меня компилятор требует.
Похоже что мплабх не знает где находится Hi-tech PICC.
Показывайте весь проект с файлом протеуса.
был в командировке так что пришлось приостановить изучение весь проект был на домашнем ПК
вот собственно проект. на данном этапе интересует собственно считывание информации с датчика. Индикация пока не отлажена (вернее она работает при измерения напряжения то биш отлажена но для индикации температуры пока нет)
https://drive.google.com/file/d/0B7PZJa0xZaSAd1h0RTh1bldQZFE/view?usp=sharing
с уважением, сергей.
блин файл протеуса не залил так что перезаливаю с файлом протеуса https://drive.google.com/file/d/0B7PZJa0xZaSAcVNMbnBXdU9YVVk/view?usp=sharing
с уважением, сергей.
уточните пожалуйста текущий статус, что не работает и как надо чтобы работало и т.д.
ну собственно проблема в следующем.
не получается считать температуру. По началу после первого цикла опроса датчик при последующих иинциализациях датчика протеус упорно говорит что на ноге QD присутствует 5В. Почесав репу дописал строку обнуления порта RC3=0;
unsigned char INIT(void)//static bit INIT (void) //функция инициализации темп датчик
{//unsigned char b;//static bit b;
RC3=0; // обнуляем приемный порт
STATE=1;
STATE=0; // проваливаем линию
__delay_us (500);
STATE=1; //переключаемся на вход
__delay_us (65);
b=PIN; //смотрм чего там на линии
__delay_us (450); //выжидаем интервал
return b; // возврашаю ноль или один
}
в итоге вроде как опросы идут судя по логу
PROSPICE 8.04.00 (Build 21003) (C) Labcenter Electronics 1993-2015.
Loaded netlist ‘C:\Users\RYABEN~1\AppData\Local\Temp\LISA9709.SDF’ for design ‘proect V_A pic16f676.pdsprj’
PIC12 model release 8.3.00 (Build 20025) simulating PIC16676 device. [U1]
[DS1822] 9 byte of Persistent EEprom created. [U3_U1]
Loaded 128 bytes of persistent EEPROM data. [U1]
Loading HEX file ‘test_dev.X\dist\default\production\test_dev.X.production.hex’. [U1]
Read total of 1832 bytes from file ‘test_dev.X\dist\default\production\test_dev.X.production.hex’. [U1]
[D1WIO] Device Information [U3_I1]
[D1WIO] Family Code:………………28H [U3_I1]
[D1WIO] Serial N. 48-bit:…000000B8C530H [U3_I1]
[D1WIO] CRC-8 Code:……………….8EH [U3_I1]
[D1WIO] Overdrive Support:…………No [U3_I1]
[DS1822] CONVERT T [44H] command. [U3_U1]
[DS1822] New data converted. [U3_U1]
[DS1822] READ SCRATCHPAD [BEh] command. [U3_U1]
[DS1822] CONVERT T [44H] command. [U3_U1]
[DS1822] New data converted. [U3_U1]
[DS1822] READ SCRATCHPAD [BEh] command. [U3_U1]
[DS1822] CONVERT T [44H] command. [U3_U1]
но блин неважно какая температура на датчике не была он всегда отдает младший и старший байты
temp1=0x80;
temp2=0x80;
собственно на этом пока буксую
Да уж я времени прилично убил на понимание вашего кода и макарон в схематике.
Если у вас что-то не работает, зачем все мешать в кучу и пытаться разбираться, проще ведь локализовывать проблему когда меньше лишних вещей.
Вы неправильно переписали функцию RX(), там должно быть d>>=1, а не просто d>>1;
У меня выводит значение температуры в эти переменные, причем правильное.
P.S. Еще смотрите аккуратно с таймером, я его пока вырубил, чтобы он не мешал, но я чувствую там могут быть тоже проблемы.
ну щас попробую собственно я осваиваю камень опыт в схемотехнике у меня достаточно большой а вот с камнями дело имел только с готовыми прошивками. Начал учится все что сделано было так кнопка с антидребезгом была написана потом прикрутил прерывания потом прикрутил динамическую индикацию потом не хвалило пинов поставил микру дешифратор потом освоил АЦП и вот чет на датчике споткнулся в принципе для понимания я закоментил ненужные вещи типа кнопки и прочее. Так что вроди иду по наростающей…… Таймер у меня делает только одно раз в 1024 слушает не нажата ли кнопка и если нажата по просто обрабатывает ее ну собственно и все. Щас попробую исправить замечания.
За ранее большое спасибо!
да и еще вопрос чем было мотивировано обьявление переменной как static? ее почему то протеус не видит.
Честно говоря не помню.
Возможно в хайтече нельзя объявить тип переменной bit без статика, но это так, обрывочные воспоминания, не уверен совсем в них.
Переношу оп Вашей просьбе сюда.
В протеусе пока не собирал, но вот что нашел в процессе поиска решения (взято отсюда – http://forum.chipmk.ru/index.php/topic/863-%d1%83%d0%bf%d1%80%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%b2%d1%8b%d1%85%d0%be%d0%b4%d0%be%d0%bc-pic12f629-%d0%bf%d1%80%d0%b8-%d0%b2%d0%bd%d0%b5%d1%88%d0%bd%d0%b5%d0%b9-%d0%bf%d0%be%d0%b4%d1%82%d1%8f%d0%b6%d0%ba%d0%b5/page__p__13833__hl__ds18b20__fromsearch__1#entry13833 ):
Проблема из-за структуры портов в этих МК, у них защёлки обновляются по типу чтение-модификация-запись. Т.е. при работе (любой) с портом, сначала читаются реальные значения на выходах, заносятся в защёлки, потом модифицируются биты, с которыми производятся операции. В итоге, остальные биты меняют свои значения на реальные, висящие на порте.
Вспомнил один из своих проектов, там надо было прочитать порты, после этого заработало : GPIO=GPIO;
Поэтому решил добавить в Ваш код тоже самое после изменения направления порта, т.е. после STATE = 1 ил 0; добавил GPIO=GPIO;
Увы, пока без изменений.
Уведомление: Pic Lab, PIC16, Experiment #16, One-wire protocol and DS18b20 temperature sensor | diymicro.org