Проект FONAREVKA.RU специализируется на предоставлении всей необходимой информации по светотехнике:
— светодиодные фонари;
— различные источники питания;
— разнообразные зарядные устройства;
— освещение помещений и наружное освещение;
— световые приборы для личного, пассажирского и грузового транспорта;
— специальные световые приборы для медицины, для растений, для аквариумов, для террариумов, а также аварийно-сигнальные световые приборы;
— альтернативные источники света;
— лазеры и лазерная техника.
Если у вас есть вопросы по выбору фонарей, аккумуляторов и зарядных устройств ознакомьтесь с FAQ от наших экспертов:
Программная реализация драйвера на примере Indigo 3.0s
Будет МНОГО (я предупреждал) писанины, ассемблерного кода, осциллограмм. Тема врятли будет интересна тем, кто пишет на Си, или вообще не занимается подобными вещами (разработкой стабилизаторов на МК). Я не претендую на звание какого-то там доктора наук, профессора и т.п. Всё что будет написано ниже - просто мой опыт, которым я решил с вами поделится. Чтоб хоть как-то спрятать весь этот БОЛЬШОЙ 3.141592654... часть текста я спрячу под спойлеры...
И так:
Задача - построить стабилизатор тока на ATtiny25/45/85 с блекджеком и шлюхами .
Топология - мониторим ток на шунте, крутим напряжение самостоятельного преобразователя - TPS63030.
Техническую часть вопроса о схемотехнике я опускаю. Как рассчитывать схемки и какие детальки лепить - те кто способны это осилить и без меня знают. Поэтому я просто кину готовую схему:
Да, очень похожа на Indigo 4.0 из соседней темы, не просто так ведь и название похоже? Это, кстати, к вопросу о прошивке в свободном доступе. Тут она и будет рассматриваться, правда к чуть другой и упрощённой схеме.
Объяснять тут на первый взгляд нечего. МК следит за падением на шунте, подкручивая свой ШИМ - классика. Допустим схемку мы набросали, резисторы рассчитали, кондёры в скользких местах тоже, решили что питание МК фильтровать не надо, входы АЦП глушить фильтрами тоже. Мол и в программе эти все гадости ликвидируем (ассемблер же!). Собрали прототип:
Распаяли на нём ISP, для удобства отлаживания, и залили тестовую примитивную программу, чтоб убедится в работоспособности:
Всё готово. Начинаем писать программу, и тут начинается гора вопросов...
1. АЦП прилично шумит, как бороться с шумом?
2. Как работать с EEPROM, чтоб максимально продлить её срок службы?
3. Как реализовать обратную связь, чтоб быстро и стабильно работала?
4. Как, и какие прикрутить защиты от переразряда и перегрева?
1. Шум АЦП.
Сам по себе АЦП не шумит. У него такая архитектура, что там шуметь просто нечему. R2R ЦАП и компаратор, всё... Резисторы шумят, да, но явно не с амплитудой в 0,1%. Компаратор тоже может шуметь, но подобные вещи шумят, как правило, ещё меньше за резисторы. Есть у нас ещё ИОН, вот как он там устроен - я не знаю, но весь АЦП в сборе даёт где-то 0,25LSB шуму при тактовой 1МГц (макс. скорость) во время работы ШИМ и программы. Это даже мало, для эффективного использования передискретизации нам оптимален шум в 1LSB. Т.е. при грамотной реализации программы шум мешать нам не будет. Возникает вопрос - как грамотно реализовать?
Народ, судя по гуглу, борется с шумом двумя способами:
- Следует рекомендациям даташита. Запускаем измерение, и пока меряет - НЕ ДЫШАТЬ!!! Т.е. уходить чуть ли не Power-down, пока АЦП не разрешит дальнейшую работу ядра и всей периферии. Да, способ хороший, когда нам ничего одновременно с этим делать не надо, а разрешения в 10 бит достаточно. Передискретизация, ввиду отсутствия шума, работать будет плохо. Поэтому получим то, что написано в даташите - 10 бит, и никак не больше.
- Использует ADLAR, внимание, для того чтоб упросить процедуру отсечения последних двух якобы "всегда" зашумленных и не несущих совершенно "никакой" полезной информации бит. Вместо того чтоб заниматься усреднением ошибки - делаем ошибку искусственно достаточно большой, чтоб на её фоне не замечать исходную. Аппаратное умножение значения АЦП на 64 чудесным образом превращается в деление на 4 (X=X*64/256=X/4), достаточно просто тягать данные с ADCH, а на младшую часть, где затерялись два младших бита, просто забить. Ну да, а для чего же его ещё использовать, как не для забивания гвоздей?
В нашем случае требуется одновременная работа и ШИМ, и программы, и АЦП, поэтому бороться с шумом придётся менее примитивными способами. По-сути всё сводится к достаточно жесткой синхронизации всего этого дела, чтоб ошибка вырождалась в постоянную величину. Другими словами - наложение спектров (частоты дискретизации и помех) дало в результате 0Гц. Привязать софт к ШИМ не проблема, существуют прерывания и флаги, а вот с АЦП сложнее, и в этом нам "помогли" разработчики камня. Во первых устройство выборки/хранения (далее УВХ) там кривое. За отведённые ему 1,5...2 такта АЦП, в авто триггерном режиме, он просто не успевает "сохранить" аналоговое значение со слишком высокоомного источника сигнала. Такими источниками является, к примеру, встроенный температурный сенсор, ИОН, различные делители напряжения где-то там снаружи, даже встроенный дифференциальный усилитель, необходимый для измерения шунта, тугой на подъём. Ещё и у самого УВХ есть внутреннее сопротивление.
1,5...2 такта - очень мягко сказано. Предусмотрительные инженеры сделали так, чтоб при переключении каналов АЦП первый раз подключал УВХ на 13,5 тактов, чего всё равно оказалось мало. Как минимум один такой семпл нужно выбрасывать.
Вторая проблема авто триггерного режима - невозможность его перенастроить, пока он работает и что-то меряет. Т.е. вы закинуть в него новые настройки можете, но когда полезете забирать результат - окажется что он подсунул вам не то, а то что надо - только только начал измерять... Приходится останавливать, перенастраивать, и снова запускать.
Третья проблема такого режима - невозможность синхронизации с ШИМ. Можно синхронно запустить, даже частоту ШИМ подкрутить под частоту дискретизации АЦП, но тогда останавливать их уже нельзя, а соответственно и с реконфигурацией будут проблемы. Асинхронный же режим гарантировано обеспечит нас отменным генератором шума...
Синхронизировать АЦП с таймером можно и аппаратно, но проблема реконфигурации остаётся...
Получается что АЦП нужно использовать в режиме одиночных выборок, и каждый раз запускать вручную. Запустили, ушли делать свои дела, подождали пока таймер скажет "готово" - забрали результат, перенастроили если нужно, и снова запустили... Почему результат забираем не по прерыванию/флагу АЦП "Я готов - забирай"? Потому что в таком случае софт привязан к ШИМ, а т.к. АЦП привязан к софту - ну вы поняли...
Теперь вылазит ещё один момент. Максимальная чувствительность АЦП к грязи (субъективная) в тот момент, когда он меряет младшие биты. Те самые, которые обычно зашумлены. В эти моменты необходимо бросать всё и таки не дышать, иначе результат будет заметно хуже. В нашем случае ничего особого не требуется, достаточно посчитать количество тактов, за которые АЦП производит измерение, и выполнять код кусками, засыпая хотя бы за пару тактов АЦП до окончания измерения. К примеру таймер переполняется за 256 тактов ядра, АЦП на максимальной скорости, в режиме одиночных выборок, требуется 200 тактов ядра, при переполнении срабатывает прерывание, в котором "что-то" (об этом ниже) делается около 40 тактов, значит у нас есть 16 тактов чтоб прочитать значение и запустить АЦП (в обратном порядке глючит). Запустили АЦП, с этого момента у нас есть около 180-ти тактов, чтоб что-то сделать, дальше необходимо... заснуть? Хорошо бы, да инженеры и тут умудрились насолить. Вот нельзя спать, АЦП команду Sleep рассматривает как установку бита ADSC (видать думает что мы перестали дышать и пора значит работать), т.е. сразу же начинает мерять следующий семпл, как только справится с текущим. Из-за чего мы не можем его по понятным причинам перенастроить и запустить, ибо он уже занят... И что делать? Да что, в цикле только висеть и остаётся. Цикл нужно оформить максимально коротким и монотонным, чтоб он создавал аккуратный шум на как можно более высокой частоте. С этим хорошо справляется BRTC. Переходит сам на себя до тех пор, пока прерывание таймера не поставит флаг T. Пока АЦП меряет 1 бит - цикл успеет навернуть 4 круга (тактовая ядра 8МГц).
ФУУУУХ... С АЦП разобрались, едем дальше...
2. Работа с EEPROM.
Ну, с этим я долго не возился, почти сразу всё нормально завелось согласно задумке. Задача:
Переключение режимами осуществляется разрывами питания, для этого требуется где-то хранить информацию о предыдущем режиме, чтоб можно было глянуть что же было до этого и, соответственно, что сейчас включать. Определить длительность выключения не сложно, достаточно повесить на RESET конденсатор и при запуске опрашивать бит "EXTRF". Если он стоит, значит конденсатор успел разрядится и некоторое время удерживал лапу после подачи питания. Тогда загружаем сохранённый режим. Иначе же выключение было кратковременным и МК должен проверить флаг "PORF". Если он стоит, значит сброс сопровождался ПОЛНЫМ выключением питания, т.е. всё в порядке, и можно перезаписывать и загружать режим на шаг выше. А вот если его нет (сам он не убирается, его надо сразу же чистить при обнаружении) - значит перезагрузка произошла из-за супервизора. т.е. сел аккумулятор и переключать режим вверх нельзя - ложная тревога. Тут или пытаться запустится с током по-меньше, или вообще уйти дрыхнуть до лучших времён. Пока всё просто и понятно, проблема начинается с использования EEPROM...
EEPROM недолговечен, обещают около 100 000 циклов записи. С таким ресурсом мы очень быстро "доклацаемся", что делать? Во первых ячеек у нас вагон, нужно придумать механизм, размазывающий по ним нагрузку. Во вторых в каждой ячейке аж 8 бит, необязательно стирать всю ячейку, чтоб перезаписать 1 бит. Это недокументированная особенность, но использовать её никто не запрещает. После стирания содержимое ячейки - 0xFF. При записи бит можно только сбросить, установить - только стиранием всей ячейки. Можно производить стирание 1 раз на 8 циклов записи, что как минимум в 2...8 раз продлит ресурс каждой ячейки. А если всё таки получили повреждение? Да, нужно придумать как детектировать ошибки, и обходить их стороной, если не получается устранить. Ещё полезная недокументированная особенность AVR, которая может стать причиной глюков - регистры общего назначения (R0...R31 которые) таки хранят данные без питания, несмотря на всю свою энергозависимость. Несколько часов...недель, но хранят. Поэтому эти ячейки нужно тереть при конфигурации, иначе мусор в них может сбить с толку программу. Эту особенность полезно прикрутить к нашей кнопке на случай, если ВСЯ EEPROM окончательно загнётся. Что интересно - тут текста больше чем кода, реализующего всё это, несмотря на всю свою ассемблерность:
Код:
clr R16
in R21, MCUSR
out MCUSR, R16 ;Очистить MCUSR
sbrs R21, PORF ;Не переключать, если PORF очищен
ser R21
clr R18 ;Поиск активного байта (хоть какого-нибудь рабочего)
rjmp eeloop
rdeelp: inc R18
cpi R18, EEPROMEND-8 ;Конец памяти кнопки
brcs eeloop
clr R18 ;Альтернативная память
mov R16, R28
sbi GPIOR0, 0 ;Флаг альтернативного режима
rjmp eeend
eeloop: out EEARL, R18 ;Прочитать байт из EEPROM
sbi EECR, EERE
in R16, EEDR
cpi R16, 0x00
breq rdeelp ;Прочитать следующий, если 0x00
eeend: cpi R16, 0b11111111 ;Декодирование активного байта
breq pwr1
cpi R16, 0b01111111
breq pwr2
cpi R16, 0b00111111
breq pwr3
cpi R16, 0b00011111
breq pwr4
cpi R16, 0b00001111
breq pwr1
cpi R16, 0b00000111
breq pwr2
cpi R16, 0b00000011
breq pwr3
cpi R16, 0b00000001
breq pwr4
sbis GPIOR0, 0
rjmp rdeelp ;Прочитать следующий при ошибке
ser R16 ;Первый режим при ошибке в альтернативном режиме
pwr1: sbrc R21, EXTRF ;Установка текущего режима
rjmp pwr1ld
rcall pwrup
rjmp pwr2ld
pwr1ld: ldi R16, EEPROMEND-7
rjmp pwrout
pwr2: sbrc R21, EXTRF
rjmp pwr2ld
rcall pwrup
rjmp pwr3ld
pwr2ld: ldi R16, EEPROMEND-5
rjmp pwrout
pwr3: sbrc R21, EXTRF
rjmp pwr3ld
rcall pwrup
rjmp pwr4ld
pwr3ld: ldi R16, EEPROMEND-3
rjmp pwrout
pwr4: sbrc R21, EXTRF
rjmp pwr4ld
rcall pwrup
rjmp pwr1ld
pwr4ld: ldi R16, EEPROMEND-1
rjmp pwrout
pwrup: lsr R16 ;Сохранить следующий режим (хоть куда-нибудь)
mov R28, R16
clr R19
ldi R17, 0b00100100 ;Write Only, EEMPE
newrt: out EEDR, R16
out EEARL, R18
out EECR, R17
sbi EECR, EEPE
pwrlp: sbic EECR, EEPE
rjmp pwrlp
sbi EECR, EERE
in R17, EEDR
cp R17, R16
brne pwrer
tst R16
breq eeclr
ret
pwrer: ldi R17, 0b00000100 ;Erase and Write, EEMPE
clr R20
out EEDR, R20
out EECR, R17
sbi EECR, EEPE
pwrlp1: sbic EECR, EEPE
rjmp pwrlp1
rjmp adrinc
eeclr: ser R16
ldi R17, 0b00010100 ;Erase Only, EEMPE
adrinc: cpi R19, EEPROMEND-8 ;Счётчик количества байт
brcs ewlpok
ret
ewlpok: inc R18
inc R19
cpi R18, EEPROMEND-8 ;Конец памяти кнопки
brcs newrt
clr R18
rjmp newrt
pwrout: ;Тут загружаем из EEPROM ток, используя содержимое R16 в качестве адреса.
Можно сделать и хитрее, вместо опроса MCUSR запускать АЦП на измерение напряжения того самого конденсатора. Это позволит отключить RESET, подцепить на него полноценную кнопку и при этом оставить возможность использования альтернативного режима, ещё и более гибко определять время выключенного состояния. Для лазера не очень применимо из-за опасности самостоятельного включения при перебое питания, если используется управление передней кнопкой. А для фонарика покатит...
Разобралисть, следующая передача...
3. Обратная связь по току O_o.
Вот только сейчас понимаю, насколько много мне ещё придётся писать... Попробую по-короче.
Задача: Необходимо разобраться какой тип регулятора сюда оптимально влепить, и как его рассчитать.
Самым простым регулятором я считаю триггерный, работающий по принципу "больше - глушим, меньше - 100% тяги". Собирается обычно на компараторе с явным и неявным гистерезисом, его обожают тулить во всякие ZXSC400, LM2621 и прочий хлам. Даже бабушкин утюг работает с таким регулятором - термореле называется. Этот вариант сразу отбрасываем, у нас как минимум для него недостаточно быстродействия, хотя сам по себе он при грамотной реализации весьма не плох, т.к. характер регулирования напоминает Сигма-Дельта Модуляцию в звуковых АЦП/ЦАП. Ещё живой пример - самоосциллирующие усилители класса D, UcD, которые сейчас рвут в хлам классику по всем параметрам (лампофилы - не берите близко к сердцу). Суть в том, что на ВЧ происходит генерация из-за ПОС, но на НЧ ПОС превращается в ООС и там то как раз сигнал хорошо повторяет опорное значение. Программно такое реализовать на AVR эффективнее, чем использование АЦП, я пока не пробовал. Для небольших скоростей да, в тех же терморегуляторах прокатит. К примеру китайцы используют такой механизм в паяльных станциях Lukey. Я бы его назвал тиристорным регулятором из-за того, что он по проводам, сети и нагревателю бьёт как отбойным молотком, аж свет моргает...
Более сложный механизм - Пропорциональный (П) и Пропорционально-интегральный (ПИ) регуляторы. Почему я их рассматриваю вместе? П-регулятор как частный случай ПИ-регулятора, его ставят там, где ПИ не может работать устойчиво (об этом ниже), поэтому сейчас поведаю только о ПИ. Примеров такой ОС масса. Это классическая ОС всех наших стабилизаторов в радиоэлектронике. Т.е. ОУ (Операционный Усилитель) как П-регулятор с огромным усилением и частотной коррекцией (кондёром в цепи ОС), которая является интегратором. На большой частоте (высоких скоростях) усиление такого регулятора снижается для избежания возбуждения (перерегулирования). Снижение усиления П-регулятора приводит к возникновению статической ошибки, которую добивает интегратор на более низких частотах. Отдельно они редко используются, а вот вместе - идеальная пара. С них и начнём, но я сначала расскажу вкратце ещё о двух типах связи, забегая наперёд (пригодится, поверьте).
ПИД - все слышали, да? Весь гугл им изгажен, а толковой информации мало. Это тот же ПИ, только с дополнительным дифференциальным (Д) звеном. Его можно представить как фильтр ВЧ, который компенсирует снижение усиления ошибки ПИ-регулятора на высоких скоростях (частотах). Нам он ниже здорово пригодится, ибо как выяснится позже - без него эта схема устойчивой не будет...
Прямая связь, предикативная ОС, управление с разомкнутой петлёй, фильтр Калмана... Я периодически натыкаюсь на описание одного и того же механизма под разными названиями и разного уровня сложности, и как правильно этот тип связи назвать - не знаю. Но идея шикарная, и благодаря цифре внедряется всюду. Суть в том, что некий "чёрный ящик", написанный сумрачным гением, изучает управляемую цепь, строит её виртуальную модель, и прежде чем сделать какой-то шаг - гоняет эту модель в виртуальной среде и выясняет что же она сделает ещё до того, как это произойдёт в реальности. Звучит громоздко и ресурсожруще, правда? Да вот нифига, несколько строк кода и мы симулируем RC-фильтр, колебательный контур, или целый динамик. На AVR в реальном времени. Я вот даже фотку у себя нашел, как тинька рисует 16 бит синус виртуальным колебательным контуром, затрачивая 4 байта на переменные и порядка 20-ти тактов на семпл:
С ОС мы уже определились, попробуем начать с ПИ-регулятора, рассчитаем коэффициенты. Начнём с самого худшего сценария, когда напряжение питания максимально, а дифференциальное сопротивление нагрузки минимально. В этом случае сигнал ошибки с шунта и отклик преобразователя на изменения заполнения ШИМ будут максимальны. Нам необходимо определится с исходными данными для этого сценария. Напряжение питания у нас максимум 4,35V, но мы возьмём 5V на всякий случай. Дифференциальное сопротивление типичной для данной весовой категории нагрузки на 0,5A (макс. ток драйвера, там сопротивление это минимально) - 1...0,5Ом, если верить графикам ВАХ в даташитах на всякие CREE XP-G, фиолетовые ЛД (лазерные диоды) и т.п. Плюс 0,1Ом шунта про запас.
Ранее в схеме мы рассчитали сопротивления в цепи ОС под интересующий нас диапазон напряжений, влепили конденсатор интересующей нас ёмкости, чтоб достаточно хорошо задавить пульсации ШИМ, и теперь можем определить сколько LSB ШИМ нам требуется для изменения тока на 1A. Получить так сказать условные попугаи, с которыми можно работать в программе. Закон Ома я тут рассказывать не буду, кто рассчитал цепь - сумеет и в обратном порядке всё посчитать. Скажу только что тут у меня получилось около 10V на 100% ШИМ (7...2V при изменении среднего значения ШИМ с 0 до 2,522V). На меньших напряжениях питания зависимость будет не такая крутая, но нас это сейчас не беспокоит, сценарий худший. К тому же точно всё рассчитывать смысла нет, ибо всё равно потом добивать осциллографом.
Если мы хотим прогуляться через весь диапазон токов, т.е. получить от АЦП 0...1023 (0...65472 с ADLAR) - нам придётся изменить заполнение ШИМ всего на 100%/(10V/(0,5A*0,6Ом))=3%. Т.е. вопрос о расширении разрядности ШИМ ОЧЕНЬ актуален, посему сразу переходим в 16-ти разрядную систему, используя плотностно-импульсную модуляцию младшего бита ШИМ. Да, привет интегрирование остатка в системе с ограниченной разрядностью. Не сможем пропихнуть в один период, так хоть размажем по нескольким. Это справедливо и для АЦП, т.к. мы поставили ADLAR и собираемся использовать передискретизацию. И так, какой вывод? Для теоретической устойчивости необходимо отклонения, полученные с АЦП, делить как минимум на 32 (100%/3%=33,33), и только после этого модифицировать ими заполнение ШИМ. Это справедливо для ситуации, когда за один период система полностью примет новое значение, но на практике это не будет по трём причинам:
- На выходе ШИМ сидит RC фильтр, который противно крутит фазу и режет спектр. Лечится специальным алгоритмом компенсации (та самая прямая связь, с моделью). Благо его параметры нам известны - постоянная времени порядка 1,024мс (не просто так, да).
- Дифференциальный усилитель АЦП тоже режет спектр и крутит фазу. Частота среза по даташиту 4кГц, значит компенсировать в текущих условиях эту паразитную RC-цепь (скорее всего передаточная дифф. усилителя повторяет передаточную RC-цепи) не получится. Да и вредно это, т.к. компенсация подразумевает использование ВЧ-фильтра, который явно не способствует подавлению шума.
- Преобразователь тоже не супер мега быстрый, но на его фоне АЦП просто нервно курит в сторонке.
Всё это в сумме даёт некую общую задержку. С RC-фильтром на выходе ШИМ мы разберёмся, его отбрасываем. По сравнению с тормозами АЦП преобразователь - формула один, поэтому его тоже отбрасываем. Частота дискретизации у нас 31,25кГц, если считать что на частоте 2кГц АЦП практически ничего не искажает - мы получаем по 16 семплов на период. В периоде, как мы помним, нам необхомо делить показания на 32, а если у нас период состоит из 16-ти семплов, то каждый придётся делить на 32*16=512. Это на самом деле дофига, т.к. даже с учётом предварительного аппаратного умножения 10-ти разрядного числа на 64 мы теряем целых 2 бита информации! Поэтому придётся их тоже куда-то складывать и использовать, когда насобирается целое число. И так, пишем с коэффициентом 256. А вдруг заработает?
Написали, зашили, смотрим:
Нифига оно не заработало. Получили генератор вместо стабилизатора. Значит закидываем расчётное значение:
Что за выброс? Рассмотрим детальнее:
Похоже на косяки с компенсатором RC-цепи, поковыряю его:
Круто, а если так?:
Подробнее:
Съедобно. Получается попали в точку, коэффициент - 512, имеем код (упрощённый для понимания):
Теперь увеличиваю выходное сопротивление источника питания до 1Ом:
Во как! Это называется паразитной ПОС (положительной обратной связью), которую я и ловил добавляя сопротивление. Когда ток на выходе растёт - преобразователь начинает потреблять больше, напряжение проседает вместе со средним значением ШИМ, из-за чего ток продолжает расти. Ну и наоборот. ОС МК должна быть достаточно быстрой, чтоб своевременно реагировать на подобную ситуацию и выруливать изменения быстрее, чем они успевают пробиваться через фильтр. Тут нам помогает компенсация этого фильтра, которая делает его для ОС невидимым, в то время когда не званным помехам приходится пробираться через него самостоятельно... Но что блин делать? Быстрее ОС не сделать - возбуждение, а этой скорости мало, вот вот начнётся генерация. Да, конечно же дифференциальное звено . Прикручиваем:
Код:
;--------------------------------------------------------------------
;ОС по макс. току:
currfb: clt
lpcur: brtc lpcur
in R23, ADCL ;Записать данные АЦП в буфер
in R24, ADCH
sbi ADCSRA, 6
sbi GPIOR0, 1 ;512 семплов стабилизации тока
ser R29
curfb1: ldi ZH, HIGH(RAMEND-23)
ldi ZL, LOW(RAMEND-23)
ld R16, Z+ ;Загрузить Iref
ld R17, Z+
;--------------------------
ld R18, Z+ ;Дифференциальное звено
ld R19, Z+ ;Загрузить IrefDiffdmp
cp R18, R23 ;Сглаживание
cpc R19, R24 ;+/-
breq dmoutc
brcc morecd
mov R20, R23 ;X=(Iout-IrefDiffdmp)/512
mov R21, R24
sub R20, R18
sbc R21, R19
lsr R21
ror R20
adc R8, R20 ;Интегратор остатка от деления
adc R21, R1
add R18, R21 ;IrefDiffdmp+=X
adc R19, R1
sub R3, R21 ;Tonnom-=X
sbc R4, R1
brcc dmoutc
clr R3
clr R4
rjmp dmoutc
morecd: mov R20, R18 ;X=(IrefDiffdmp-Iout)/512
mov R21, R19
sub R20, R23
sbc R21, R24
lsr R21
ror R20
adc R8, R20 ;Интегратор остатка от деления
adc R21, R1
sub R18, R21 ;IrefDiffdmp-=X
sbc R19, R1
add R3, R21 ;Tonnom+=X
adc R4, R1
brcc dmoutc
ser R20
mov R3, R20
mov R4, R20
dmoutc: st -Z, R19 ;Записать IrefDiffdmp
st -Z, R18
;---------------------------
cp R23, R16 ;ПИ-звено
cpc R24, R17 ;+/-
brcs loTon
sub R23, R16 ;Tonnom-=(Iout-Iref)/512
sbc R24, R17
lsr R24
ror R23
adc R7, R23 ;Интегратор остатка от деления
adc R24, R1
sub R3, R24
sbc R4, R1
brcc iout1 ;Клиппинг ОС (КЗ)
pwroff: cli ;Выключение при сбое
clr R16
out TCCR1, R16 ;Вырубить ШИМ
out PLLCSR, R16 ;Остановить ФАПЧ
out ADCSRA, R16 ;Выключить АЦП
out PORTB, R16 ;Сбросить порт
ldi R17, 0b10110000
ldi R16, 0b10110100
lpwoff: out MCUCR, R16 ;BOD Sleep, Sleep, режим Power-down, BOD Sleep Enable
out MCUCR, R17 ;BOD Sleep, Sleep, режим Power-down
SLEEP ;Вечный сон...
rjmp lpwoff
loTon: sub R16, R23 ;Tonnom+=(Iref-Iout)/512
sbc R17, R24
lsr R17
ror R16
adc R7, R16 ;Интегратор остатка от деления
adc R17, R1
add R3, R17
adc R4, R1
brcc iout1
ser R16 ;Клиппинг ОС (перегрузка)
mov R3, R16
mov R4, R16
iout1: dec R29
brne crmxlp
sbic GPIOR0, 1
rjmp clrbt1
rjmp main
clrbt1: cbi GPIOR0, 1
crmxlp: clt
lpcur2: brtc lpcur2
in R23, ADCL ;Записать данные АЦП в буфер
in R24, ADCH
cpi R29, 1
brne llcmax
sbic GPIOR0, 1
rjmp llcmax
ldi R16, 0b00101100 ;Измерение Vcc
out ADMUX, R16
llcmax: sbi ADCSRA, 6
rjmp curfb1
Гораздо лучше, свою работу оно делает. На практике никогда таких аккумуляторов не будет, с целым Омом сопротивления, но устойчивость лучше проверить заранее...
Дифференциальное звено как ПИ-регулятор, только наоборот. Поэтому и коэффициент ему пришиваем такой же - 512. Всё, что происходит быстрее чем на него успевает среагировать ПИ-регулятор - притормаживается до вменяемой скорости дифференциальным звеном. Это не совсем ПИД, т.к. звено включено нестандартно и охватывает лишь шунт, с опорным значением возится ПИ. Просто это проще, а у нас ресурсов не вагон...
И так, мы реализовали работающий скелет (среду) с ОС по току. Свою прямую функцию драйвер делает, теперь нам не составит труда добавить некоторые функции, о которых и пойдёт речь ниже.
4. Защиты.
Т.к. мускулы у драйвера самоходные - реализовывать защиту от перенапряжения, перегрузки и перегрева ключа нет смысла. От переполюсовки программа вообще не защитит, но у нас есть полевик на входе. Что остаётся? Я так понимаю переразряд батареи нужно корректно отрабатывать, и перегрев. Начнём с простого, и снова ОС...
Защита от перегрева.
Простейший вариант - триггер. Добрались до какой-то там температуры - что-то ВНЕЗАПНО сделало. Переключило на мощность по-меньше, или вовсе вырубилось. Обычно я приблизительно такие реализации и вижу...
Более сложный вариант - "аналоговая" ОС, что-то из области ПИ, ПИД и т.п. Чтоб мягко ограничивала мощность, удерживая температуру на пороге. Гугл говорит что для термоконтроля ПИД вне конкуренции, изучим:
Для того чтоб заставить работать ПИД - необходимо точно знать условия, или хотя бы гарантировать их неизменность, т.к. каждое звено этого регулятора что-то требует, и требования эти противоречивы требованиям соседа. Например у нас есть фонарь (лазер) в конкретном корпусе, с казалось бы известными параметрами. Мощность нагревателя известна, теплоёмкость тоже, влияние этой всей бодяги на тормоза термодатчика можно подобрать на практике. Настроили, сунули под одеяло - возбуд. Теплоотдача ухудшилась, температура растёт быстрее, дифференциальное звено начинает перерегулировать. Сунули в стакан с водой - возбуд. Теплоёмкость увеличилась, температура растёт медленно и интегратор перерегулирует. Хуже всего то, что эти условия могут тянутся практически до бесконечности (вместо стакана озеро, к примеру). "Тело" не пойми как себя может вести, поэтому требуется максимально устойчивая ОС. Условия то на самом деле хренпоймические! Причём настолько, что даже ПИ-регулятор будет неустойчив (пример с интегратором уже привёл). Что остаётся, П? Выходит что так, у него нет никакой памяти, он действует по факту и грубо. Но хотя бы действует... Устойчивость задаётся усилением ошибки. Тормоза (инерция) на стабильность влияют в лучшую сторону, поэтому если заставить стабильно работать голый драйвер, то с радиатором он уж точно заведётся нормально. Тут ещё придётся поплясать с бубном вокруг АЦП, который очень плохо работает с температурным сенсором, давая около 1LSB на градус. Шум давить тяжело, учитывая что программа у нас меряет температуру всего 60 раз в секунду и шум спокойно может стать заметным на глаз. С одной стороны чем больше мы сделаем усиление, тем точнее будет держатся температура, но тем грубее будет работа регулятора (меньше ступенек). Я решил остановится на 10LSB. Т.е. граница будет размазана на 10 градусов, в зависимости от условий охлаждения и подводимой мощности. Система обладает не хилой инерцией, этим грех не воспользоваться. Можно влепить фильтр НЧ, чтоб он сглаживал шум. Главное чтоб его постоянная времени была достаточно меньшей за постоянную времени системы нагреватель-радиатор-термодатчик, иначе можно получить возбуждение. Т.к. нагреваться радиатор (рядом с нагревателем, где и находится термодатчик) обычно быстрее, чем остывает - имеет смысл делать асимметричный фильтр, чтоб положительные изменения температуры пропускал быстрее отрицательных. Голой плату использовать не планируется, поэтому желательно ОС сделать такой, чтоб получить на этой плате колебания. Это поднимет характеристики драйвера в конечном изделии, на какой бы кусок говна его там не прикрутили. Опыт показал, что оптимальными коэффициенты оказались в 32 для положительных, и 128 для отрицательных изменений соответственно. Числа эти показывают 1/(61Гц/X) постоянной времени в секундах. Т.е. задежка вверх порядка 0,5с, а вниз - 2с. Этого достаточно, чтоб голая "звезда" вот вот начинала "раскачиваться" при токе в пару ампер (мучил китайские 1W). Понятное дело что так издеваться врятли кто будет...
Та же ситуация - хочется плавного ограничения. Я вижу две скользкие ситуации. Первая - удерживание напряжения на пороге 3V, регулируя под него ток нагрузки. При достижении этого порога фонарь начнёт вести себя как китаец на батарейках - всё тусклее и тусклее светить, и конца этому нет... Для этой задачи достаточно ПИ-регулятора, т.к. изменения очень медленные и отследить их не проблема. Но есть и второй момент:
В момент включения нельзя допустить перезагрузку, если батарея дохлая или её выходное сопротивление слишком высокое и она даже при полном заряде проседает ниже плинтуса (2,7V, ниже МК рестартует аппаратно). У нас в программе реализован софт-старт (0,25с), поэтому скорость роста тока нормирована, благодаря чему возможен расчёт дифференциального звена, чтоб оно притормаживало скорость, если напряжение питания слишком быстро для ПИ-звена падает. Максимальная скорость нарастания тока - 31мА (0...500мА размазано на 16 ступенек). В худшем случае такой скачок увеличит потребление с батареи миллиампер на 70. И если мы возьмём максимальное гарантированное внутреннее сопротивление батареи в 1Ом, то получим 0,07V просадки на ступеньку. Д не должен допустить снижения напряжения ниже 2,8V, однако на более высоких значениях его усиление необходимо притуплять, чтоб он не мешал там, где такие моменты не страшны. Например ATX БП сильно шумит, этот шум прекрасно пролазит через это звено и "срёт в эфир" (серьёзно, у меня даже приёмник начинает шуметь). Чтоб шум АЦП не доставал при нормальной работе - придётся таки отсечь пару младших битов. Всё равно точность тут ненужна, это аварийный стоп-кран.
Код:
;--------------------------------------------------------------------
;Напряжение питания:
main: ldi R29, 10 ;Пропустить 10 семплов АЦП
lptvcc: clt
lpvcc: brtc lpvcc
dec R29
breq lpvout
sbi ADCSRA, 6
rjmp lptvcc
lpvout: in R23, ADCL ;Записать данные АЦП в буфер
in R24, ADCH
ldi R16, 0b10101111 ;Измерение температуры
out ADMUX, R16
sbi ADCSRA, 6
vccld: ldi ZH, HIGH(RAMEND-27) ;Дифференциальное звено
ldi ZL, LOW(RAMEND-27)
ld R19, -Z ;Загрузить VccLoad
ld R18, -Z
ld R17, -Z ;Загрузить VccDiffdmp
ld R16, -Z
sbic GPIOR2, 6 ;Пропустить сглаживание,
rjmp diffok ;если выборка первая
sbi GPIOR2, 6
mov R16, R23
mov R17, R24
diffok: clr R0
cp R16, R23 ;Сглаживание
cpc R17, R24 ;+/-
breq dmotvj
brcc morevd
mov R20, R23 ;VccLoad+=(ADC-VccDiffdmp-256)*2/1...8
mov R21, R24
sub R20, R16
sbc R21, R17
subi R21, 2 ;Порог LSB
brcs advlok
lsl R20 ;Коэффициент
rol R21
cpi R24, 0x58 ;Порог 3,2V
brcc lower1
lsr R21 ;/2
ror R20
lower1: cpi R24, 0x4E ;Порог 3,6V
brcc lower2
lsr R21 ;/4
ror R20
lower2: cpi R24, 0x40 ;Порог 4,4V
brcc lower3
lsr R21 ;/8
ror R20
lower3: add R18, R20
adc R19, R21
brcc advlok
ser R18
ser R19
advlok: mov R20, R23 ;VccDiffdmp+=(ADC-VccDiffdmp)/32
mov R21, R24
sub R20, R16
sbc R21, R17
lsl R20
rol R21
rol R20
rol R21
rol R20
rol R21
rol R20
andi R20, 0b00000111
cp R21, R0
cpc R20, R0
brne okdnvd
inc R21
okdnvd: add R16, R21
adc R17, R20
dmotvj: rjmp dmoutv
morevd: mov R20, R16 ;VccLoad-=(VccDiffdmp-ADC-256)*2/1...8
mov R21, R17
sub R20, R23
sbc R21, R24
subi R21, 2 ;Порог LSB
brcs sbvlok
lsl R20 ;Коэффициент
rol R21
cpi R24, 0x58 ;Порог 3,2V
brcc lower4
lsr R21 ;/2
ror R20
lower4: cpi R24, 0x4E ;Порог 3,6V
brcc lower5
lsr R21 ;/4
ror R20
lower5: cpi R24, 0x40 ;Порог 4,4V
brcc lower6
lsr R21 ;/8
ror R20
lower6: sub R18, R20
sbc R19, R21
brcc sbvlok
clr R18
clr R19
sbvlok: mov R20, R16 ;VccDiffdmp-=(VccDiffdmp-ADC)/32
mov R21, R17
sub R20, R23
sbc R21, R24
lsl R20
rol R21
rol R20
rol R21
rol R20
rol R21
rol R20
andi R20, 0b00000111
cp R21, R0
cpc R20, R0
brne okupvd
inc R21
okupvd: sub R16, R21
sbc R17, R20
dmoutv: st Z+, R16 ;Записать VccDiffdmp
st Z+, R17
ldi R20, 0xC0 ;ПИ-звено
ldi R21, 0x5D ;Порог 3V
cp R20, R23
cpc R21, R24
brcc morevl ;+/-
sub R23, R20 ;VccLoad+=(ADC-Vccmin)/32
sbc R24, R21
lsl R23
rol R24
rol R23
rol R24
rol R23
rol R24
rol R23
andi R23, 0b00000111
cp R24, R0
cpc R23, R0
brne modupv
inc R24
modupv: add R18, R24
adc R19, R23
brcc vpiout
ser R18
ser R19
rjmp vpiout
morevl: sub R20, R23 ;VccLoad-=(Vccmin-ADC)/32
sbc R21, R24
lsl R20
rol R21
rol R20
rol R21
rol R20
rol R21
rol R20
andi R20, 0b00000111
cp R21, R0
cpc R20, R0
brne moddnv
inc R21
moddnv: sub R18, R21
sbc R19, R20
brcc vpiout
clr R18
clr R19
vpiout: st Z+, R18 ;Записать VccLoad
st Z+, R19
Остальное.
А что делать, когда напряжение уже не держится на 3,0V и продолжает падать? Ток к этому моменту будет минимален, запустится таймер, посчитает до 10-ти (секунд) и если за это время состояние не улучшится - уйдёт в Power-down. Задержка необходима на случай, если произойдёт перерегулирование. Драйвер ещё может выйти на режим, поэтому сразу же отключать не стоит. Этот же механизм сработает, если перегретое устройство не успеет остыть за это время. Может возникнуть ситуация, когда драйвер всё таки возбудился, или же когда не хватает напряжения питания, чтоб опустить напряжение на выходе до требуемого значения (к примеру КЗ при питании 3V). В такой ситуации драйвер засыпает немедленно.
Очень полезный механизм, которого тут не хватает - не дающий ОС уйти в насыщение, когда преобразователь по тем или иным причинам не может выдать требуемый ток. Актуально для лазеров, ибо в таком состоянии ОС пропустит выброс, если преобразователь ВНЕЗАПНО осилит то, что от него требуют. Но я пока не придумал адекватного механизма. В голову приходит только модуляция тока, с проверкой реальной амплитуды, возможно реализую...
Всякие феньки, свистелки, перделки и т.п. я тут не рассматриваю, т.к. они не вызывают проблем у программистов. Скажу только что "та странная фиговина, торчащая на схеме как аппендицит" - электромагнитный привод коллиматора, поэтому и буква "s" в названии - special. Заточенная под конкретную конструкцию система. Другой вариант имеет подстроечник и ещё ряд фенек, но из-за полукоммерческой основы я им светить не могу...
Вот весь код. Забыл свалить в общую кучу переменные в ОЗУ. Код достался по наследству от Indigo 4.0 и переделывался выпиливанием всего лишнего. Пооставались дырки в памяти...
Код:
;Драйвер на связке ATiny25 (45) и TPS63030 (20), версия с переключателем фокуса
.INCLUDE "tn25def.inc"
;Векторы прерываний
rjmp RESET
reti
reti
reti
reti
set ;Over0
;/*
;Расширитель разрядности с компенсацией ("прямая связь"):
cp R5, R3 ;Компенсация RC-цепи (T=1,024ms)
cpc R6, R4
brcs locap ;Выбор знака (псевдознаковая арифметика)
mov R18, R5 ;Ton=C-(C-FB)*32
mov R19, R6
sub R18, R3
sbc R19, R4
cpi R19, 8
brcc xmin
lsr R18
ror R19
ror R18
ror R19
ror R18
ror R19
ror R18
mov R16, R5
mov R17, R6
sub R16, R19
sbc R17, R18
brcc xok1
xmin: clr R16
clr R17
xok1: mov R0, R17
add R2, R16
adc R0, R1
out OCR1A, R0
mov R18, R5 ;C-=(C-Ton)/32
mov R19, R6
sub R18, R16
sbc R19, R17
lsl R18
rol R19
rol R18
rol R19
rol R18
rol R19
rol R18
andi R18, 0b00000111
sub R5, R19
sbc R6, R18
reti
locap: mov R16, R3 ;Ton=(FB-C)*32+C
mov R17, R4
sub R16, R5
sbc R17, R6
cpi R17, 8
brcc xmax
lsr R16
ror R17
ror R16
ror R17
ror R16
ror R17
ror R16
add R17, R5
adc R16, R6
brcs xmax
cpi R16, 0xFF
brcs xok2
xmax: ser R16
clr R17
xok2: mov R0, R16
add R2, R17
adc R0, R1
out OCR1A, R0
sub R17, R5 ;C+=(Ton-C)/32
sbc R16, R6
lsl R17
rol R16
rol R17
rol R16
rol R17
rol R16
rol R17
andi R17, 0b00000111
add R5, R16
adc R6, R17
reti
;*/
/*
mov R0, R4 ;Расширение разрядности ШИМ (16 бит, без компенсации)
add R2, R3
adc R0, R1
out OCR1A, R0
reti
*/
;====================================================================
;Конфигурация:
RESET: ;Ldi R16, HIGH(RAMEND-34) ;инициализация стека
;out SPH, R16
Ldi R16, LOW(RAMEND-34)
out SPL, R16
ldi R16, 0b00100110 ;Подтягивание
out PORTB, R16
ldi R16, 0b00000111 ;Выходы
out DDRB, R16
ldi R16, 0b10000000 ;Отключить аналоговый компаратор
out ACSR, R16
ldi R16, 0b00011000 ;Выключить цифровые входные буферы
out DIDR0, R16
ldi R16, 0b00000010 ;Запустить ФАПЧ
out PLLCSR, R16
ldi R16, 0b10000111 ;Измерение Ioffset
out ADMUX, R16
ldi R16, 0b11000011 ;ADEN, ADSC, тактовая clk/8
out ADCSRA, R16
clr R1
clr R2
clr R3
clr R4
ser R16
mov R5, R16
mov R6, R16
clr R7
clr R8
clr R27
ldi ZH, HIGH(RAMEND+1) ;Очистить ОЗУ
ldi ZL, LOW(RAMEND+1)
ldi R16, 34 ;Количество очищаемых ячеек
upclr: st -Z, R3
dec R16
brne upclr
ldi R16, 0x80 ;5mA min
ldi R17, 0x02
ldi ZH, HIGH(RAMEND-10)
ldi ZL, LOW(RAMEND-10)
st Z+, R16 ;Записать Irefdamped
st Z+, R17
ldi ZH, HIGH(RAMEND-6)
ldi ZL, LOW(RAMEND-6)
tnordy: sbic EECR, EEPE
rjmp tnordy
ldi R16, 119 ;Загрузить порог температуры
out EEARL, R16
sbi EECR, EERE
in R16, EEDR
cpi R16, 120 ;Проверка на диапазон (40...95гр.)
brcs tmaxok
ldi R16, 120
tmaxok: cpi R16, 57
brcc tminok
ldi R16, 57
tminok: st Z+, R16 ;Записать tempmax
;--------------------------------------------------------------------
;Задняя кнопка:
clr R16
in R21, MCUSR
out MCUSR, R16 ;Очистить MCUSR
sbrs R21, PORF ;Не переключать, если PORF очищен
ser R21
clr R18 ;Поиск активного байта (хоть какого-нибудь рабочего)
rjmp eeloop
rdeelp: inc R18
cpi R18, EEPROMEND-8 ;Конец памяти кнопки
brcs eeloop
clr R18 ;Альтернативная память
mov R16, R28
sbi GPIOR0, 0 ;Флаг альтернативного режима
rjmp eeend
eeloop: out EEARL, R18 ;Прочитать байт из EEPROM
sbi EECR, EERE
in R16, EEDR
cpi R16, 0x00
breq rdeelp ;Прочитать следующий, если 0x00
eeend: cpi R16, 0b11111111 ;Декодирование активного байта
breq pwr1
cpi R16, 0b01111111
breq pwr2
cpi R16, 0b00111111
breq pwr3
cpi R16, 0b00011111
breq pwr4
cpi R16, 0b00001111
breq pwr1
cpi R16, 0b00000111
breq pwr2
cpi R16, 0b00000011
breq pwr3
cpi R16, 0b00000001
breq pwr4
sbis GPIOR0, 0
rjmp rdeelp ;Прочитать следующий при ошибке
ser R16 ;Первый режим при ошибке в альтернативном режиме
pwr1: sbrc R21, EXTRF ;Установка текущего режима
rjmp pwr1ld
rcall pwrup
rjmp pwr2ld
pwr1ld: ldi R16, EEPROMEND-7
rjmp pwrout
pwr2: sbrc R21, EXTRF
rjmp pwr2ld
rcall pwrup
rjmp pwr3ld
pwr2ld: ldi R16, EEPROMEND-5
rjmp pwrout
pwr3: sbrc R21, EXTRF
rjmp pwr3ld
rcall pwrup
rjmp pwr4ld
pwr3ld: ldi R16, EEPROMEND-3
ldi R27, 12 ;Переключить фокус на перетяжку
rjmp pwrout
pwr4: sbrc R21, EXTRF
rjmp pwr4ld
rcall pwrup
rjmp pwr1ld
pwr4ld: ldi R16, EEPROMEND-1
ldi R27, 12 ;Переключить фокус на перетяжку
rjmp pwrout
pwrup: lsr R16 ;Сохранить следующий режим (хоть куда-нибудь)
mov R28, R16
clr R19
ldi R17, 0b00100100 ;Write Only, EEMPE
newrt: out EEDR, R16
out EEARL, R18
out EECR, R17
sbi EECR, EEPE
pwrlp: sbic EECR, EEPE
rjmp pwrlp
sbi EECR, EERE
in R17, EEDR
cp R17, R16
brne pwrer
tst R16
breq eeclr
ret
pwrer: ldi R17, 0b00000100 ;Erase and Write, EEMPE
clr R20
out EEDR, R20
out EECR, R17
sbi EECR, EEPE
pwrlp1: sbic EECR, EEPE
rjmp pwrlp1
rjmp adrinc
eeclr: ser R16
ldi R17, 0b00010100 ;Erase Only, EEMPE
adrinc: cpi R19, EEPROMEND-8 ;Счётчик количества байт
brcs ewlpok
ret
ewlpok: inc R18
inc R19
cpi R18, EEPROMEND-8 ;Конец памяти кнопки
brcs newrt
clr R18
rjmp newrt
pwrout: ldi ZH, HIGH(RAMEND-8) ;Загрузка выбранного режима
ldi ZL, LOW(RAMEND-8)
ld R22, -Z ;Загрузить Irefdamped
ld R21, -Z
out EEARL, R16
sbi EECR, EERE
in R17, EEDR
inc R16
out EEARL, R16
sbi EECR, EERE
in R18, EEDR
ldi R19, 0x80 ;5mA min
ldi R20, 0x02
cp R17, R19
cpc R18, R20
brcc cmaxok
mov R17, R19
mov R18, R20
cmaxok: ldi R19, 0xC0 ;Макс значение 0,5A
ldi R20, 0xEF
cp R19, R17
cpc R20, R18
brcc cminok
mov R17, R19
mov R18, R20
cminok: st -Z, R18 ;Записать Irefmax
st -Z, R17
cp R17, R21
cpc R18, R22
brcc moresp
sub R21, R17
sbc R22, R18
mov R17, R21
mov R18, R22
rjmp spdiv
moresp: sub R17, R21
sbc R18, R22
spdiv: swap R17 ;Irefmax/16
swap R18
mov R19, R18
andi R17, 0b00001111
andi R18, 0b00001111
andi R19, 0b11110000
or R17, R19
st -Z, R18 ;Записать speed_soft_start
st -Z, R17
plloop: in R16, PLLCSR ;Ждём стабилизацию ФАПЧ
sbrs R16, PLOCK
rjmp plloop
ldi R16, 0b00000001 ;Запуск таймера0, clk/1
out TCCR0B, R16
ldi R16, 0b00000010 ;Включение Over0
out TIMSK, R16
ldi R16, 0b00000111 ;Подключить ФАПЧ к таймеру
out PLLCSR, R16
ldi R16, 0b01110001 ;Fast PWM inverting mode, clk/1
out TCCR1, R16
sei ;Разрешить прерываниЯ
;С этого момента софт синхронизирован с ШИМ.
ldi R29, 128 ;Ждём 4мс (128 циклов), пока зарядится RC-фильтр
lptstr: clt ;И выйдет на режим АЦП
lpstr: brtc lpstr ;Тупим в цикле синхронизации (ждём флаг T)
sbi ADCSRA, 6 ;Запустить измерение следующего семпла
dec R29
brne lptstr
ldi R29, 64 ;Калибровка токового шунта
clr R23
clr R24
offset: clt
lpofst: brtc lpofst
in R16, ADCL ;Записать данные АЦП в буфер
in R17, ADCH
sbi ADCSRA, 6
add R23, R16 ;Интегрирование семплов
adc R24, R17
dec R29
brne offset
ldi ZH, HIGH(RAMEND-25)
ldi ZL, LOW(RAMEND-25)
st Z+, R23 ;Записать Ioffset
st Z+, R24
ldi R16, 0b00101100 ;Измерение Vcc
out ADMUX, R16
sbi ADCSRA, 6
sbi PORTB, 0 ;Запустить преобразователь
;====================================================================
;Глобальные переменные
;R1 - Zero
;R2 - Ton_error
;R4:R3 - Ton
;R6:R5 - Ton_comp
;R7 - PIcur_error
;R8 - Dcur_error
;R27 - Таймер переключателя фокуса
;R28 - Резервная память режимов
;R29 - Main Loop Counter
;RAMEND-5 - temperature
;RAMEND-6 - tempmax
;RAMEND-10 - Irefdamped
;RAMEND-12 - Irefmax
;RAMEND-14 - Speed_soft_start
;RAMEND-21 - IrefDiffdmp
;RAMEND-23 - Iref
;RAMEND-25 - Ioffset
;RAMEND-27 - timeout
;RAMEND-29 - VccLoad
;RAMEND-31 - VccDiffdmp
;--------------------------------------------------------------------
;Напряжение питания:
main: ldi R29, 10 ;Пропустить 10 семплов АЦП
lptvcc: clt
lpvcc: brtc lpvcc
dec R29
breq lpvout
sbi ADCSRA, 6
rjmp lptvcc
lpvout: in R23, ADCL ;Записать данные АЦП в буфер
in R24, ADCH
ldi R16, 0b10101111 ;Измерение температуры
out ADMUX, R16
sbi ADCSRA, 6
vccld: ldi ZH, HIGH(RAMEND-27) ;Дифференциальное звено
ldi ZL, LOW(RAMEND-27)
ld R19, -Z ;Загрузить VccLoad
ld R18, -Z
ld R17, -Z ;Загрузить VccDiffdmp
ld R16, -Z
sbic GPIOR2, 6 ;Пропустить сглаживание,
rjmp diffok ;если выборка первая
sbi GPIOR2, 6
mov R16, R23
mov R17, R24
diffok: clr R0
cp R16, R23 ;Сглаживание
cpc R17, R24 ;+/-
breq dmotvj
brcc morevd
mov R20, R23 ;VccLoad+=(ADC-VccDiffdmp-256)*2/1...8
mov R21, R24
sub R20, R16
sbc R21, R17
subi R21, 2 ;Порог LSB
brcs advlok
lsl R20 ;Коэффициент
rol R21
cpi R24, 0x58 ;Порог 3,2V
brcc lower1
lsr R21 ;/2
ror R20
lower1: cpi R24, 0x4E ;Порог 3,6V
brcc lower2
lsr R21 ;/4
ror R20
lower2: cpi R24, 0x40 ;Порог 4,4V
brcc lower3
lsr R21 ;/8
ror R20
lower3: add R18, R20
adc R19, R21
brcc advlok
ser R18
ser R19
advlok: mov R20, R23 ;VccDiffdmp+=(ADC-VccDiffdmp)/32
mov R21, R24
sub R20, R16
sbc R21, R17
lsl R20
rol R21
rol R20
rol R21
rol R20
rol R21
rol R20
andi R20, 0b00000111
cp R21, R0
cpc R20, R0
brne okdnvd
inc R21
okdnvd: add R16, R21
adc R17, R20
dmotvj: rjmp dmoutv
morevd: mov R20, R16 ;VccLoad-=(VccDiffdmp-ADC-256)*2/1...8
mov R21, R17
sub R20, R23
sbc R21, R24
subi R21, 2 ;Порог LSB
brcs sbvlok
lsl R20 ;Коэффициент
rol R21
cpi R24, 0x58 ;Порог 3,2V
brcc lower4
lsr R21 ;/2
ror R20
lower4: cpi R24, 0x4E ;Порог 3,6V
brcc lower5
lsr R21 ;/4
ror R20
lower5: cpi R24, 0x40 ;Порог 4,4V
brcc lower6
lsr R21 ;/8
ror R20
lower6: sub R18, R20
sbc R19, R21
brcc sbvlok
clr R18
clr R19
sbvlok: mov R20, R16 ;VccDiffdmp-=(VccDiffdmp-ADC)/32
mov R21, R17
sub R20, R23
sbc R21, R24
lsl R20
rol R21
rol R20
rol R21
rol R20
rol R21
rol R20
andi R20, 0b00000111
cp R21, R0
cpc R20, R0
brne okupvd
inc R21
okupvd: sub R16, R21
sbc R17, R20
dmoutv: st Z+, R16 ;Записать VccDiffdmp
st Z+, R17
ldi R20, 0xC0 ;ПИ-звено
ldi R21, 0x5D ;Порог 3V
cp R20, R23
cpc R21, R24
brcc morevl ;+/-
sub R23, R20 ;VccLoad+=(ADC-Vccmin)/32
sbc R24, R21
lsl R23
rol R24
rol R23
rol R24
rol R23
rol R24
rol R23
andi R23, 0b00000111
cp R24, R0
cpc R23, R0
brne modupv
inc R24
modupv: add R18, R24
adc R19, R23
brcc vpiout
ser R18
ser R19
rjmp vpiout
morevl: sub R20, R23 ;VccLoad-=(Vccmin-ADC)/32
sbc R21, R24
lsl R20
rol R21
rol R20
rol R21
rol R20
rol R21
rol R20
andi R20, 0b00000111
cp R21, R0
cpc R20, R0
brne moddnv
inc R21
moddnv: sub R18, R21
sbc R19, R20
brcc vpiout
clr R18
clr R19
vpiout: st Z+, R18 ;Записать VccLoad
st Z+, R19
;--------------------------------------------------------------------
;Температура
ldi R29, 5 ;Пропустить 5 семплов АЦП
lpttmp: clt
lptmp: brtc lptmp
dec R29
breq lptout
sbi ADCSRA, 6
rjmp lpttmp
lptout: in R23, ADCL ;Записать данные АЦП в буфер
in R24, ADCH
ldi R16, 0b10100111 ;Измерение Iout
out ADMUX, R16
sbi ADCSRA, 6
ldi ZH, HIGH(RAMEND-6)
ldi ZL, LOW(RAMEND-6)
ld R17, Z+ ;Загрузить tempmax
clr R16 ;t=(t-tmax)*64
sec ;+256
ror R17 ;x64
ror R16
lsr R17
ror R16
sub R23, R16 ;Подготовка значения АЦП
sbc R24, R17
brcc subtok
clr R23
clr R24
subtok: cpi R24, 4
brcc ovrmul
lsr R23
ror R24
ror R23
ror R24
ror R23
rjmp multok
ovrmul: ser R24
ser R23
multok: ld R18, Z+ ;Загрузить температуру
ld R19, Z+
sbic GPIOR2, 5 ;пропустить сглаживание,
rjmp tdmpok ;если выборка первая
sbi GPIOR2, 5
mov R18, R24
mov R19, R23
tdmpok: clr R22
cp R24, R18 ;Сглаживание
cpc R23, R19 ;+/-
breq equalt
brcc moretd
mov R20, R18 ;?
mov R21, R19
sub R20, R24
sbc R21, R23
lsl R20 ;/128
rol R21
brcc okdnt1
ser R21
rjmp okdnt2
okdnt1: brne okdnt2
inc R21
okdnt2: sub R18, R21
sbc R19, R22
rjmp equalt
moretd: sub R24, R18
sbc R23, R19
lsl R24 ;/32
rol R23
rol R24
rol R23
rol R24
rol R23
rol R24
andi R24, 0b00000111
cp R23, R22
cpc R24, R22
brne okupt2
inc R23
okupt2: add R18, R23
adc R19, R24
equalt: st -Z, R19 ;Записать температуру
st -Z, R18
;--------------------------------------------------------------------
;Сглаживание Irefmax, защиты и фокус
clt
lpcdmp: brtc lpcdmp
sbi ADCSRA, 6
ldi ZH, HIGH(RAMEND-14) ;Плавный пуск/выключение
ldi ZL, LOW(RAMEND-14)
ld R16, Z+ ;Загрузить speed_soft_start
ld R17, Z+
ld R18, Z+ ;Загрузить Irefmax
ld R19, Z+
ld R20, Z+ ;Загрузить Irefdamped
ld R21, Z+
cp R18, R20 ;+/-
cpc R19, R21
brcs refdwn
add R20, R16
adc R21, R17
brcs dmpov1
cp R18, R20
cpc R19, R21
brcc rfupok
dmpov1: mov R20, R18
mov R21, R19
rjmp rfupok
refdwn: sub R20, R16
sbc R21, R17
brcs overok
cp R20, R18
cpc R21, R19
brcc rfupok
overok: mov R20, R18
mov R21, R19
rfupok: st -Z, R21 ;Записать Irefdamped
st -Z, R20
;/*
ldi ZH, HIGH(RAMEND-29) ;Ограничение по напряжению питания
ldi ZL, LOW(RAMEND-29)
ld R18, Z+ ;Загрузить VccLoad
ld R19, Z+
clr R16
cp R18, R16
cpc R19, R16
breq vrfok
sub R20, R18 ;Iref-=VccLoad
sbc R21, R19
brcc vrfok
clr R20
clr R21
vrfok: ldi ZH, HIGH(RAMEND-5) ;Ограничение по температуре
ldi ZL, LOW(RAMEND-5)
ld R16, Z+ ;Загрузить температуру
ld R17, Z+
ser R18
ser R19
sub R18, R16
sbc R19, R17
cp R18, R20
cpc R19, R21
brcc trfok
mov R20, R18
mov R21, R19
;*/
trfok: ldi ZH, HIGH(RAMEND-25) ;Таймаут
ldi ZL, LOW(RAMEND-25)
ld R19, -Z ;Загрузить timeout
ld R18, -Z
ldi R16, 0x80 ;5mA min
ldi R17, 0x02
cp R20, R16
cpc R21, R17
brcc mrfok
mov R20, R16
mov R21, R17
ldi R16, 1
clr R17
add R18, R16
adc R19, R17
ldi R16, 0x58 ;Задержка перед выключением (10s)
ldi R17, 0x02
cp R18, R16
cpc R19, R17
brcs timout
rjmp pwroff ;Выключение при таймауте
mov R18, R16
mov R19, R17
rjmp timout
mrfok: clr R18
clr R19
timout: st Z+, R18 ;Записать timeout
st Z+, R19
/*
sbic GPIOR2, 7 ;Модулятор тока (для отлаживания)
rjmp fulcur
sbi GPIOR2, 7
rjmp modout
fulcur: cbi GPIOR2, 7
lsr R21
ror R20
modout:
*/
ld R18, Z+ ;Загрузить Ioffset
ld R19, Z+
add R20, R18 ;Калибровка
adc R21, R19
brcc crnorm
rjmp pwroff ;Выключение при переполнении
crnorm: st Z+, R20 ;Записать Iref
st Z+, R21
sbi GPIOR0, 1 ;512 семплов стабилизации тока
ser R29
tst R27 ;Таймер переключателя фокуса
breq currfb
dec R27
brne currfb
sbi PINB, 2 ;Инвертировать состояние
;--------------------------------------------------------------------
;ОС по макс. току:
currfb: clt
lpcur: brtc lpcur
in R23, ADCL ;Записать данные АЦП в буфер
in R24, ADCH
sbi ADCSRA, 6
curfb1: sbic PINB, 0 ;CТАБИЛИЗАЦИЯ
rjmp pwmon ;Сбросить прогресс,
clr R3 ;если преобразователь отключен
clr R4
rjmp iout1
pwmon: ldi ZH, HIGH(RAMEND-23) ;Дифференциальное звено
ldi ZL, LOW(RAMEND-23)
ld R16, Z+ ;Загрузить Iref
ld R17, Z+
;/*
ld R18, Z+ ;Загрузить IrefDiffdmp
ld R19, Z+
cp R18, R23 ;Сглаживание
cpc R19, R24 ;+/-
breq dmoutc
brcc morecd
mov R20, R23 ;X=(Iout-IrefDiffdmp)/512
mov R21, R24
sub R20, R18
sbc R21, R19
lsr R21
ror R20
adc R8, R20 ;Интегратор остатка от деления
adc R21, R1
add R18, R21 ;IrefDiffdmp+=X
adc R19, R1
sub R3, R21 ;Tonnom-=X
sbc R4, R1
brcc dmoutc
clr R3
clr R4
rjmp dmoutc
morecd: mov R20, R18 ;X=(IrefDiffdmp-Iout)/512
mov R21, R19
sub R20, R23
sbc R21, R24
lsr R21
ror R20
adc R8, R20 ;Интегратор остатка от деления
adc R21, R1
sub R18, R21 ;IrefDiffdmp-=X
sbc R19, R1
add R3, R21 ;Tonnom+=X
adc R4, R1
brcc dmoutc
ser R20
mov R3, R20
mov R4, R20
dmoutc: st -Z, R19 ;Записать IrefDiffdmp
st -Z, R18
;*/
cp R23, R16 ;ПИ-звено
cpc R24, R17 ;+/-
brcs loTon
sub R23, R16 ;Tonnom-=(Iout-Iref)/512
sbc R24, R17
lsr R24
ror R23
adc R7, R23 ;Интегратор остатка от деления
adc R24, R1
sub R3, R24
sbc R4, R1
brcc iout1 ;Клиппинг ОС (КЗ)
/*
clr R3 Отключение защиты от КЗ (для отлаживания)
clr R4
rjmp iout1
*/
pwroff: cli ;Выключение при сбое
clr R16
out TCCR1, R16 ;Вырубить ШИМ
out PLLCSR, R16 ;Остановить ФАПЧ
out ADCSRA, R16 ;Выключить АЦП
out PORTB, R16 ;Сбросить порт
ldi R17, 0b10110000
ldi R16, 0b10110100
lpwoff: out MCUCR, R16 ;BOD Sleep, Sleep, режим Power-down, BOD Sleep Enable
out MCUCR, R17 ;BOD Sleep, Sleep, режим Power-down
SLEEP ;Вечный сон...
rjmp lpwoff
loTon: sub R16, R23 ;Tonnom+=(Iref-Iout)/512
sbc R17, R24
lsr R17
ror R16
adc R7, R16 ;Интегратор остатка от деления
adc R17, R1
add R3, R17
adc R4, R1
brcc iout1
ser R16 ;Клиппинг ОС (перегрузка)
mov R3, R16
mov R4, R16
iout1: dec R29
brne crmxlp
sbic GPIOR0, 1
rjmp clrbt1
rjmp main
clrbt1: cbi GPIOR0, 1
crmxlp: clt
lpcur2: brtc lpcur2
in R23, ADCL ;Записать данные АЦП в буфер
in R24, ADCH
cpi R29, 1
brne llcmax
sbic GPIOR0, 1
rjmp llcmax
ldi R16, 0b00101100 ;Измерение Vcc
out ADMUX, R16
llcmax: sbi ADCSRA, 6
rjmp curfb1
.eseg ;EEPROM
.org EEPROMEND-8
.db 85 ;Температура 60гр. (44LSB=25гр., 0,857гр. LSB)
.db 0x00,0x13 ;Ток 1 40mA (0,525mA LSB x64 L,H)
.db 0x40,0xD6 ;Ток 2 450mA
.db 0x00,0x13 ;Ток 3 40mA (с фокусом)
.db 0x40,0xD6 ;Ток 4 450mA (с фокусом)
Вот Архив.rar. Тут и исходники, и хексы, и схема, и плата...
Этот драйвер можно завести на полевике вместо преобразователя (что-то вроде форумного драйвера), на частоте 2...4МГц. Изначально мишуры было гораздо больше ("передняя" умная кнопка, подстроечник, программирующий все режимы, модуляция тока, няшный индикатор и т.п.) но всё это было попилено...
Да, забыл же осциллограммы результата показать:
Переменная составляющая напряжения на выходе (под нагрузкой), 10мВ/Дел. По виду там в среднем 5мВ пульсаций, посмотрим по-ближе:
Тут просматривается пила самого преобразователя на частоте в пару мегагерц, а если отойти по-дальше - видно пилу самого ШИМ МК (жирная линия такая прорисовываться на фоне мелкой пилы). Как видно сам МК вклад вносит незначительный, хоть по-идее и ШИМ там хреновый, и АЦП шумит, и вообще МК гавно... Поэтому я и говорю что поднять частоту ШИМ можно до 2МГц без ухудшения результата. Выручит плотностно-импульсная модуляция его последнего бита. АЦП таки может хорошо работать, попросить просто надо. А арифметика если использует деление - остаток не выбрасывайте, складывайте где-то в кучку, а когда мелочи соберётся достаточно - меняйте на рубль (LSB) и вперёд. Не впишется в один семпл, так хоть размажется по нескольким...
Кстати, как ни странно - рассчитанный ток попадает метко. Зашил в EEPROM 450мА - получил 449...451мА. На 40мА получаю 39...41мА. Подстроечником так фиг настроишь. Были проблемы с калибровкой смещения, почему-то на 25-х постоянно замечаю не хилый offset. Тут он занизил ток на 16мА (порядка +30LSB к показаниям АЦП), а вот на 44-х вообще не актуален. Поэтому тинька сначала калибрует шунт при запуске, ток в этот момент через него течь не должен, а уже затем запускает преобразователь...
По поводу чисто программной реализации драйвера (пример - "форумный" драйвер) VS полуаппаратной (такой, как тут на схеме) - Сначала я не любил программные реализации из-за неумения достаточно эффектно писать. Связку АЦП+ШИМ у AVR ещё попробуй заставь работать с качеством микрофонного усилителя... Затем я всё таки начал предпринимать попытки, но из-за огородности решений выбрал управление готовым преобразователем. Один чип вместо жмени компонентов, который может и повышать, и понижать. Ещё и частота у него запредельная по меркам МК - >2МГц, и защиты свои собственные, домашние... ОС там тоже шустрая, АЦП просто не сможет так эффективно давить помехи выше десятка килогерц. Теперь же меня прижимают ограничения таких решений. Годные преобразователи пересчитать можно на пальцах, а иногда ведь хочется чего-то не совсем шаблонного. Например какую-нибудь повышалку для трипла/квадрипла, или понижалку с питанием от 10...30V. Тут уже снова напрашивается дискретный вариант. Чисто программный, на рассыпухе так сказать. Но внезапно для себя я обнаружил, что те же самые драйверы с полевиками, которыми в норме должен дёргать ШИМ МК, можно заставить работать самостоятельно. Вводится ПОС на ВЧ, и ООС на НЧ, и всё. Имеем мультивибратор или усилитель класса D. Деталей ненамного больше, но обвес живёт своей жизнью как готовый преобразователь, со всеми его преимуществами...
P.S. Если кого смущает странная арифметика в коде, на месте которой знаковая смотрелась бы куда удобнее и понятнее - я не вкурил эту многобайтную знаковую арифметику на ассемблере. Для 8-ми разрядной арифметики всё понятно и работает, но вот с переносами беда, даже Си там такого огородит, что для одной только токовой ОС не хватило бы ресурсов всего камня - ПРИМЕР. Вот и приходится выкручиваться. Или тратить по 10 тактов на банальное сложение, или один раз потратить 3...4 такта на определение знака и дальше тратить по паре тактов на подобные операции. Если знаковая арифметика таки может работать эффективнее - я с радостью посмотрю как это сделать...
Вообще же ассемблер меня давно достал, но к сожалению для AVR альтернативы нет, а для ниши, в которой у меня сидят тиньки - альтернативы нет им, вот и приходится мучатся...
Re: Программная реализация драйвера на примере Indigo 3.0s
INFERION, спасибо, интересно было почитать.
Правда мне кажется проблема с EEPROM сильно надумана. Если давить на кнопку питания фонаря по 10 раз в сутки (что соответствует достаточно суровой эксплуатации фонаря), EEPROM хватит на 100000/10/365=27лет А реально еще больше, т.к. 100 000 циклов записи производитель гарантирует, а отработать может и 150000 циклов записи.
Re: Программная реализация драйвера на примере Indigo 3.0s
EEPROM можно угробить за несколько минут ошибкой в коде, можно засрать до отказа дохлой батареей (проходили уже) и т.п. Да и не все же жмут 10 раз в сутки? Я рассчитываю лет на пять непрерывного клацанья, если ресурсы позволяют - почему бы не реализовать? Один китаец с убитой EEPROM мне уже знаком, какой-то топовый, но линейный. Сдох за пол года эксплуатации. Стало быть внимания этому моменту уделяют недостаточно...
Re: Программная реализация драйвера на примере Indigo 3.0s
про загубленный еепром возможно могу подтвердить. фонарей есть ужо цельный ящик(как у многих тут). пользуюсь парой на протяжении лет наверное 2-3. трастфаер ст-50 обхмеленный и щадоу вг-20. пользуюсь активно (говорила мама - иди на повара учиться. дак не же - лепездричество мне нравиться... таперь вот по подвалям и чердакам лазию - всяческую тепловую кибернетику ломаю, щоб людям в квартирах тепло було блин. раньше нас трубочистами звали). если не сказать оченно активно. на обоих или у обоев - драйвера менялись. после тыщеразных матюков в темном мля подвале с зажигалкой в руке (ну вот непокупается пачамута наклющник) и тыщеразной же переборкой оных. ибо алгоритм работы их менялся на совершенно загадочный плод внеземного разума.
зы ждемс почтовых голубей с драйверками.
ззы зерно уже на подоконнике рассыпал. (на соседском естесстно - нэхай радуются гандурасы ночному пению птиц).
засим прощаюсь с вами - уважаемые читатели. с уважением ваш вечно лукавый искрящийся призрак ночи.
Re: Программная реализация драйвера на примере Indigo 3.0s
у 25-й тиньки (наверняка и у более жирных представителей её семейства) очень плохо работает ёмкость на RESET'е. Когда 44-й микрофарада хватает на 20 секунд (приходится мегаомный резистор допаивать) - 25-й ёмкость подобрать нереально. 2мкФ хватает лишь на очень короткий удар по кнопке, а 220мкФ долго удерживает RESET при запуске. Пришлось программировать фьюз "RSTDIBL" и цеплять на PB5 (ADC0) АЦП. При запуске меряем напряжение и только затем подтягиваем. Теоретически утечка происходит через какой-нибудь P-N переход в линию питания, когда оно опускается ниже плинтуса (хотя мультиметр показал бесконечность). Поэтому при напряжениях ниже 0,6...0,3V конденсатор может работать нормально. Исходя из этого я выбрал порог в 0,1V (cpi ADCH, 23 с ADLAR и ИОН на 1,1V) и получил такой вот практический результат: с микрофарадом сопротивление в 100...200k (параллельно ёмкости) даёт паузу около секунды. Оптимальный (на мой взгляд) результат получается при сопротивлении 68k. Без резистора держит десятки секунд, первые 5 семплов надо пропустить.
Пришлось рискнуть и залить не отлаженный вариант с АЦП и отключить RESET. Хорошо что с первого раза завелось, а-то пришлось бы выпаивать МК в QFN корпусе, не зацепив и не поджарив соседей...
Хоть и для 12х Blu-ray предназначен, однако им должно быть удобно питать "наключники". Пол ампера вытянет. В нонижающем режиме обещают вообще 800мА, но программа стабилит в диапазоне 5...500мА. 4 настраиваемых режима, на всякие феньки вроде индикации заряда и т.п. памяти не хватило. Температурное ограничение фиксировано и заточено под лазерные диоды - 60...70 градусов. Кнопка поумнела, теперь при сбое (перезагрузке из-за попытки подключить критически севшую батарею) она выискивает режим с током по-меньше текущего, но максимально близкий к нему. Если перезагрузка произошла с и так уже минимальным режимом - уходит спать. Но всё это костыль, т.к. не требуется в теории, если основной механизм ограничения тока под напряжение питания работает достаточно быстро.
При успешной же загрузке заводится механизм ограничения тока под минимальное напряжение питания, как и раньше. Т.е. на батарее удерживается ровно 3V, а ток продолжает падать. При достижении 5мА запускается таймер и через 10с драйвер уходит спать...
Re: Программная реализация драйвера на примере Indigo 3.0s
Если ИОН не переключается, то хватит и одного семпла, иначе 5 семплов (0,16мс) при переключении на внутренний ИОН, и 10 семплов (0,32мс) при переключении на Vcc в качестве ИОН. Это минимум, что я нащупал.
P.S. Сейчас я по-другому работаю с АЦП. Он запускается аппаратно, компаратором на каком-нибудь таймере. Причём момент запуска с каждым семплом смещается на 1 такт, и так гуляет между значениями в компараторе 8...40 или -24...40. Т.е. ползает по одному или двум целым периодам ШИМ (32 такта), это уменьшает влияние его заполнения на статическую ошибку (шум изначально задавлен жесткой синхронизацией).
Re: Программная реализация драйвера на примере Indigo 3.0s
нашел затык в программе. частота выборок была маленькая. Бросил старую прогу и написал тестовую с нуля. Стало значительно лучше, примерно 15 mA работают стабильно на шунте 0.05Om. Сейчас буду по новому все переписывать. Спасибо за Ваши советы!
Re: Программная реализация драйвера на примере Indigo 3.0s
INFERION, пока у меня всё ещё теплится внутри надежда сделать тёплый ламповый простенький фулл-аналог, так сказать спрошу-вы пробывали прикручивать ОУ к tps63020? Экспериментировали с таким? Получается всё не хорошо?
А то в защиту надежды всплыло воспоминание что алиэкспресс завален дешёвыми модулями имульсными со стабилизацией тока, в которых всё ну совсем просто-самая банальная lm358 и вот вам стабильзация тока, и работает всё ну очень хорошо
Правда микросхема очень древняя и кпд низкий.
Ну и частота у неё не маленькая, но и не мегагерцная как в tps, 340кГц вроде.
Как в них всё работает? За счёт чего?
Я как посмотрел, как всё просто, как переделывается стабилизатор напряжения в стабилизатор тока, на примере этих китайских поделий, а потом начал разбираться и отрадовался обратно.
Или при 340кГц всё легко и хорошо, и ухудшается по экспоненте при приближении к 2МГц?
Типа таких модули: https://m.aliexpress.ru/ite...
*тут и ОУ, и крутящийся патенциометр о котором вы говорили, что с ним вообще беда будет в высокочастотных схемах
Re: Программная реализация драйвера на примере Indigo 3.0s
Цитата:
Сообщение от INFERION :
Задача - построить стабилизатор тока на ATtiny25/45/85 с блекджеком и
Попадаются утверждения, что у низкочастотных схем с большой индуктивностью лучше КПД должен быть, по крайней мере потенциально, ну и по логике оно вроде так.
Однако по обзорам и читая даташиты-самые высокие КПД у самых высокочастотных микросхем преобразователей. Почему так?
Цитата:
Сообщение от INFERION :
Задача - построить стабилизатор тока на ATtiny25/45/85 с блекджеком и
Ну и применительно к ATtiny-почему бы сразу не сделать импульсный стабилизатор тока исключительно на атини? И ток на шунте им мерить, и частосту с продолжительностью импульсов согласно ему ати'ней и изменять. И сделать всему этому небольшую частоту работы(импульсов на индуктивность), для стабильности.
Не удастся выжать хорошие (как у мегагерцных заводских схем) КПД? Почему?
Re: Программная реализация драйвера на примере Indigo 3.0s
Ну и 3-й финальный:
Что посоветуете с учётом максимального сохранения изначальной концепции(tps63020+ОУ+регуляция тока патенциометром+высокий кпд и на низких токах порядка 20мА)-т.е. максимальной простоты, но чтобы работало хорошо:
tps63020+Атини+цифровой переменный резистор?
Атини измерять ток, им же щёлкать переменный цифровой резистор встроенный в стандартный делитель напряжения обратной связи tps, и патенциометр на входы атини для управления?
завести сигнал с токового монитора прямиком на FB пытались многие, и я тоже - хрень выходит, возбуждается легко чуть что.
Цитата:
Сообщение от Artik555 :
Как в них всё работает? За счёт чего?
Тупая дубовая ОС, которая терпит такие издевательства, плюс конские индуктивности и ёмкости - так и работает. Низкая частота работы уже подразумевает низкие скорости работы и самой ОС.
Цитата:
Сообщение от Artik555 :
как всё просто, как переделывается стабилизатор напряжения в стабилизатор тока
Ага, просто оно с виду, а возбуд ловит даже LM317 со штатной схемой из даташита. Есть вот такая штука во всех этих регуляторах: http://www.gaw.ru/html.cgi/...
Эта и следующая страницы. Вот если разобраться, то выходит не всё так просто...
Цитата:
Сообщение от Artik555 :
тут и ОУ, и крутящийся патенциометр о котором вы говорили, что с ним вообще беда будет в высокочастотных схемах
Тут потенциометр без длинных соплей, и сам мелкий. Да, хуже парочки 0603 резисторов у самого ОУ, но не катастрофа. Вот если проводами выносить переменник куда-нибудь в корпусе под регулировку руками... Но и это всё можно сделать, если понимать как и что компенсировать.
Цитата:
Сообщение от Artik555 :
Или при 340кГц всё легко и хорошо, и ухудшается по экспоненте при приближении к 2МГц?
Почему по экспоненте? Просто есть вот задержка в контуре регулирования. Она определяется много чем, но в основном LC фильтром в преобразователе. И вот нужно чтоб ОС давала усиление ошибки ниже 1 на частотах, где эта задержка двигает фазу более чем на 90...120 градусов - всё. Под это дело её нужно частотно корректировать. Снизить усиление по переменному току в ОУ не сложно, но я помню что TPS63020 так просто не сдавался. Деталей не помню. Может, сейчас бы с текущим опытом и знаниями - поборол бы.
Re: Программная реализация драйвера на примере Indigo 3.0s
Цитата:
Сообщение от Artik555 :
Попадаются утверждения, что у низкочастотных схем с большой индуктивностью лучше КПД должен быть, по крайней мере потенциально, ну и по логике оно вроде так.
Однако по обзорам и читая даташиты-самые высокие КПД у самых высокочастотных микросхем преобразователей. Почему так?
Не встречал низкочастотных схем, требующих такие же мелкие индукторы как высокочастотные. А в индукторе обычно куча потерь и его сопротивление напрямую зависит от индуктивности. Да и низкочастотные обычно ж самые примитивные с асинхронным выпрямителем, тогда как высокочастотные обычно наоборот.
Цитата:
Сообщение от Artik555 :
Ну и применительно к ATtiny-почему бы сразу не сделать импульсный стабилизатор тока исключительно на атини?
Не удастся выжать хорошие (как у мегагерцных заводских схем) КПД? Почему?
Всё удаётся и давно уже делается. Просто темы нужно читать не настолько избирательно . Я вот сейчас только программные преобразователи и делаю. Даже в мощных сетевых импульсниках к усилителям. Да и сами усилители (звуковые) у меня уже тоже - цифра (https://forum.fonarevka.ru/...). Это всё хороший путь, но и самый сложный. Ну и если нужна сверхэкономичность на малых токах - тут потребление МК значительно больше чем у аппаратных преобразователей.
Цитата:
Сообщение от Artik555 :
tps63020+Атини+цифровой переменный резистор?
Наверное. Это самое простое из рабочего у "ардуинщиков" (людей с низким порогом вхождения в это всё дело), и одновременно с этим МК позволит поиграться с более сложными вещами. Например, логарифмировать регулировочную характеристику органа управления, прикрутить кнопки, индикатор заряда и т.п.
Re: Программная реализация драйвера на примере Indigo 3.0s
Цитата:
Сообщение от INFERION :
Почему по экспоненте?
Думал из-за усиления наводок от работы самой схемы на высоких частотах, думал фонить намного сильнее они начинают на высоких частотах работы, не линейный рост фона от частоты думал.
Ну и из-за "частоты единичного усиления" операционников думал сложно им по экспоненте с высокими частотами работать.
Re: Программная реализация драйвера на примере Indigo 3.0s
Цитата:
Сообщение от INFERION :
Не встречал низкочастотных схем, требующих такие же мелкие индукторы как высокочастотные. А в индукторе обычно куча потерь и его сопротивление напрямую зависит от индуктивности.
С большими индуктивностям естественно, а не такими же.
Я просто думал что чем больше индуктивность тем меньше потери-толстым проводом намотал-и потери меньше
Думал в нём именно потери только по длине как в проводнике.
Ну и в фон уходящие, на высоких частотах, если он всё-таки не линейно усиливается от увеличения частоты, пока так и не разобрался.
Цитата:
Сообщение от Artik555 :
Всё удаётся и давно уже делается.
Прям вплоть до 96+% как в лучших представителях заводских аналоговых преобразователей?
Просто мы по тонкому льду ходим Немного туда-немного сюда кпд, а там и глядишь линейные 81% рядом...
Не выйдет ли так, что как бы проще реализовать на микроконтроллере одном только драйвер, чем заставлять хорошо стабильно работать аналоговую микросхему у которой с завода 96% кпд,
но вот дотянуть самодельный преобразователь на микроконтроллере до тех же 96% потребует опять жёстких исследований и погружения в едрёную теорию на долгие месяцы безвылазно...
Вот в чём вопрос
за ссылку спасибо, почитаю про индиго 5.0, с навигацией по сайту было сложно, находил только через гугл темы по фразам из них по памяти, в которые раньше также случайно из гугла попадал
сейчас вроде разобрался
Re: Программная реализация драйвера на примере Indigo 3.0s
Цитата:
Сообщение от Artik555 :
Думал из-за усиления наводок от работы самой схемы на высоких частотах, думал фонить намного сильнее они начинают на высоких частотах работы, не линейный рост фона от частоты думал.
Я не замечал проблем из-за этого. Это ж не СВЧ, где всё нужно проектировать с учётом волновых сопротивлений проводников. Тут нужно сильно косячить в дизайне, чтоб это вылезло. Но накосячить можно на любой частоте.
Цитата:
Сообщение от Artik555 :
Ну и из-за "частоты единичного усиления" операционников думал сложно им по экспоненте с высокими частотами работать.
От этой частоты только зависит глубина местной ОС, т.е. точность работы этого ОУ на конкретной частоте, и всё. Есть ещё второй лимит - скорость нарастания выходного напряжения. Если в оба этих параметра вписываемся - ОУ уже может выполнять свою задачу, а какой там вклад в общее усиление вносит его внешняя цепь ООС - вторично. Вспомнилась простая понятная статейка от Аудиокиллера: https://electroclub.info/ar...
И да, ОС в преобразователях работает на десятках-сотнях килогерц. Много ниже за частоту преобразования.
Цитата:
Сообщение от Artik555 :
Я просто думал что чем больше индуктивность тем меньше потери-толстым проводом намотал-и потери меньше
А то что провод намного длиннее и при этом материал сердечника имеет бОльшую массу (и бОльшие потери на гистерезис и вихревые токи при прочих равных)? Достаточно глянуть на Rdc обмоток используемых там дросселей...
Цитата:
Сообщение от Artik555 :
Ну и в фон уходящие, на высоких частотах
Да нету там такого. Это ж не радио. Если что-то уходит в фон - нарушается электромагнитная совместимость и это говорит о косяках схемотехники. Значит где-то звенит какой-то паразитный контур на плате, и его или убирать, или если это невозможно - замедлять ключи и снижать КПД уже динамическими потерями в них (а не на вещание в эфир).
Цитата:
Сообщение от Artik555 :
Прям вплоть до 96+% как в лучших представителях заводских аналоговых преобразователей?
Можно и выше. Смотря какая задача. Аналоговые преобразователи имеют ту же самую силовую часть что и цифровые.
Цитата:
Сообщение от Artik555 :
Не выйдет ли так, что как бы проще реализовать на микроконтроллере одном только драйвер, чем заставлять хорошо стабильно работать аналоговую микросхему у которой с завода 96% кпд,
но вот дотянуть самодельный преобразователь на микроконтроллере до тех же 96% потребует опять жёстких исследований и погружения в едрёную теорию на долгие месяцы безвылазно...
На МК давно собирают мощную высокоэффективную силовуху, вроде контроллеров двигателей (т.н. частотники) и прочих контроллеров PMSM с резольверами во всяких дорогущих ЧПУ - и ничего. Целые семейства МК существуют и развиваются. Есть даже со встроенными затворными драйверами на 3 полумоста (3 фазы) и DC-DC для собственного питания с силовых линий. Пример: https://www.st.com/resource...
На таком можно сразу собирать мультифазный преобразователь хоть для питания какого-нибудь процессора на материнке. КПД мультифазников выше однофазных преобразователей, и пульсации меньше. Или использовать фазы как три независимых канала.
Re: Программная реализация драйвера на примере Indigo 3.0s
Цитата:
Сообщение от INFERION :
Тупая дубовая ОС, которая терпит такие издевательства, плюс конские индуктивности и ёмкости - так и работает
Какую именно дубовость ОС вы имеете ввиду? Дубовость ОС в самой микросхеме, или дубовость ОС в виде системы шунт-ОУ, т.е. с большим падением напряжения, и большими потерями-низким кпд, но что позволяет на фоне большого напряжения падения не замечать много шумов и помех, типа пульсаций, напряжения смещения и т.д.?
А большая индуктивность как тут влияет положительно? Добавляет большую инерцию системы?
Если совсем не стоит вопрос компактности конечной платы, может получится по простому/по тупому заставить нормально работать связку tps+ОУ с помощью больших ёмкостей?
Или не прокатит и такой путь безрезультатный?
Re: Программная реализация драйвера на примере Indigo 3.0s
Цитата:
Сообщение от Artik555 :
Дубовость ОС в самой микросхеме
Да.
Цитата:
Сообщение от Artik555 :
дубовость ОС в виде системы шунт-ОУ, т.е. с большим падением напряжения, и большими потерями-низким кпд, но что позволяет на фоне большого напряжения падения не замечать много шумов и помех
Не замечал проблемы из-за каких-то шумов ОС. Она же всё равно интегрирует среднее значение. А большое падение на шунте повышает дифференциальное сопротивление нагрузки, тем самым снижая усиление ошибки, вот и всё.
Цитата:
Сообщение от Artik555 :
А большая индуктивность как тут влияет положительно? Добавляет большую инерцию системы?
Да. Замедляет реакцию системы на воздействие ОС - это снижает уровень ошибки регулирования (почти то же, что снижение её усиления). При этом не ухудшает стабильность на переходных процессах в нагрузке, т.к. то что не успевает регулятор - берёт на себя пассивный фильтр. Но это если ОС классическая на обычном усилителе ошибки.
Цитата:
Сообщение от Artik555 :
может получится по простому/по тупому заставить нормально работать связку tps+ОУ с помощью больших ёмкостей?
Может и получится, но это нужно грамотно проверять и отлаживать.
Re: Программная реализация драйвера на примере Indigo 3.0s
INFERION, назрело пару вопросов которые моими методами не гуглятся
Насколько непрерывное и равномерное напряжение на выходу у ЦАП? Допустим при плавном изменении с 0.5 до 1 вольта за 1 секунду, что будет на выходе если смотреть осциллографом который по характеристикам всё происходящее точно показать?
Какое сопротивление у цифрового переменного резистора между переключениями? На долю секунды во время переключения он в обрыв уходит или как?
И совсем оффтоп для этой темы, но всё же,
не могу никак понять, система "стабилизатор напряжения" + "шунт" + "оу" ну и + конденсаторы на выходе, это система стремящаяся к самозатуханию колебаний, к усилению колебаний или к бесконечному продолжению колебаний на одном уровне?
Т.е. вот изменили усиление на выходе ОУ, он подал меньшее напряжение на пин обратной связи микросхемы, она увеличила напряжение на выходе, увеличился ток через нагрузку и через шунт соответственно, ОУ подал бОльшее напряжение на пин обратной связи микросхемы, она уменьшила напряжение на выходе, уменьшился ток...
и пошло-поехало
Не нужен ли тут какой-то аналоговый компонент, который будет вычитать какую-то величину из управляющего сигнала, по времени и/или в зависимости от величины управляющего сигнала, для затухания колебаний как в ПИД-регуляторах программа делает?
Re: Программная реализация драйвера на примере Indigo 3.0s
Цитата:
Сообщение от Artik555 :
Насколько непрерывное и равномерное напряжение на выходу у ЦАП?
У какого? Они тоже разные ведь бывают. Дельта-Сигма? Ну там вполне себе аналоговый сигнал с каким-то количеством шума. SAR, R-2R, ШИМ, цифровой потенциометр? Ну тогда у голого ЦАП (без реконструкции сигнала) будут ступеньки, линейность ниже, но скорость выше. У ШИМ ещё и пульсации.
Цитата:
Сообщение от Artik555 :
что будет на выходе если смотреть осциллографом который по характеристикам всё происходящее точно показать?
В первом посте этой же темы есть вот это: Пила преобразователя и где-то вон в ней спряталась более низкочастотная пила ШИМ. Это выход всего драйвера.
Цитата:
Сообщение от Artik555 :
Какое сопротивление у цифрового переменного резистора между переключениями?
Смотря ж какого? Читать даташит на конкретный чип. Там ещё и время этого переключения будет указано. Но обычно такими вещами пренебрегают, насколько мне известно. Сам я цифровые потенциометры ещё ни разу не использовал.
Цитата:
Сообщение от Artik555 :
не могу никак понять, система "стабилизатор напряжения" + "шунт" + "оу" ну и + конденсаторы на выходе, это система стремящаяся к самозатуханию колебаний, к усилению колебаний или к бесконечному продолжению колебаний на одном уровне?
Поддерживать колебания на одном уровне - задача вообще сложная, и есть такая проблема в генераторах синуса на ОУ (там и лампочки обычные ставят для стабилизации амплитуды). А вот будет затухать или наоборот - зависит от этих самых ""стабилизатор напряжения" + "шунт" + "оу" ну и + конденсаторы на выходе". Обратную связь делят на положительную (ПОС) и отрицательную (ООС). Пока она отрицательная - имеем затухание, а когда становится положительной (из-за задержки сигнала во времени разворачивается фаза) - возбуд. Если нам не нужен возбуд - на его частотах усиление должно быть меньше единицы, чтоб эта ПОС просто не приводила к усилению колебаний. Вот и крутишь частотную коррекцию, обеспечивая требуемое снижение усиления на ВЧ. Чем сильнее снизишь - тем устойчивее ОС, но и тем она медленнее.
Цитата:
Сообщение от Artik555 :
Т.е. вот изменили усиление на выходе ОУ, он подал меньшее напряжение на пин обратной связи микросхемы, она увеличила напряжение на выходе, увеличился ток через нагрузку и через шунт соответственно, ОУ подал бОльшее напряжение на пин обратной связи микросхемы, она уменьшила напряжение на выходе, уменьшился ток...
Вот если ОУ подаст больше чем нужно "микросхеме" - будет возбуд. Внутри микросхемы штатная ОС всегда немного недорегулирует, поэтому на условые 0.1А отклонения "микросхема" отреагирует только условными 0.07А. А затем на оставшиеся 0.03А отреагирует изменив ещё на 0.03*0.7=0.021А, и так пока не выйдет на режим. Тут имеем усиление ошибки 0.7. Если оно будет >2 - "микросхема" начнёт промахиваться больше, чем было до этого. Вместо стабилизации получим генерацию. При усилении 1...2 - будет заметен звон - затухающие колебания.
Цитата:
Сообщение от Artik555 :
Не нужен ли тут какой-то аналоговый компонент, который будет вычитать какую-то величину из управляющего сигнала, по времени
Нужен - частотной коррекцией называется. Уже есть в "микросхеме", есть в ОУ, и есть в "конденсаторах на выходе".