Пример программы для датчика

Сегодня, в заключительной статье о датчиках температуры DS18B20, мы полностью рассмотрим и проанализируем работу программы общения DS18B20 с микроконтроллерами AVR (или наоборот). И хотя программа будет в Algorithm Builder (графический ассемблер), главное понять алгоритм общения датчика DS18B20 с микроконтроллером, а перевести ее потом в «классический ассемблер» большого труда не составит.

Программа общения DS18B20 с микроконтроллером

Последовательность операций для работы с датчиком DS18B20

При работе с датчиком DS18B20 очень важно выполнять последовательность выполнения операций:
1. Инициализация
2. Команда ROM
3. Функциональная команда
Если не следовать этой последовательности (что-то пропустить, или поменять местами), то датчик DS18B20 откажется общаться с микроконтроллером

Пример программы для управления роботом с помощью датчика расстояния.

Инициализация датчика DS18B20

В прошлой статье мы рассматривали этот вопрос (инициализация датчика DS18B20), но повторимся, что бы не прыгать по страницам сайта.

Процесс инициализации DS18B20 состоит из трех пунктов:
Исходное состояние — на выводе порта, к которому подключен вывод DQ датчика (шина DQ) — логическая 1 (за счет подтягивающего резистора)

1. Микроконтроллер передает в линию импульс сброса (длительность 480 микросекунд):
— переводит шину DQ в состояние логического нуля (вывод микроконтроллера переводится на вывод информации и в него записывается логический ноль) на время не менее 480 микросекунд (я обычно округляю длительность импульса до 500 микросекунд)
— через 480 микросекунд (или 500) микроконтроллер переводит шину DQ на ввод информации, в результате чего за счет подтягивающего резистора на шине восстанавливается логическая единица
— микроконтроллер ждет импульс присутствия от датчика DS18B20

2. Датчик DS18B20 передает в линию импульс присутствия (общая длительность 480 микросекунд):
— при переводе микроконтроллером шины в логический ноль на время не менее 480 микросекунд, датчик понимает, что к нему обращаются
— через 15-60 микросекунд, после того как МК перевел шину в состояние логической единицы, датчик передает импульс присутствия — переводит шину DQ в состояние логического нуля на время 60-240 микросекунд

3. Прием импульса присутствия:
— через 15-60 микросекунд, после того как микроконтроллер перевел шину в состояние логической единицы, проверяется наличие логического нуля на шине DQ (брать лучше по максимуму — 60 микросекунд)
— если на линии — логический ноль — значит датчик присутствует на шине и можно продолжать дальнейшую работу
— так-как общая длительность импульса присутствия должна быть не менее 480 микросекунд, микроконтроллер формирует дополнительную паузу длительностью 420 микросекунд

Временной график инициализации DS18B20

В ходе процесса инициализации могут возникнуть три ошибки, которые мы разберем непосредственно в программе:

Процесс инициализации датчика DS18B20 оформляется в виде подпрограммы, в данном случае имя подпрограммы «DS_Reset_Pulse»
В этой подпрограмме:
— Term_Error1 — переменная, в которой будет хранится код ошибки
— DQ_Pin, DQ_Port, DQ_DDR — присвоенные мной имена выводу порта к которому подключен датчик. Делается это не только для наглядности (можно конечно и ничего не присваивать) но в больше степени для быстрой смены вывода порта в программе, если мы вдруг захотим подключить датчик к другому выводу (не надо будет по всей программе искать где этот РВ6, чтобы заменить его, к примеру, на РС5 — проще один раз поменять в таблице)

Состояние разряда шины DQ DS18B20

Ну а теперь рассмотрим подробно саму подпрограмму «Инициализация датчика DS18B20» :

Инициализация датчика DS18B20

