«V6» — измеритель RMS-значений напряжения, тока, активной и полной мощности (Atmega 8)
Весьма часто возникает необходимость знать величину потребляемой (активной) мощности различными нагрузками. Для решения данной задачи и благодаря комментариям и пожеланиям, высказанные к моей предыдущей статье «Вольтметр-амперметр переменного тока с вычислением мощности на PIC16F690» и был построен новый прибор «V6», измеряющий действующие (RMS) значения этих величин на частоте 50 Гц.
Камрад, рассмотри датагорские рекомендации
? Полезные и проверенные железяки, можно брать
Опробовано в лаборатории редакции или читателями.
↑ Как посчитать правильно?
Действующее значение тока и напряжения можно определить по формуле:
Вычисляется активная мощность через интеграл по одному периоду от мощности мгновенной:
Для цепей несинусоидального тока электрическая мощность равна сумме соответствующих средних мощностей отдельных гармоник.
Активная мощность характеризует скорость необратимого превращения электрической энергии в другие виды энергии (тепловую и электромагнитную). Активная мощность может быть также выражена через силу тока, напряжение и активную составляющую сопротивления цепи.
Полная мощность — величина, равная произведению действующих значений периодического электрического тока I в цепи и напряжения U на её зажимах:
Полная мощность имеет практическое значение, как величина, описывающая нагрузки, фактически налагаемые потребителем на элементы подводящей электросети (провода, кабели, распределительные щиты, трансформаторы, линии электропередачи), так как эти нагрузки зависят от потребляемого тока, а не от фактически использованной потребителем энергии. Именно поэтому номинальная мощность трансформаторов и распределительных щитов измеряется в Вольт-Амперах, а не в Ваттах.
На практике измеряемый период делим на некоторое число частей и измеряем напряжение и ток на каждом участке. Таким образом, мы получаем функции U (t) и I (t). Точнее, не сами функции, а таблицу их значений – поэтому от интегрирования переходим к суммированию:
Здесь N – количество отсчётов, приходящееся на один период сетевого напряжения.
↑ Технические характеристики измерителя RMS-значений
1. Пределы изменения измеряемого напряжения 50 – 255 Вольт, дискретность 0,5 Вольта. Показания отображаются с шагом 1 Вольт.
2. Пределы изменения измеряемого тока:
1-й диапазон 0 – 1 Ампер, дискретность — 2 mA
2-й диапазон 1 – 20 Ампер, дискретность — 0,04 A
3. Активная мощность отображается в Ваттах.
4. Полная мощность отображается в Вольт-Амперах.
↑ Принципиальная схема
Исключён фрагмент. Полный вариант доступен меценатам и полноправным членам сообщества.
Измеряемое напряжение через входной делитель поступает на вход микроконтроллера ADC4 (PC4) через конденсатор.
Измеряемый ток протекает по шунту, напряжение, снимаемое с него, усиливается инвертирующими операционными усилителями и поступает на вход микроконтроллера ADC1 (PC1) на первом диапазоне и на вход ADC2 (PC2) на втором диапазоне. В связи с тем, что размах напряжения, снимаемого с ОУ должен быть около 5 Вольт, на микросхему усилителя поступает повышенное питание (9-15 Вольт).
На вход ADC3 (PC3) поступает постоянное стабилизированное смещение около 2,5 Вольта. Это напряжение позволяет правильно измерять положительный и отрицательный полупериоды входных напряжений.
На вход ADC5 (PC5) поступает сетевое напряжение, ограниченное до 5-и Вольт, для синхронизации измерений с сетью (аппаратный детектор перехода через ноль).
Конденсаторы, подключённые параллельно входам ADC, служат для уменьшения помех при работе АЦП микроконтроллера.
К микроконтроллеру подключён LCD дисплей, с отображением 2-х строк по 16 символов.
Питание прибора осуществляется от встроенного бестрансформаторного источника на микросхеме LNK306DN c выходным напряжением 10 Вольт. Стабилизатор питания +5 Вольт собран на микросхеме 78L05 и особенностей не имеет.
Внимание! Устройство имеет гальваническую связь с питающей сетью. При настройке необходимо все подключения необходимо производить только при отключённом сетевом питании устройства.
Обязательно проверить по даташиту правильность подсоединения питания (ножки 1 и 2) своего ЖК индикатора.
↑ Программа
Программа написана на языке Bascom-AVR и снабжена комментариями. Применён микроконтроллер Atmega8 с довольно большим объёмом памяти, что позволило применить LCD дисплей и подготовить программу на языке высокого уровня без особых ухищрений. Тактовая частота внутреннего генератора выбрана 8 МГц, опорное напряжение выбрано равным напряжению питания микроконтроллера с целью уменьшения помех и повышения точности измерения.
Измерения производятся, согласно рисунку в течение одного периода входного напряжения
Предварительно измеряется 2,5V, получаемое от микросхемы TL431, и затем измеряется входное напряжение. В случае положительной полуволны измеряемое напряжение будет равно разности Uвх – U2,5V, а в случае отрицательной полуволны будет равно U2,5-Uвх. За один период делается около 175-190 измерений. Затем производится обработка результатов измерений и вычисление значений. Следует учесть, что напряжение с токового шунта усиливается инвертирующим усилителем и, поэтому при вычислениях его необходимо инвертировать повторно. Это хорошо видно на приведённой диаграмме.
Переключение диапазонов тока и форматирование результатов производиться автоматически, практически незаметно для пользователя.
Описание программы очень подробно отображено в комментариях к ней.
Программируется микросхема в режиме последовательного программирования через SPI, используя разъем дисплея. Дисплей вынимается на время программирования и подключается программатор. При этом прибор должен быть отключён от сети! Конфигурационные биты указаны на снимке
↑ Детали и конструкция
Все детали установлены на печатной плате. Дисплей вставляется в разъёмы платы. Шунт выполнен в виде четырёх параллельно соединённых резисторов мощностью 0,1 Ом 2 Вт. В связи с тем, что различные типы дисплеев могут иметь различное подключение питания (ножки 1 и 2), то на печатной плате питание на ножки дисплея 1 и 2 поступает через перемычки. Их надо коммутировать правильно, в соответствии с применяемым дисплеем.
Вид собранного прибора без кожуха
Вид на плату со стороны печати
Исключён фрагмент. Полный вариант доступен меценатам и полноправным членам сообщества.
Плата установлена в корпусе из пластмассы.
↑ Настройка
Необходимо проверить монтаж платы и включить устройство.
1. Вращая резистор (первый слева), выставляем оптимальную контрастность дисплея. На индикаторе должны отобразиться нулевые значения, тока и мощности.
2. Вращая резистор (четвёртый слева), устанавливаем показания напряжения на дисплее равными напряжению, поданному на вход.
3. Для настройки правильных показаний амперметра на 1-ом диапазоне подключаем нагрузку (лампу накаливания на 60 – 100 Ватт) через контрольный амперметр. При этом на индикаторе должны отобразиться значения тока. Вращая резистор (третий слева), устанавливаем показания тока на дисплее равными току на контрольном амперметре.
4. Для настройки правильных показаний амперметра на 2-ом диапазоне подключаем нагрузку (лампу накаливания или какую-либо другую нагрузку мощностью более 200 Вт) через контрольный амперметр. При этом на индикаторе должны отобразиться значения тока. Вращая резистор (второй слева), устанавливаем показания тока на дисплее равными току на контрольном амперметре.
Исключён фрагмент. Полный вариант доступен меценатам и полноправным членам сообщества.
↑ Итоги:
Познакомился с микроконтроллерами AVR и научился программировать на языке Bascom-AVR.
Получился прибор, легко меряющий мощности, потребляемые различной бытовой техникой как активную, так и полную.
↑ При разработке использованы следующие материалы:
↑ Файлы:
Схема, печатная плата и программа с прошивкой:
21-03-2014 Обновлена схема ?RMS-V6.7z 57.53 Kb ⇣ 274
Наш файловый сервис предназначен для полноправных участников сообщества «Datagor Electronics».
Для получения файла зарегистрируйтесь и войдите на сайт с паролем.
Иван Внуковский,
г. Днепропетровск, Украина
ATtiny85: прототип беспроводного сенсора
Обычно, для перехода от идеи к реализации, необходим прототип устройства, удобный для проверки и отладки на месте, что особенно важно для мобильного устройства. Далее постараюсь максимально подробно разобрать процесс создания прототипа беспроводного сенсора на базе ATtiny85.
Цель — создать сенсор работающий, условно говоря, в коробке с искусственным освещением и передающий температуру и статус освещения с немедленной реакцией на изменение освещения: включилось, отключилось, мигнуло. Сенсор решено было сделать мобильным и питать от элемента CR2032, иначе говоря, при разряде до 2.7V (предел для датчика TMP36), можно рассчитывать на 200mAh.
Микроконтроллер ATtiny85 имеет всего 5 портов ввода/вывода и возможность отключить RESET в пользу дополнительного порта. Данный бюджет был распределён следующим образом:
- 3 порта — радиомодуль NRF24L01+, спецификация требует пять портов, но в данном случае это не приемлемо и будет использована 3-х пиновая конфигурация;
- 1 порт — датчик освещения на базе фототранзистора BPW17N;
- 2 порта — температурный датчик на базе TMP36, второй порт нужен для подачи питания, чтобы иметь возможность отключать датчик при необходимости.
Элементная база определена, можно приступать к проектированию.
RESET в качестве порта ввода/вывода
Ситуация требует использовать RESET в качестве порта ввода/вывода. Однако RESET необходим для программирования микроконтроллера, в том числе и для прошивки настроек (Fuses). Соответственно, после отключения RESET программировать устройство будет не возможно. Таким образом, чтобы не получить «кирпич» первым делом необходимо раздобыть высоковольтный (12V) программатор, для сброса Fuse-ов.
Подходящую схему и скетч для Arduino можно найти по ссылке ATTiny Fuse Reset with 12 Volt Charge Pump. Данная схема не требует дополнительного питания, и для получения 12V используется умножитель (Charge Pump).
Описание работы схемы высоковольтного программатора
Схема находится на сайте по ссылке, расположенной выше.
На вывод (4) подается питание 5V, при этом на выводе (2) устанавливается низкий уровень (земля), конденсатор C1 заряжается до 5V. Затем на (2) подается 5V и на обкладках С1 образуются 10V, они через диод D2, при низком уровне на (3), попадают на C2. Затем уровни на (3) и (2) меняются местами и уже 15V поступают на C3, следующая итерация сливает 20V (на самом деле ~17V) на C4. Затем напряжение через делитель R2/R3 попадает на аналоговый вход A0, где сравнивается с опорным значением, и если напряжение выше 12V, то раскачка (смена уровней на (2) и (3)) выключается. Транзистор Q1 нужен для быстрой разрядки конденсаторов и формирования необходимого импульса для программатора. Резисторы на выводах ATtiny85 необходимы для защиты микроконтроллера при неправильном подключении.
У меня получился следующий дизайн:
Высоковольтный программатор в сборе (фото и комментарии)
Неудобно, что USB разъём накрывает контактные пины, но дизайн платы таков, что здесь к микроконтроллеру сложно даже штатное питание подвести. Проще говоря, было место на плате, появилась идея добавить пины для подключения обычного программатора в следующей конфигурации:
Позже стало очевидно, что использовать схему в таком виде целесообразно только в самом крайнем случае.
Принципиальная схема
Разработка схемы вместе с макетной платой будет производится в KiCad EDA, это свободный пакет для автоматизации разработки электронных схем.
Исходные файлы KiCad проекта размещены на GitHub, в папке attwlight_sensor.pcb.
Существует множество хороших инструкций по работе с данным редактором, например стоит посмотреть ролик на YouTube KiCAD Quick-Start Tutorial.
Если совсем кратко, то для начала работы над схемой в KiCad достаточно запустить редактор схемы и справа пройтись по иконкам, чтобы добавить: элементы
, питание
, провода
и метки
.
Метки нужны, чтобы не загромождать схему проводами. При разводке печатной платы все одинаковые метки необходимо будет соединить дорожками.
Если в доступных библиотеках нет необходимого элемента его можно создать с нуля либо изменить один из существующих. В последнем случае достаточно поставить курсор на нужный элемент и нажать Ctrl+E (редактировать элемент в редакторе) и в новом окне: создать элемент из текущего , отредактировать его внешний вид и свойства
, затем сохранить в новую библиотеку
.
Чтобы созданную библиотеку можно было использовать, её необходимо подключить в , воспользовавшись кнопкой Add в верхней части. Для добавления в библиотеку других элементов, её нужно выбрать и после добавления сохранить
.
По сути, устройство состоит из трех независимых частей. Проверив их по отдельности вполне можно ожидать, что в сборе всё заработает как было задумано. Прототип как раз и нужен, чтобы проверить насколько данное ожидание соответствует действительности. Ниже, в комментариях к схеме, я остановлюсь на том, что пошло не так.
Датчик температуры TMP36
В рабочем режиме питание на датчик подается с порта PB5 микроконтроллера. В отладочной конфигурации подать питание на датчик можно установив перемычку между выводами VCC и D10 контактной площадки P2. Сигнал RESET активен при низком уровне и высокий уровень не мешает работе микроконтроллера и даже увеличивает стабильность по сравнению с висящим в воздухе RESET. Для съема данных с датчика используется аналоговый порт ADC2.
Засада ждала в виде ёмкости C1, которая была подключена непосредственно к RESET микроконтроллера. Понятно, что емкость на RESET снижает время активности сигнала или даже блокирует его, мешая программированию устройства, поэтому до сборки был проведен эксперимент, но как оказалось не достаточно чистый. Дело в том, что если на работающую схему добавить данный конденсатор, то процесс проходит успешно ровно один раз, если включить схему с ним — то не разу. Пока дело дошло до конденсатора, была впаяна перемычка между D1 и SCK, очевидно, что эта часть схемы не мешает установке на SCK низкого уровня, но подозреваемых больше не было. После установки перемычки между C1 и землей, программировать устройство стало можно, но только с питанием 5V. Однако в инструкции указано 1.8-5.5V и эксперименты с этим микроконтроллером и питанием 3V были успешными. Оказалось что на питании TMP36 есть встроенная ёмкость 10nF и этого достаточно, чтобы блокировать процесс при низком напряжении. В результате перемычка встала на своё место.
Радиомодуль NRF24L01+
Нумерация выводов у штыревых разъёмов в KiCad не соответствует принятой для данного модуля, поэтому нумерация здесь зеркальная относительно указанной в инструкции для NRF24L01+. Для работы радиомодуля в 3х пиновой конфигурации необходимо поменять местами выводы MISO и MOSI, и добавить часть схемы справа, состоящую из элементов: R3, D1 и C2. Схема данной конфигураци взята отсюда nRF24L01+ with ATtiny85 3 Pins.
Работа данной схема возможна по тому, что на CE (chip enable) можно сразу установить высокий уровень, а CSN (chip select) нужен только для передачи модулю сигнала о начале новой команды, что достигается сбросом высокого уровня в низкий. Соответственно, CSN можно объединить с одним из оставшихся выводов, в данном случае SCK (clock). Штатные импульсы SCK не успевают зарядить конденсатор C2 и на CSN остается низкий уровень, перед началом передачи команды нужно установить на SCK высокий уровень на более длительное время, чтобы получить высокий уровень на CSN. С чем связанна необходимость поменять местами MISO и MOSI я не понял, видимо так было удобно.
Для стабильной работы модуля желательно наличие ёмкости 1-10μF на выводах питания модуля, для этих целей на схеме предусмотрен конденсатор C4.
После устранения проблем с датчиком температуры, первым делом была проверена возможность прошить ATtiny85 с установленным радиомодулем. Процесс не пошёл, avrdude ругнулся на не верную сигнатуру, но я проявил упорство и запустил прошивку… и в итоге тоже ошибка. В результате эксперимента радиомодуль не пострадал, правда и шансов что-то в него прошить не было из-за модификаций приведенных выше. А вот микроконтроллер пришлось реанимировать с помощью высоковольтного программатора. Идея оказалась неудачной, но попробовать стоило.
Микроконтроллер ATtiny85
В процессе тестирования микроконтроллер успешно работал от внутреннего осциллятора без делителя на частоте 8MHz, однако с непосредственно подключенным радиомодулем его работа стала не стабильной. В том смысле, что если соединить радиомодуль с разъёмом проводами длиной 30cm, всё работает нормально, а с модулем, установленным в разъем, что-либо отправить невозможно. Все попытки стабилизировать систему дополнительными конденсаторами, ровно как и сделать её нестабильной, разместив вынесенный радиомодуль вплотную к микроконтроллеру, ни к чему не привели. В результате пришлось включить делитель, что вернуло микроконтроллер к заводским настройкам. Работа ATtiny85 на частоте 1MHz каких либо сложностей не вызвала, но заметно сократила ожидаемое время работы от батареи. Подробнее об этом в конце статьи.
Датчик освещения на BPW17N
С этим датчиком всё относительно просто, на него попадает только искусственное освещение, а регистрировать он должен только моменты его включения и отключения. Для максимально быстрой реакции на событие датчик генерирует прерывание на порту PCINT3 (Pin Change Interrupt).
Элементы R1 и C3 подстроечные, для них предусмотрено место на плате, но они не распаяны. С3 параллельно с фототранзистором Q1 может понадобиться, чтобы исключить дребезг сигнала. Проблема в том, что стандартный для таких случаев конденсатор 0.1μF, разряжается через резистор 10MΩ около секунды, что не приемлемо для регистрации события — свет мигнул. Соответственно, можно установить конденсатор меньшей ёмкости и/или уменьшить сопротивление резистора R2, но сначала нужно проверить, есть ли в этом смысл.
Поскольку здесь используется прерывание может показаться, что в схеме нужен компаратор, но эти мысли сразу отпадают, как только становится понятно сколько эта штука потребляет. Правда и потребление микроконтроллера, при уровне сигнала вблизи центра рабочего диапазона, подскакивает с 7μA примерно до 260μA в режиме сна. Однако данную проблему проще решить программно, а именно, после срабатывания прерывания (PCINT3 или WDT), можно проверить напряжение на аналоговом входе ADC3, если оно неприемлемо, сообщить о проблеме и пропустить активацию PCINT3. В любом случае это нештатная ситуация, которая требует внимания.
Макетная плата
Готовую схему необходимо развести на плате. Это также можно сделать в KiCad. Для этого сначала требуется завершить работу со схемой:
- Промаркировать элементы на схеме
, все настройки можно оставить по умолчанию.
- Сопоставить элементы схемы с посадочными местами на плате
. Появится следующее окно:
Здесь для наглядности следует включить просмотр выбранного элемента.
Поскольку предстоит работать с макетной платой необходимо следить, чтобы все посадочные места укладывались в сетку 1/10″ (2.54mm). Если требуемый элемент найти не удаётся, проще назначить похожий, затем, в редакторе печатной платы, изменить его (Ctrl+E) и записать в свою библиотеку.
Как только все посадочные места будут назначены нужно сохранить результат и вернуться в редактор схемы. - Сгенерировать netlist
, настройки по умолчанию вполне приемлемы.
Редактор схемы можно закрыть и перейти к редактору печатной платы . Здесь нажать
, затем загрузить ранее созданный netlist , убедиться, что не было ошибок и закрыть окно. Если схема была изменена или элементам были назначены другие посадочные места, необходимо обновить netlist и загрузить его здесь снова.
При первой загрузке netlist все посадочные места окажутся стопкой в одной куче. Сначала необходимо переключить сетку на 1/10″ (2.54mm), растащить элементы (используя кнопку m), убрать лишние надписи и, пользуясь подсказками редактора, соединить элементы дорожками.
Результат будет выглядеть примерно так:
Оставшиеся белые линии, указывают какие посадочные места необходимо соединить проводами. К сожалению в KiCad нет способа вывести результат, так чтобы его было удобно воспроизвести на макетной плате. Но в данном случае макетная плата получилась не большой, и с ней можно поступить просто: отключить лишние надписи , снять скриншот и зеркально отразить его по вертикали.
Тут два несоответствия со схемой выше: не распаяны подстроечные элементы R1,C3 и диод короче на одно отверстие.
Плата немного побита жёсткой отладкой, но на то она и макетная. Большинство других вариантов, после всех модификаций, выглядело бы хуже.
Подготовка IDE и программатора
Arduino IDE наиболее доступное кроссплатформенное программное обеспечение для программирования AVR микроконтроллеров и вполне удобное для небольших проектов.
Не смотря на то, что Arduino IDE официально не поддерживает ATtiny85, существует штатный способ добавить данный микроконтроллер в список поддерживаемых. Как это сделать, подробно описано по ссылке Programming an ATtiny w/ Arduino 1.6. Однако данный способ годится только для непосредственного программирования ATtiny85 и не позволяет использовать Arduino код. С одной стороны это хорошо, код чище будет, но задача в данном случае — разработать скетч демонстрирующий способность устройства функционировать как было задумано, и такая оптимизация здесь преждевременна.
Значит нужно Arduino ядро для ATtiny85, его можно взять здесь arduino-tiny, версия для Arduino 1.5 подойдет. Доступны будут не все функции, но базовый набор, включая программный последовательный порт, для отладки, можно будет использовать. К сожалению в актуальной, на данный момент, Arduino IDE v1.6.6 что-то сломали, и c этим ядром необходимо использовать версию 1.6.5.
Подключение ядра к Arduino IDE v1.6.5
- В директории с Arduino скетчами нужно создать папку hardware;
- Скачать в эту папку файл arduino-tiny-0150-0020.zip и распаковать его;
- Скопировать файл в boards.txt в той же директории, и зачистить последний от ненужных конфигураций, в данном случае всё что не ATtiny85, чтобы не мешались;
- Теперь если запустить или перезапустить Arudino IDE в меню в самом низу списка появятся добавленные конфигурации.
Осталось приготовить программатор. Для этого совсем не обязательно использовать специальное устройство, можно в ближайшую Arduino прошить скетч из , затем соединить с сенсором, как указанно на принципиальной схеме, и не забыть про конденсатор 10μF, который необходимо установить между RST и GND выводами Arduino.
Программировать устройство нужно без установленных радиомодуля и перемычек. В меню нужно выбрать конфигурацию микроконтроллера, в данном случае: , в меню выбрать USB порт программатора и в меню указать тип программатора, в данном случае: , затем воспользоваться кнопкой Upload.
Программное обеспечение
Тестовый скетч для работы с сенсором получился довольно интересным, в нем можно найти широкий набор примеров, а именно:
- Работа с радиомодулем NRF24L01+;
- Измерение напряжения питания средствами микроконтроллера;
- Перевод микроконтроллера в режим сна;
- Пробуждение по таймеру WDT (WatchDog Timer);
- Пробуждение по Pin Change прерыванию PCINT;
- Работа с аналоговыми и цифровыми портами.
Чтобы скомпилировать скетч необходимы сторонние библиотеки:
Это оптимизированные версии библиотек для работы с радиомодулем NRF24L01+, которые подходят для микроконтроллера ATtiny85. Их необходимо скачать и распаковать в папку libraries в директории со скетчами Arduino.
Ниже будут приведены наиболее интересные куски кода с подробными комментариями. Полностью скетч можно найти на GitHub в папке attwlight_sensor, также в папке attwlight_rx находится скетч для приёмника.
Перевод микроконтроллера в режим сна
Функция void sleep() вызывается в главном цикле void loop() и останавливает его до тех пор пока не сработает одно из прерываний PCINIT3 либо WDT. Затем выполняется полезная работа и снова вызывается данная функция.
Функция содержит обращения к регистрам микроконтроллера, и что при этом происходит не всегда очевидно. Чтобы с этим разобраться нужно заглянуть в документацию на микроконтроллер ATtiny85 datasheet раздел «23. Register Summary», в нем приведен полный список регистров с прямой ссылкой на страницу с подробным описанием для чего предназначен тот или иной бит.
#include #include #include #define WDTO_INFINITE 255 #define SLEEP_PERIOD WDTO_8S #define SKIP_WDT_WAKEUPS 14 // x SLEEP_PERIOD + SLEEP_PERIOD (14 ~= 120 sec) // Вектор (функция) прерывания PCINT, у ATtiny85 один порт (B), следовательно только один вектор PCINT ISR(PCINT0_vect) < light_pcint = true; >// Вектор прерывания таймера WDT ISR(WDT_vect) < wakeup_counter++; >void sleep() < GIMSK = _BV(PCIE); // Включить Pin Change прерывания if (SLEEP_PERIOD == WDTO_INFINITE || payload.light != LIGHT_FUZZY) PCMSK |= _BV(LIGHT_PIN); // PCINT3; включить если нет проблем с освещением или иначе нет шансов проснуться ADCSRA &= ~_BV(ADEN); // отключить ADC; уменьшает энергопотребление if (SLEEP_PERIOD != WDTO_INFINITE) < wdt_enable(SLEEP_PERIOD); // установить таймер WDTCR |= _BV(WDIE); // включить прерывания от таймера; фикс для ATtiny85 >// Установить режим сна Power-down; MCUSR &= ~_BV(SM0); MCUSR |= _BV(SM1); set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); // разрешить режим сна; MCUSR |= _BV(SE); sei(); // включить прерывания sleep_cpu(); // заснуть cli(); // отключить прерывания; для безопасного отключения PCINT3 PCMSK &= ~_BV(LIGHT_PIN); // PCINT3; отключить sleep_disable(); // запретить режим сна; MCUSR &= ~_BV(SE); ADCSRA |= _BV(ADEN); // включить ADC sei(); // включить прерывания; иначе таймеры не будут работать >
Опрос датчика освещения
Особенности работы с данным датчиком уже были разобраны в разделе «Принципиальная схема». Отмечу только, что параметры здесь выбраны так, чтобы потребление микроконтроллера в режиме сна не превышало 20μA.
#define FUZZY_VOLTAGE 0.4 // ширина зоны неприемлемого напряжения в середине, включая концы const unsigned int fuzzy_start = int(1024.0 * (1.0 - FUZZY_VOLTAGE) / 2.0); const unsigned int fuzzy_stop = 1023 - fuzzy_start; light_t readLightStatus(unsigned int ms) < delay(ms); // если нужно время на стабилизацию int input = analogRead(LIGHT_PIN); // ADC3 // оценка надёжности результата if (input >= fuzzy_start && input fuzzy_stop) return LIGHT_ON; // заглушка; на случай ошибки в коде return LIGHT_UNKNOWN; >
Измерение напряжения питания
Если не использовать вывод AREF микроконтроллера, который предназначен для задания опорного напряжения, все измерения на аналоговом порту будут привязаны к напряжению питания. Батарея не стабилизированный источник, следовательно для корректного считывания показаний датчика температуры, необходим способ измерять напряжение питания в процессе работы. Напряжение питания также является хорошим индикатором состояния батареи.
Ниже приведен код, который позволяет произвести требуемые измерения полагаясь на стабильность внутреннего опорного напряжения Vbg (Bandgap reference voltage) равного 1.1V. Согласно спецификации Vbg может находится в пределах 1.0-1.2V, поэтому необходимо убедиться в точности измерений и при необходимости скорректировать константу 1125300.
long readVcc() < // Установка референса в Vcc и измерение внутреннего Vbg == 1.1V ADMUX = _BV(MUX3) | _BV(MUX2); delay(2); // требуется минимум 1ms для стабилизации ADCSRA |= _BV(ADSC); // ADC Start conversion - начать опрос while (bit_is_set(ADCSRA, ADSC)); // ADSC == 1 пока опрос активен // Чтение ADCL блокирует ADCH, чтение ADCH разблокирует оба регистра =>ADCL читать первым uint8_t low = ADCL; // младший регистр данных ADC uint8_t high = ADCH; // старший регистр данных ADC long result = (high
Измерение температуры
Получив напряжение питания, легко вычислить точное напряжение на аналоговом входе микроконтроллера и перевести его в температуру.
float readTmp36(long vcc) < int input = analogRead(TMP36_PIN); // напряжение относительно refVolts float refVolts = float(vcc) / 1000.0; float voltage = float(input) * refVolts / 1024.0; // фактическое напряжение float temperatureC = (voltage - 0.5) * 100.0; // смещение 500mV & 10mV == 1°C return temperatureC; >
Остальная часть скетча нужна, чтобы собрать данные вместе и отправить их наиболее оптимальным образом, с точки зрения энергопотребления. Ничего интересного там нет, обычная логика, надеюсь, комментариев в коде достаточно, чтобы разобраться как всё работает.
Если скомпилировать данный код, можно увидеть, что он свободно умещается в 8KiB и даже есть куда развернуться:
Sketch uses 7,120 bytes (86%) of program storage space. Maximum is 8,192 bytes. Global variables use 227 bytes (44%) of dynamic memory, leaving 285 bytes for local variables. Maximum is 512 bytes.
Осталось проверить, что всё работает как было задумано, и можно прошивать Fuses переключающие RESET в порт ввода/вывода. Готовой конфигурации в Arduino IDE для этого нет, поэтому нужно воспользоваться командной строкой:
avrdude -c stk500v1 -p attiny85 -P /dev/ttyUSB0 -v -C /etc/avrdude.conf -b 19200 -U lfuse:w:0x62:m -U hfuse:w:0x5f:m -U efuse:w:0xff:m
- avrdude — команда из пакета Arduino IDE находящаяся в папке hardware/tools/avr/bin/;
- /dev/ttyUSB0 — устройство программатора;
- /etc/avrdude.conf — ссылка на файл находящийся в директории с Arduino IDE по адресу hardware/tools/avr/etc/avrdude.conf.
Что из себя представляют данные Fuses, можно посмотреть в калькуляторе Engbedded Atmel AVR® Fuse Calculator.
Если кратко, то эти настройки предписывают: использовать встроенный осциллятор с делителем частоты на 8 (итоговая частота 1MHz); использовать стандартные задержки при старте, необходимые для стабилизации осциллятора; отключить BOD (Brown-out Detector) — перезагрузка при низком напряжении питания для защиты памяти от записи мусора; включить возможность считывать прошивку; и собственно отключить RESET.
Энергопотребление
Пришло время измерить энергопотребление получившегося устройства. Вводные следующие: данные передается каждые 120sec, если пришло прерывание от датчика освещения, то немедленно; событиями от датчика освещения можно пренебречь; ёмкость батареи 200mAh.
Частота | 1MHz | 8MHz |
---|---|---|
Радиомодуль (потребление) | 15.7mA | 18.3mA |
Микроконтроллер (потребление) | 1.1mA | 4.3mA |
Режим сна (потребление) | 7μA | 7μA |
Радиомодуль (активен за цикл) | 51ms | 16ms |
Микроконтроллер (активен за цикл) | 15ms | 4ms |
Оценка времени работы (дней) | 513 | 739 |
Как видно, тут было ради чего бороться. К сожалению, стабильности на 8MHz добиться не удалось. Видимо без внешнего осциллятора, это не реально.
Все исходные коды проекта доступны на GitHub
Аналоговые пины
В прошлом уроке мы разобрали измерение и вывод цифрового сигнала, а в этом разберём аналоговый сигнал. Зачем нужно читать аналоговый сигнал? Микроконтроллер может выступать в роли вольтметра, измерять собственное напряжение питания, например от аккумулятора, может измерять ток через шунт (если вы знаете закон Ома), можно измерять сопротивление, а также работать с потенциометрами (крутильными, линейными, джойстиками), которые являются очень удобными органами управления.
В уроке про возможности микроконтроллера мы обсуждали аналоговые входы, т.е. входы, подключенные к АЦП – аналогово-цифровому преобразователю (ADC). Взглянем на распиновку популярных плат (Arduino Nano и Wemos Mini):
Пины, на которых выведен ADC, могут измерять аналоговый сигнал. На плате Nano это пины, маркированные буквой А (A0–A7), а у esp8266 такой пин всего один – A0.
Чтение сигнала
“Аналоговые” пины могут принимать напряжение от 0V (GND) до опорного напряжения и преобразовывать его в цифровое значение, просто в какие-то условные единицы. АЦП на AVR и esp8266 имеет разрядность в 10 бит, т.е. мы получаем измеренное напряжение в виде числа от 0 до 1023 .
Функция, которая оцифровывает напряжение, называется analogRead(pin) . Она принимает в качестве аргумента номер аналогового пина и возвращает оцифрованное напряжение. Сам пин должен быть сконфигурирован как INPUT (вход). Нумерация:
- Arduino Nano:
- Просто номером А-пина: A0 – 0
- Как на плате: A0 – A0
- Порядковым номером GPIO: А0 – 14 , A1 – 15 .. А7 – 21
- Просто номером А-пина: A0 – 0
- Как на плате: A0 – A0
Пример, опрашивающий пин А0:
int value1 = analogRead(0); // считать напряжение с пина A0 int value2 = analogRead(A0); // считать напряжение с пина A0 int value3 = analogRead(14); // считать напряжение с пина A0
Хранить полученное значение разумно в переменной типа int , потому что значение варьируется от 0 до 1023.
Нельзя подавать на аналоговый пин напряжение выше напряжения питания МК. Через ограничивающий резистор (~10k) – можно, но всё равно не рекомендуется этого допускать.
Потенциометры
Аналоговые пины очень часто используются при работе с потенциометрами (переменный резистор). При помощи полученного значения можно влиять на ход работы программы, менять какие-то настройки и тому подобное. У потенциометра всегда три ноги: две крайние и одна центральная. Всё вместе это представляет собой делитель напряжения, который и позволяет менять напряжение в диапазоне 0-VCC:
К Arduino потенциометр подключается следующим образом: средний вывод на любой A-пин, крайние – на GND и питание. От порядка подключения GND и питания зависит направление изменения значения. Что касается сопротивления, то читай заметку по делителям напряжения ниже в этом уроке. Чаще всего для МК ставят потенциометры с сопротивлением 10 кОм, но диапазон в принципе очень широк: от 1 кОм до 100 кОм. Чем больше, тем более шумным будет приходить сигнал, а если брать меньше – пойдут потери тока в нагрев потенциометра, а это никому не нужно.
Опорное напряжение (для AVR Arduino)
Опорное напряжение играет главную роль в измерении аналогового сигнала, потому что именно от него зависит максимальное измеряемое напряжение и вообще возможность и точность перевода полученного значения 0-1023 в Вольты. Изучим функцию analogReference(mode) , где mode:
- DEFAULT : опорное напряжение равно напряжению питания МК. Активно по умолчанию
- INTERNAL : встроенный источник опорного на 1.1V (для ATmega168 или ATmega328P) и 2.56V (на ATmega8)
- INTERNAL1V1 : встроенный источник опорного на 1.1V (только для Arduino Mega)
- INTERNAL2V56 : встроенный источник опорного на 2.56V (только для Arduino Mega)
- EXTERNAL : опорным будет считаться напряжение, поданное на пин AREF
После изменения источника опорного напряжения (вызова analogReference() ) первые несколько измерений могут быть нестабильными. Значение 1023 функции analogRead() будет соответствовать выбранному опорному напряжению или напряжению выше его.
В режиме DEFAULT мы можем оцифровать напряжение от 0 до напряжения питания VCC. Если напряжение питания 4.5 Вольта, и мы подаём 4.5 Вольт – получим оцифрованное значение 1023. Если подаём 5 Вольт – опять же получим 1023, т.к. выше опорного. Это правило работает и дальше, главное не превышать 5.5 Вольт. Как измерять более высокое напряжение, читайте ниже.
Что касается точности: при питании от 5V и режиме DEFAULT мы получим точность измерения напряжения (5 / 1024) ~4.9 милливольт. Поставив INTERNAL мы можем измерять напряжение от 0V до 1.1V с точностью (1.1 / 1024) ~0.98 милливольт. Весьма неплохо, особенно если баловаться с делителем напряжения.
Что касается внешнего источника опорного напряжения: нельзя подавать напряжение меньше 0V (отрицательное) или выше 5.5V в качестве внешнего опорного в пин AREF. Также при подключении внешнего опорного напряжения нужно вызвать analogReference(EXTERNAL) до первого вызова функции analogRead() (начиная с запуска программы), иначе можно повредить микроконтроллер!
Чтобы “на лету” переключаться между внутренними и внешним опорными, можно подключить его на AREF через резистор на ~5 кОм. Вход AREF имеет собственное сопротивление в 32 кОм, поэтому реальное опорное будет вычисляться по формуле REF = V * 32 / (R + 32), где R – сопротивление резистора (кОм), через которое подключено опорное напряжение V (Вольт). Например для 2.5V получим 2.5 * 32 / (32 + 5) = ~2.2V реальное опорное.
Измерение напряжения
0-5 Вольт
Простой пример, как измерить напряжение на аналоговом пине и перевести его в Вольты. Плата питается от 5V.
float voltage = (float)(analogRead(0) * 5.0) / 1024;
Таким образом переменная voltage получает значение в Вольтах, от 0 до 5. Чуть позже мы поговорим о более точных измерениях при помощи некоторых хаков. Почему мы делим на 1024, а не на 1023 , ведь максимальное значение измерения с АЦП составляет 1023? Ответ можно найти в даташите:
АЦП при преобразовании отнимает один бит, т.е. 5.0 Вольт он в принципе может измерить только как 4.995, что и получится по формуле выше: 1023 * 5 / 1024 == 4.995.. . Таким образом делить нужно на 1024.Сильно больше 5 Вольт
Для измерения постоянного напряжения больше 5 Вольт нужно использовать делитель напряжения на резисторах (Википедия). Схема подключения, при которой плата питается от 12V в пин Vin и может измерять напряжение источника (например, аккумулятора):
Код для перевода значения с analogRead() в Вольты с учётом делителя напряжения:// GND -- [ R2 ] -- A0 -- [ R1 ] -- VIN #define VREF 5.1 // точное напряжение на пине 5V (в данном случае зависит от стабилизатора на плате Arduino) #define DIV_R1 10000 // точное значение 10 кОм резистора #define DIV_R2 4700 // точное значение 4.7 кОм резистора void setup() < float voltage = (float)analogRead(0) * VREF * ((DIV_R1 + DIV_R2) / DIV_R2) / 1024; >void loop() <>
Как выбрать/рассчитать делитель напряжения?
- Согласно даташиту на ATmega, сумма R1 + R2 не рекомендуется больше 10 кОм для достижения наибольшей точности измерения. В то же время через делитель на 10 кОм будет течь ощутимый ток, что критично для автономных устройств (читай ниже). Если девайс работает от сети или от аккумулятора, но МК не используется в режиме сна – ставим делитель 10 кОм и не задумываемся. Также рекомендуется поставить конденсатор между GND и аналоговым пином для уменьшения помех.
- Если девайс работает от аккумулятора и микроконтроллер “спит”: пусть аккумулятор 12V, тогда через 10 кОм делитель пойдёт ток 1.2 мА. Сам микроконтроллер в режиме сна потребляет ~1 мкА, что в тысячу раз меньше! На самом деле можно взять делитель с гораздо бОльшим суммарным сопротивлением (но не больше 20 МОм, внутреннего сопротивления самого АЦП), но обязательно поставить конденсатор на ~0.1 мкФ между аналоговым пином и GND (вот здесь проводили эксперимент). Таким образом например при при R1+R2 = 10 МОм (не забыть про конденсатор) ток через делитель будет 1.2 мкА, что уже гораздо лучше!
- Коэффициент делителя (не тот, который в Википедии) равен (R1 + R2) / R2 . Коэффициент должен быть таким, чтобы при делении на него измеряемого напряжения не получилось больше напряжения питания МК. У меня в примере (10 + 4.7) / 4.7 ~ 3.13 . Я хочу измерять литиевый аккумулятор с максимальным напряжением 12.8 Вольт. 12.8 / 3.13 ~ 4 Вольта – отлично. Например для измерения 36 Вольт я бы взял делитель с плечами 100к и 10к.
- Можно воспользоваться онлайн-калькулятором.
Сильно меньше 5 Вольт
Для более точных измерений маленького напряжения можно подключить пин AREF к источнику низкого опорного напряжения (об этом было выше), чтобы “сузить” диапазон работы АЦП. Источник может быть как внешний, так и внутренний, например изменив опорное на внутреннее 1.1V ( analogReference(INTERNAL) ) можно измерять напряжение от 0 до 1.1 Вольта с точностью 1.1/1024 ~ 1.01 мВ.
Видео
Полезные страницы
- Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
- Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
- Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
- Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
- Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
- Поддержать автора за работу над уроками
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])