PIC мк. Эксперимент №16. One-wire на примере DS18b20.

Задача: Измерение температуры с помощью датчика DS18b20

Исходный материал: PIC16f628a, DS18b20, MAX232 level converter, devboard, proteus.

В данной статье рассмотрен пример работы в случае присутствия на линии одного датчика DS18b20, также здесь приводится текст стандартных функций для работы с протоколом 1-wire.

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

sch

Линия данных должна быть обязательна подтянута к питанию через резистор (в даташите рекомендуют 4.7 кОм). Курим даташит дальше – смотрим на организацию памяти:

Всего 9 байт памяти:

  1. младший байт значения температуры
  2. старший байт значения температуры
  3. триггер срабатывания по максимуму
  4. триггер срабатывания по минимуму
  5. конфигурационный регистр
  6. внутренние нужды
  7. внутренние нужды
  8. внутренние нужды
  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. Для того чтобы снять показания температуры нам нужно выполнить ряд команд (для случая одного датчика на шине):

  1. инициализация
  2. 0xCC – пропускаем идентификацию
  3. 0х44 – запуск конвертации температуры
  4. ждем необходимое время(750 мс для 12 битного разрешения)
  5. повторная инициализация
  6. 0xCC – пропускаем идентификацию
  7. 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 года 🙂

PIC мк. Эксперимент №16. One-wire на примере DS18b20.: 114 комментариев

  1. добрый день!
    в своем коде за основу работы с датчиком взял ваш код…..в принципе все за исключением организации индикации. Так вот у меня слдующая проблема при моделировании в протеусе первый раз датчик инициализируется затеем считываю с него показания и отдаю на индикацию затем при входжении в следующий цикл вернее в фукции INIT (); при переходе от строчки STATE 0; к строке __delay_us (500); на шине DQ появляется 5 вольт и все и никакой дальнейщей реакции не происходит при последующих циклах . я правильно понимаю что перед каждым опросом датчика надо проходить процедуру инициализации

    Ответить

    sarge Ответил:

    Добрый день,

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

    Ответить

    сергей Ответил:

    Добрый день!
    Так и не получилось снять информацию с датчика. Предыдущий пост был не совсем корректным у меня 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.
    я новичок и только начинаю разбираться и не совсем понял что от меня компилятор требует.

    Ответить

    sarge Ответил:

    Похоже что мплабх не знает где находится Hi-tech PICC.
    Показывайте весь проект с файлом протеуса.

    Ответить

    сергей Ответил:

    был в командировке так что пришлось приостановить изучение весь проект был на домашнем ПК
    вот собственно проект. на данном этапе интересует собственно считывание информации с датчика. Индикация пока не отлажена (вернее она работает при измерения напряжения то биш отлажена но для индикации температуры пока нет)

    https://drive.google.com/file/d/0B7PZJa0xZaSAd1h0RTh1bldQZFE/view?usp=sharing

    с уважением, сергей.

    Ответить

    сергей Ответил:

    блин файл протеуса не залил так что перезаливаю с файлом протеуса https://drive.google.com/file/d/0B7PZJa0xZaSAcVNMbnBXdU9YVVk/view?usp=sharing

    с уважением, сергей.

    Ответить

    sarge Ответил:

    уточните пожалуйста текущий статус, что не работает и как надо чтобы работало и т.д.

    Ответить

    сергей Ответил:

    ну собственно проблема в следующем.
    не получается считать температуру. По началу после первого цикла опроса датчик при последующих иинциализациях датчика протеус упорно говорит что на ноге 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;
    собственно на этом пока буксую

    Ответить

    sarge Ответил:

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

    Вы неправильно переписали функцию RX(), там должно быть d>>=1, а не просто d>>1;
    У меня выводит значение температуры в эти переменные, причем правильное.

    P.S. Еще смотрите аккуратно с таймером, я его пока вырубил, чтобы он не мешал, но я чувствую там могут быть тоже проблемы.

    Ответить

    сергей Ответил:

    ну щас попробую собственно я осваиваю камень опыт в схемотехнике у меня достаточно большой а вот с камнями дело имел только с готовыми прошивками. Начал учится все что сделано было так кнопка с антидребезгом была написана потом прикрутил прерывания потом прикрутил динамическую индикацию потом не хвалило пинов поставил микру дешифратор потом освоил АЦП и вот чет на датчике споткнулся в принципе для понимания я закоментил ненужные вещи типа кнопки и прочее. Так что вроди иду по наростающей…… Таймер у меня делает только одно раз в 1024 слушает не нажата ли кнопка и если нажата по просто обрабатывает ее ну собственно и все. Щас попробую исправить замечания.
    За ранее большое спасибо!

    Ответить

    сергей Ответил:

    да и еще вопрос чем было мотивировано обьявление переменной как static? ее почему то протеус не видит.

    Ответить

    sarge Ответил:

    Честно говоря не помню.
    Возможно в хайтече нельзя объявить тип переменной bit без статика, но это так, обрывочные воспоминания, не уверен совсем в них.

    Ответить

  2. Переношу оп Вашей просьбе сюда.
    В протеусе пока не собирал, но вот что нашел в процессе поиска решения (взято отсюда – 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;
    Увы, пока без изменений.

    Ответить

Добавить комментарий

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.