1. Обнуляем переменную в которую будет записан код ошибки («Term_Error1»)
2. Проверяем, есть ли на шине DQ логическая единица:
— если на шине присутствует высокий уровень (логическая единица), то переходим к формированию импульса сброса
— если на шине нет высокого уровня, то записываем в переменную «код ошибки» — 1 (код ошибки отсутствия высокого уровня на шине DQ) и выходим из подпрограммы. Затем, при выводе данных на индикатор, высвечиваем, к примеру, — «Er1». Отсутствие высокого уровня в 99 случаях означает, что не подключен подтягивающий резистор формирующий высокий уровень на шине.
3. При высоком уровне на шине микроконтроллер формирует импульс сброса продолжительностью не мене 480 мкс, путем перевода вывода порта в режим «выхода» и записи в него логического нуля.
4. Микроконтроллер отпускает шину путем перевода вывода порта в режим «входа»
5. Через 60 мкс МК поверяет наличие импульса присутствия от датчика (на шине должен присутствовать низкий уровень (логический ноль)
6. Проверяем, есть ли на шине DQ логический ноль:
— при низком уровне на шине, означающем что датчик выдал импульс присутствия, МК формирует паузу в 420 мкс
— высокий уровень на шине означает, что датчик не выдал импульс присутствия. В этом случаем записываем в переменную «код ошибки» — 2 (код ошибки отсутствия импульса присутствия от датчика), а при выводе данных на индикатор — выводим «Er2»
7. После паузы в 420 мкс проверяем наличие высокого уровня на шине после окончания импульса присутствия:
— если на шине логическая единица, значит датчик отработал и выходим из подпрограммы инициализации датчика
— если на шине логический ноль, значит датчик не вернул шину в высокий уровень. Записываем в переменную «код ошибки» — 3 (код ошибки «не восстановлен высокий уровень после импульса присутствия»)
На этом инициализация датчика заканчивается.
Если ошибок нет, то МК переходит к выполнению следующих шагов — посылает на датчик команду ROM, а затем функциональную команду.
Если ошибка присутствует, то микроконтроллер дальше не идет, а постоянно выполняет подпрограмму инициализации до устранения ошибки.

Читайте также:
Как подключить программу егаис

Команда ROM DS18B20

Считывание показаний датчиков с помощью Arduino

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

  • датчик освещенности;
  • датчик движения;
  • датчик температуры;
  • датчик магнитного поля;
  • датчик силы тяжести;
  • датчик влажности;
  • датчик вибрации;
  • датчик давления;
  • датчик электрических полей;
  • звуковой датчик;
  • датчик положения.

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

Эксперимент 1: датчик расстояния

В этом эксперименте мы будем использовать датчик расстояния Sharp GP2Y0A21YK для управления яркостью светодиода.

Инфракрасный (IR) датчик SHARP

Необходимые комплектующие

  • 1 x Arduino Mega2560;
  • 1 x макетная плата;
  • 1 x светодиод;
  • 5 x перемычка;
  • 1 x резистор 470 Ом;
  • 1 X датчик расстояния Sharp GP2Y0A21YK.

Схема соединений

Схема соединений

Датчик расстояния Sharp может обнаруживать объекты на расстояниях от 10 до 80 см. Он излучает импульс инфракрасного света, а затем определяет угол, на котором отражается этот свет. Чем дальше объект, тем ниже выходное напряжение. Если датчик не принимает отраженный свет, то напряжение на его выходе составит 0 В. Если объект находится на расстоянии 10 см или ближе, выходное напряжение будет равно 5 В (в этом эксперименте мы подаем на датчик напряжение питания 5 В).

Выход датчика подключается к аналоговому входу Arduino. Аналого-цифровой преобразователь (ADC) Arduino затем преобразует это напряжение в значение от 0 до 1023. Затем это значение преобразуется в значение от 0 до 255, и это число используется для установки коэффициента заполнения сигнала на широтно-модулированном (ШИМ) выходе, который управляет яркостью светодиода. В результате, чем ближе объект к датчику расстояния, тем ярче светит светодиод.

Код

const int pwm = 2 ; // определение вывода для ШИМ const int adc = A0 ; // определение вывода для АЦП void setup() < pinMode(pwm,OUTPUT) ; // настроить вывод, подключенный к светодиоду, на выход >void loop() < int sensor_val = analogRead(adc); /* ———— функция map ———— Данная функция масштабирует выходное значение АЦП, который является 10-битным и выдает значения от 0 до 1023, в значения от 0 до 255 для функции analogWrite, которая принимает значения только в диапазоне от 0 до 255. */ sensor_val = map(sensor_val, 0, 1023, 0, 255); analogWrite(pwm, sensor_val); // установить значение датчика в ШИМ >

Видео

Эксперимент 2: датчик температуры

В этом эксперименте Arduino будет измерять температуру с помощью микросхемы датчика LM35. LM35 – это низковольтная микросхема, которая требует питания постоянным напряжением от +4 до +20 вольт. Это идеально, потому что мы можем подключить датчик к выводу +5V на плате Arduino. LM35 имеет всего 3 вывода: два для питания и один для аналогового выхода.

Выходной вывод представляет собой аналоговый выход, напряжение на котором линейно пропорционально температуре в градусах Цельсия. Выходной сигнал находится в диапазоне от 0 до 1,5 вольта. Выходное напряжение 9 В соответствует температуре 0°C, и при каждом повышении температуры на один градус оно увеличивается на 10 мВ. Чтобы преобразовать выходное напряжение в температуру, вам необходимо просто разделить выходное напряжение в мВ на 10. Например, если выходное напряжение равно 315 мВ (0,315 В), температура равна 31,5°C.

Назначение выводов микросхемы LM35

Назначение выводов микросхемы LM35

Необходимые комплектующие

  • 1 x датчик температуры LM35;
  • 2 x светодиод;
  • 1 x коробок спичек;
  • 2 X резистор 470 Ом;
  • 1 x Arduino Mega2560;
  • 1 x макетная плата;
  • 10 x перемычка.

Схема соединений

Схема соединений

Код

Выходной вывод LM35 (вывод 2) подключен к выводу A0 Arduino. Код использует функцию analogRead() для преобразования выходного напряжения в число между 0 и 1023. Умножение этого числа на 0.48828125 преобразует его в градусы Цельсия, которые и отображаются в мониторе последовательного порта.

const int adc = 0; // назначение вывода 0 для работы с АЦП const int high = 8; // для включения и выключения желтого светодиода const int low = 9; // для включения и выключения зеленого светодиода void setup() < Serial.begin(9600); // запуск последовательного порта на скорости 9600 pinMode(high, OUTPUT); // установка выводов светодиодов в режим выхода pinMode(low, OUTPUT); >void loop() < // чтение аналогового напряжения и сохранение его как целое число int adc = analogRead(0); // преобразование показаний датчика в градусы Цельсия adc = adc * 0.48828125; Serial.print(«TEMPRATURE = «); // вывод в монитор последовательного порта Serial.print(adc); // показания температуры Serial.print(«*C»); // TEMPRATURE = 27*C Serial.println(); // закрыть строку delay(1000); // задержка 1 секунда /* ЛОГИКА: если (температура (adc) >70°C) зажечь желтый светодиод погасить зеленый светодиод иначе погасить желтый светодиод зажечь зеленый светодиод */ if(adc > 70) < digitalWrite(high, HIGH); digitalWrite(low, LOW); >else < digitalWrite(high, LOW); digitalWrite(low, HIGH); >>

Видео

Источник: radioprog.ru

Пример программы для датчика

Описываемый здесь цифровой термометр имеет диапазон измеряемых температур от -55ºC до +125ºC и выводит температуру на индикатор с разрешением 0.1ºC.

Оглавление
Пример использования цифрового датчика температуры DS18B20

Смотрите также
Использование UART для реализации 1-Wire

Введение

В этой статье рассмотрим программу для микроконтроллера, которая измеряет температуру с помощью цифрового датчика температуры DS18B20 и выводит результат измерения на 4-х разрядный семисегментный светодиодный индикатор FYQ-3641A (общий катод, предназначен для динамической индикации). Используется установленный на оценочной плате STM32VLDISCOVERY микроконтроллер STM32F100RBT6B.

О датчике DS18B20 рассказывалось в предыдущей статье. О семисегментных индикаторах и динамической индикации речь шла здесь.

Читайте также:
Архикад 20 как работать с программой

Используемый микроконтроллер не имеет аппаратной поддержки интерфейса 1-Wire, который используется для взаимодействия с цифровым датчиком температуры DS18B20. Поэтому потребуется программная реализация интерфейса. Это не так сложно, потому что используемый протокол довольно прост.

Всё что требуется — настроить используемый для обмена данными вывод микроконтроллера для работы в качестве выхода порта общего назначения с открытым стоком. Затем нужно лишь, аккуратно следуя требованиям протокола шины 1-Wire и с учётом набора команд датчика, формировать требуемые уровни сигнала на выходе микроконтроллера на заданные промежутки времени и считывать состояние шины в определённые моменты времени. Как управлять портами ввода-вывода микроконтроллера обсуждалось статье «GPIO: порты ввода/вывода общего назначения», а вопросы измерения интервалов времени обсуждались в статье о системном таймере микроконтроллера.

Схема подключения DS18B20

Индикатор подключён к микроконтроллеру в точности также, как в примере к статье «Семисегментный индикатор. Динамическая индикация».

Схема подключения DS18B20 к микроконтроллеру в цифровом термометре.

Рис. %img:cir

Цифровой датчик температуры подключён к выводам PB0 и PB1. PB1 используется для питания датчика. Питание от вывода микроконтроллера используется для того, чтобы можно было в целях эксперимента программно переключаться из режима питания от внешнего источника в режим паразитного питания и обратно. PB0 используется для обмена данными.

Резистор R14 является подтягивающим к высокому уровню для шины данных. Резисторы R13 и R15 защищают микроконтроллер во время аварийных режимов работы, например при коротком замыкании в линии с помощью которой датчик подключается к микроконтроллеру.

Программная реализация протокола шины 1-Wire

Для низкоуровневого взаимодействия с шиной будем использовать следующие 3 функции:

// Низкоуровневые функции для взаимодействия с датчиком. // Реализованы для случая использования вывода PB0. // Установить на шине 1. inline void set_dq1() < GPIOB->BSRR=GPIO_BSRR_BS0; > // Установить на шине 0. inline void set_dq0() < GPIOB->BRR=GPIO_BRR_BR0; > // Считывает и возвращает состояние шины (0 — false или 1-true). inline bool get_dq() < return GPIOB->IDR >

Так как все подключённые к шине устройства, и ведущее, и ведомые, имеют выходы по схеме с открытым стоком, то установка 1 на шине означает освобождение шины, в результате чего высокий уровень устанавливается за счёт подтягивающего резистора. Так что выражения «установить на выходе 1» и «освободить шину» используются как синонимы.

Для инициализации подключённых к шине ведомых устройств, ведущее устройство должно сначала сформировать импульс сброса длительностью не менее 480 мкс, а затем освободить шину на время не менее 480 мкс, для того чтобы дать возможность ведомым устройствам ответить на сброс импульсом присутствия. При паразитном питании длительность импульса сброса не должна превышать 960 мкс, чтобы исключить возможность сброса по питанию.

Выберем длительность интервалов равной 500 мкс — с небольшим запасом. После завершения импульса сброса, ведомые устройства ждут 15..60 мкс и отвечают импульсом присутствия, устанавливая на шине 0 на время 60..240 мкс. Так что импульс присутствия может начаться, самое позднее, через 60 мкс от завершения импульса сброса и может закончится (при каких-то условиях), самое раннее, через 15+60=75 мкс от завершения импульса сброса. Для считывания состояния шины выбираем значение в районе среднего между крайними значениями 60 и 75 мкс, например, 68 мкс.

Надо сказать, что для установления в шине высокого уровня под действием подтягивающего резистора после освобождения шины также требуется время, определяемое переходными процессами в RC-цепи. Например, если шина с подключёнными устройствами имеет ёмкость порядка 200 пФ, сопротивление подтягивающего резистора около 5 кОм, тогда время нарастания сигнала будет иметь порядок 1 мкс. В данном случае этим временем можно пренебречь.

Инициализация будет выполняться с помощью функции reset. Функция инициализации использует определённые ранее функции низкоуровневого доступа к 1-Wire шине. Также используются средства для формирования задержек с высокой точностью: функция ndelay и макрос USTON . Подробнее о формировании задержек говорится в статье о системном таймере.

// Инициализация: сброс и проверка присутствия устройств на шине. // Возвращает true, если на шине есть ведомые устройства. bool reset() < const uint32_t RESET_TIME_LOW=500; const uint32_t RESET_TIME_HIGH=500; const uint32_t PRESENCE_DETECT_POINT=68; // Запрещаем исключения для точного измерения времени. bool primask=__get_PRIMASK(); __disable_irq(); set_dq0(); // Импульс сброса. ndelay(USTON(RESET_TIME_LOW)); set_dq1(); // Освободить шину. ndelay(USTON(PRESENCE_DETECT_POINT)); bool res=!get_dq(); // Восстанавливаем состояние регистра запрета // исключений: не требуется высокая точность // при формировании следующего интервала времени. __set_PRIMASK(primask); ndelay(USTON(RESET_TIME_HIGH-PRESENCE_DETECT_POINT)); return res; >

Далее, нам потребуется реализация функций, которые формируют тайм-слоты для записи и для чтения.

Длительность тайм-слотов записи составляет минимум 60 мкс и максимум 120 мкс; выбираем среднее значение — 90 мкс. При формировании слота записи 1 важно успеть освободить шину, чтобы к моменту времени 15 мкс от начала слота, на шине установился уровень логической 1, но при этом в начале слота требуется удерживать на шине логический 0 не менее 1 мкс; выберем величину 7 мкс, оставив на переход шины к высокому уровню 8 мкс.

Для тайм-слотов чтения минимальная длительность определена в спецификации равной 60 мкс. Но можно выделить и больше, лишнее время просто увеличит время восстановления между соседними слотами и только повысит надёжность обмена данными. Гарантируется, что данные, которые устанавливает ведомое устройство на шине, действительны до момента времени 15 мкс от начала тайм-слота чтения. К этому моменту ведущее устройство должно успеть освободить шину, дождаться завершения переходных процессов (в случае, если ведущее устройство передаёт 1, в шине должен установиться высокий уровень под действием подтягивающего резистора) и определить состояние шины. Можно, например, установить 0 на шине на 6 мкс, освободить шину на 6 мкс, после чего прочитать состояние шины (это будет момент 12 мкс от начала тайм-слота).

Читайте также:
Как посмотреть какие программы сколько места занимают на ПК

Между тайм-слотами как в случае чтения, так и случае записи должны быть интервалы, когда шина остаётся свободной — время восстановления. В спецификации определена минимальная величина времени восстановления, равная 1 мкс. Максимальная длительность не ограничена. Для большей надёжности передачи, лучше сделать интервалы немного больше минимально допустимой величины. А ещё лучше — много больше, например, 10 мкс.

// Слот записи бита 0. inline void slot_w0() < set_dq0(); ndelay(USTON(90)); set_dq1(); >// Слот записи бита 1. inline void slot_w1() < set_dq0(); ndelay(USTON(7)); set_dq1(); ndelay(USTON(83)); >// Запись бита + формирование интервала восстановления. void slot_w(bool bit) < bool primask=__get_PRIMASK(); __disable_irq(); if(bit) slot_w1(); else slot_w0(); __set_PRIMASK(primask); ndelay(USTON(10)); >// Слот чтения бита от устройства. bool slot_r()

После того, как функции, формирующие слоты определены, задачи отправки и получения байта решаются тривиально.

// Передача данных осуществляется, начиная с младшего бита. // Отправить байт устройству. void send_byte(uint8_t b) < for(uint32_t i=0; i>=1; > > // Получить байт от устройства. uint8_t receive_byte() < uint8_t res=0; for(uint32_t i=0; i>=1; res|=slot_r() return res; >

Пример программы

Исходный код всего проекта

Теперь приведём пример функции измерения температуры для простейшего случая: используется внешнее питание датчика DS18B20, к 1-Wire шине подключён только один датчик. Будем считать, что DS18B20 сконфигурирован для работы в 12-битном режиме.

Функция блокирующая, т.е. возвращает управление после завершения процесса измерения температуры, что требует до 750 мс времени, но во время выполнения функции разрешена обработка исключений. Функция возвращает двухбайтовое целое число со знаком, равное измеренной температуре в ºC, умноженной на 16. Такая форма возвращаемых данных выбрана для того, чтобы не связываться с числами с плавающей точкой, которые существенно «утяжеляют» программу. В случае ошибки функция возвращает значение 0x8000.

int16_t get_themp_x16() < int16_t t=0x8000; if(reset()) < send_byte(0xCC); // Skip ROM. send_byte(0x44); // Convert T. while(slot_r()==0) // Ждём готовности. delay(75); // Пауза, если ещё не готовы. if(reset()) < send_byte(0xCC); // Skip ROM. send_byte(0xBE); // Read scratchpad. t=receive_byte(); // Читаем младший байт. t|=receive_byte()> return t; >

Далее приводится пример программы, в которой периодически с датчика считывается температура и полученные данные выводятся на индикатор.

int main() < // Конфигурируем выводы для подключения индикатора. display_config(); // Конфигурируем выводы для подключения датчика. // PB1 — out (power); PB0 — in/out (open drain). RCC->APB2ENR|=RCC_APB2ENR_IOPBEN; const uint32_t mask=GPIO_CRL_CNF0|GPIO_CRL_MODE0| GPIO_CRL_CNF1|GPIO_CRL_MODE1; GPIOB->CRL=GPIOB->CRL // DS18B20 Power on: GPIOB->BSRR=GPIO_BSRR_BS1; SysTick_Config(SystemCoreClock/1000); while(true) < int tx16=get_themp_x16(); int D=tx16*100/16; if(D<0) D-=5; else D+=5; D/=10; display(D, 1); delay(500); >>

Ничего сложного здесь нет. Единственное о чём стоит упомянуть, это то, что в программе операции выполняются только с целыми, что не мешает отображать дробные числа на индикаторе: датчик имеет разрешение 0.0625ºC, поэтому используется отображение результата на индикаторе с точностью 0.1ºC.

Если процессор не имеет аппаратной поддержки чисел с плавающей точкой (а используемый STM32F100RB не имеет, как и другие Cortex-M3), то операции с ними реализуются программно на основе целочисленной арифметики. При этом компилятор подключает к программе соответствующие функции. Размер программы существенно увеличивается, операции над вещественными числами программно выполняются медленно. Поэтому если нет острой необходимости, лучше обходиться без использования чисел с плавающей точкой на устройствах без их аппаратной поддержки.

Один из вариантов — использовать числа с фиксированной точкой. При этом сложение, вычитание таких чисел, умножение и деление их на целое (имеется в виду, что числа в операциях имеют одинаковое расположение точки) производится также, как если бы они были целыми, а при умножении или делении двух чисел с фиксированной точкой потребуется нормирование результата. Числа с фиксированной точкой, например, использует тот же самый датчик DS18B20, который в такой форме возвращает измеренную температуру.

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

В нашем случае мы хотим вывести на индикатор температуру T с точностью до 0.1ºC. Это то же самое, что вывести на индикатор целое число 10*T и отобразить на индикаторе точку перед младшим разрядом. Так как датчик даёт число T16=16*T (если рассматривать это число T16 как целое), то нам нужно отобразить на индикаторе целое число D=(10*T16)/16 и не забыть показать точку в нужном месте.

При целочисленном делении дробная часть отбрасывается. В программе используется чуть более сложный вариант с округлением. Для округления вычисляем результат как целое с точностью до сотых T100=(100*T16)/16, корректируем на ±5 в зависимости от знака и делим нацело на 10. Полученное D будет значением с округлением до десятых и масштабированное до целых.

Для вывода числа на индикатор и отображения точки используется функция display , определённая в файле dynamic_display.cpp проекта.

// Функция выводит на индикатор число n/(10**p). // Иначе говоря, выводит целое число n (возможно, // с необходимым числом ведущих нулей) и отображает // точку после разряда p (p=0 соответствует точке // после младшего разряда — в конце числа; если p=1, // отображаемое число имеет один разряд после точки // и т.д.). // Если p

Исходный код всего проекта

Источник: www.rotr.info

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru