Подписаться на получение новых статей на почту:

Датчик влажности DHT11.Подключаем к AVR.Часть 5. Шаг №20

Обновлено 5.01.16. Всем привет. Итак продолжим наработку нашего проекта. В предыдущей статье мы рассмотрели низкоуровневые операции с SD-картой. Но прежде чем мы начнем использовать файловую систему, мы  добавим в наш проект и рассмотрим датчик влажности и температуры DHT 11, который  применяется исключительно для бытовых целей в помещении. Как вошло уже в правило, ознакомимся с теорией.

Что такое влажность воздуха – это величина, характеризующая содержание водяных паров в атмосфере Земли. Для обозначения количества влаги используют разные величины и обозначения. Мы же будем измерять — относительную влажность воздуха – которая представляет собой отношение давления пара к давлению насыщенного пара, т. е. абсолютной влажности воздуха к максимальной [% относительной влажности], либо RH (от слов relative humidity); обозначается греческой буквой «фи»

Теперь немного  о датчиках. Датчик влажности, тот же гигрометр. может иметь разные принципы работы: емкостные, резистивные, термисторные, оптические. Я же для бытовых целей взял самый дешевый – резистивный.

                     Что такое резистивный датчик?. Датчик такого типа представляет из себя два электрода нанесенных на подложку (бифилярная намотка), а сверху этих электродов наносится слой материала с достаточно низким сопротивлением (часто используют оксид алюминия), которое зависит от влажности, хорошо впитывает его и следовательно меняет свое удельное сопротивление. Т.е. сопротивление обратно пропорционально влажности. Время отклика для большинства датчиков от 10 до 30 секунд. Резистивные датчики не является полностью резистивные за счет емкостного эффекта в диапазоне более 10-100 МОм.

Перейдем непосредственно к нашему датчику. На рисунке ниже изображен датчик DHT11 в разобранном состоянии, вид спереди и вид сзади.

DHT11 - вид изнутриДатчик влажности DHT11- вид ацп

На некоторых ресурсах пишут что данный прибор имеет емкостный датчик, на некоторых – резистивный. В технической документации четко указано – резистивный тип. Этот датчик включает в себя резистивный компонент измерения влажности и компонент измерения температуры с отрицательным температурным коэффициентом (NTC), которые подключены к высокопроизводительному 8-битному микроконтроллеру. Каждый датчик DHT11 строго калибруется, коэффициенты калибровки хранятся в виде программ в однократно программируемой энергонезависимой памяти, и используются во внутренних процессах обработки сигнала.

                    Ниже представлена схема подключения датчика и таблица с его характеристиками. Протокол 1-Wire он не поддерживает (а жаль).

Подключение DHT11

Характеристики DHT11

Как видите из подключения необходим подтягивающий резистор, для того чтобы в режиме ожидания линия была в высоком состоянии. Когда будете читать документацию, увидите что датчик отсылает 5 байт, 2 из которых десятичные, так вот не обольщайтесь, только целые значения.

Теперь рассмотрим описание временных задержек для работы с датчиком.

Итак сперва запомним что датчик всегда находится в спящем режиме при высоком состоянии на линии. Чтобы общаться с ним нам необходимо его разбудить, получить подтверждение и принять 40 бит данных. В документации описаны временные задержки. Вот по ним мы  и начнем писать код

Шаг №1. Инициализация.

Инициализация DHT11

Процесс инициализации начинается с подтягивания линии на 18 мс, далее отпускаем на 20-40 мкс и переводим ножку микроконтроллера в режим чтения. В принципе очень простые операции.

 

 

Шаг№2. Подтверждение.

Подтверждение DHT11

После того как перевели контроллер в режим чтения, ждем когда начнется 0, далее задержка и проверяем на высокое состояние. Все датчик откликнулся – живой.

Шаг №3. Прием данных.

Прием данных DHT11

В передаче данных, в принципе также все просто. Здесь различие в бите определяется задержкой высокого состояния. Первые 8 бит влажность, 2- тоже влажность, но десятые , которые он не измеряет. 3и 4-й температура соответственно. 5-й – бит контрольной суммы, который должен быть равен сумме всех 4-х байтов.

