Ультро звуковой датчик расстояния HC-SR04
Дата: 2 Апреля 2015. Автор: Алексей
Наконец-то я собрал все железяки в кучу и начал писать программу для управления водоснабжением. Но вот не задача. Для тестирования программы работы с датчиком уровня воды мне пришлось использовать стакан с водой и купать в нем щупы. Все вроде бы шло хорошо, но через пару часов вода стала мутнеть, а к концу дня стала чуть ли не молоко. Че за фигня? А проблема скрылась в протекании тока через воду. Из под крана течет не дистиллят, а все же как не как питьевая. А как всем известно, питьевая вода насыщена разными минеральными

Тыкс. Теперь как оно работает. Смотрим даташит (если кому интересно его можно взять тут). И внимательно читаем. Для того чтобы измерить росстояние, нужно сначало подать импульс на ножку Trig длительностью не менее 10 мкс, а затем смотреть на состояние ножки Echo. Как только датчик гаркнет своими 40 КГц в эфир, то сразу же выставит на этой ножке единицу и будет ее держать до тех пор пока звук не вернется. Как только датчик получет свой звук обратно, тут же прижмет ножку Echo к нулю. Собственно вот и все. Далее нам нужно из этих данных получить расстояние. А как? Смотрим снова даташит. Там написано что для получения расстояния в сантиметрах, нужно посчитать сколько микросекунд держалась единица на ножке Echo, а потом разделить его на 58. Теперь давайте подумаем как нам этот огород реализовать в МК. Первое что нужно, так это режим вывода данных. Мы же не терминаторы и подключатся к UART пальцем не можем (конечно можно, но мало что из этого выйдет). Поэтому давайте выводить данные на ЖК дисплей. По мне это самый простой способ. Из расчета того что я управляющий МК для водопровода взял ATmega128A, будем тестить датчик на нем. Если все же у вас другой, то расстраиваться не надо. Данный код подойдет для практически любого AVR МК, будь это ATmega8 или Tiny2313. Лишь бы был вход внешнего прерывания INTx. И так, с железом разобрались, теперь переходим к программе. Не. Давайте ка сначала перейдем к алгоритму.
- 1 Стартуем преобразование
- 2 Смотрим что на Echo
- 3 Если на Echo 1, обнуляем таймер
- 4 Если на Echo 0, вычитываем значения таймера
- 5 Форматируем значение и выводим на ЖК
- 6 Отдыхаем секунду и все снова и по хорошему
Это самый приближенный алгоритм что мы будем использовать. Далее по тексту я буду описывать только самое необходимое для работы датчика, а всю программу можно будет скачать в конце статьи. Проект написан в шестой студии и закоментирован на смерть. Так что любой разберется для чего нужна та или иная строка. И так:
Блин. А про схему-то я забыл. :)

Я надеюсь питание и управление контрастностью подключите сами, не маленькие коль до данной статьи дошли. Кварц я использовал на 8 МГц. Пару слов про подключение датчика. Ножка PE3 настраивается как выход. Ей будем дергать Trig. Ножка PE4 это вход внешнего прерывания INT4. Если у вас другой МК, то можно перевесить на любой другой INT0...7.
Для начала давайте создадим два псевдонима START и STOP.
#define START 0 #define STOP 1Они нам понадобятся для определение в каком состоянии датчик, преобразует или уже все готово. Далее давайте создадим две глобальные переменные.
volatile unsigned int distance = 0x00; volatile unsigned char status = START;Первая нужна для сохранения количества микросекунд, а вторая как раз для определения состояния датчика. Напомню, если требуется использовать глобальные переменные в обработчике прерывания, всегда объявляйте ее как volatile, а иначе долго будите искать косяк, почему переменная не обрабатывается в прерывавании. Далее настраиваем периферию.
// Настройка таймемера на 1 МГц (Значит один тик равен одной микросекунде) TCCR1A = 0x00; TCCR1B |= (1 << CS11); TCNT1H = 0x00; TCNT1L = 0x00; // Настройка внешнего прерывания по любому изменению EICRB |= (1 << ISC00); EIMSK |= (1 << INT4); // Разрешаем глобальные прерывания sei(); // Нога на выход для тригера PORTE = 0x00; DDRE |= (1 << DDRE3);Теперь давайте напишем тело обработчика внешнего прерывания.
ISR(INT4_vect) // Обработчик прерывания по изменению на ножке INT4 { // Если пришла единица сбрасываем таймер и ставим состояние старта преобразования if(PINE & (1 << PINE4)) { TCNT1 = 0x00; status = START; } // Если пришел ноль, значит звук вернулся. Записываем количества микросекунд и выставляем состояние на конец преобразования else { distance = TCNT1; status = STOP; } }Как это работает. Изначально мы настроили таймер на тиканье раз в 1мкс. То есть каждый раз когда пройдет 1 мкс, таймер приплюсует к регистру TCNT1 единицу. Он это будет делать бесконечно. Так как данный регистр 16 битный, то он досчитав до 65535 перевалит в ноль и все заново. Что происходит дальше. Как только на ножке Echo появляется 1, для внешнего прерывания это значит событие. МК прекращает обрабатывать основную программу, пихает в стек последний арес команды и бежит обрабатывать прерывание. Как только он пришел в обработчик, он смотрит, а что у нас на ножке Echo? Если единица, значит датчик гаркнул в эфир и нужно обнулить таймер, так как там сейчас бог знает что. После выставляем состояние в START. Теперь основная программа знает в каком состоянии датчик. Проделав эту операцию МК возвращается со спокойной душой к работе с основной программой. А тем временем пока звук бегает до цели и обратно, тикает таймер увеличивая свое значение на единицу раз в 1 мкс. Как только звук пришел на приемник, на ножке Echo появляется ноль. О, но это тоже событие для внешнего прерывания. МК снова пихает текущий адрес команды в стек и несется сломя голову в обработчик прерывания. Там снова смотрит что на ноге Echo, а там ноль. О, значит нам нужно сохранить значение регистра счетчика и поставить состояние в STOP. Теперь основная программа знает что у нас есть свежие данные по расстоянию.
// Если состояние датчика STOP if(status) { lcd_xy(0,1); // Переходим на нижнюю строку sprintf(str, "%.1f cm ", (float)distance/58); // Создаем форматированную строку для вывода. lcd_putsf(str); // Выводим строку status = START; // Выставляем состояние на Старт для следующего преобразования _delay_ms(1000); // Для удобства восприятия ЖК задерживаемся на 1 секунду. }Вот и весь процесс работы с этим датчиком. Если все же что-то будет не понятно, я всегда открыт для вопросов. Вот и все.
Проект для ATmega128A AtmelStudio 6.2
JW Player goes here
АНОНИМ 09.04.15 07:29
>Алексей 16.02.15 08:12
С коррозией проблем не будет. Хочу
использовать либо алюминиевые, либо
нержовейку щупы. На работе используем
промышленный РОС-301. На его принципе и
отталкивался. У него щупы из нержовейки
и ни один не ржавеет.
>Короче датчик уровня воды отпал сам
собой.
Бгы гы гы
Алексей 09.04.15 08:31
А с коррозией проблем нет. Проблемы с
реакциями минералов в воде и проходящего
тока через него. Если контроль вести не
питьевой воды, то можно на это забить. А мне
нужно контролировать питьевую воду. Вот я и
перешёл на УЗ. Теперь вопрос сколько
проживут УЗ головки. Они открытые. Замена
на герметичные ничего не дала, модуль
перестал работать. Пока оставлю эти, а со
временем напишу свою программу конкретно
под УЗ головки.
Юрий 08.05.16 19:38
Давно побороли эту проблему с металлическими датчиками - нужно собирать своего рода генератор - таким образом пропуская через воду при замерах "переменку". Иными словами полярность на электродах все время меняется и электролиза не будет!!!! нержавейку конечно использовать предпочтительнее но только с точки зрения коррозии самих электродов.
Алексей 08.05.16 20:44
Ну я в принципе на герконах собрал. Второй сезон пошел, пока все работает как часы.
Юрий 08.05.16 22:00
Герконы, механика - HC-SR04 все таки получше. Хотя мы в своем проекте и контактные используем. Но когда нужна более плавная градация - ультразвук самое то.
Юрий 08.05.16 22:15
Кстати по поводу замены УЗ головок на закрытые - попробовали? Как работает? В плане точности измерений различия есть?
Алексей 09.05.16 00:44
После замены на закрытые, модуль вообще перестал работать. Упрпаляется все китайской нонеймовской микросхемой, поэтому изучение схемы ни к чему не привело. Смотрел готовые модули, работают от 150 мм против 20 мм открытых.