Прибор для измерения мгновенного тока потребления электродвигателя

В данной статье пойдет речь о простом самодельном амперметре, способном производить непрерывные измерения силы тока 150 раз в секунду, высчитывать минимальное, максимальное и среднеквадратическое значение, а также строить небольшой график изменения силы тока во времени. Дополнительно прибор позволяет контролировать температуру устройства и выдавать предупреждающие звуковые сигналы при его перегреве. Прибор построен на основе микроконтроллера Microchip ATtiny85, монохромного OLED-дисплея разрешением 128х64 точек, а также самодельного импульсного источника питания. Если описание вас заинтересовало, то садитесь поудобнее и будем начинать, но сразу предупреждаю, текста в этот раз получилось много.
На самом деле, я бы купил её и значительно раньше, так как давно задавался таким вопросом, но очень смущала необходимая ей площадь в квартире, ведь это практически 2 квадратных метра, а дорожка — не третий по счету 3D-принтер по акции, место для которого найдется всегда. И тут вдруг в начале этого года внезапно была разобрана лоджия, образовалось достаточное свободное пространство, которое можно было занять без ущерба бытовой повседневной жизни, и звезды, наконец, сошлись.
Модель выбирали долго, так как не хотели брать самые дешевые из страха столкнуться с плохим качеством, а более профессиональные не могли взять из-за их быстро возрастающего веса – хотелось, чтобы манипуляции с беговой дорожкой можно было проводить самостоятельно и, соответственно, хотелось ограничить вес сверху на отметке 70 кг. Такое ограничение было, в том числе, и из-за вопроса начальной сборки дорожки, потому что мы не были уверены, что мастер от продавца согласится собирать и устанавливать дорожку на лоджии, так как один мой знакомый уже столкнулся с такой проблемой. В его случае мастер сказал, что если дорожка будет эксплуатироваться на лоджии, то гарантия на неё будет автоматически аннулирована, т.к. лоджия не соответствует требуемым условиям эксплуатации беговой дорожки. Возможно, ему просто не повезло с мастером, однако, дополнительные опасения это добавило.
В итоге, выбор пал на модель CardioPower T45 New – она хоть и немного не вписывалась в обозначенный лимит по весу, но имела весьма интересные эксплуатационные характеристики для домашнего применения, да и по самому бренду CardioPower у нас к тому времени уже набралось несколько положительных отзывы от знакомых, которые приобрели дорожки данной марки ранее.
Когда с моделью определились, стали искать, где её можно приобрести, да не просто приобрести, а еще и с доставкой в нашу «глубинку». И это стало дополнительным квестом, т.к. далеко не все продавцы предлагают доставку крупногабаритного товара до квартиры, у многих доставка предлагается только до подъезда, а дальше уже мучайся как хочешь – или знакомого бери, или водителя уговаривай, или отдельных грузчиков нанимай. Ситуацию еще осложняет временной диапазон доставки, который в большинстве случаев покрывает весь световой день, а о конкретном времени тебя известят только за 10-30 минут до непосредственно самого момента доставки. То есть, даже нанять грузчиков заранее не выйдет. Но и эта проблема, в конечном счете, была разрешена и продавец с доставкой непосредственно до квартиры был найден. Правда, он не предлагал совершенно никакой услуги сборки, но к тому времени я уже посмотрел пару роликов по сборке дорожки на ютюбе, поэтому был морально (и физически) готов собрать её самостоятельно.
Сама покупка прошла без каких-то особенностей – положили в корзину, оформили заказ, оплатили, подождали и, наконец, в назначенный день (что даже немного удивило) два сильных мужчины занесли к нам в квартиру здоровенную двухметровую картонную коробку. Позвонили они, как и ожидалось, только за 15 минут, но тут уже это не имело совершенно никакого значения, ведь единственной нашей задачей оставалось лишь открыть им входную дверь.
Сборка дорожки прошла тоже без каких-либо проблем – всё-таки предварительный просмотр обучающих видео работает очень хорошо, да и, по сути, сама сборка совершенно несложная, тебе нужно лишь только уметь пользоваться отверткой и шестигранником, а также быть мускулистым и плечистым. Последнее особенно важно в момент доставки двух исходных частей дорожки от входной двери до лоджии, но тут можно немного считерить и сначала дополнительно разобрать дорожку, сделав, таким образом, из двух частей три. Это, кстати, еще и сильно помогает занести её в достаточно узкий дверной проем лоджии без повреждений. Но потом, конечно, собирать придется побольше и уже самому, без обучающих видео.
Последующая эксплуатация дорожки как будто и не заслуживает дополнительного внимания, так как она совершенно стандартная – включил, выбрал скорость и наклон, включил на планшете любимый канал и пошел совмещать приятное с полезным. И тут, кстати, размещение дорожки на лоджии имеет неоспоримый плюс – всегда можно открыть окно на улицу и добавить к своему увлекательному путешествию на одном месте аромат и ветерок настоящего свежего воздуха – прямо 5D-погружение, как в современном кинотеатре.
В общем, концепция беговой дорожки нам очень понравилась, да так, что эксплуатировать стали мы её, в общей сложности, ежедневно и по часу. И эксплуатировали бы и дальше долго и счастливо (наверное), но через три месяца она сломалась. Ну, не прямо так сломалась, что совсем перестала работать, а стала через 15 минут уверенной ходьбы издавать странный неприятный запах, который похож на аромат горелого пластика. Снятие верхнего кожуха моторного отсека показало, что запах этот исходит из самого двигателя, который по какой-то причине очень даже серьезно греется. Так как дорожка имеет год гарантии, решено было предпринять типовые ответные действия – обратиться в сервисный центр.
И вот тут уж я с вашего позволения опущу детальные подробности последующего месячного удаленного общения с сервисным центром, не буду рассказывать, тянут ли они время, и приходится ли им постоянно названивать и написывать, а перейду сразу к главному – дорожку, в итоге, починили. Заменить пришлось двигатель и полотно, и это, наверное, половина стоимости новой дорожки. Локального мастера в нашем городе они не нашли, поэтому просто высылали комплектующие, а я сам менял. Но после месячного ожидания возврата былого удовольствия пробежки на одном месте я был готов на всё, лишь бы она заработала. Да и замены эти, в принципе, не так сложны и даже в какой-то степени интересны и волнительны, особенно если делаешь это в первый раз – ведь всегда можно что-то сломать. Но, к счастью, обошлось.
Резюмируя произошедшее, по какой-то причине на дорожке за такой короткий промежуток времени износилось беговое полотно, у него поднялось механическое сопротивление движению, что увеличило нагрузку на мотор, который такого не выдержал, начал сильно греться и вонять. Да, несмотря на то что на дворе 2025 год, в дорожке стоит обычный коллекторный мотор постоянного тока 180 В 7.5 А (то есть, 1350 Ватт при заявленной мощности дорожки 3.25 лошадиных силы), на котором даже нет минимального термодатчика, позволяющего контроллеру следить за температурой двигателя! И это при цене изделия в 70 тысяч.

Предвосхищая ваши вопросы – причины такого быстрого износа полотна я назвать не могу. Не смог это сделать и сам сервисный центр, они просто указали, что стандартный срок службы (и гарантия) на полотно составляет всего 6 месяцев. И да, полотно было у меня правильно натянуто и смазано, за этими моментами я следил с самого начала, т.к. перед эксплуатацией дорожки всё же прочитал инструкцию полностью. Также температурный режим эксплуатации дорожки был всегда в допустимых руководством пределах. Возможно, полотно было изначально бракованное или просто некачественное, я склоняюсь именно к такому варианту. И тогда тут остается лишь только надеяться, что новое полотно таким не будет и прослужит дольше.
Еще в процессе ремонта сервисный центр просил меня замерять ток потребления двигателя под нагрузкой, так как именно по нему можно судить о состоянии полотна. И вот тут я обнаружил, что сделать это обычными приборами не представляется возможным – ток потребления сильно прыгает в такт с моей ходьбой, поэтому мультиметры показывают любые промежуточные значения от минимального до максимального. Немного спасает барграф UT61E, но он позволяет оценить потребление лишь верхнеуровнево и в моменте, а если хочется получить более точные значения или просто быстро найти максимум, то остается единственный вариант – это использовать шунт и подключать осциллограф. Понятно, что это уже намного более сложная задача, таким даже раз в неделю заниматься не будешь.
С другой стороны, совсем не хочется, чтобы и с новым двигателем произошла аналогичная история, а так как со стороны контроллера дорожки никакой защиты нет, мне захотелось установить в цепь двигателя дополнительный амперметр и, таким образом, контролировать его потребление в реальном времени. Но обычный амперметр за пару баксов тут не подойдет, значения меняются слишком быстро, поэтому нужно что-то посерьезней. Возможно, готовые устройства, обладающие требуемой функциональностью и существуют, но вот я таких за умеренную цену не видел. Да даже если бы и видел, скорее всего, его доставку пришлось бы ждать месяц, а хотелось здесь и сейчас, поэтому снова решил сделать что-то свое, ведь и задача, вроде как, не такая уж сложная.
Немного подумав над элементной базой, остановился на следующих компонентах:
Поскольку общее максимальное потребление устройства я оценил как 5 В 200 мА, первоначально для его питания хотел использовать самую слабую из имеющихся готовых плат блоков питания на напряжение 5 В, однако, чуть поразмыслив решил и блок питания сделать свой. А что, ведь раньше я любил этим занимался, почему бы не вспомнить былые времена? Так как мощность нужна минимальная, делать решил на самом младшем ШИМ-контроллере серии TNY27x, который у меня есть, и им оказался TNY275. Обратноходовый БП на этом контроллере предельно прост, требует минимум внешних деталей и не нуждается в отдельной обмотке трансформатора для питания контроллера, так как контроллер умудряется питаться непосредственно от высокого напряжения. То есть, бери и делай, остается только трансформатор посчитать.

Входное напряжение 220 В через гасящий резистор R6 и предохранитель F1 поступает на диодный мост D1. Резистор и предохранитель специально установлены в разных сетевых проводах – поскольку цепь двигателя, куда будет подключаться устройство непосредственно связана с сетью (об этом расскажу чуть подробнее далее), и если в устройстве вдруг произойдет какой-либо пробой, ток короткого замыкания пойдет только по одному из двух сетевых проводов, причем неизвестно заранее, по какому именно, поэтому каждый из них должен иметь какую-либо защиту.
После диодного моста установлен накопительный конденсатор С1 ёмкостью 2.2 мкФ. Традиционно при разработке импульсного БП для напряжения 220 В придерживаются эмпирического соотношения 1 мкФ ёмкости накопительного конденсатора на 1 Вт выходной мощности, однако у меня в наличии оказались конденсаторы на 2.2 мкФ, поэтому решил ставить такой. Дальнейшая высоковольтная часть схемы блока питания стандартная и взята непосредственно из даташита на ШИМ-контроллер, поэтому детально разбирать её не буду. Отмечу только, что с помощью конденсатора С2 ёмкостью 1 мкФ ШИМ-контроллер переводится в режим пониженного тока (режим совместимость с предыдущей моделью TNY274), а также что схема снаббера тут более сложная – и на конденсаторе, и на супрессоре.
Вторичная цепь блока тоже достаточно стандартная, это выпрямитель и стабилизатор напряжения на TL431 с обратной связью через оптрон. Резистор R4 параллельно светодиоду оптрона необходим для того, чтобы ток холостого питания TL431 не вызывал свечения диода и, таким образом, не передавал сигнал обратной связи контроллеру постоянно. Конденсатор С5 в цепи обратной связи TL431 оказался не нужен, блок работает лучше без него (это связано с особенностью ШИМ-контроллера), однако место на плате под него я решил оставить, поэтому на схеме его ёмкость обозначена как 0 нанофарад.
Напряжение с выхода блока питания подается непосредственно на дисплей, схему зуммера и датчик температуры, а вот микроконтроллер питается через дополнительный двойной фильтр на дросселе L3 и резисторе R17, призванном минимизировать помехи от работы блока. Скорее всего, применение двухзвенного LRC-фильтра тут излишне, хватило бы и однозвенного LC-варианта, но в какой-то момент я почему-то решил, что нужно именно два звена.
Для подключения датчика температуры и дисплея на плате устройства предусмотрены два отдельных разъема I²C, соединенных параллельно. Также к контроллеру подключен разъема SPI для осуществления его внутрисхемного программирования.
Еще можно отметить несколько необычное подключение пьезоэлектрического излучателя между затвором и стоком полевого транзистора – такой вариант позволяет увеличить громкость звучания излучателя, так как удваивает эффективное напряжение на нем. Поскольку с точки зрения схемы излучатель является конденсатором без явно выраженных резонансных качеств, данный вариант подключения вполне допустим.
Ну и, наконец, токоизмерительным шунтом схемы являются пара резисторов R19/R29, включенных параллельно. В реальности на плате будет установлен только один из них, потому что для моей задачи сопротивление в 5 мОм является оптимальным. Однако, если вдруг схему захочется использовать для другого диапазона измерения, я предусмотрел возможность установки двух резисторов типоразмера 2510 параллельно. Сигналы с шунта через резисторы R10 и R11 подаются на соответствующие входы АЦП МК, образуя совместно с конденсаторами С10 и С11 ФНЧ для фильтрации высокочастотных помех.
Шунт установлен в схеме так, что его нижний конец оказывается жестко заземлен и, таким образом, инверсный вход АЦП всегда сидит на земле, что фактически превращает дифференциальный вход АЦП в однополярный. Да, действительно, в данном устройстве нет необходимости в дифференциальном АЦП, потому что схема имеет свой собственный блок питания и ток нагрузки ни коим образом не проходит по питающим дорожкам платы, то есть, не создает на них никакого дополнительного падения напряжения. Однако, режим усиления 20х в АЦП МК доступен только в дифференциальном варианте включения, поэтому пришлось реализовывать именно его. Но зато это позволит быстро перепрофилировать устройство на питание от измеряемой схемы, если вдруг это понадобится для какой-либо другой задачи.
Теперь, когда с принципиальной схемой устройства разобрались, рассмотрим, как подключается плата к двигателю беговой дорожки используя следующую упрощенную схему подключения:

Чтобы было более понятней, я частично нарисовал основные высоковольтные цепи блока управления дорожкой, прозвонив их предварительно мультиметром. Вопрос вызывает лишь шунт R1 на плате контроллера – его я визуально не увидел, а мультиметром найти такие вещи нельзя, т.к. его сопротивление составляет всего единицы миллиом, как и в моем устройстве. Однако, он определенно должен быть, так как контроллер должен следить за потреблением двигателя, поэтому будем считать, что он есть. Возможно, он в SMD-исполнении и находится с нижней стороны платы, куда обзор закрыт алюминиевой защитной пластиной.
Из особенностей ШИМ-контроллера дорожки следует отметить то, что в нем нет выходного сглаживающего дросселя, его роль выполняет непосредственно сам электродвигатель. А это означает, что по питающим проводам мотора проходят высокочастотные пульсации весьма существенной амплитуды, которые могут создавать помехи на другие узлы схемы и, в частности, на мой измеритель. Это надо будет иметь в виду.
Также из схемы становится понятно, что высоковольтные части двух разных блоков питания оказываются дополнительно соединены через межобмоточную ёмкость трансформатора Т1 и через защитный конденсатор С7. То есть, подключение амперметра в цепь двигателя добавляет к ней дополнительную ёмкость, и, например, включить его в разрыв плюсового провода двигателя уже было бы нельзя, ведь эта ёмкость оказалась бы подключена к точке соединения силовых транзисторов и очень негативно влияла бы на их переключение. К счастью, в минусовом проводе она никакого влияния оказывать не будет, так как там ожидается только наличие шунта, параллельно которому и так должен стоять какой-либо фильтрующий конденсатор.
Дополнительно следует рассмотреть негативный сценарий пробоя трансформатора или Y-конденсатора С7. По схеме становится видно, что серьезных повреждений контроллеру дорожки такой пробой не нанесет, так как весь ток пробоя пойдет только через шунт на плате контроллера, который должен быть способен выдерживать большой ток достаточное время, чтобы во входной цепи блока питания сгорел предохранитель или гасящий резистор (в зависимости от того, какой полярностью блок будет подключен к сети).
На этом электрическую часть устройства можно считать полностью рассмотренной и пора переходить к следующей – математической, а именно расчету импульсного трансформатора.
F = 132 KHz
Fmax = 140 KHz
Tmin = 7.14 мкс
Dmax = 65%
I = 250 mA
Imax = 300 mA (с запасом)
Так как мощность, которая нужна мне на выходе блока питания весьма небольшая, будем ориентироваться на режим разрывных токов. В этом режиме энергия, запасенная в индуктивности в начале текущего импульса полностью успевает передаться в нагрузку к его концу, обеспечив, таким образом, «чистый старт» следующего импульса с нулевого тока ключа. Это уменьшает потери при открытии ключа и, соответственно, снижает его нагрев.
Возьмем отраженное рабочее напряжение в первичной обмотке Uinv = 150 В. Такое значение, с одной стороны, обеспечит достаточно быстрый «разряд» индуктивности, с другой – относительно невысокое напряжение на закрытом ключе (~310 В + 150 В = ~460 В при максимально допустимом 700 В). Dmax у TNY275 составляет 65%, что дает рабочие временные характеристики Tonmax = 4.64 мкс и Toffmin = 2.5 мкс. Так как количество энергии, которое входит в катушку за Ton должно равняться количеству энергии, которое выходит из неё за Toff, отношение данных временных характеристик (1.856) определяет минимальное напряжение, от которого преобразователь всё еще сможет работать на полной мощности (если вам что-то непонятно, задавайте вопросы в комментариях, я поясню). В данном случае оно составит Uinv/1.856 = 81 В, что очень даже неплохо (хотя и совершенно не нужно для разрабатываемого устройства).
Теперь у нас есть вся необходимая информация, чтобы рассчитать индуктивность первичной обмотки. Она должна быть такой, чтобы при напряжении 81 В ток в ней поднялся до 250 мА за 4.64 мкс:
L1 = (UΔt)/I = (81*4.64*10⁻⁶)/0.25 = 1.5 мГн
А зная индуктивность, можно посчитать максимальную энергию, которую преобразователь сможет отдать в нагрузку за секунду и, таким образом, определить его максимальную выходную мощность:
Aimp = (LI²)/2 = (1.5*10⁻³*(0.25)²)/2 = 46.9 мкДж
P = Aimp*F = 46.9*10⁻⁶*132000 = 6.2 Вт
То есть, преобразователь на TNY275 с таким трансформатором вполне мог бы обеспечить на своем выходе 5 В 1 А во всем диапазоне входного питающего напряжения от 100 до 230 В! Неплохо, конечно, но мне нужно всего 1 Вт, поэтому мотать трансформатор я буду на самом маленьком сердечнике, который только у меня есть:

Взят этот сердечник был, скорее всего, из небольшой компактной люминесцентной или светодиодной лампы. Кстати, на фото хорошо видно, что в процессе вытаскивания из лампы ферритовый сердечник был поломан, а позже склеен цианоакрилатом. Основные параметры сердечника:
Площадь магнитопровода S = 2.5*4.8 = 12 мм²
Средняя длина магнитной линии l = 30 мм
Зазор g = 0.6 мм
Прежде всего посчитаем, какое количество витков должно быть в первичной обмотке. Сначала попробуем сделать это по упрощенной формуле индуктивности обмотки на сердечнике с зазором:
L = (µ₀*S*N²)/g
Или
N = sqrt((L*g)/(µ₀*S)) = sqrt((1.5*10⁻³*0.6*10⁻³)/(4π*10⁻⁷*12*10⁻⁶) = 244 витка
А теперь проверим результат, сравнив его с измеренной индуктивностью изначально намотанной на сердечнике обмотки, которая составила 2.13 мГн для 256 витков. Т.к. индуктивность пропорциональна квадрату числа витков, при таких исходных данных получим, что для 1.5 мГн надо всего 215 витков. 215 и 244, конечно, числа близкие, однако всё равно отличаются друг от друга достаточно сильно, поэтому я предпочту использовать данные, полученные практическим путем – так результат будет более точным. Ведь формула, которую я представил выше – приближенная, если углубляться во все детали теории электромагнитного поля, на точный расчет одного трансформатора не хватит даже объема всей статьи.
Тем не менее, приближенные формулы тоже нужны, и по следующей такой формуле можно оценить максимальный ток, который сможет выдержать данный трансформатор в первичной обмотке до входа в насыщение:
Imax = (Bmax*g)/(µ₀*N) = (0.25*0.6*10⁻³)/(4π*10⁻⁷*215) = 555 мА
Получается, трансформатор будет с приличным запасом по току, и это очень хорошо. Скорее всего, электрическая лампочка, из которой он был позаимствован была на большую мощность, чем 6 Вт.
Посчитаем число витков во вторичной обмотке. Если в первичной обмотке будет 215 витков при напряжении 150 В, для получения 6 В во вторичной обмотке понадобится 215*6/150 = 8.6 витка. Так как намотать 8.6 витка нельзя, следует выбрать одно из двух ближайших целых значений – 8 или 9 витков. Если взять 8 витков, напряжение на вторичной обмотке получится 5.58 В, если же взять 9 витков, напряжение будет 6.28 В. Так как падение напряжения на выпрямительном диоде может составить 0.8 – 1 В, в принципе, можно выбрать любое из этих значений. Я возьму 9 витков – это даже чуть уменьшит отраженное напряжение в первичной обмотке (до примерно 143 В).
Наконец, остался последний момент – выбор толщины провода для обмоток. По-хорошему, диаметр провода выбирается исходя из среднеквадратического значения тока, но я поступлю проще – изначальная обмотка из 256 витков была намотана проводом диаметром 0.18 мм без какой-либо межвитковой изоляции и занимала практически все место на каркасе. Мне надо намотать чуть меньше витков, но нужна изоляция и вторичная обмотка, так что проводом 0.18 мм намотать первичку я уже не смогу – для него просто не хватит места. То есть, надо брать что-то потоньше. Оптимально, наверное, был бы провод диаметром 0.15 мм, но у меня такого нет, есть только 0.11 мм. Значит, им и буду мотать, и тогда гарантированно освободится достаточно места для межвитковой изоляции и вторичной обмотки, для которой я уже возьму провод потолще, скажем, 0.35 мм.
Итого, имеем следующие данные для намотки:
Первичная обмотка – 215 витков провода диаметром 0.11 мм.
Вторичная обмотка – 9 витков провода диаметром 0.35 мм.
Теперь можно приступить непосредственно к намотке трансформатора. Первым делом я намотал на голую катушку два слоя каптонового скотча – он немного сгладит углы, уменьшая возможные повреждения изоляции провода, а также создаст дополнительную изоляцию обмотки от катушки. Затем намотал первую половину (110 витков) первичной обмотки и снова проложил два слоя скотча, после чего намотал вторую половину (105 витков). Финальную изоляцию между обмотками делал также из каптонового скота, только уложил его уже в 4 слоя. После намотки первичной обмотки временно вставил половинки сердечника в катушку и замерил получившуюся индуктивность:

Шикарная точность, отклонение от требуемой индуктивности получилось менее 1%! После замера я снова вынул сердечник и намотал поверх изоляции вторичную обмотку из 9 витков, после чего замотал весь трансформатор своим любимым зеленым малярным скотчем:


После сборки трансформатора я повторно замерил индуктивность первичной обмотки, и она чуть возросла – стала примерно 1.53 мГн. Произошло так потому, что скотч сильнее прижал половинки сердечника друг к другу и, таким образом, уменьшил немагнитный зазор. Но это не страшно, отклонение получилось очень незначительное. Также попробовал замерить и индуктивность рассеяния – делается это достаточно просто, во время измерения индуктивности первичной обмотки вторичная замыкается накоротко. Это как бы замыкает и всю ту часть первичной обмотки, которая связана со вторичной, оставляя для измерения лишь несвязанную часть, которая и называется индуктивностью рассеяния. Результат получился весьма высоким – на частоте 100 КГц индуктивность рассеяния составила порядка 210 мкГн.
Да, это много, но и причина понятна – по правилам надо было разбивать первичную обмотку на две части и мотать вторичную обмотку между этих частей, но это существенно усложняет конструкцию трансформатора, особенно, когда речь идет о таких небольших габаритах. Поэтому морально я был готов к высокому значению. На практике это будет означать повышенную нагрузку на снаббер, его более высокий нагрев и, таким образом, более низкий КПД всего блока питания. Однако, мы здесь говорим о выходной мощности около ватта, так что такими мелочами можно и пренебречь. Но если вдруг вам придется проектировать обратноходовой преобразователь на большую мощность, обязательно учтите этот момент.
Сначала я расположил все детали в длинную узкую полоску, но потом понял, что плату такой длины я не смогу поместить в свой фотополимерный принтер (он у меня достаточно маленький), поэтому перекомпоновал плату в более квадратную, всё равно места в корпусе дорожки достаточно:

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


После изготовления платы сначала собрал блок питания и проверил его работу, чтобы случайно не сжечь микроконтроллер и дисплей, если с блоком что-то не так. Но он заработал сразу без какой-либо настройки. Осциллограмму его работы (напряжение на стоке ключевого транзистора) вы можете увидеть ниже:

Такая интересная форма сигнала получается из-за большой индуктивности рассеяния первичной обмотки. Дело в том, что после закрытия ключа эта индуктивность как бы «повисает в воздухе» и совместно с межобмоточной ёмкостью и ёмкостью подключенных к ней деталей образует колебательный контур, в котором возникают затухающие колебания. Они накладываются на нормальный график напряжения на стоке и получается то, что вы видели выше.
Далее я решил проверить, насколько сильно блок греется от расчетной нагрузки в 200 мА. После 30-ти минут работы платы на открытом воздухе картина нагрева была такой:

Видно, что вся плата нагрета более-менее равномерно – температура и ШИМ-контроллера, и трансформатора, и снаббера составила порядка 42 градусов. Чуть сильнее грелся только выходной диод, его температура приближалась к 50-ти градусам. Но для электронных компонентов это сущие мелочи, поэтому можно смело считать, чтоб блок питания сможет выдать 1 Вт выходной мощности в любых разумных условиях эксплуатации.
Пульсации напряжения на выходном конденсаторе получились в районе 40 мВ PP:

После LCR-фильтра (без нагрузки) амплитуда пульсаций упала и составила менее 5 мВ РР:

Тогда я собрал остальную часть платы:


Как это часто случается, на этапе сборки и наладки устройства в схему были внесены небольшие изменения, поэтому на фото можно рассмотреть незапланированно установленные детали. Саму схему и трассировку дорожек в электронном варианте я уже обновил, но переделывать и перепаивать плату конечно же не стал.
Теперь перейдем к датчику температуры TMP100. Чтобы ускорить процесс, я взял одну из имеющихся у меня печатных плат, которые остались от предыдущих проектов. Выбрал самую маленькую, где не разведены перемычки, позволяющие изменять адрес датчика на шине I²C. Датчик должен подключаться к основной плате устройства с помощью четырехпроводного кабеля, и вот тут, вспомнив про то, что ШИМ-контроллер дорожки может создавать сильные помехи, я захотел использовать экранированный кабель. Но где его взять?
Выручил Чиподип – оказалось, что нашем городе в наличии есть волшебный четерыхпроводный экранированный микрофонный кабель КММ-4х0.12 в шикарной толстой ПВХ-изоляции, способной выдержать аж 600 В! Последний момент очень важен, ведь через блок управления дорожкой низковольтная часть амперметра тоже оказывается связанной с сетью, а металлический корпус дорожки, рядом с которым будет проходить кабель заземлен. То есть, между экраном кабеля и корпусом дорожки будет действовать полное сетевое напряжение, поэтому изоляция кабеля должна быть достаточно надежной.
Итак, кабель был куплен, после чего датчик был запаян на плату:

Экранирующую оплетку кабеля я решил подключить к общему проводу только со стороны основной платы, а со стороны датчика оставить неподключенной. Мне кажется, в этом случае наведенное в оплетке напряжение радиопомех никак не сможет влиять на сигналы, проходящие внутри кабеля. Если же подключить экран с двух сторон, он оказывается включенным в сигнальную цепь и, таким образом, наведенное в нем напряжение будет мешать нормальной работе устройства. Но опыта в экранировании цепей у меня немного, поэтому если кто из вас знает, как надо делать правильно, напишите в комментариях.
С другой стороны кабеля я установил стандартный 4-контактный JST-разъем с шагом 2.54 мм, где уже соединил экран с общим проводом питания схемы:

После чего затянул датчик в термоусадку:

По мне, получилось весьма красиво. Когда все части устройства были собраны, провел еще одно испытание самодельного блока питания, но теперь уже с реальной нагрузкой. Оказалось, он вполне запускается от постоянного напряжения 30 В на входе! И потом сохраняет нормальную работу при его снижении чуть ниже 20 В! Выглядело это просто нереальным, ведь это в 10 раз меньше номинального значения, но такие уж особенности у обратноходовых блоков питания. По входу, кстати, потребление составило около 150 мВт, то есть, намного ниже 1 Вт, на который я рассчитывал изначально.
Чтобы ничего не сверлить в основании дорожки, для платы я разработал вот такую «кроватку», которую просто приклею к основанию моторного отсека на двусторонний скотч:

А корпус и крепление дисплея к трубе получились такими:

Внешне корпус дисплея очень похож на наручные часы и состоит из четырех частей – собственно корпуса, стекла дисплея, дужки крепления на трубу и маленькой планки прижима кабеля. Стекло я вырезал из какой-то прозрачной упаковки от конфет, а остальные детали напечатал:

Для крепления частей друг к другу в основном корпусе дисплея сделаны полости, куда необходимо вплавить обычные гайки М3 – в них со стороны дужки будет закручиваться винт с шестигранной головкой. Сам дисплей и планка прижима кабеля будут закреплены мелкими саморезами 2х6. С основным блоком, находящемся в моторном отсеке дисплей будет соединен тем же экранированным микрофонным кабелем КММ-4х0.12, что и датчик температуры. Экран здесь я тоже решил подключать к общему проводу только со стороны платы.
Сборку корпуса дисплея я начал с вплавления гаек в отведенные под них места. На этапе проектирования я переживал, что пространства там достаточно мало и вплавить их будет непросто, но на практике это оказалось совсем не сложно. Далее вклеил стекло в корпус с помощью клея-герметика В7000, который частенько используют при ремонте мобильных телефонов. Это оказалось сложнее, чем вплавить гайки, и я немного запачкал клеем само стекло. Вроде как основное пятно со стекла удалось оттереть, однако по краям остатки клея удалить не получилось, и внешне конструкция стала смотреться хуже, чем мне хотелось бы.
А вот установка дисплея в корпус оказалась еще более сложной задачей. При проектировании я учитывал размер платы дисплея, и она сама в корпус входила нормально, хоть и чуть впритык. Но при сборке я решил сначала припаять к плате кабель, чтобы сделать это красиво и в свободной обстановке на столе, а плату поместить в корпус уже потом. И вот тут меня ждало фиаско – с подпаянным кабелем плату совершенно не удавалось расположить под тем углом, под которым она проходила бы в корпус. Помучившись некоторое время, я неудачно схватил плату пинцетом и отколол сбоку от дисплея маленький кусочек стекла. Вроде как он ни за что на дисплее не отвечает, однако после откалывания у изображения пропали все четные строки. Пользоваться таким дисплеем было уже нельзя.
Вдвойне усиливало проблему то, что дисплей этот был у меня в единственном экземпляре, второго такого в наличии не было. На часах был час ночи по московскому времени, все радиомагазины города были, естественно, закрыты, поэтому изрядно расстроенный я пошел копаться в своей коробке с околоардуиновыми платами. И вдруг совершенно случайно нашел там похожий дисплей, только с несколько другой платой, сконфигурированной для подключения по протоколу SPI. Но на плате были указания, как переключить её на протокол I²C, что я и попытался сделать:

Я перепробовал несколько разных вариантов и, конечно, у меня ничего не вышло, плата по I²C не заработала. Переделывать готовый код на SPI совершенно не хотелось, но тут вдруг я осознал, что можно просто перепаять сам дисплей с контроллером с этой платы на предыдущую, ведь у них совершенно одинаковые шлейфы. С помощью жала 1402 для Т12 сделать это оказалось совершенно несложно, его ширина идеально совпала с шириной шлейфа, и где-то к двум часам ночи дисплей был восстановлен. Небольшим удивлением для меня оказался его цвет – предыдущий OLED был голубым, а этот оказался белым, но внешне это выглядело даже более красиво.
На этот раз я решил не рисковать и сначала установить дисплей в корпус, а потом уже припаивать к нему кабель. Нормально припаять в таком малом пространстве вряд ли получится, но уж пусть будет как будет, ломать еще один дисплей (который уже совершенно точно последний) я не хотел. В итоге, через 30 минут мучений сборка дисплея была завершена:

Поскольку дисплей будет находиться достаточно далеко от основной платы, длина соединительного кабеля тоже вызывала у меня опасения – будет ли работать весьма высокоомная шина I²C с кабелем такой длины? Но эти опасения развеялись очень быстро, когда после сборки дисплея я подключил его к плате через весь остаток кабеля длиной 4.5 метра, и дисплей прекрасно заработал. Если уж через такое расстояние работает, то через полтора (которые мне нужны) будет работать абсолютно точно.

На следующий день мне, наконец, удалось установить всё на беговую дорожку. Плату приклеил скотчем к основанию моторного отсека, как и хотел:

А дисплей установил на трубу и затянул винтами:

Во включенном состоянии это выглядит так:

Так как дисплей монохромный, один байт данных отвечает за отображение сразу 8-ми пикселей на экране. С точки зрения слабого МК ATtiny это удобно, ведь надо передавать меньше данных. А вот с точки зрения компоновки изображения – нет, ведь соседние элементы интерфейса могут попасть в один и тот же байт дисплея и их надо будет как-то совмещать программно. Еще надо учитывать, что байты данных у этого контроллера дисплея расположены вертикально, а не горизонтально, как мы привыкли, то есть, один байт заполняет сразу 8 строк в одной колонке экрана.
Еще немного подумав, я понял, что эта особенность даже мне на руку – достаточно сделать шрифт высотой 8, 16 или 24 пикселей и тогда никакого наложения элементов интерфейса никогда не произойдет, каждый из них будет находиться целиком в своем байте. Проведя небольшой эксперимент, я понял, что оптимальным для меня будет шрифт высотой 16 пикселей, так как шрифт высотой 8 выглядит нереально маленьким, а со шрифтом высотой 24 или больше на экран влезет слишком мало информации.
Готового шрифта подходящего размера я не нашел, поэтому как во времена Спектрума просто нарисовал по пикселям в фотошопе свой, тем более, мне не нужны все 96 символов ASCII, достаточно было нарисовать и закодировать лишь те, которые я буду использовать. Ими оказались 10 цифр, стрелки вверх и вниз, заглавная буква «А», значок градуса, 5 картинок небольшой звездочки и, как ни странно, пробел:

Основной и единственный экран решил сделать таким – слева вверху отображается стрелка вверх и максимальное значение тока за последнюю пару секунд, правее отображается текущее значение тока. Во второй строке слева отображается стрелка вниз и минимальное значение тока, а правее – температура двигателя. Оставшаяся нижняя половина экрана отображает график тока во времени. В реальности это будет выглядеть вот так (фото настоящее и сделано уже после установки на дорожку):

Сам протокол, по сути, достаточно простой и состоит из двух сигнальных линий – тактовой SCL и линии данных SDA. На шине может сидеть много устройств, которые делятся на два класса – главные (Master) и подчиненные (Slave). Главные устройства управляют шиной и генерируют тактовые импульсы, а подчиненные – только пляшут под их дудку. Все устройства имеют выходы с открытым коллектором (стоком), а сама шина подтянута к плюсу питания через резисторы, что создает аппаратное объединение сигналов по AND и не вызывает физических проблем, если вдруг два устройства заходят вывести на одну шину разные уровни. Для шины определены несколько сценариев использования, основные из которых – Start, Stop, Repeated Start, Master Write и Master Read. Передача каждого байта данных физически занимает 9 бит, так как предусматривает 9-й бит ответа от принимающего устройства, которое должно подтвердить прием. Стандартной (высокой) частотой следования тактовых импульсов для I²C считается 400 КГц.
Любую передачу данных всегда инициирует главное устройство, она начинается со сценария Start, после чего Master передает 7-битный адрес подчиненного устройства и 8-й бит типа операции – чтение (1) или запись (0). Если адресованное подчиненное устройство есть на шине, оно отвечает 9-м битом низкого уровня (ACK), если же его нет или оно не в настроении отвечать (такое тоже возможно), оно ничего не делает и за счет подтяжки к плюсу на шине формируется сигнал высокого уровня, который расценивается мастером как NACK.
Если изначально была инициирована команда записи (Master Write), после передачи адреса и получения ACK мастер начинает передавать данные. Подчиненное устройство подтверждает прием каждого байта отдельным ACK’ом, а если такого не случилось, передачу требуется прервать. Если же была инициирована команда чтения (Master Read), то после передачи адреса и получения от подчиненного устройства ACK, мастер начинает генерировать тактовые импульсы на шину, а данные передает подчиненное устройство. В этом случае уже мастер должен подтверждать получение каждого байта отправкой ACK. Транзакция завершается сценарием Stop или Repeated Start. Последний эквивалентен двум последовательным сценариям Stop + Start, однако позволяет не терять владения шиной (для шин с одним мастером не актуально).
Более детально про этот протокол можно прочитать практически везде, включая даташит любой микросхемы, которая его поддерживает, а мы перейдем непосредственно к реализации. Сначала я написал код поддержки протокола на чистом Си, но потом понял, что мне придется вызывать метод отправки данных из ассемблерного кода отрисовки графика и будет значительно проще, если функция отправки будет тоже написана на ассемблере – тогда у меня будет абсолютное понимание, какие регистры процессора она использует, а какие регистры не трогает и в них могут сохраняться данные. А вот функцию приема переписывать на ассемблер необходимости не было, поэтому она осталась на Си:

Если посмотреть код внимательно, будет видно, что, по сути, аппаратная поддержка I²C в ATtiny85 не очень-то и упрощает задачу – всё то же самое можно было бы легко сделать и на чистых операциях ввода/вывода в порты. Кстати, если кто вдруг не знает как в этом случае реализовать выход с открытым стоком, то это очень просто – в сам порт выводим постоянное значение 0, а вывод данных осуществляем переключением режима работы порта на ввод (при выводе логической 1) или на вывод (при выводе логического 0).
Да, еще один важный момент – скорость передачи данных. Поскольку аппаратная поддержка I²C в МК очень слабая, тайминги приходится соблюдать программно, для этого я написал две функции задержки на ассемблере:

Для правильной работы этих функций необходимо предварительно определить две константы — CPU_SPEED_KHZ и I2C_SPEED_KHZ. Из названий понятно, что они задают скорости работы процессора МК и шины в килогерцах. Исходя из этих констант рассчитывается, сколько пустых циклов необходимо сделать процессору, чтобы добиться нужной задержки между сигналами. Если необходимости соблюдать строгие тайминги нет и нужна максимальная скорость, можно определить макрос I2C_MAX_SPEED, и тогда задержки превратятся в вызовы пустых функций.

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

Ну, и, конечно же, для преобразования числа в строку перетащил из своего предыдущего проекта функцию I16ToString. Поскольку операции деления в AVR не завезли, работает она по принципу циклического вычитания и написана, соответственно, на ассемблере. Если интересно, её код можно посмотреть в исходниках.
Если вспомнить изначальные требования, я хотел выводить на экран график потребления дорожки в реальном времени. Экран имеет ширину 128 точек, то есть, для вывода графика мне нужно 128 последних измерений. Но если АЦП будет производить 150 измерений в секунду, на экран не влезет статистика даже за последнюю секунду, а это значительно снизит её информативность. Поэтому решил обновлять график один раз в три измерения АЦП – это и хорошую скорость обновления в 50 Гц даст, и позволит на экране отображать статистику за последние 2.5 секунды. Также из этого буфера я буду брать минимальное и максимальное значения, чтобы тоже выводить их на экран в численном виде.
Но тут возникает вопрос, как правильно усреднить три измерения в одно? Конечно, можно найти среднее арифметическое, но это будет означать, что фактически у меня останется лишь 50 измерений в секунду, а это уже мало. Поэтому я решил не усреднять их никак – буду считать минимальное и максимальное значение из трех, и график буду выводить не точкой, а отрезком от минимума до максимума, потому что это значительно улучшит визуальное отображение. В итоге, алгоритм получился такой – прерывание АЦП производит 150 измерений в секунду, в каждом из которых обновляет две глобальных переменных g_adcValueMin и g_adcValueMax. А основной код 50 раз в секунду считывает и сбрасывает эти значения, начиная, таким образом, новый цикл измерений.

График по вертикали имеет размер всего 32 точки, поэтому если за минимум всегда принять значение 0 А, а за максимум – 40 А, реальные изменения тока будут практически не видны в таком масштабе. Значит, масштабировать его имеет смысл динамически, чтобы минимальное значение тока находилось всегда в самой нижней строке, а максимальное – в самой верхней. Математически это выглядит как формула:
y(n) = (I(n) – Imin)*31/(Imax – Imin)
Посмотрев на неё внимательней, я понял, что мне нужно сократить размерность хранимых данных с 12-ти бит до 11-ти, тогда максимальное хранимое значение будет 2047 и для умножения его на 31 снова хватит 16-ти бит. На пользовательские характеристики это никак не повлияет, т.к. изначально запланированный максимальный ток составлял как раз 20 А и для хранения такого значения 11-ти бит достаточно. А больший ток, прежде всего, нельзя пропускать через шунт сопротивлением 5 мОм, т.к. даже при постоянных 20 А на нем будет падать целых 2 ватта, что разогреет его до слишком высокой температуры и он просто отпаяется от платы.
Когда с форматом и преобразованием данных было решено, оставалась последняя небольшая задача – написать функцию, которая будет быстро рисовать на экране вертикальный отрезок от y1 до y2. Учитывая, что допустимым значением для параметров является диапазон [0..31], это дает всего 1024 возможных варианта результата, что при его размере в 4 байта, в принципе, позволяет использовать лукап-таблицу, размер которой составит 4 КБ. И это даже влезет в ПЗУ МК, но отдавать его половину под такую таблицу мне совершенно не хотелось.
С другой стороны, т.к. речь идет всего о 32-х пикселях, можно сделать отрисовку и обычным циклом – такая функция не потребует вообще никакой таблицы, но будет занимать достаточно много процессорного времени. Тоже не самое лучшее решение для маленького и слабого МК, поэтому я решил найти некоторый компромисс.
Для начала я напишу некую функцию F, которая будет принимать только один параметр N и рисовать на экране столбик в диапазоне [N..31], то есть, от указанной точки по вертикали до верха графика. Если результат этой функции проинвертировать, я автоматически получу другую функцию, которая рисует на экране столбик в диапазоне [0..N), то есть, от нуля и на один пиксель меньше, чем указанная по вертикали точка. А тогда искомый мной отрезок можно будет получить с помощью следующих вычислений:
R = F(y1) AND (NOT F(y2 + 1))
Теперь задача упрощается – надо быстро нарисовать столбик пикселей в диапазоне [N..31]. В принципе, здесь уже можно воспользоваться лукап-таблицой, так как она будет занимать всего 128 байт, но я решил попробовать более интересное решение, основанное на математике. Если предположить, что результат – это 32-битное значение, где младший бит представляет собой самую верхнюю точку, а старший – самую нижнюю, то математически функцию F можно описать как:
F(n) = 2⁽³²⁻ⁿ⁾ – 1
И именно такую функцию я и решил реализовывать:

Функция все еще использует лукап-таблицу для быстрого вычисления 2ⁿ для 0 <= n <= 7, но такая таблица занимает всего 8 байт. Еще функция использует интересную особенность AVR, что первые 32 байта адресного пространства ОЗУ на самом деле попадают на 32 регистра общего назначения процессора R0 – R31. Это позволяет команде st Z, R18 осуществлять запись байта в нужный регистр результата R22 – R25.
В итоге, рисование столбика занимает 25 тактов процессора без учета команд вызова и возврата из подпрограммы. Для сравнения, если бы я использовал лукап-таблицу из 128-ми байт, функция могла бы занимать 18 тактов.
Используя все наработки, описанные выше, я написал функцию display::UpdateDiagram(), которая:
Теперь требовалось оценить производительность полученной функция. Точных подсчетов я не делал, но по приблизительным оценкам сверху функция UpdateDiagram тратит не более двух тысяч тактов на обработку каждого измерения (включая вывод на экран) или около 256К тактов на весь график. Поскольку желаемая частота обновления составляет 50 Гц, между соседними обновлениями будет 320К тактов, что оставляет мне запас в 64К тактов на выполнение каких-либо других действий.
Это хорошо, но для данного устройства не совсем критично – ведь если бы я не уложился в отведенный лимит, я бы просто чуть повысил тактовую частоту I²C, ускорив, таким образом, отправку байтов на дисплей. Это значительно ускорило бы функцию рисования графика целиком, потому что основную задержку в неё вносит именно отправка. Контроллер дисплея, кстати, это позволяет сделать вплоть до работы в режиме MAX_SPEED, который на поверку оказывается в 2-2.5 раза быстрее стандартного. Вопрос оставался бы лишь в том, потянет ли такую частоту TMP100. Но испытывать, к счастью, не пришлось.
По сути, здесь никаких сложностей не возникло – я просто беру каждое максимальное значение из 128-ми пар, возвожу его в квадрат, складываю все полученные квадраты, после чего делю сумму на 128 и извлекаю из полученного значения квадратный корень. Поскольку значения в буфере хранятся 12-битные, их квадрат помещается в 24 бита, сумма – в 32 бита, а среднее значение квадратов – снова в 24 бита. Остается только написать код вычисления квадратного корня из 24-разрядного числа:

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


Чтобы немного защитить дисплей амперметре, решил добавить ему хранитель экрана (Screen Saver). Подумал, что если максимальный ток двигателя в течение минуты не превышает какого-либо предела, например, 0.5 А, то очищу весь экран, чтобы снизить бессмысленное выгорание пикселей. Однако, наблюдать чисто черный экран неинтересно, ведь даже не будет видно, что устройство работает, поэтому решил выводить на экран небольшую анимацию зажигания и угасания маленькой звездочки, возникающую по случайным координатам. Саму звездочку я предварительно нарисовал как 6 символов шрифта, где один из символов полностью пустой. Это позволяет не задумываться о необходимости стирания с экрана текущей звездочки перед выводом новой.
Но теперь появилась новая задача – где взять генератор случайных чисел для получения случайных координат? В теории, можно было воспользоваться тем, что есть в стандартной библиотеке С++, но я решил просто перенести уже имеющийся код генератора из одного из предыдущих проектов:

Генератор основан на LFSR — Linear-feedback shift register. Это простой сдвиговый регистр, на вход которого подается сигнал обратной связи, посчитанный исходя из текущего состояния некоторых битов регистра. Для сложения битов применяется функция XOR, т.к. она не теряет информации своих операндов. Следует отметить, что правильно спроектированный LFSR позволяет перебирать 2ⁿ — 1 (то есть, все кроме одного) своих состояний в некой псевдослучайной последовательности, поэтому и подходит для генератора псевдослучайных чисел. В моем конкретном случае используется регистр длиной 31 бит с обратной связью XNOR от битов 27 и 30 (если считать от 0). Использование XNOR позволяет регистру начать свою работу с полностью нулевого состояния, чего было бы невозможно добиться использованием функции XOR, в остальном никаких отличий между ними нет.
Кстати, если вам 40+, скорее всего вы познакомились с LFSR гораздо раньше, чем узнали, что это вообще такое. Помните тот переключатель на 4 елочных гирлянды с 7-ю режимами на логических микросхемах серии К155? Он был опубликован в журнале Радио № 11 за 1986-й год:

У него был «магический» 6-й режим, который по описанию авторов должен был «повторять разнообразие всех предыдущих режимов», но для его работы требовалась отдельная логическая микросхема ИЛИ-НЕ К155ЛЕ1, которую было сложно найти, поэтому даже не все радиолюбители его реализовывали. Так вот, узел, собранный на этой микросхеме (я обвел его на схеме красным прямоугольником) был ни чем иным, как элементом «Исключающее ИЛИ» или, по-современному, XOR. А совместно со сдвиговым регистром на 4-х D-триггерах он образовывал LFSR размером 4 бита с обратной связью от битов 2 и 3. И именно эту бегущую псевдослучайную последовательность из 15 разных состояний, генерируемую LFSR авторы и представили как интересный дополнительный режим переключения ёлочных гирлянд.
Кстати, схемотехника LFSR очень проста, что позволяет с легкостью реализовывать его аппаратно. И раньше это любили делать внутри больших микросхем, например, для синтеза «белого шума». За один сдвиг LFSR генерирует 1 бит псевдослучайной последовательности, соответственно, если вам нужно 8, сдвинуть регистр надо 8 раз. Брать результат можно из любого бита регистра (т.к. рано или поздно один и тот же бит проедет по всему регистру), главное делать это всегда из одного и того же места.
В качестве звукового извещателя используется стандартный пьезокерамический излучатель в пластиковом корпусе, который был у меня в наличии. Его особенностью является то, что от низкого напряжения он громко звучит только около своей резонансной частоты, которую желательно предварительно измерить с помощью генератора, после чего занести в программу, так как у разных экземпляров она может немного отличаться. У моего это оказалось 3660 Гц. При этом какого-либо заметного электрического резонанса мне у него обнаружить не удалось.
Однако я прекрасно понимаю, что вряд ли кому-либо из вас понадобится решать аналогичную задачу, поэтому я подумал и о паре других случаев, где обозреваемое устройство можно было бы использовать. Во-первых, это измерение тока пуска двигателя автомобиля – устройство для этого понадобится чуть переделать, заменить шунт на меньший, сделать автоматический старт по достижению некоторой амплитуды тока, автоматический стоп при его снижении и т.д. Но общий принцип может остаться таким же.
Во-вторых, устройство легко можно переделать в измеритель тока потребления аккумуляторного электроинструмента, какие часто используют в обзорах. Для этого тоже придется снизить шунт, немного (раза в 4) замедлить скорость движения графика, вернуть отображение текущего значения тока вместо среднеквадратического, но и здесь общая логика функционирования вряд ли изменится. Возможно, кстати, что однажды я соберу такое и себе, так как часто бывает интересно, сколько потребляет какой-либо электроинструмент в разных режимах. Пишите в комментариях, если вам эта тема тоже интересна.
Что касается получаемой точности измерений, на своем приборе я заметил, что не смотря на усреднение 64 последовательных выборок АЦП, текущие показания тока прыгают в пределах 0.08 А. Точность RMS значения тока выше, и составляет около 0.02 А, но она вообще меняется значительно медленней. Таким образом, при максимальном измеряемом значении в 20 А можно считать, что разрешение прибора составляет 0.1 А, а точность – не хуже 1%±0.1 А. Результаты не особо выдающиеся, но вполне подходящие для всех описанных выше задач. Кстати, причиной такого большого разброса мгновенных показаний я склонен считать встроенный в МК усилитель АЦП, так как без него разброс значений значительно ниже.
Исходный код проекта, схему, плату и корпус можно найти в репозитории по ссылке:
github.com/kdekaluga/continuous_current_meter
На этом у меня всё, спасибо за внимание.
Предыстория или зачем это вообще нужно?
Поскольку работа у нас сидячая и домашние дела тоже делаются, в основном, дома, часто на прогулки банально не остается свободного времени. А когда такое время вдруг получается выкроить, может наступить вечер или испортиться погода, и идти на улицу уже просто не захочется. Но регулярно ходить пешком очень полезно для здоровья, поэтому весной этого года мы решились на покупку беговой дорожки.На самом деле, я бы купил её и значительно раньше, так как давно задавался таким вопросом, но очень смущала необходимая ей площадь в квартире, ведь это практически 2 квадратных метра, а дорожка — не третий по счету 3D-принтер по акции, место для которого найдется всегда. И тут вдруг в начале этого года внезапно была разобрана лоджия, образовалось достаточное свободное пространство, которое можно было занять без ущерба бытовой повседневной жизни, и звезды, наконец, сошлись.
Модель выбирали долго, так как не хотели брать самые дешевые из страха столкнуться с плохим качеством, а более профессиональные не могли взять из-за их быстро возрастающего веса – хотелось, чтобы манипуляции с беговой дорожкой можно было проводить самостоятельно и, соответственно, хотелось ограничить вес сверху на отметке 70 кг. Такое ограничение было, в том числе, и из-за вопроса начальной сборки дорожки, потому что мы не были уверены, что мастер от продавца согласится собирать и устанавливать дорожку на лоджии, так как один мой знакомый уже столкнулся с такой проблемой. В его случае мастер сказал, что если дорожка будет эксплуатироваться на лоджии, то гарантия на неё будет автоматически аннулирована, т.к. лоджия не соответствует требуемым условиям эксплуатации беговой дорожки. Возможно, ему просто не повезло с мастером, однако, дополнительные опасения это добавило.
В итоге, выбор пал на модель CardioPower T45 New – она хоть и немного не вписывалась в обозначенный лимит по весу, но имела весьма интересные эксплуатационные характеристики для домашнего применения, да и по самому бренду CardioPower у нас к тому времени уже набралось несколько положительных отзывы от знакомых, которые приобрели дорожки данной марки ранее.
Когда с моделью определились, стали искать, где её можно приобрести, да не просто приобрести, а еще и с доставкой в нашу «глубинку». И это стало дополнительным квестом, т.к. далеко не все продавцы предлагают доставку крупногабаритного товара до квартиры, у многих доставка предлагается только до подъезда, а дальше уже мучайся как хочешь – или знакомого бери, или водителя уговаривай, или отдельных грузчиков нанимай. Ситуацию еще осложняет временной диапазон доставки, который в большинстве случаев покрывает весь световой день, а о конкретном времени тебя известят только за 10-30 минут до непосредственно самого момента доставки. То есть, даже нанять грузчиков заранее не выйдет. Но и эта проблема, в конечном счете, была разрешена и продавец с доставкой непосредственно до квартиры был найден. Правда, он не предлагал совершенно никакой услуги сборки, но к тому времени я уже посмотрел пару роликов по сборке дорожки на ютюбе, поэтому был морально (и физически) готов собрать её самостоятельно.
Сама покупка прошла без каких-то особенностей – положили в корзину, оформили заказ, оплатили, подождали и, наконец, в назначенный день (что даже немного удивило) два сильных мужчины занесли к нам в квартиру здоровенную двухметровую картонную коробку. Позвонили они, как и ожидалось, только за 15 минут, но тут уже это не имело совершенно никакого значения, ведь единственной нашей задачей оставалось лишь открыть им входную дверь.
Сборка дорожки прошла тоже без каких-либо проблем – всё-таки предварительный просмотр обучающих видео работает очень хорошо, да и, по сути, сама сборка совершенно несложная, тебе нужно лишь только уметь пользоваться отверткой и шестигранником, а также быть мускулистым и плечистым. Последнее особенно важно в момент доставки двух исходных частей дорожки от входной двери до лоджии, но тут можно немного считерить и сначала дополнительно разобрать дорожку, сделав, таким образом, из двух частей три. Это, кстати, еще и сильно помогает занести её в достаточно узкий дверной проем лоджии без повреждений. Но потом, конечно, собирать придется побольше и уже самому, без обучающих видео.
Последующая эксплуатация дорожки как будто и не заслуживает дополнительного внимания, так как она совершенно стандартная – включил, выбрал скорость и наклон, включил на планшете любимый канал и пошел совмещать приятное с полезным. И тут, кстати, размещение дорожки на лоджии имеет неоспоримый плюс – всегда можно открыть окно на улицу и добавить к своему увлекательному путешествию на одном месте аромат и ветерок настоящего свежего воздуха – прямо 5D-погружение, как в современном кинотеатре.
В общем, концепция беговой дорожки нам очень понравилась, да так, что эксплуатировать стали мы её, в общей сложности, ежедневно и по часу. И эксплуатировали бы и дальше долго и счастливо (наверное), но через три месяца она сломалась. Ну, не прямо так сломалась, что совсем перестала работать, а стала через 15 минут уверенной ходьбы издавать странный неприятный запах, который похож на аромат горелого пластика. Снятие верхнего кожуха моторного отсека показало, что запах этот исходит из самого двигателя, который по какой-то причине очень даже серьезно греется. Так как дорожка имеет год гарантии, решено было предпринять типовые ответные действия – обратиться в сервисный центр.
И вот тут уж я с вашего позволения опущу детальные подробности последующего месячного удаленного общения с сервисным центром, не буду рассказывать, тянут ли они время, и приходится ли им постоянно названивать и написывать, а перейду сразу к главному – дорожку, в итоге, починили. Заменить пришлось двигатель и полотно, и это, наверное, половина стоимости новой дорожки. Локального мастера в нашем городе они не нашли, поэтому просто высылали комплектующие, а я сам менял. Но после месячного ожидания возврата былого удовольствия пробежки на одном месте я был готов на всё, лишь бы она заработала. Да и замены эти, в принципе, не так сложны и даже в какой-то степени интересны и волнительны, особенно если делаешь это в первый раз – ведь всегда можно что-то сломать. Но, к счастью, обошлось.
Резюмируя произошедшее, по какой-то причине на дорожке за такой короткий промежуток времени износилось беговое полотно, у него поднялось механическое сопротивление движению, что увеличило нагрузку на мотор, который такого не выдержал, начал сильно греться и вонять. Да, несмотря на то что на дворе 2025 год, в дорожке стоит обычный коллекторный мотор постоянного тока 180 В 7.5 А (то есть, 1350 Ватт при заявленной мощности дорожки 3.25 лошадиных силы), на котором даже нет минимального термодатчика, позволяющего контроллеру следить за температурой двигателя! И это при цене изделия в 70 тысяч.

Предвосхищая ваши вопросы – причины такого быстрого износа полотна я назвать не могу. Не смог это сделать и сам сервисный центр, они просто указали, что стандартный срок службы (и гарантия) на полотно составляет всего 6 месяцев. И да, полотно было у меня правильно натянуто и смазано, за этими моментами я следил с самого начала, т.к. перед эксплуатацией дорожки всё же прочитал инструкцию полностью. Также температурный режим эксплуатации дорожки был всегда в допустимых руководством пределах. Возможно, полотно было изначально бракованное или просто некачественное, я склоняюсь именно к такому варианту. И тогда тут остается лишь только надеяться, что новое полотно таким не будет и прослужит дольше.
Еще в процессе ремонта сервисный центр просил меня замерять ток потребления двигателя под нагрузкой, так как именно по нему можно судить о состоянии полотна. И вот тут я обнаружил, что сделать это обычными приборами не представляется возможным – ток потребления сильно прыгает в такт с моей ходьбой, поэтому мультиметры показывают любые промежуточные значения от минимального до максимального. Немного спасает барграф UT61E, но он позволяет оценить потребление лишь верхнеуровнево и в моменте, а если хочется получить более точные значения или просто быстро найти максимум, то остается единственный вариант – это использовать шунт и подключать осциллограф. Понятно, что это уже намного более сложная задача, таким даже раз в неделю заниматься не будешь.
С другой стороны, совсем не хочется, чтобы и с новым двигателем произошла аналогичная история, а так как со стороны контроллера дорожки никакой защиты нет, мне захотелось установить в цепь двигателя дополнительный амперметр и, таким образом, контролировать его потребление в реальном времени. Но обычный амперметр за пару баксов тут не подойдет, значения меняются слишком быстро, поэтому нужно что-то посерьезней. Возможно, готовые устройства, обладающие требуемой функциональностью и существуют, но вот я таких за умеренную цену не видел. Да даже если бы и видел, скорее всего, его доставку пришлось бы ждать месяц, а хотелось здесь и сейчас, поэтому снова решил сделать что-то свое, ведь и задача, вроде как, не такая уж сложная.
Технические характеристики
Чтобы понимать, что, собственно, надо сделать, начал проектирование устройства с желаемых технических характеристик:- Измерение тока не менее 100 раз в секунду.
- Отображение текущего, а также минимального и максимального значения тока за последнюю пару секунд.
- Точность не хуже 0.1 А.
- Максимальный измеряемый ток до 20 А в пике, 10 А долговременно.
- Построение небольшого графика тока потребления за последнюю пару секунд – поможет выявить какие-либо отклонения в работе.
- Контроль температуры двигателя с помощью внешнего термодатчика.
- Выдача предупреждающего звукового сигнала при слишком высоком нагреве двигателя.
- Собственный блок питания устройства, чтобы исключить какое-либо вмешательство в электрическую схему и плату контроллера дорожки.
- Простота решения, чтобы устройство можно было быстро реализовать на имеющихся компонентах без необходимости закупки и ожидания доставки новых.
Немного подумав над элементной базой, остановился на следующих компонентах:
- Шунт 5 мОм из манганина. Комплект таких я покупал при изготовлении зарядки для Макиты, там они показали себя крайне хорошо. При токе 10 А на шунте будет падать 50 мВ и 0.5 Вт, что, с одной стороны, вполне достаточно для измерения, а с другой стороны не будет приводить к чрезмерному его нагреву.
- Микроконтроллер ATtiny85. Покупались давно, но всё еще есть в наличии, так как использовались редко. В принципе, это достаточно слабый МК с маленьким количеством выводов, но для данной конкретной задачи его возможностей должно хватить, всё же он имеет 8 КБ ПЗУ и 512 байт ОЗУ. Зато его система команда и архитектура давно мне известна, что серьезно упростит написание программного кода. Также у ATtiny85 оказалась «киллер-фича», сделавшая его еще более подходящим – у его АЦП есть режим дифференциального измерения со встроенным усилителем с коэффициентом усиления 20. То есть, он сможет считывать напряжение прямо с шунта без дополнительного ОУ.
- Монохромный OLED-экран 0.96 дюйма с разрешением 128х64 точек и подключением по протоколу I²C. Он просто был, давно лежал и «просился» поставить его куда-нибудь, но для большинства задач его размер оказывался слишком маленьким, а вот для данной – самое то.
- В качестве датчика температуры двигателя решил использовать имеющиеся микросхемы TMP100. Они хорошо зарекомендовали себя в предыдущих проектах, имеют разрешение 0.06 градуса, а также работают по протоколу I²C, что позволит подключить такой датчик прямо параллельно дисплею и не потребует дополнительных выводов МК.
Поскольку общее максимальное потребление устройства я оценил как 5 В 200 мА, первоначально для его питания хотел использовать самую слабую из имеющихся готовых плат блоков питания на напряжение 5 В, однако, чуть поразмыслив решил и блок питания сделать свой. А что, ведь раньше я любил этим занимался, почему бы не вспомнить былые времена? Так как мощность нужна минимальная, делать решил на самом младшем ШИМ-контроллере серии TNY27x, который у меня есть, и им оказался TNY275. Обратноходовый БП на этом контроллере предельно прост, требует минимум внешних деталей и не нуждается в отдельной обмотке трансформатора для питания контроллера, так как контроллер умудряется питаться непосредственно от высокого напряжения. То есть, бери и делай, остается только трансформатор посчитать.
Схема
Полистав документацию на выбранные компоненты, нарисовал следующую схему устройства:
Входное напряжение 220 В через гасящий резистор R6 и предохранитель F1 поступает на диодный мост D1. Резистор и предохранитель специально установлены в разных сетевых проводах – поскольку цепь двигателя, куда будет подключаться устройство непосредственно связана с сетью (об этом расскажу чуть подробнее далее), и если в устройстве вдруг произойдет какой-либо пробой, ток короткого замыкания пойдет только по одному из двух сетевых проводов, причем неизвестно заранее, по какому именно, поэтому каждый из них должен иметь какую-либо защиту.
После диодного моста установлен накопительный конденсатор С1 ёмкостью 2.2 мкФ. Традиционно при разработке импульсного БП для напряжения 220 В придерживаются эмпирического соотношения 1 мкФ ёмкости накопительного конденсатора на 1 Вт выходной мощности, однако у меня в наличии оказались конденсаторы на 2.2 мкФ, поэтому решил ставить такой. Дальнейшая высоковольтная часть схемы блока питания стандартная и взята непосредственно из даташита на ШИМ-контроллер, поэтому детально разбирать её не буду. Отмечу только, что с помощью конденсатора С2 ёмкостью 1 мкФ ШИМ-контроллер переводится в режим пониженного тока (режим совместимость с предыдущей моделью TNY274), а также что схема снаббера тут более сложная – и на конденсаторе, и на супрессоре.
Вторичная цепь блока тоже достаточно стандартная, это выпрямитель и стабилизатор напряжения на TL431 с обратной связью через оптрон. Резистор R4 параллельно светодиоду оптрона необходим для того, чтобы ток холостого питания TL431 не вызывал свечения диода и, таким образом, не передавал сигнал обратной связи контроллеру постоянно. Конденсатор С5 в цепи обратной связи TL431 оказался не нужен, блок работает лучше без него (это связано с особенностью ШИМ-контроллера), однако место на плате под него я решил оставить, поэтому на схеме его ёмкость обозначена как 0 нанофарад.
Напряжение с выхода блока питания подается непосредственно на дисплей, схему зуммера и датчик температуры, а вот микроконтроллер питается через дополнительный двойной фильтр на дросселе L3 и резисторе R17, призванном минимизировать помехи от работы блока. Скорее всего, применение двухзвенного LRC-фильтра тут излишне, хватило бы и однозвенного LC-варианта, но в какой-то момент я почему-то решил, что нужно именно два звена.
Для подключения датчика температуры и дисплея на плате устройства предусмотрены два отдельных разъема I²C, соединенных параллельно. Также к контроллеру подключен разъема SPI для осуществления его внутрисхемного программирования.
Еще можно отметить несколько необычное подключение пьезоэлектрического излучателя между затвором и стоком полевого транзистора – такой вариант позволяет увеличить громкость звучания излучателя, так как удваивает эффективное напряжение на нем. Поскольку с точки зрения схемы излучатель является конденсатором без явно выраженных резонансных качеств, данный вариант подключения вполне допустим.
Ну и, наконец, токоизмерительным шунтом схемы являются пара резисторов R19/R29, включенных параллельно. В реальности на плате будет установлен только один из них, потому что для моей задачи сопротивление в 5 мОм является оптимальным. Однако, если вдруг схему захочется использовать для другого диапазона измерения, я предусмотрел возможность установки двух резисторов типоразмера 2510 параллельно. Сигналы с шунта через резисторы R10 и R11 подаются на соответствующие входы АЦП МК, образуя совместно с конденсаторами С10 и С11 ФНЧ для фильтрации высокочастотных помех.
Шунт установлен в схеме так, что его нижний конец оказывается жестко заземлен и, таким образом, инверсный вход АЦП всегда сидит на земле, что фактически превращает дифференциальный вход АЦП в однополярный. Да, действительно, в данном устройстве нет необходимости в дифференциальном АЦП, потому что схема имеет свой собственный блок питания и ток нагрузки ни коим образом не проходит по питающим дорожкам платы, то есть, не создает на них никакого дополнительного падения напряжения. Однако, режим усиления 20х в АЦП МК доступен только в дифференциальном варианте включения, поэтому пришлось реализовывать именно его. Но зато это позволит быстро перепрофилировать устройство на питание от измеряемой схемы, если вдруг это понадобится для какой-либо другой задачи.
Теперь, когда с принципиальной схемой устройства разобрались, рассмотрим, как подключается плата к двигателю беговой дорожки используя следующую упрощенную схему подключения:

Чтобы было более понятней, я частично нарисовал основные высоковольтные цепи блока управления дорожкой, прозвонив их предварительно мультиметром. Вопрос вызывает лишь шунт R1 на плате контроллера – его я визуально не увидел, а мультиметром найти такие вещи нельзя, т.к. его сопротивление составляет всего единицы миллиом, как и в моем устройстве. Однако, он определенно должен быть, так как контроллер должен следить за потреблением двигателя, поэтому будем считать, что он есть. Возможно, он в SMD-исполнении и находится с нижней стороны платы, куда обзор закрыт алюминиевой защитной пластиной.
Из особенностей ШИМ-контроллера дорожки следует отметить то, что в нем нет выходного сглаживающего дросселя, его роль выполняет непосредственно сам электродвигатель. А это означает, что по питающим проводам мотора проходят высокочастотные пульсации весьма существенной амплитуды, которые могут создавать помехи на другие узлы схемы и, в частности, на мой измеритель. Это надо будет иметь в виду.
Также из схемы становится понятно, что высоковольтные части двух разных блоков питания оказываются дополнительно соединены через межобмоточную ёмкость трансформатора Т1 и через защитный конденсатор С7. То есть, подключение амперметра в цепь двигателя добавляет к ней дополнительную ёмкость, и, например, включить его в разрыв плюсового провода двигателя уже было бы нельзя, ведь эта ёмкость оказалась бы подключена к точке соединения силовых транзисторов и очень негативно влияла бы на их переключение. К счастью, в минусовом проводе она никакого влияния оказывать не будет, так как там ожидается только наличие шунта, параллельно которому и так должен стоять какой-либо фильтрующий конденсатор.
Дополнительно следует рассмотреть негативный сценарий пробоя трансформатора или Y-конденсатора С7. По схеме становится видно, что серьезных повреждений контроллеру дорожки такой пробой не нанесет, так как весь ток пробоя пойдет только через шунт на плате контроллера, который должен быть способен выдерживать большой ток достаточное время, чтобы во входной цепи блока питания сгорел предохранитель или гасящий резистор (в зависимости от того, какой полярностью блок будет подключен к сети).
На этом электрическую часть устройства можно считать полностью рассмотренной и пора переходить к следующей – математической, а именно расчету импульсного трансформатора.
Расчет трансформатора
Поскольку основные рабочие параметры у TNY275 фиксированные, расчет трансформатора для преобразователя значительно упрощается – нет необходимости заниматься выбором тока ключа и расчетом передаваемой энергии, трансформатор рассчитывается и изготавливается под те параметры, которые имеет ШИМ-контроллер. Вот и начнем с того, что выпишем их из даташита:F = 132 KHz
Fmax = 140 KHz
Tmin = 7.14 мкс
Dmax = 65%
I = 250 mA
Imax = 300 mA (с запасом)
Так как мощность, которая нужна мне на выходе блока питания весьма небольшая, будем ориентироваться на режим разрывных токов. В этом режиме энергия, запасенная в индуктивности в начале текущего импульса полностью успевает передаться в нагрузку к его концу, обеспечив, таким образом, «чистый старт» следующего импульса с нулевого тока ключа. Это уменьшает потери при открытии ключа и, соответственно, снижает его нагрев.
Возьмем отраженное рабочее напряжение в первичной обмотке Uinv = 150 В. Такое значение, с одной стороны, обеспечит достаточно быстрый «разряд» индуктивности, с другой – относительно невысокое напряжение на закрытом ключе (~310 В + 150 В = ~460 В при максимально допустимом 700 В). Dmax у TNY275 составляет 65%, что дает рабочие временные характеристики Tonmax = 4.64 мкс и Toffmin = 2.5 мкс. Так как количество энергии, которое входит в катушку за Ton должно равняться количеству энергии, которое выходит из неё за Toff, отношение данных временных характеристик (1.856) определяет минимальное напряжение, от которого преобразователь всё еще сможет работать на полной мощности (если вам что-то непонятно, задавайте вопросы в комментариях, я поясню). В данном случае оно составит Uinv/1.856 = 81 В, что очень даже неплохо (хотя и совершенно не нужно для разрабатываемого устройства).
Теперь у нас есть вся необходимая информация, чтобы рассчитать индуктивность первичной обмотки. Она должна быть такой, чтобы при напряжении 81 В ток в ней поднялся до 250 мА за 4.64 мкс:
L1 = (UΔt)/I = (81*4.64*10⁻⁶)/0.25 = 1.5 мГн
А зная индуктивность, можно посчитать максимальную энергию, которую преобразователь сможет отдать в нагрузку за секунду и, таким образом, определить его максимальную выходную мощность:
Aimp = (LI²)/2 = (1.5*10⁻³*(0.25)²)/2 = 46.9 мкДж
P = Aimp*F = 46.9*10⁻⁶*132000 = 6.2 Вт
То есть, преобразователь на TNY275 с таким трансформатором вполне мог бы обеспечить на своем выходе 5 В 1 А во всем диапазоне входного питающего напряжения от 100 до 230 В! Неплохо, конечно, но мне нужно всего 1 Вт, поэтому мотать трансформатор я буду на самом маленьком сердечнике, который только у меня есть:

Взят этот сердечник был, скорее всего, из небольшой компактной люминесцентной или светодиодной лампы. Кстати, на фото хорошо видно, что в процессе вытаскивания из лампы ферритовый сердечник был поломан, а позже склеен цианоакрилатом. Основные параметры сердечника:
Площадь магнитопровода S = 2.5*4.8 = 12 мм²
Средняя длина магнитной линии l = 30 мм
Зазор g = 0.6 мм
Прежде всего посчитаем, какое количество витков должно быть в первичной обмотке. Сначала попробуем сделать это по упрощенной формуле индуктивности обмотки на сердечнике с зазором:
L = (µ₀*S*N²)/g
Или
N = sqrt((L*g)/(µ₀*S)) = sqrt((1.5*10⁻³*0.6*10⁻³)/(4π*10⁻⁷*12*10⁻⁶) = 244 витка
А теперь проверим результат, сравнив его с измеренной индуктивностью изначально намотанной на сердечнике обмотки, которая составила 2.13 мГн для 256 витков. Т.к. индуктивность пропорциональна квадрату числа витков, при таких исходных данных получим, что для 1.5 мГн надо всего 215 витков. 215 и 244, конечно, числа близкие, однако всё равно отличаются друг от друга достаточно сильно, поэтому я предпочту использовать данные, полученные практическим путем – так результат будет более точным. Ведь формула, которую я представил выше – приближенная, если углубляться во все детали теории электромагнитного поля, на точный расчет одного трансформатора не хватит даже объема всей статьи.
Тем не менее, приближенные формулы тоже нужны, и по следующей такой формуле можно оценить максимальный ток, который сможет выдержать данный трансформатор в первичной обмотке до входа в насыщение:
Imax = (Bmax*g)/(µ₀*N) = (0.25*0.6*10⁻³)/(4π*10⁻⁷*215) = 555 мА
Получается, трансформатор будет с приличным запасом по току, и это очень хорошо. Скорее всего, электрическая лампочка, из которой он был позаимствован была на большую мощность, чем 6 Вт.
Посчитаем число витков во вторичной обмотке. Если в первичной обмотке будет 215 витков при напряжении 150 В, для получения 6 В во вторичной обмотке понадобится 215*6/150 = 8.6 витка. Так как намотать 8.6 витка нельзя, следует выбрать одно из двух ближайших целых значений – 8 или 9 витков. Если взять 8 витков, напряжение на вторичной обмотке получится 5.58 В, если же взять 9 витков, напряжение будет 6.28 В. Так как падение напряжения на выпрямительном диоде может составить 0.8 – 1 В, в принципе, можно выбрать любое из этих значений. Я возьму 9 витков – это даже чуть уменьшит отраженное напряжение в первичной обмотке (до примерно 143 В).
Наконец, остался последний момент – выбор толщины провода для обмоток. По-хорошему, диаметр провода выбирается исходя из среднеквадратического значения тока, но я поступлю проще – изначальная обмотка из 256 витков была намотана проводом диаметром 0.18 мм без какой-либо межвитковой изоляции и занимала практически все место на каркасе. Мне надо намотать чуть меньше витков, но нужна изоляция и вторичная обмотка, так что проводом 0.18 мм намотать первичку я уже не смогу – для него просто не хватит места. То есть, надо брать что-то потоньше. Оптимально, наверное, был бы провод диаметром 0.15 мм, но у меня такого нет, есть только 0.11 мм. Значит, им и буду мотать, и тогда гарантированно освободится достаточно места для межвитковой изоляции и вторичной обмотки, для которой я уже возьму провод потолще, скажем, 0.35 мм.
Итого, имеем следующие данные для намотки:
Первичная обмотка – 215 витков провода диаметром 0.11 мм.
Вторичная обмотка – 9 витков провода диаметром 0.35 мм.
Теперь можно приступить непосредственно к намотке трансформатора. Первым делом я намотал на голую катушку два слоя каптонового скотча – он немного сгладит углы, уменьшая возможные повреждения изоляции провода, а также создаст дополнительную изоляцию обмотки от катушки. Затем намотал первую половину (110 витков) первичной обмотки и снова проложил два слоя скотча, после чего намотал вторую половину (105 витков). Финальную изоляцию между обмотками делал также из каптонового скота, только уложил его уже в 4 слоя. После намотки первичной обмотки временно вставил половинки сердечника в катушку и замерил получившуюся индуктивность:

Шикарная точность, отклонение от требуемой индуктивности получилось менее 1%! После замера я снова вынул сердечник и намотал поверх изоляции вторичную обмотку из 9 витков, после чего замотал весь трансформатор своим любимым зеленым малярным скотчем:


После сборки трансформатора я повторно замерил индуктивность первичной обмотки, и она чуть возросла – стала примерно 1.53 мГн. Произошло так потому, что скотч сильнее прижал половинки сердечника друг к другу и, таким образом, уменьшил немагнитный зазор. Но это не страшно, отклонение получилось очень незначительное. Также попробовал замерить и индуктивность рассеяния – делается это достаточно просто, во время измерения индуктивности первичной обмотки вторичная замыкается накоротко. Это как бы замыкает и всю ту часть первичной обмотки, которая связана со вторичной, оставляя для измерения лишь несвязанную часть, которая и называется индуктивностью рассеяния. Результат получился весьма высоким – на частоте 100 КГц индуктивность рассеяния составила порядка 210 мкГн.
Да, это много, но и причина понятна – по правилам надо было разбивать первичную обмотку на две части и мотать вторичную обмотку между этих частей, но это существенно усложняет конструкцию трансформатора, особенно, когда речь идет о таких небольших габаритах. Поэтому морально я был готов к высокому значению. На практике это будет означать повышенную нагрузку на снаббер, его более высокий нагрев и, таким образом, более низкий КПД всего блока питания. Однако, мы здесь говорим о выходной мощности около ватта, так что такими мелочами можно и пренебречь. Но если вдруг вам придется проектировать обратноходовой преобразователь на большую мощность, обязательно учтите этот момент.
Плата
Устройство будет состоять из двух печатных плат – основной, на которой расположится блок питания и микроконтроллер и маленькой платы термодатчика, которая будет закреплена непосредственно на двигателе дорожки. Начнем с основной платы.Сначала я расположил все детали в длинную узкую полоску, но потом понял, что плату такой длины я не смогу поместить в свой фотополимерный принтер (он у меня достаточно маленький), поэтому перекомпоновал плату в более квадратную, всё равно места в корпусе дорожки достаточно:

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


После изготовления платы сначала собрал блок питания и проверил его работу, чтобы случайно не сжечь микроконтроллер и дисплей, если с блоком что-то не так. Но он заработал сразу без какой-либо настройки. Осциллограмму его работы (напряжение на стоке ключевого транзистора) вы можете увидеть ниже:

Такая интересная форма сигнала получается из-за большой индуктивности рассеяния первичной обмотки. Дело в том, что после закрытия ключа эта индуктивность как бы «повисает в воздухе» и совместно с межобмоточной ёмкостью и ёмкостью подключенных к ней деталей образует колебательный контур, в котором возникают затухающие колебания. Они накладываются на нормальный график напряжения на стоке и получается то, что вы видели выше.
Далее я решил проверить, насколько сильно блок греется от расчетной нагрузки в 200 мА. После 30-ти минут работы платы на открытом воздухе картина нагрева была такой:

Видно, что вся плата нагрета более-менее равномерно – температура и ШИМ-контроллера, и трансформатора, и снаббера составила порядка 42 градусов. Чуть сильнее грелся только выходной диод, его температура приближалась к 50-ти градусам. Но для электронных компонентов это сущие мелочи, поэтому можно смело считать, чтоб блок питания сможет выдать 1 Вт выходной мощности в любых разумных условиях эксплуатации.
Пульсации напряжения на выходном конденсаторе получились в районе 40 мВ PP:

После LCR-фильтра (без нагрузки) амплитуда пульсаций упала и составила менее 5 мВ РР:

Тогда я собрал остальную часть платы:


Как это часто случается, на этапе сборки и наладки устройства в схему были внесены небольшие изменения, поэтому на фото можно рассмотреть незапланированно установленные детали. Саму схему и трассировку дорожек в электронном варианте я уже обновил, но переделывать и перепаивать плату конечно же не стал.
Теперь перейдем к датчику температуры TMP100. Чтобы ускорить процесс, я взял одну из имеющихся у меня печатных плат, которые остались от предыдущих проектов. Выбрал самую маленькую, где не разведены перемычки, позволяющие изменять адрес датчика на шине I²C. Датчик должен подключаться к основной плате устройства с помощью четырехпроводного кабеля, и вот тут, вспомнив про то, что ШИМ-контроллер дорожки может создавать сильные помехи, я захотел использовать экранированный кабель. Но где его взять?
Выручил Чиподип – оказалось, что нашем городе в наличии есть волшебный четерыхпроводный экранированный микрофонный кабель КММ-4х0.12 в шикарной толстой ПВХ-изоляции, способной выдержать аж 600 В! Последний момент очень важен, ведь через блок управления дорожкой низковольтная часть амперметра тоже оказывается связанной с сетью, а металлический корпус дорожки, рядом с которым будет проходить кабель заземлен. То есть, между экраном кабеля и корпусом дорожки будет действовать полное сетевое напряжение, поэтому изоляция кабеля должна быть достаточно надежной.
Итак, кабель был куплен, после чего датчик был запаян на плату:

Экранирующую оплетку кабеля я решил подключить к общему проводу только со стороны основной платы, а со стороны датчика оставить неподключенной. Мне кажется, в этом случае наведенное в оплетке напряжение радиопомех никак не сможет влиять на сигналы, проходящие внутри кабеля. Если же подключить экран с двух сторон, он оказывается включенным в сигнальную цепь и, таким образом, наведенное в нем напряжение будет мешать нормальной работе устройства. Но опыта в экранировании цепей у меня немного, поэтому если кто из вас знает, как надо делать правильно, напишите в комментариях.
С другой стороны кабеля я установил стандартный 4-контактный JST-разъем с шагом 2.54 мм, где уже соединил экран с общим проводом питания схемы:

После чего затянул датчик в термоусадку:

По мне, получилось весьма красиво. Когда все части устройства были собраны, провел еще одно испытание самодельного блока питания, но теперь уже с реальной нагрузкой. Оказалось, он вполне запускается от постоянного напряжения 30 В на входе! И потом сохраняет нормальную работу при его снижении чуть ниже 20 В! Выглядело это просто нереальным, ведь это в 10 раз меньше номинального значения, но такие уж особенности у обратноходовых блоков питания. По входу, кстати, потребление составило около 150 мВт, то есть, намного ниже 1 Вт, на который я рассчитывал изначально.
Корпус
Основная плата амперметра будет стоять в моторном отсеке дорожки рядом с её контроллером и двигателем, чтобы проводные соединения между платами были короче. Однако, разместить там же дисплей не получится – во-первых, я не хочу пилить корпус моторного отсека, чтобы в случае чего не лишиться гарантии (а она уже пригодилась), а, во-вторых, дисплей у меня достаточно маленький и с такого расстояния цифры на нем будет просто трудно рассмотреть. Поэтому я решил вынести дисплей поближе к пользователю дорожки и установить его на круглую трубу диаметром 32.5 мм, которая соединяет левую и правую рукоятки дорожки.Чтобы ничего не сверлить в основании дорожки, для платы я разработал вот такую «кроватку», которую просто приклею к основанию моторного отсека на двусторонний скотч:

А корпус и крепление дисплея к трубе получились такими:

Внешне корпус дисплея очень похож на наручные часы и состоит из четырех частей – собственно корпуса, стекла дисплея, дужки крепления на трубу и маленькой планки прижима кабеля. Стекло я вырезал из какой-то прозрачной упаковки от конфет, а остальные детали напечатал:

Для крепления частей друг к другу в основном корпусе дисплея сделаны полости, куда необходимо вплавить обычные гайки М3 – в них со стороны дужки будет закручиваться винт с шестигранной головкой. Сам дисплей и планка прижима кабеля будут закреплены мелкими саморезами 2х6. С основным блоком, находящемся в моторном отсеке дисплей будет соединен тем же экранированным микрофонным кабелем КММ-4х0.12, что и датчик температуры. Экран здесь я тоже решил подключать к общему проводу только со стороны платы.
Сборку корпуса дисплея я начал с вплавления гаек в отведенные под них места. На этапе проектирования я переживал, что пространства там достаточно мало и вплавить их будет непросто, но на практике это оказалось совсем не сложно. Далее вклеил стекло в корпус с помощью клея-герметика В7000, который частенько используют при ремонте мобильных телефонов. Это оказалось сложнее, чем вплавить гайки, и я немного запачкал клеем само стекло. Вроде как основное пятно со стекла удалось оттереть, однако по краям остатки клея удалить не получилось, и внешне конструкция стала смотреться хуже, чем мне хотелось бы.
А вот установка дисплея в корпус оказалась еще более сложной задачей. При проектировании я учитывал размер платы дисплея, и она сама в корпус входила нормально, хоть и чуть впритык. Но при сборке я решил сначала припаять к плате кабель, чтобы сделать это красиво и в свободной обстановке на столе, а плату поместить в корпус уже потом. И вот тут меня ждало фиаско – с подпаянным кабелем плату совершенно не удавалось расположить под тем углом, под которым она проходила бы в корпус. Помучившись некоторое время, я неудачно схватил плату пинцетом и отколол сбоку от дисплея маленький кусочек стекла. Вроде как он ни за что на дисплее не отвечает, однако после откалывания у изображения пропали все четные строки. Пользоваться таким дисплеем было уже нельзя.
Вдвойне усиливало проблему то, что дисплей этот был у меня в единственном экземпляре, второго такого в наличии не было. На часах был час ночи по московскому времени, все радиомагазины города были, естественно, закрыты, поэтому изрядно расстроенный я пошел копаться в своей коробке с околоардуиновыми платами. И вдруг совершенно случайно нашел там похожий дисплей, только с несколько другой платой, сконфигурированной для подключения по протоколу SPI. Но на плате были указания, как переключить её на протокол I²C, что я и попытался сделать:

Я перепробовал несколько разных вариантов и, конечно, у меня ничего не вышло, плата по I²C не заработала. Переделывать готовый код на SPI совершенно не хотелось, но тут вдруг я осознал, что можно просто перепаять сам дисплей с контроллером с этой платы на предыдущую, ведь у них совершенно одинаковые шлейфы. С помощью жала 1402 для Т12 сделать это оказалось совершенно несложно, его ширина идеально совпала с шириной шлейфа, и где-то к двум часам ночи дисплей был восстановлен. Небольшим удивлением для меня оказался его цвет – предыдущий OLED был голубым, а этот оказался белым, но внешне это выглядело даже более красиво.
На этот раз я решил не рисковать и сначала установить дисплей в корпус, а потом уже припаивать к нему кабель. Нормально припаять в таком малом пространстве вряд ли получится, но уж пусть будет как будет, ломать еще один дисплей (который уже совершенно точно последний) я не хотел. В итоге, через 30 минут мучений сборка дисплея была завершена:

Поскольку дисплей будет находиться достаточно далеко от основной платы, длина соединительного кабеля тоже вызывала у меня опасения – будет ли работать весьма высокоомная шина I²C с кабелем такой длины? Но эти опасения развеялись очень быстро, когда после сборки дисплея я подключил его к плате через весь остаток кабеля длиной 4.5 метра, и дисплей прекрасно заработал. Если уж через такое расстояние работает, то через полтора (которые мне нужны) будет работать абсолютно точно.

На следующий день мне, наконец, удалось установить всё на беговую дорожку. Плату приклеил скотчем к основанию моторного отсека, как и хотел:

А дисплей установил на трубу и затянул винтами:

Во включенном состоянии это выглядит так:

Интерфейс пользователя
По сути, интерфейса пользователя у данного устройства нет, есть лишь только основной экран, который отображает всю необходимую информацию. Никакого интерактивного взаимодействия пользователя с устройством не предусмотрено, поэтому никаких настроек или разных вариантов отображения в устройстве не будет. А если так, то и рисовать интерфейс пользователя на компьютере я не стал, просто раскидал отображаемые величины по экрану «в уме».Так как дисплей монохромный, один байт данных отвечает за отображение сразу 8-ми пикселей на экране. С точки зрения слабого МК ATtiny это удобно, ведь надо передавать меньше данных. А вот с точки зрения компоновки изображения – нет, ведь соседние элементы интерфейса могут попасть в один и тот же байт дисплея и их надо будет как-то совмещать программно. Еще надо учитывать, что байты данных у этого контроллера дисплея расположены вертикально, а не горизонтально, как мы привыкли, то есть, один байт заполняет сразу 8 строк в одной колонке экрана.
Еще немного подумав, я понял, что эта особенность даже мне на руку – достаточно сделать шрифт высотой 8, 16 или 24 пикселей и тогда никакого наложения элементов интерфейса никогда не произойдет, каждый из них будет находиться целиком в своем байте. Проведя небольшой эксперимент, я понял, что оптимальным для меня будет шрифт высотой 16 пикселей, так как шрифт высотой 8 выглядит нереально маленьким, а со шрифтом высотой 24 или больше на экран влезет слишком мало информации.
Готового шрифта подходящего размера я не нашел, поэтому как во времена Спектрума просто нарисовал по пикселям в фотошопе свой, тем более, мне не нужны все 96 символов ASCII, достаточно было нарисовать и закодировать лишь те, которые я буду использовать. Ими оказались 10 цифр, стрелки вверх и вниз, заглавная буква «А», значок градуса, 5 картинок небольшой звездочки и, как ни странно, пробел:

Основной и единственный экран решил сделать таким – слева вверху отображается стрелка вверх и максимальное значение тока за последнюю пару секунд, правее отображается текущее значение тока. Во второй строке слева отображается стрелка вниз и минимальное значение тока, а правее – температура двигателя. Оставшаяся нижняя половина экрана отображает график тока во времени. В реальности это будет выглядеть вот так (фото настоящее и сделано уже после установки на дорожку):

Программный код
Задача у программного кода амперметра, в целом, несложная – считывать данные с АЦП и датчика температуры, сохранять в память, преобразовывать в числовые значения и отображать на дисплее. Ну, и еще пищалкой управлять. Но обо всем по порядку.I²C
У ATtiny85 есть аппаратная поддержка последовательных протоколов, но она сильно кастрированная. Например, сдвиговый регистр вроде и аппаратный, но управлять им, включая сдвиг битов, надо программно. Тем не менее, буду использовать что есть и начну с поддержки протокола I²C.Сам протокол, по сути, достаточно простой и состоит из двух сигнальных линий – тактовой SCL и линии данных SDA. На шине может сидеть много устройств, которые делятся на два класса – главные (Master) и подчиненные (Slave). Главные устройства управляют шиной и генерируют тактовые импульсы, а подчиненные – только пляшут под их дудку. Все устройства имеют выходы с открытым коллектором (стоком), а сама шина подтянута к плюсу питания через резисторы, что создает аппаратное объединение сигналов по AND и не вызывает физических проблем, если вдруг два устройства заходят вывести на одну шину разные уровни. Для шины определены несколько сценариев использования, основные из которых – Start, Stop, Repeated Start, Master Write и Master Read. Передача каждого байта данных физически занимает 9 бит, так как предусматривает 9-й бит ответа от принимающего устройства, которое должно подтвердить прием. Стандартной (высокой) частотой следования тактовых импульсов для I²C считается 400 КГц.
Любую передачу данных всегда инициирует главное устройство, она начинается со сценария Start, после чего Master передает 7-битный адрес подчиненного устройства и 8-й бит типа операции – чтение (1) или запись (0). Если адресованное подчиненное устройство есть на шине, оно отвечает 9-м битом низкого уровня (ACK), если же его нет или оно не в настроении отвечать (такое тоже возможно), оно ничего не делает и за счет подтяжки к плюсу на шине формируется сигнал высокого уровня, который расценивается мастером как NACK.
Если изначально была инициирована команда записи (Master Write), после передачи адреса и получения ACK мастер начинает передавать данные. Подчиненное устройство подтверждает прием каждого байта отдельным ACK’ом, а если такого не случилось, передачу требуется прервать. Если же была инициирована команда чтения (Master Read), то после передачи адреса и получения от подчиненного устройства ACK, мастер начинает генерировать тактовые импульсы на шину, а данные передает подчиненное устройство. В этом случае уже мастер должен подтверждать получение каждого байта отправкой ACK. Транзакция завершается сценарием Stop или Repeated Start. Последний эквивалентен двум последовательным сценариям Stop + Start, однако позволяет не терять владения шиной (для шин с одним мастером не актуально).
Более детально про этот протокол можно прочитать практически везде, включая даташит любой микросхемы, которая его поддерживает, а мы перейдем непосредственно к реализации. Сначала я написал код поддержки протокола на чистом Си, но потом понял, что мне придется вызывать метод отправки данных из ассемблерного кода отрисовки графика и будет значительно проще, если функция отправки будет тоже написана на ассемблере – тогда у меня будет абсолютное понимание, какие регистры процессора она использует, а какие регистры не трогает и в них могут сохраняться данные. А вот функцию приема переписывать на ассемблер необходимости не было, поэтому она осталась на Си:

Если посмотреть код внимательно, будет видно, что, по сути, аппаратная поддержка I²C в ATtiny85 не очень-то и упрощает задачу – всё то же самое можно было бы легко сделать и на чистых операциях ввода/вывода в порты. Кстати, если кто вдруг не знает как в этом случае реализовать выход с открытым стоком, то это очень просто – в сам порт выводим постоянное значение 0, а вывод данных осуществляем переключением режима работы порта на ввод (при выводе логической 1) или на вывод (при выводе логического 0).
Да, еще один важный момент – скорость передачи данных. Поскольку аппаратная поддержка I²C в МК очень слабая, тайминги приходится соблюдать программно, для этого я написал две функции задержки на ассемблере:

Для правильной работы этих функций необходимо предварительно определить две константы — CPU_SPEED_KHZ и I2C_SPEED_KHZ. Из названий понятно, что они задают скорости работы процессора МК и шины в килогерцах. Исходя из этих констант рассчитывается, сколько пустых циклов необходимо сделать процессору, чтобы добиться нужной задержки между сигналами. Если необходимости соблюдать строгие тайминги нет и нужна максимальная скорость, можно определить макрос I2C_MAX_SPEED, и тогда задержки превратятся в вызовы пустых функций.
Дисплей
Когда реализация протокола была готова, я перешел к коду поддержки OLED-дисплея. Первым делом контроллер необходимо инициализировать, для этого ему надо отправить определенные команды. Я не стал детально вспоминать, какие именно, просто взял рабочий код инициализации из одного из своих предыдущих проектов. Затем написал первую графическую функцию – функцию очистки дисплея. Она устанавливает границы окна на весь экран, после чего заполняет его нулями.
Теперь пришло время для вывода текста. Поскольку байты у контроллера дисплея располагаются вертикально, логично и вывод делать не по привычной схеме вправо-вниз, а по обратной – сначала вниз, потом вправо, благо контроллер такой режим поддерживает. Тогда и шрифты в памяти можно хранить в таком же формате. Поскольку все символы имеют строго одинаковую высоту и отличаются только шириной, решил не создавать отдельной структуры, описывающих каждый символ, а просто хранить массив указателей на них. Тогда ширину символа можно будет вычислить по разности между адресом начала текущего и следующего символов непосредственно при выводе. В итоге, код вывода даже обошелся даже без ассемблера:

Ну, и, конечно же, для преобразования числа в строку перетащил из своего предыдущего проекта функцию I16ToString. Поскольку операции деления в AVR не завезли, работает она по принципу циклического вычитания и написана, соответственно, на ассемблере. Если интересно, её код можно посмотреть в исходниках.
АЦП
Так как в данном устройстве считывать надо всего лишь один канал, решил, что АЦП будет уместно работать в автоматическом непрерывном режиме. В документации пишут, что оптимальной для АЦП является частота тактирования от 50 КГц до 200 КГц, поэтому выбрал коэффициент деления 128, что дает частоту 125 КГц. Каждое преобразование занимает 13 тактов АЦП, то есть, в итоге я буду получать примерно 9615 преобразований в секунду. Это сильно больше, чем мне надо, поэтому сразу решил складывать результат 64 последовательных преобразований. Почему именно 64? Во-первых, это понижает результирующую частоту до подходящих для меня 150 измерений в секунду, во-вторых, АЦП в AVR 10-битный и максимально возможное оцифрованное значение – 1023, то есть, сумма из 64-х таких значений всё еще умещается в 16-битную переменную.Если вспомнить изначальные требования, я хотел выводить на экран график потребления дорожки в реальном времени. Экран имеет ширину 128 точек, то есть, для вывода графика мне нужно 128 последних измерений. Но если АЦП будет производить 150 измерений в секунду, на экран не влезет статистика даже за последнюю секунду, а это значительно снизит её информативность. Поэтому решил обновлять график один раз в три измерения АЦП – это и хорошую скорость обновления в 50 Гц даст, и позволит на экране отображать статистику за последние 2.5 секунды. Также из этого буфера я буду брать минимальное и максимальное значения, чтобы тоже выводить их на экран в численном виде.
Но тут возникает вопрос, как правильно усреднить три измерения в одно? Конечно, можно найти среднее арифметическое, но это будет означать, что фактически у меня останется лишь 50 измерений в секунду, а это уже мало. Поэтому я решил не усреднять их никак – буду считать минимальное и максимальное значение из трех, и график буду выводить не точкой, а отрезком от минимума до максимума, потому что это значительно улучшит визуальное отображение. В итоге, алгоритм получился такой – прерывание АЦП производит 150 измерений в секунду, в каждом из которых обновляет две глобальных переменных g_adcValueMin и g_adcValueMax. А основной код 50 раз в секунду считывает и сбрасывает эти значения, начиная, таким образом, новый цикл измерений.
Вывод графика
Для вывода графика тока потребления мне нужно хранить в ОЗУ МК результаты последних 128-ми измерений АЦП. Но каждое измерение теперь состоит из двух значений – минимума и максимума, то есть, мне нужно хранить в памяти МК 128 пар 16-битных значений или 512 байт данных. Выбранный МК имеет всего 512 байт ОЗУ, и очевидно, что какую-то его часть надо оставить под служебные нужны, например, под стек, значит, хранить такой объем данных я просто не могу. Поэтому я решил хранить не сырые 16-битные значения, а уже вычисленные значение тока, которые имеют две цифры после запятой и, таким образом, вполне умещаются в 12 бит с максимально возможным значением 40.95 А (то есть, даже в два раза больше, чем мне нужно). Два таких значения занимают лишь 3 байта, что позволяет всему массиву уложиться в 384 байта и, таким образом, оставить 128 байт для всех остальных задач.
График по вертикали имеет размер всего 32 точки, поэтому если за минимум всегда принять значение 0 А, а за максимум – 40 А, реальные изменения тока будут практически не видны в таком масштабе. Значит, масштабировать его имеет смысл динамически, чтобы минимальное значение тока находилось всегда в самой нижней строке, а максимальное – в самой верхней. Математически это выглядит как формула:
y(n) = (I(n) – Imin)*31/(Imax – Imin)
Посмотрев на неё внимательней, я понял, что мне нужно сократить размерность хранимых данных с 12-ти бит до 11-ти, тогда максимальное хранимое значение будет 2047 и для умножения его на 31 снова хватит 16-ти бит. На пользовательские характеристики это никак не повлияет, т.к. изначально запланированный максимальный ток составлял как раз 20 А и для хранения такого значения 11-ти бит достаточно. А больший ток, прежде всего, нельзя пропускать через шунт сопротивлением 5 мОм, т.к. даже при постоянных 20 А на нем будет падать целых 2 ватта, что разогреет его до слишком высокой температуры и он просто отпаяется от платы.
Когда с форматом и преобразованием данных было решено, оставалась последняя небольшая задача – написать функцию, которая будет быстро рисовать на экране вертикальный отрезок от y1 до y2. Учитывая, что допустимым значением для параметров является диапазон [0..31], это дает всего 1024 возможных варианта результата, что при его размере в 4 байта, в принципе, позволяет использовать лукап-таблицу, размер которой составит 4 КБ. И это даже влезет в ПЗУ МК, но отдавать его половину под такую таблицу мне совершенно не хотелось.
С другой стороны, т.к. речь идет всего о 32-х пикселях, можно сделать отрисовку и обычным циклом – такая функция не потребует вообще никакой таблицы, но будет занимать достаточно много процессорного времени. Тоже не самое лучшее решение для маленького и слабого МК, поэтому я решил найти некоторый компромисс.
Для начала я напишу некую функцию F, которая будет принимать только один параметр N и рисовать на экране столбик в диапазоне [N..31], то есть, от указанной точки по вертикали до верха графика. Если результат этой функции проинвертировать, я автоматически получу другую функцию, которая рисует на экране столбик в диапазоне [0..N), то есть, от нуля и на один пиксель меньше, чем указанная по вертикали точка. А тогда искомый мной отрезок можно будет получить с помощью следующих вычислений:
R = F(y1) AND (NOT F(y2 + 1))
Теперь задача упрощается – надо быстро нарисовать столбик пикселей в диапазоне [N..31]. В принципе, здесь уже можно воспользоваться лукап-таблицой, так как она будет занимать всего 128 байт, но я решил попробовать более интересное решение, основанное на математике. Если предположить, что результат – это 32-битное значение, где младший бит представляет собой самую верхнюю точку, а старший – самую нижнюю, то математически функцию F можно описать как:
F(n) = 2⁽³²⁻ⁿ⁾ – 1
И именно такую функцию я и решил реализовывать:

Функция все еще использует лукап-таблицу для быстрого вычисления 2ⁿ для 0 <= n <= 7, но такая таблица занимает всего 8 байт. Еще функция использует интересную особенность AVR, что первые 32 байта адресного пространства ОЗУ на самом деле попадают на 32 регистра общего назначения процессора R0 – R31. Это позволяет команде st Z, R18 осуществлять запись байта в нужный регистр результата R22 – R25.
В итоге, рисование столбика занимает 25 тактов процессора без учета команд вызова и возврата из подпрограммы. Для сравнения, если бы я использовал лукап-таблицу из 128-ми байт, функция могла бы занимать 18 тактов.
Используя все наработки, описанные выше, я написал функцию display::UpdateDiagram(), которая:
- Высчитывает минимальное и максимальное значение тока за последние 128 измерений и помещает их в переменные g_samplesMin и g_samplesMax.
- Рассчитывает масштаб отображения исходя вычисленных значений.
- Выводит график последних 128 измерений на дисплей с учетом минимального и максимального тока в каждом измерении.
- Сдвигает значения в памяти на одно измерение влево (если смотреть по экрану).
Теперь требовалось оценить производительность полученной функция. Точных подсчетов я не делал, но по приблизительным оценкам сверху функция UpdateDiagram тратит не более двух тысяч тактов на обработку каждого измерения (включая вывод на экран) или около 256К тактов на весь график. Поскольку желаемая частота обновления составляет 50 Гц, между соседними обновлениями будет 320К тактов, что оставляет мне запас в 64К тактов на выполнение каких-либо других действий.
Это хорошо, но для данного устройства не совсем критично – ведь если бы я не уложился в отведенный лимит, я бы просто чуть повысил тактовую частоту I²C, ускорив, таким образом, отправку байтов на дисплей. Это значительно ускорило бы функцию рисования графика целиком, потому что основную задержку в неё вносит именно отправка. Контроллер дисплея, кстати, это позволяет сделать вплоть до работы в режиме MAX_SPEED, который на поверку оказывается в 2-2.5 раза быстрее стандартного. Вопрос оставался бы лишь в том, потянет ли такую частоту TMP100. Но испытывать, к счастью, не пришлось.
Среднеквадратическое значение тока
Запустив первую версию устройства в реальную эксплуатацию, я понял, что выводить текущее значение тока совершенно неинформативно. Оно просто принимает какое-то промежуточное значение от минимума до максимума и в каждый конкретный момент времени никакой полезной информации пользователю не дает. Решил, что намного лучше будет выводить среднее значение тока за последние 128 измерений, ведь так оно и прыгать будет значительно меньше. Однако, просто взять среднее арифметическое не имеет смысла, ведь для оценки нагрева двигателя надо использовать среднеквадратическое значение, поэтому решил считать именно его.По сути, здесь никаких сложностей не возникло – я просто беру каждое максимальное значение из 128-ми пар, возвожу его в квадрат, складываю все полученные квадраты, после чего делю сумму на 128 и извлекаю из полученного значения квадратный корень. Поскольку значения в буфере хранятся 12-битные, их квадрат помещается в 24 бита, сумма – в 32 бита, а среднее значение квадратов – снова в 24 бита. Остается только написать код вычисления квадратного корня из 24-разрядного числа:

Так как вычислять квадратный корень надо всего лишь один раз перед отображением, выбрал самый простой алгоритм последовательного приближения, на каждой итерации которого текущее значение результата возводится в квадрат, сравнивается с исходным значением и, таким образом, вычисляется следующий бит результата. Быстродействие получившегося кода не высчитывал, но предполагаю, что одно вычисление занимает не более 2000 тактов процессора.
Главный экран
Основной элемент главного экрана – график тока, который надо обновлять 50 раз в секунду. Все остальные отображаемые числовые значения надо обновлять значительно реже, около 5 раз в секунду, иначе они будут мелькать и станут совершенно нечитаемыми. Но просто взять и обновить все числовые значения сразу нельзя, потому как такое обновление займет слишком много времени и вызовет подергивание движения графика тока.Поэтому решил, что каждый раз после обновления графика тока буду выполнять только одно ресурсоёмкое действие – выводить максимальный ток, выводить минимальный ток, рассчитывать среднеквадратический ток, выводить среднеквадратический ток, получать температуру с датчика TMP100 или выводить температуру на экран. Каждое из таких действий гарантированно укладывается в остающиеся от вывода графика тока 64К тактов процессора и, таким образом, не нарушит равномерность отображения.

Screen saver
К сожалению, у OLED-дисплеев есть одно неприятное свойство – их диоды в процессе эксплуатации способны выгорать и терять яркость свечения, поэтому если постоянно выводить на дисплей одну и ту же картинку, со временем она «отпечатается» на матрице диодов и будет мешать нормальному отображению других данный. Это можно очень хорошо наблюдать на моей паяльной станции Т12, которой уже несколько лет:
Чтобы немного защитить дисплей амперметре, решил добавить ему хранитель экрана (Screen Saver). Подумал, что если максимальный ток двигателя в течение минуты не превышает какого-либо предела, например, 0.5 А, то очищу весь экран, чтобы снизить бессмысленное выгорание пикселей. Однако, наблюдать чисто черный экран неинтересно, ведь даже не будет видно, что устройство работает, поэтому решил выводить на экран небольшую анимацию зажигания и угасания маленькой звездочки, возникающую по случайным координатам. Саму звездочку я предварительно нарисовал как 6 символов шрифта, где один из символов полностью пустой. Это позволяет не задумываться о необходимости стирания с экрана текущей звездочки перед выводом новой.
Но теперь появилась новая задача – где взять генератор случайных чисел для получения случайных координат? В теории, можно было воспользоваться тем, что есть в стандартной библиотеке С++, но я решил просто перенести уже имеющийся код генератора из одного из предыдущих проектов:

Генератор основан на LFSR — Linear-feedback shift register. Это простой сдвиговый регистр, на вход которого подается сигнал обратной связи, посчитанный исходя из текущего состояния некоторых битов регистра. Для сложения битов применяется функция XOR, т.к. она не теряет информации своих операндов. Следует отметить, что правильно спроектированный LFSR позволяет перебирать 2ⁿ — 1 (то есть, все кроме одного) своих состояний в некой псевдослучайной последовательности, поэтому и подходит для генератора псевдослучайных чисел. В моем конкретном случае используется регистр длиной 31 бит с обратной связью XNOR от битов 27 и 30 (если считать от 0). Использование XNOR позволяет регистру начать свою работу с полностью нулевого состояния, чего было бы невозможно добиться использованием функции XOR, в остальном никаких отличий между ними нет.
Кстати, если вам 40+, скорее всего вы познакомились с LFSR гораздо раньше, чем узнали, что это вообще такое. Помните тот переключатель на 4 елочных гирлянды с 7-ю режимами на логических микросхемах серии К155? Он был опубликован в журнале Радио № 11 за 1986-й год:

У него был «магический» 6-й режим, который по описанию авторов должен был «повторять разнообразие всех предыдущих режимов», но для его работы требовалась отдельная логическая микросхема ИЛИ-НЕ К155ЛЕ1, которую было сложно найти, поэтому даже не все радиолюбители его реализовывали. Так вот, узел, собранный на этой микросхеме (я обвел его на схеме красным прямоугольником) был ни чем иным, как элементом «Исключающее ИЛИ» или, по-современному, XOR. А совместно со сдвиговым регистром на 4-х D-триггерах он образовывал LFSR размером 4 бита с обратной связью от битов 2 и 3. И именно эту бегущую псевдослучайную последовательность из 15 разных состояний, генерируемую LFSR авторы и представили как интересный дополнительный режим переключения ёлочных гирлянд.
Кстати, схемотехника LFSR очень проста, что позволяет с легкостью реализовывать его аппаратно. И раньше это любили делать внутри больших микросхем, например, для синтеза «белого шума». За один сдвиг LFSR генерирует 1 бит псевдослучайной последовательности, соответственно, если вам нужно 8, сдвинуть регистр надо 8 раз. Брать результат можно из любого бита регистра (т.к. рано или поздно один и тот же бит проедет по всему регистру), главное делать это всегда из одного и того же места.
Защита от перегрева
Полноценную защиту от перегрева двигателя без изменения схемотехники дорожки реализовать невозможно, можно только уведомлять пользователя, что двигатель достиг слишком высокой температуры. Именно это я и решил сделать. Уведомление происходит в несколько этапов – когда двигатель разогревается до 60 градусов, устройство издает одиночный звуковой сигнал. Если нагрев двигателя продолжается, то при достижении температуры 61 градус устройство издаст двойной звуковой сигнал. При достижении 62 – тройной и так далее до 65 градусов, после чего устройство будет пищать непрерывно. Отключение звукового сигнала и сброс состояния происходит при охлаждении двигателя до 58 градусов.В качестве звукового извещателя используется стандартный пьезокерамический излучатель в пластиковом корпусе, который был у меня в наличии. Его особенностью является то, что от низкого напряжения он громко звучит только около своей резонансной частоты, которую желательно предварительно измерить с помощью генератора, после чего занести в программу, так как у разных экземпляров она может немного отличаться. У моего это оказалось 3660 Гц. При этом какого-либо заметного электрического резонанса мне у него обнаружить не удалось.
Итоги
Возможно, если дорожка будет работать нормально, создание обозреваемого амперметра было некоторым перебором, и вместо него хватило бы просто прикрутить к двигателю любой датчик температуры, но сейчас мне так не кажется. Собранным устройством я доволен, оно делает именно то, что я хотел, плюс на движущийся график всегда интересно смотреть.Однако я прекрасно понимаю, что вряд ли кому-либо из вас понадобится решать аналогичную задачу, поэтому я подумал и о паре других случаев, где обозреваемое устройство можно было бы использовать. Во-первых, это измерение тока пуска двигателя автомобиля – устройство для этого понадобится чуть переделать, заменить шунт на меньший, сделать автоматический старт по достижению некоторой амплитуды тока, автоматический стоп при его снижении и т.д. Но общий принцип может остаться таким же.
Во-вторых, устройство легко можно переделать в измеритель тока потребления аккумуляторного электроинструмента, какие часто используют в обзорах. Для этого тоже придется снизить шунт, немного (раза в 4) замедлить скорость движения графика, вернуть отображение текущего значения тока вместо среднеквадратического, но и здесь общая логика функционирования вряд ли изменится. Возможно, кстати, что однажды я соберу такое и себе, так как часто бывает интересно, сколько потребляет какой-либо электроинструмент в разных режимах. Пишите в комментариях, если вам эта тема тоже интересна.
Что касается получаемой точности измерений, на своем приборе я заметил, что не смотря на усреднение 64 последовательных выборок АЦП, текущие показания тока прыгают в пределах 0.08 А. Точность RMS значения тока выше, и составляет около 0.02 А, но она вообще меняется значительно медленней. Таким образом, при максимальном измеряемом значении в 20 А можно считать, что разрешение прибора составляет 0.1 А, а точность – не хуже 1%±0.1 А. Результаты не особо выдающиеся, но вполне подходящие для всех описанных выше задач. Кстати, причиной такого большого разброса мгновенных показаний я склонен считать встроенный в МК усилитель АЦП, так как без него разброс значений значительно ниже.
Исходный код проекта, схему, плату и корпус можно найти в репозитории по ссылке:
github.com/kdekaluga/continuous_current_meter
На этом у меня всё, спасибо за внимание.
Самые обсуждаемые обзоры
+24 |
1935
155
|
+27 |
1198
35
|
+29 |
1197
43
|
Ну и точность клещей на малых токах действительно низкая. Они используются при больших токах — в сотни ампер, где значительно выигрывают у классических амперметров в удобстве.
fluke из 370-ой серии, или fluke 381 например.
т.е. 1000 раз в секунду?
Имхо, у вас просто слишком много свободного времени )
А что покажет стрелочный прибор? Он недостаточно медленный, чтобы показать среднее и недостаточно быстрый, чтобы правильно показать минимум и максимум.
Нет, времени свободного как раз немного, такими вещами занимаюсь постепенно, по мере возможности, так как нравится.
Зачем вам мгновенное значение мощности? Его длительность измеряется миллисекундами, и для оценки нагрузки на двигатель не нужно. А что касается минимального — не совсем понятно, для чего оно в рамках данного случая.
Мне интересна амплитуда изменения тока, а для её оценки надо два значения. Плюс, минимум всё равно нужен, чтобы график масштабировать. В остальном, я считаю и отображают правильное RMS-значение.
а 100Гц он вообще не увидит никогда, оно сквозь него пройдет. т.е. всплески длиной десяток миллисекунд он не замечает.
как раз недавно видео смотрел на этот счет: , освежал в памяти.
В этом и дело, что я не хочу временных решений. Если уж контролировать потребление, то пусть это будет постоянное решение, работающее всегда. Я вот сейчас думаю сделать еще одно похожее, но уже для измерения тока потребления электроинструмента.
Да и некуда мне применять игрушечные осциллографы.
C термопарой k-type напрямую сможет работать? Если ТЭДС +250С=10.153mV,
-150C=-4.913 mV, т.е. диапазон измерения напряжений от -5мВ до +10мВ.
Есть солнечная панель мелкая, при дневном свете ее мощности не хватает питать мелкий двигатель, однако, если поставить кондер тысяч на 10мкф то зарядив его и разрядив на двигатель вполне хватает чтобы он некотрое время вращался.
Вопрос — можно ли на аналоге собрать триггер такого плана:
режим1: нагрузка включена, по достижении напряжения нижнего порога — включать режим2
режим2: нагрузка выключена, по достижении напряжения верхнего порога, переключиться в режим1
Да, я понимаю, что двигатель будет импульсами вращаться, меня бы это устроило.
«компаратор» поможет отследить уровень относительно порога и выдаст логический сигнал (0 — меньше или 1 — если больше). подключение нагрузки при уровне 1 не сложно сделать, например мосфетом. заодно и отключение зарядки при уровне 1 — тоже. вприципе любые подойдут, сдутые например с дохлой материнки. Надо лишь будет поискать с нужным типом для задачи, потому что на материнке обычно все одного типа. А тут, будто бы для задачи отключения зарядки высоким логическим уровнем нужен depletion-mode MOSFET. Или вместо этого добавить инвертор логического уровня, тогда и делать все на enhancement mode MOSFETS, которых в изобилии на материнке. Логический инвертор на коленке — тобишь усилитель инвертирующего типа, тоже чуть ли не из пары транзисторов делается.
вот где взять правильный и нежручий ОУ для роли компаратора, или прям специально искать именно компаратор — понятия не имею, надо углубляться.
а так, возможно далее подскажут что вместо ОУ в роли компаратора, можно будет обойтись таймером 555, или вообще парой-тройкой транзисторов для этой цели. т.к. сверх точность в обработке порога не требуется, то можно будет очень по-разному задачу решать.
P.S. Для «мелкой солнечной панельки» даже у мелкого реле может быть великоват ток потребления, тогда смотреть на твердотельные, они тоже бывают с гистерезисом.
А вот трёхвольтовых электромагнитных реле на рынке пруд пруди и напряжение удержания у них как раз порядка 1V и будет. В крайнем случае, можно взять несколько реле с разным номиналом (3V, 4V, 4.5V, 5V, 6V) и проверить на месте. У большинства реле реальное напряжение срабатывания существенно (20%-50%) ниже номинального и варьируется от экземпляра к экземпляру, поэтому так вы сможете максимально точно подогнать верхний порог под ваши 3V. Ну а нижний порог — удержания (если реле разборное, ну или «условно -разборное») можно в некоторой степени регулировать, наклеивая слои скотча на торец магнита.
Надо же иметь в виду, что оно будет жрать непрерывно, и в процессе нарастания — тоже.
Альтернативно можно попробовать сделать на К561ЛН1 — она потребляет мало, а из двух инверторов можно собрать триггер с положительной обратной связью. Но тут пороговое напряжение вызывает вопросы, надо экспериментировать.
Или натурально брать триггер из двух транзисторов и подбирать/рассчитывать его параметры так, чтобы переключался при нужных напряжениях. Но будет зависимость от характеристик транзисторов.
Схема — один ОУ и немного мелочи.
Вот на чём сделать микроамперный опорник — хз, надо поискать.
зыж «микротоковый стабитрон» нашёл от 50 мкА )))
ОУ жрет меньше
www.chipdip.ru/product/ltc1440cs8-analog-devices-8013611853
Компаратор с опорником в сборе, типовой Icc всего 2мкА, это просто чудо какое-то!
Собственно, только ПОС посчитать и низковольтный мосфет найти на выход.
Питание организовать от того же источника через диод шоттки и отдельный конденсатор.
откуда его значение берется?
по-идее у микроконтролера очень много мест, где практически настоящее случайное значение возникает само по себе, из-за фундаментальных принципов работы тех или иных элементов. например в момент старта при включении статической памяти (те самые триггеры на 6-ти транзисторах). важно лишь найти такой элемент и прочитать из него значения до «очистки» перед работой.
тут судя по коду используется R23, что есть регистр микроконтроллера. который скорее всего автоматически очищается после сброса, еще задолго до начала работы программы. нету ли возможности залезть в процедуру обработки прерывания сброса микроконтроллера (и вклиниться до момента очистки памяти) — чтобы прочитать оттуда случайное значение? или тут достаточно просто прочитать последний байт из памяти или что-то такое?
просто не уверен, что код, который будет включен компилятором из «стандартной» библиотеки не делает чего-то лишнего, в т.ч. и очистку памяти при старте.
Тогда аппартаный генератор ставить если уж надо железно.
А так — ADC можно померять температуру проца — помех будет меньше, тинька это умеет.
Это очень похоже на ответ какой-то нейросети.
Такая возможность, думаю, есть, но я не уверен, что содержимое памяти МК или его регистров будет настолько уж случайным, чтобы смело использовать его как seed. Например, в случае быстрого перезапуска устройства очень велик шанс, что содержимое памяти практически полностью сохранится, и тогда этот метод не даст ожидаемых результатов.
Мне кажется, если прямо есть задача найти качественное стартовое значение, лучше всего использовать АЦП — собрать несколько тысяч выборок, сложить их и использовать младший байт полученного результата. Вот его значение уже будет случайным в гораздо большей степени.
Квадратный корень можно вычислять с помощью итерационной формулы Герона. Алгоритм быстро сходится, количество итераций можно делать малым (на компьютере можно промоделировать, сколько нужно для конкретного случая). Вот пример извлечения корня из 32-разрядного числа:
Я знаю эту формулу, но почему-то у меня в голове она сохранена под именем «метод Ньютона». Реализовывал её для STM32, для моей задачи хватило всего 3-х итераций, максимальная ошибка составила 1. Но там был ключевой момент — правильный выбор начального значения с помощью команды CLZ, она очень сильно ускоряла (или повышала точность). В итоге, занимала функция около 50 тактов, но команда деления там аппаратная.
Здесь 24-битное деление придется писать самому, и не факт, что то количество итераций, которое даст хорошую точность во всем диапазоне входных значений окажется сильно быстрее, чем простой подбор 12-ти битов. Хотя, конечно, можно же и сначала биты посчитать.
В общем, определенно, если надо сделать метод максимально быстрым, надо провести дополнительное исследование)
Прочитал с огромным удовольствием, олдскулы заклинило, слеза ностальгии навернулась, поставил бы пять плюсов, было бы можно))
Если постоянно пищать начнет, то даже через наушники слышно. За счет резонансной частоты и удвоенного напряжения пищит излучатель сильно.