Черный ящик курильщика
Многие люди курят слишком много, особенно когда увлекаются чем-то и не замечают как курят одну сигарету за другой. Черный ящик курильщика (ЧЯК) не позволяет взять следующую сигарету до тех пор пока не пройдет определенный промежуток времени. В этой статье я уделю внимание некоторым деталям, которые могут быть полезны и для других разработок, особенно это касается не слишком известного Teency LC контроллера (Arduino family).
Механика.
Детали ЧЯКа напечатаны на 3D принтере и после сборки нижней, средней и верхней частей склеены вместе.
Запорный механизм сделан электромеханическим, при этом основные запорные функции реализованы чисто механически, это затрудняет взлом ЧЯК путем манипулирования с зазорами и выниманием батареек.
Запорная система состоит из рейки с зубьями и бистабильной трещотки, которая позволяет двигаться лотку только в направлении закрытия. По достижению заданного промежутка времени серво совершает однократное движение туда-сюда и толкает трещотку, которая устанавливается во второе стабильное состояние «открыто». ЧЯК остается открытым до тех пор пока пользователь механически не выдвинет лоток. При выдвижении лоток толкает трещотку и переводит ее во взведенное состояние снова.
Загрузчик сделан в виде барабана с 4 углублениями для сигарет. Чтобы избежать попытки достать сигарету через загрузчик, храповой механизм не позволяет движение в обратную сторону, со стороны которой сделаны отбойники.
На верхней крышке проделаны прорези, они позволяют ликвидировать возможные перекосы сигарет внутри коробки и вытрясать табачную крошку.
Интерфейс.
Для контроля за временем, количеством сигарет в ЧЯКе, и количеством вынутых сигарет следит OLED display. Он выключен почти все время, чтобы не разряжать батарею и включается только по сигналу с центрального емкостного сенсора, срабатывающего при поднесении руки к ЧЯКу или сигнала с кнопки при загрузке сигарет. Еще одна кнопка фиксирует момент закрытия лотка и запускает следующий цикл задержки. Два дополнительных емкостных сенсора расположены на задней стенке и служат для подстройки счетчиков сигарет (необходимых, например, при смене батарей).
Электроника.
Микроконтроллер это Teency LC. Это ардуиноподобное устройство, совместимое с большинством ардуиновских библиотек, было выбрано из-за того, что в нем есть поддержка емкостных сенсоров (touch sense interface (TSI)). Сенсоры настолько чувствительны, что легко чувствуют поднесенную руку на расстоянии сантиметра. Teency LC имеет так называемый LLWU режим, в этом режиме все модули находятся в спящем режиме, за исключением 1-kHz осциллятора. Из этого спящего режима можно выйти 4 способами a) получить прерывание с емкостного сенсора, b) получить прерывание с пина, с) получить переполнение 1-kHz счетчика (low-power timer, LPTMR), d) получить прерывание от alarm.
Вот тут автора и ожидала неприятность: в первоначальных планах было использовать TSI для вывода из спящего состояния при поднесении руки, а LPTMR для периодических прерываний для подстройки TSI уровней (зависят от окружающих условий) и контроля за временем. Но оказалось, что LPTMR используется для функционирования TSI, и, соответственно, не может быть использован как счетчик времени. (прерывание по переполнению LPTMR есть hardware triggering для TSI, и, конечно, должно быть быстрым, чтобы следить за сенсором. Обычно этот счетчик предустанавливается на минус один, чтобы TSI опрашивался с максимально возможно частотой в 1 kHz).
Другой возможностью было бы использование прерывание RTC alarm, но дело в том, что у Tenncy LC нет real time clock (RTC). Вернее RTC есть в самом процессоре, но нет разводки для RTC кварца на плате. Однако, разработчик процессора оставил некоторую лазейку для пытливых умов. 1кГц осциллятор (который работает в спящем режиме) может быть использован как источник для RTC регистров контроллера. Тогда получается, что RTC может считать не секундные интервалы (как при использовании кварца на 32кГц), а 32-ух секундные, если использовать 1 кГц осциллятор. Этой точности, конечно маловато. Но есть и выход.
Вот как это работает:
Есть RTC Time Prescaler Register (RTC_TPR). Этот 16-битный регистр считает импульсы осциллятора. При его переполнении RTC Time Seconds Register (RTC_TSR) увеличивается на единицу. В классическом режиме это и есть секунды, которые сравниваются с RTC Time Alarm Register (RTC_TAR), при совпадении генерируется alarm interrupt. При использовании обычного 32кГц кварца RTC_TSR не предустанавливается, а каждый раз считается с нуля (32768 до переполнения (секунды)). Но если мы каждый раз будем предустанавливать RTC_TSR с учетом того, что у нас медленный 1кГц осциллятор мы можем получать alarm interrupt вплоть до миллисекундной точности (не учитывая неточность самого осциллятора). Конечно, RTC_TAR тоже должен быть соответственно пересчитан.
Например, если мы хотим установить период в 87 секунд, мы должнв записать 2 в RTC_TSR (2×32768=65536ms=65.536s), 2 в RTC_TAR и 32768-(87×1000–65536)=11304 в RTC_TSR. Тогда до первого переполнения RTC_TPR пройдет 32.768–11.304=21.464 секунды, и к ним добавится два полных цикла 2×32.768=65.536, что составит как раз 21.464+65.536=87 секунд
В общем случае так:
void setAlarm(uint32_t seconds ) {
RTC_SR = 0; //disable RTC
RTC_TPR=32768-(seconds*1000%32768);
RTC_TSR=0; //RTC counter
RTC_SR = RTC_SR_TCE; //enable RTC
RTC_TAR = seconds*1000/32768;
}
И даже мы можем следить за общем временем (с точностью до погрешности 1kHz осциллятора), например, если в начале программы все RTC регистры были нулевые: timeEllapsed=(RTC_TSR*32768+RTC_TPR)/1000
Точность 1kHz осциллятора невелика, но для наших целей вполне хватит.
Надо учесть, что при запуске нового alarm мы модифицируем регистры RTC, поэтому, если надо следить за временем, до запуска alarm они должны быть запомнены, а после выхода из alarm interrupt пересчитаны снова с учетом времени проведенного в спячке. Подробно я описал это здесь: о RTC для Teency LC
В ЧЯК прерывание каждые 10 минут измеряет и запоминает уровень сигнала с емкостных сенсоров в отсутствии руки. Это сделано для того, чтобы можно было надежно отслеживать изменение сигналов при приближениии. При background в ~500 единиц мы используем уровень превышения в ~20 единиц, это дает возможность чувствовать руку на расстоянии 5–10 мм. Уровень background зависит от температуры и влажности, поэтому для надежности он должен быть периодически подстраиваем. Вот это, я считаю, есть недоработка разработчиков процессора. Почему они не дали возможности иметь одновременно wake up от TSI и какого-нибудь еще одного low power counter (всего-то надо было добавить еще один регистр), ведь периодическая подстройка уровней TSI практически обязательна, даже если не нужен таймер для других целей!
Теперь про потребляемую энергию. В LLWU спящем режиме Teency LC при отсутствии обвеса потребляет около 15 uA. Нам надо было подключить еще OLED display и серво. Оба этих устройства имеют большие токи утечки даже в пассивном состоянии.
С OLED все просто, это Adafruit 0.96» Monochrome 128×64 OLED display, он питается от 3.3V, потребляет ток порядка 20 mA во включенном состоянии (зависит от количества задействованных пикселей) и управляется по SPI. То есть достаточно подключить его вход питания к 20 миллиамперному выходу Teency LC (у Teency есть выходы разных типов) и готово. Когда все спит этот выход просто переводятся в 3-е состояние и ток через дисплей не идет, при wake up выход переводятся в output high state и становится Vcc для дисплея.
С серво немного сложнее. Серво в режиме ожидания потребляет ток около 2 мА, что, конечно, не приемлемо. Поэтому надо его полностью выключать на время сна. В отличие от классических Ардуин, Teency LC имеет довольно небольшой выбор питаний: это или 1.7–3.3 В включенных прямо, или 2.6–5.5 В (c включенным внутренним voltage regulator). Обычно доступные серво работают как минимум от 1S Lipo, а это 3.3–4.2 В. Поэтому нам надо включить последовательно три стандартные батарейки, чтобы иметь от 3.3 (разряд) до 4.6 В (новые). Для большинства серво 3.3 В на пределе срабатывания, поэтому прямо выходы Teency использовать нельзя (как для OLED). Да и ток во время вращения порядка 50 мА, что многовато для Teency LC. Поэтому серво включен через MOSFET:
При таком включении может показаться, что это не безопасно так как при выключенном MOSFET напряжение на PWM ctrl входе превышает 3.3 В, а входы Tenncy LC не 5V tolerant (в отличие от классических Ардуин). Но опасаться этого не стоит, помня о том, что ток PWM сtrl очень мал, и опасаться за ограничивающий диод на входе процессора не надо (причина недопустмости превышения Vcc+0.5 именно в этом диоде), более того, при токах сравнимых с токами закрытого диода он даже не будет находится в открытом состоянии.
в ЧЯК я использую HK282 с порогом 3.3В (просто потому что были у меня). Какие нибудь более низковольтные и маломощные серво, может быть можно питать и напрямую с выходов Teency, по схеме используемой для OLED.
В результате ток в режиме сна получился в около 50 uA, наверное, можно было еще уменьшить, но я решил, что этого достаточно (если только спит, батарей должно хватить больше чем на 4 года: 2000 мАч/0.05 мА).
Техническое.
Печаталось на Monoprice Ultimate 3D принтер, PLA пластик, 0.4 мм nozzle, слой 0.2 мм. Для деталей запорного механизма слой 0.1 мм для точности. Пружинки тоже напечатаны на 3D принтере. Печатать долго. Например, средняя самая болшая часть (она имеет много внутренних деталей и двойные стенки для электроники и проводов) печаталась 20 часов. Скливалось циан-акрилатным клеем. Если внутри что-нибудь сломается, разобрать нельзя (защита от маньяков-курильщиков), придется разбивать как свинью-копилку и печатать заново (аргумент в пользу 3D принтеров). Чертежи и анимация сделаны в SolidWorks, среда разработки AtmelStudio (да, поддерживаются и AVR (teency) прямо из коробки, как и ардуино, через VisualMicro).