Ну и последнее, после последнего переданного бита датчик передает ноль 50 мкс. И дальше входит в режим ожидания.

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

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

Что же такое универсальность? Это возможность подключить, задействовать код к другому контроллеру и к другим ножкам. Для это мы в программе используем директиву препроцессора #define  и переопределение типов typedef. Они являются почти аналогом друг-друга за исключением того что #define   обрабатывается перед компиляцией путем простой замены всех вхождений, а с помощью typedef можно объявить имя функции или массива. Более подробно в следующей статье (№21).

Код я искал исключительно на Си. Но просторы интернета навязчиво выдают библиотеку под  Arduino, который является электронным конструктором, а его среда  –это использование диалога Processing/Wiring.(смесь языков СИ и Си++). Это не подходит….

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

#include <avr/io.h> 
#include <util/delay.h>
#include <avr/interrupt.h>
#include <math.h>
/* Здесь указываем порт к которому подключен датчик*/
#define DHT_PORT PORTC
#define DHT_DDR DDRC
#define DHT_PIN PINC
#define DHT_BIT 3
/*Порты вывода на индикатор*/
#define SEGM_PORT PORTD
#define SEGM_DDR DDRD

/*Порты управление индикатором*/

#define DSEGM_PORT PORTB
#define DSEGM_DDR DDRB
#define DSEGM_PIN1 PINB7 // 4-й элемент индикатора вывод едениц
#define DSEGM_PIN2 PINB6 //3-й элемент- вывод десятых и точки
#define DSEGM_PIN3 PINB0 // 2-й элемент индикатора вывод сотен
#define DSEGM_PIN4 PINB4 //1-й элемент- вывод знаков

/*директивы для настройки индикатора. */

#define a 128

/*Директивы те же и не меняются*/

………………….
#define dp 4
uint8_t datadht[5]; /* массив для значений датчика*/
typedef unsigned int byte; /*переопределение типов вводит синоним для существующего типа*/
unsigned char Slot[11]; /*Массив, в котором хранятся числа, которые нужно*/
byte i2,j1,k,j;
void Slot_init ()
{
    …………………………………...
}
char Elem1, Elem2, Elem3;
/* Функция вывода на индикатор. */
void Display (float i)
{
………………………………….
}
/*Функция инициализации датчика. Важно, перед вызовом данной функции отключить прерывания*/
int dhtread ()
{
        byte j = 0, i = 0; /*локальные переменные*/
        datadht[0] = datadht[1] = datadht[2] = datadht[3] = datadht[4] = 0 ;             
         /*Шаг №1*/
        DHT_DDR|=(1<<DHT_BIT); // выход
        DHT_PORT&=~(1<<DHT_BIT); /*низкий уровень — подтягиваем линию-       разбудим датчик*/
        _delay_ms (18); /*18 мс по условиям документации. В принципе в ходе экспериментов я ставил задержку 30 мс, то существенной разницы не почувствовал. */
        DHT_PORT|=(1<<DHT_BIT); /*отпускаем линию*/
         _delay_us (40); /*задержка по условию*/
  /*Шаг №2*/
        DHT_DDR&=~(1<<DHT_BIT); // вход
        if (DHT_PIN&(1<<DHT_BIT)) //датчик должен ответить 0
                       { return 0; }
         _delay_us (80); // задержка
        if (!(DHT_PIN&(1<<DHT_BIT))) /*по истечению 80 мкс, датчик должен отпустить шину*/
                       { return 0; }
/*Высокий сигнал на линии продлится также приблизительно 80 мкс*/

/*Шаг№3*/

         while (DHT_PIN&(1<<DHT_BIT)); /* ждем пока контроллер датчика начнет передавать данные*/
//передача начинается с нуля
         for (j=0; j<5; j++)
          {
                 datadht[j]=0;
                 for (i=0; i<8; i++)
                 {
                           cli (); // запрещаем прерывания 
                           while (!(DHT_PIN&(1<<DHT_BIT)));   /* ждем когда датчик отпустит шину */
                          _delay_us (30); /*задержка высокого уровня при 0 30 мкс*/
                          if (DHT_PIN&(1<<DHT_BIT)) /*если по истечению времени сигнал на линии высокий, значит передается 1*/
                                       datadht[j]|=1<<(7-i); /*тогда i-й бит устанавливаем 1*/
                          while (DHT_PIN&(1<<DHT_BIT));  // ждем окончание 1 
                          sei ();// разрешаем общее прерывание
                  }
          }
          return 1;
}
int main (void)
{
// тут начальные установки программы
        DSEGM_DDR = 0Xff;
        SEGM_DDR = 0xff; /*все выводы порта D сконфигурировать как выходы*/
        SEGM_PORT = 0×00; //устанавливаем 0 
        Slot_init ();  
        //инициализация таймера Т0
        TIMSK = (1<<TOIE0);//Флаг разрешенияя по переполнению таймера счетчика Т0
        TCCR0 = (0<<CS02)|(1<<CS01)|(0<<CS00);// 1000000/8 = 125000
        _delay_ms (1000); /* 1 сек на стабилизацию напряжения.Наверное ненадо,т.к. проходит                      тестирование других железяк*/
/ *Выводим датчик из спящего режима*/
        for (;;)
        { 
               _delay_ms (1000); 
               i2= 0; /*для вывода на индикатор обозначений*/
               cli (); // запрещаем прерывания
               dhtread (); //опрос датчика
               sei ();// разрешаем общее прерывание
               /*Проверка датчика на ошибки*/
              if (datadht[0]==0 && datadht[1]==0 && datadht[2]==0 && datadht[3]==0)
              { Display (000); }
               /* Проверка контрольной суммы*/
              else if (datadht[0] + datadht[1] + datadht[2] + datadht[3] == datadht[4])
             { /* Если сумма совпадает, то ок*/
                      i2=0;
                      i2++;
                      Display (datadht[0]);// передаем показания влажности
                      _delay_ms (2000);
                      i2++;
                      Display (datadht[2]);// передаем показания температуры
                      _delay_ms (2000);
              }
              else
             {  Display (000);}
        }
  }
//прерывания таймера Т0 — вывод на индикато
ISR (TIMER0_OVF_vect)
       DSEGM_PORT &= 0x2e; //Очистка PB7, PB6, PB0, PB4
for (j = 0; j<=30; j++){}; // Задержка для выключения транзистора
(k == 3) ? k = 0 : k++; // изменили к=3, т.к. 4 сигмента
switch (k)
{
/*Обработка элементов индикатора
        case 0:
               DSEGM_PORT |= (1 << DSEGM_PIN1); // Единицы
               SEGM_DDR = Elem3;
        break;
        case 1:
               DSEGM_PORT |= (1 << DSEGM_PIN2); // Десятки
               SEGM_DDR = Elem2;
        break;
        case 2:
               DSEGM_PORT |= (1 << DSEGM_PIN3); // Сотни
               SEGM_DDR = Elem1;
        break;
        case 3:
if (i2==1)
{
/* На первый элемент индикатора я вывожу буквы H и С. Соответственно влажность и температура.*/
            DSEGM_PORT |= (1 << DSEGM_PIN4); // отвечает за вывод знака H
            SEGM_DDR = 121; /*активируем ножки, отвечающую за элемент G,B,C,F,E на индикаторе, который покажет H*/
         }
         if (i2==2)
         {
            DSEGM_PORT |= (1 << DSEGM_PIN4); // отвечает за вывод знака C
            PORTD = 195; /*активируем ножки, отвечающую за элемент A,F,E,D на индикаторе, который покажет C */
         }
   }
}  

В результате работы этого кода наш датчик заработал и выдал показания температуры и влажности. Температуру я сравнивал, приблизительно равна, иногда показывает ± 1 град. Влажность я сравнил с аспирационным психрометром Ассмана. В комнате показания температуры сухого и влажного термометра составило соответственно 29.6 и 21.2, разность 8.4, по психрометрической таблице при разнице 8 и 30 град Цельсия относительная влажность составляет  50 %. На рисунке ниже видно, что наш датчик под управлением Atmega8, показывает 51%, что в пределах допустимых отклонений данного прибора.

Психрометр Ассмана и DHT11

 

 

 

 

 

 

 

 

 

В следующей статье разобьем весь наш код на отдельные библиотеки, а это у нас будут: библиотека DS18B20, библиотека индикатора, библиотека DHT11. Также использую приемы для универсальности «разложим все аккуратно по полочкам».

Также хочется добавить несколько рекомендаций по работе с датчиком:

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

Вот в принципе и все. На этом пока поставим точку. Спасибо Всем за внимание.

Просмотрено 16933 раз.

Я на Google+

Датчик влажности DHT11.Подключаем к AVR.Часть 5. Шаг №20: 14 комментариев

  1. Скажите, а у меня показания влажности почти всегда в районе 40... хотя гидрометео передаёт что влажность до 70, и почти нет корреляции с реальной погодой, т.е. солнце-дождь отклонение на 10-15 единиц... Пробовал 3 датчика но из одной партии... возможно ли что они неисправные или я что то не пойму?

    • Здравствуйте.Я так понимаю Вы измеряете влажность в помещение, а гидрометео передает для окружающей средыито в среднем. 40 для помещения суховато, но если отопление подсушивает воздух,то вполне возможно. Если в сырости используете, то посмотрите выдерживаете ли Вы паузу, а также помните что это дешевый датчик с погрешностью 5%. Конечно может китайцы подсунули фуфло.

    • Попробуйте поменять подтягивающее сопротивление- увеличить до 10кОм или уменьшить до 5 или 4,7кОм

  2. Здравствуйте, не могли бы Вы откомпилить эту прогу под проц Мега16? Цель — рабочая прошивка, чтобы проверить датчик. Я тут бьюсь над собственной реализацией протокола на внешних прерываниях, в ассемблере. Впечатление, что датчик не отвечает. В Си пока не смыслю вообще, а то бы сам откомпилил.

      • Саша, спасибо огромное, кажись датчик негодный. Кажет 00.0 хоть с ним, хоть без него. Резистор пробовал разный ставить — пофиг. Китаецы подсунули фехню.

  3. Здравствуйте,отличная статья, благодаря ей разобрался с датчиком!!

    Только с return 0; в шаге 2 не заработало,убрал оба return 0;и сразу дело пошло. И еще, запрет прерываний лучше сделать так:

    for (j=0; j<5; j++)

    {

    datadht[j]=0;

    for (i=0; i<8; i++)

    {

    cli (); // запрещаем прерывания

    while (!(DHT_PIN&(1<<DHT_BIT))); /* ждем когда датчик отпустит шину */

    _delay_us (30); /*задержка высокого уровня при 0 30 мкс*/

    if (DHT_PIN&(1<<DHT_BIT)) /*если по истечению времени сигнал на линии высокий, значит передается 1*/

    datadht[j]|=1<<(7-i); /*тогда i-й бит устанавливаем 1*/

    while (DHT_PIN&(1<<DHT_BIT)); // ждем окончание 1

    sei ();// разрешаем общее прерывание

    }

    }

    return 1;

    Иначе прерывания запрещаются на длительное время ,и у меня были сбои в работе шим при опросе датчика. А за статью большое спасибо!Только с ней разобрался с этим датчиком.

    • Здравствуйте. Страмно, что Вы убрали return 0 и заработало. ведь это значит, что не выполняются условия. Чудеса китайской электроники))). За прерывание да действительно. У меня просто уже комплекс библиотек и прерывание запрещается ранее. Спасибо внесу корректировку.

  4. Здравствуйте! Переделал программу под жк дисплей, циклы с опросом датчика не менял, уже неделю ломаю голову над кодом. Могли бы посмотреть код и написать в чем может быть ошибка? Подключал информационную ножку к осциллографу, такое ощущение как будто датчик не отвечает. Могли бы помочь решить эту проблему?

  5. брехню у тебя показывает твой психрометр.

    чтобы он показывал верно нужно создать условия для этого

    а именно скорость аспирации (потока воздуха)

    которая указана на психрометре или смотреть в докумментации

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

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting