Хабрахабр

Разработка buck-преобразователя на STM32F334: принцип работы, расчеты, макетирование

В двух своих последних статьях я рассказал о силовом модуле и плате управления на базе микроконтроллера STM32F334R8T6, которые созданы специально для реализации систем управления силовыми преобразователями и электроприводом. Так же был рассмотрен пример DC/AC преобразователя, который являлся демонстрацией, а не завершенной конструкцией. Теперь пришло время сделать что-то простое, но полезное, а главное завершенное.

Ведь buck-преобразователь (он же buck converter) является основной для большинства интересных проектов: это и драйвер для LED светильников, и основа MPPT контроллера для солнечных панелей, и зарядные устройства и вообще много чего еще. Большинство вопросов, касающихся проекта и силовой электроники, связаны с конкретными топологиями: кому-то интересно узнать алгоритм управления PFC, кому-то хочется научиться строить LLC полумост, но наиболее популярная топология — это несомненно buck.

Пора это исправить. В сети достаточно много информации по buck, в том числе и даташиты, но она разрозненна и мне лично не встречался материал, где подробно описан процесс создания buck-преобразователя с цифровым управлением. Математики практически нет, объяснения «на пальцах», поэтому будет интересно всем, кто хоть как-то связан с электроникой.

Введение

Для начала нужно понять, что мы хотим получить в итоге и какие вводные у нас есть. Топология buck является понижающей, то есть позволяет построить понижающий преобразователь напряжения. Как вы увидите далее напряжение на выходе buck-преобразователя практически линейно зависит от напряжения на входе, поэтому необходимо добавить обратную связь. Сегодня я расскажу о простой обратной связи по напряжению (voltage mode), которая является наиболее наглядной и позволит вам понять принцип работы, при этом такой обратной связи вам хватит для реализации большинства задач.

Диапазон входного напряжения — 15... В конце статьи мы получим работающий стабилизированный источник напряжения по топологии «синхронный buck», работающий на достаточно высокой частоте с цифровым управлением, реализованным на STM32F334R8T6 с применение High Resolution PWM (HRPWM). 60В, выходное напряжение — 12В, максимальный выходной ток — 2А.

Глава 1. Принцип работы топологии buck

Начну я рассказывать начиная с самых основ и постепенно будем улучшать наш преобразователь, т.к. «синхронный buck» это версия улучшенный вариант с повышенными КПД и сложностью управления. Базовый вариант топологии, который вы наверняка использовали, выглядит следующим образом:

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

Принцип работы у данной схемы очень простой, на транзистор VT1 подается ШИМ сигнал, сама работа разделяется на 2 этапа, которые чередуются друг за другом:

  • Стадия накопления энергии в LC-контуре. На данном этапе транзистор VT1 открыт и ток протекает через транзистор в нагрузку, попутно накапливая энергию в катушке индуктивности и выходной емкости:

  • Стадия разряда. На данном этапе транзистор VT1 закрывается и тут начинается самое интересное. Дроссель — эта такая штука, которая накапливает энергию если к нему приложить потенциал (открыть VT1) и отдает ее, если потенциал пропал (VT1 закрыли). При этом он стремится не просто отдать энергию, а сохранить значение тока и его направление, поэтому чтобы использовать данное свойство нужно добавить диод VT1, чтобы замкнуть цепь, ведь ток протекает только в замкнутой цепи:

Когда классе в 6-7м я познакомился с данной топологией, то не понял сразу почему диод не проводит ток на 1-й стадии, сейчас это кажется банальным, но думаю стоит упомянуть. Когда VT1 открыт, то на катод диода VD1 прикладывается потенциал +VIN, например, +20В, а на аноде диода соответственно потенциал земли. Чтобы ток через диод протекал должно быть ровно наоборот: потенциал на аноде должен быть больше потенциала на катоде, поэтому в buck-е на стадии накопления энергии диод «закрыт». На стадии разряда диод уже замыкает цепь, на его катод не действует потенциал +VIN и не «запирает» его. Надеюсь понятно объяснил.

Как всегда все просто: Тут у вас должен был возникнуть вопрос: «А какое напряжение будет на выходе, если мы подали на вход 20В?».

Если кто-то не знает или забыл «коэффициент заполнение (duty)» — это отношение времени, которое транзистор находится в открытом состояние к длительности периода. Как видно из формулы напряжение на выходе линейно зависит от коэффициента заполнения (duty) ШИМ сигнала, который мы подаем на транзистор VT1. Дальше мы будет оперировать именно этой цифрой при управление преобразователем, но для понимая сути давайте подставим это отношение в формулу: Данный коэффициент может принимать значение от 0 до 1 или от 0 до 100%.

Получается, что выходное напряжение напрямую зависит двух физических величин: Частота работы buck-преобразователя величина постоянная и выбирается при проектирование, в процессе работы она не меняется, а значит и период (T) величина постоянная.

  • от времени на которое мы открывает верхний транзистор (VT1) — чем дольше он открыт, тем больше энергии успевает накопиться в LC-фильтре и соответственно выше напряжение на выходе;
  • от входного напряжения, например, если мы зафиксировали заполнение на 50% и меняем Vin от 20 до 40В, то на выходе напряжение будет так же меняться от 10 до 20В.

Я думаю у вас начала прорисовываться общая картина и принцип работы, давайте теперь ее закрепим и посмотрим на реальные осциллограммы и проверим данное соотношение на практике. У меня собран макет buck-а, который нагружен светодиодом на 10 Вт. Я задействовал 3 канала осциллографа, которые включены в следующие точки:

Опыт №1 — Входное напряжение (Vin) постоянное 20В, изменяется коэффициент заполнения

  • Vin = 20V, D = 25%, Vout = D * Vin = 0,25 * 20V = 5V

  • Vin = 20V, D = 50%, Vout = D * Vin = 0,5 * 20V = 10V

Как вы можете видеть на осциллограммах — соотношение между выходным напряжение и коэффициентом заполнения верное. Конечно же это был «идеализированный» эксперимент, в реальности напряжение на входе не бывает стабильным и плавает в достаточно широком диапазоне. Давайте теперь посмотрим какова будет зависимость от входного напряжения при фиксированном заполнении.

Опыт №2 — Входное напряжение (Vin) изменяется, коэффициент заполнения постоянный и равен 50%

  • Vin = 15V, D = 50%, Vout = D * Vin = 0.5 * 15V = 7.5V

  • Vin = 20V, D = 50%, Vout = D * Vin = 0.5 * 20V = 10V

  • Vin = 30V, D = 50%, Vout = D * Vin = 0.5 * 30V = 15V

Теперь мы на практике убедились, что выходное напряжение так же линейно зависит от входного при фиксированном коэффициенте заполнения. Поняли уже как будет стабилизировать выход? Принцип стабилизации простой как сама формула — Vout равно 12В и константа, коэффициент заполнения мы можем менять с помощью микроконтроллера, значит если Vin увеличивается, то увеличивается и напряжение выхода и в этот момент мы уменьшаем коэффициент заполнения (duty) пока опять не станет 12В. Соответственно при уменьшение Vin мы начинаем увеличивать коэффициент заполнения пока опять же напряжение Vout не станет 12В.

Наверняка вам интересно, как ШИМ с амплитудой 20В после транзистора в итоге превратился в постоянное напряжение с мизерными пульсациями? На что еще хотелось бы обратить внимание в теоретическом разделе… Ах, да! Действительно, если мы поставим красный щуп осциллографа в исток транзистора VT1, зеленый щуп после LC-фильтра, то увидим такую картину:

Получаем, что в момент, когда ШИМ перед дросселем стал 0В напряжение на выходе обеспечивается запасенной в фильтре энергией, которая не рассасывается мгновенно и ее хватает чтобы поддерживать напряжение в момент закрытия VT1. Вы можете видеть, как LC-фильтр «смазывает» напряжение переменное в постоянное, а дело все в том, что энергия запасенная в индуктивности и емкости не может мгновенно израсходоваться, следовательно и напряжение не может мгновенно измениться. Семенова «Силовая электроника: от простого к сложному», там целая глава по buck (чопперу). Это все на пальцах разумеется, если интересно углубиться, то как всегда советую для начал книгу Б.Ю.

Борьба за КПД

Как я чуть ранее писал — это был базовый вариант топологии. Основной ее минус — высокие потери на запирающем диоде. Какой ток в несложных системах работающих на МК и CPLD? Обычно в пределах 1А, иногда 2А, если имеется какой-то TFT дисплей. В таком случае потери даже при использовании диода Шоттки составят 0,4В * 2А = 0,8 Вт. В принципе терпимо, рассеивать столько на корпусе SMA/SMB можно без проблем, хотя при напряжении 3.3В и 2А потери 0.8В — это все таки 12% КПД!

Это может быть и MPPT контроллер, и система питания большой FPGA и много чего еще. Теперь представим себе случай, когда ток у нас 20А. Что это значит? В таком случае потери составят 0,4В * 20А = 8 Вт! Что можно сделать? Например, в случае MPPT у вам будет меньше запасаться энергии в АКБ, в случае питания FPGA это будут дополнительные 8 Вт тепла, которые надо куда-то рассеивать и в обоих случаях это несомненно потеря общего КПД. А давайте заменим диод VD1 на еще один N-канальный Mosfet и получим вот такую схему:

Диод, который был в базовой версии не требовал управления, сейчас же за улучшение характеристик мы вынуждены заплатить дополнительным каналом управления с ШИМ сигналом. Теперь транзистор VT2 выполняет роль диода, то есть проводит ток, когда VT1 закрыт.

Сопротивление канала современного mosfet-а составляет несколько мОм. Для начала давайте посчитаем насколько мы уменьшили потери. 3 мОм. В качестве примера давайте возьмем транзистор из моего силового модуля о котором я рассказывал в прошлых статьях — IPP083N10N5AKSA1 с сопротивление канала 8. Еще разумеется будут динамические потери, которые с адекватно спроектированным драйвером составят не более 20%, то есть суммарные потери у нас составят 4 Вт. Получаем статические потери равные 0,0083 * 20А * 20А = 3,32 Вт. Получаем, что переход от обычного buck-а к синхронному позволяет вдвое уменьшить потери на диоде.

Как мы уже поняли запирающий диод проводил ток когда был закрыт VT1. Давайте теперь разберем усложнившееся управление. Если проще — транзисторы работают попеременно: или один открыт иди другой, если оба транзистора откроются, то возникнет сквозной ток, т.к. Из этого следует, что VT2 должен быть закрыт когда открыт VT1 и соответственно VT2 открыт когда закрыт VT1. Давайте посмотрим какой должен быть сигнал, где «желтый канал» — транзистор VT1 и «зеленый канал» — транзистор VT2: они замкнут между собой VIN и GND.

Получаем, что VT1 накачивает энергию в LC-фильтр, а VT2 замыкает контур на стадии разрядки. Как видите, если в желтой канале (на VT1) установлена логическая «1», то в это время обязательно должен быть установлен логический «0» в зеленом канале (на VT2).

Дело в том, что реальный, а не идеальный транзистор (mosfet) имеет на затворе определенную емкость, то есть в реальности он не мгновенно переходит из лог. Есть еще один момент о которым вы уже ранее слышали или читали выше — сквозной ток. 1, да и энергия в транзисторе не рассасывается мгновенно, в результате чего транзисторы на короткое время в момент переключения могут оказаться оба открыты. 0 в лог. сквозной ток это обычное короткое замыкание (КЗ). Это может привести в лучшем случае к повышенным потерям, а значит нагреву и в худшем случае в бабаху, т.к. Выглядит это следующим образом: Чтобы этого избежать между выключением одного транзистора и включением другого вводят задержку или так называемый dead-time.

Я установил его заведомо большой (около 3%), чтобы вы могли его увидеть, в реальности он значительно меньше. Думаю вы заметили, что на границе переключения сигнала есть небольшой пробел. Его можно рассчитать, а можно подобрать опытным путем, лично я считаю и тот и тот вариант нормальным, но бородатые джедаи наверняка скажут вам: «Нужно считать обязательно, а лучше моделировать!». Вообще dead-time (далее dt) устанавливается как можно короче, но при этом достаточный чтобы транзисторы успели закрыться. Это конечно правильно, но решайте сами — если не лень моделируйте в LTspice с учетом паразитных индуктивностей и емкостей проводников и компонентов.

Мой модуль позволяет установить его значительно меньше, т.к. Для стенда в данной статья я установил dt равный ~100 мкс (на самом деле 104). Вот чтобы не бахнуло из-за соплей оставлю dt с запасом и если у вас нормальная разводка на плате, то вы сами можете его уменьшить — дальше в главе посвященной коду вы увидите как, а пока смотрим действительно ли есть dt: драйвер очень суровый применен, но наверняка многие из вас будут собирать свой макет без моего модуля, а значит там с большой долей вероятностью будут сопли.

Надеюсь вы поняли зачем нужен dt, сколько он должен быть по длительности и как вообще работает преобразователь по топологии buck. Тут видно, что dt длится 2,5 деления и каждое деление 40 мкс — значит длительность составляет ~100 мкс как и было задумано. Если не поняли, то как обычно вопросы в комментариях, лс и на почту принимаются, пока вроде отвечаю всем.

Глава 2. Расчет основных компонентов

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

Напомню вводные данные:

  • Входное напряжение: 15...30В
  • Выходное напряжение: 12В
  • Номинальный выходной ток: 2А
  • Частота коммутации: 100 кГц

Выходное напряжение 12В выбрано, т.к. в качестве нагрузки планирую использовать светодиод 12В 20Вт, который оказался под рукой и является весьма наглядной нагрузкой. Предвидя вопросы «экспертов» в комментариях — да, я в курсе, что светодиоду нужна стабилизация тока, но мы то делаем стабилизатор напряжения и светодиод просто нагрузка.

60В сделать, поэтому если вам интересен другой диапазон, то можете посчитать для него сами значение компонентов. Входное напряжение выбрано от балды, можно и 15... Сам светодиод при 12В потребляет около 1,82... Номинальный ток 2А выбран чтобы получить мощность на выходе 12В * 2А = 24 Вт, то есть чуть больше, чем нужно для светодиода. 1,9А.

Какая она должна быть? Остался самый интересный параметр — рабочая частота преобразователя. Выбор основывается на двух тезисах: Ответить тут придется вам самим, в моем случае это 100 кГц.

  • Увеличение частоты приводит к уменьшению необходимой индуктивности дросселя, входного и выходного конденсатора. Если говорить проще — с увеличением частоты уменьшаются габариты устройства. С уменьшением частоты габариты увеличиваются.
  • Уменьшение частоты приводит к увеличению КПД, т.к. динамические потери при переключение транзисторов уменьшаются. Увеличение частоты увеличивает динамическую составляющую транзисторов и соответственно уменьшает КПД.

Сейчас рассуждать о выборе частоты я не буду, просто примем, что 100 кГц. После того, как я покажу методику расчетов мы вернемся к этому вопросу, т.к. по формулам будет более наглядно видна зависимость номиналов основных компонентов от рабочей частоты.

Выбор транзисторов Шаг 1.

К транзистору прикладывается весь потенциал источника напряжения (Vin), а так же присутствуют выбросы в момент переключения. Нас в основном будут интересовать 3 параметра: максимальное напряжение «сток-исток», сопротивление канала в открытом состоянии и емкость затвора. В моем случае на силовом модуле стоят транзисторы на 100В, а при входном 30В это огромный запас по напряжению, даже 60В хватило чтобы обойтись без снабберов и обезопасить транзистор от пробоя. У вас есть 2 варианта: взять транзистор VT1 и VT2 с запасом по напряжению или повесть RC-снаббер на VT2.

При уменьшение сопротивления канала мы уменьшаем статические потери (I2*R), но технология такова, что увеличивается емкость затвора, а это приводит к увеличению динамических потерь. Сопротивление канала — тут чем меньше, тем лучше, но есть одно НО. Для напряжение до 100В советую обратить внимание на транзисторы серии OptiMOS от Infineon, на большие напряжения уже смотрите сами по параметрическому поиску или даже в сторону IGBT-транзисторов. Вам предстоит найти золотую середину между «сопротивление канала» и «емкость затвора». Последние так же поддерживаются моим силовым модулем и не требуют никаких изменений в драйвере.

Расчет индуктивности дросселя Шаг 2.

Необходимо посчитать минимальное значение индуктивности, которая позволит работать нашему dc/dc преобразователю в режиме неразрывных токов (Lmin):

Это допустимые пульсации тока в дросселе, обычно выбирают значение 20... По переменным я думаю понятно все, кроме — kind. Чем меньше пульсация тока, тем дальше мы будем от границы насыщения сердечника на котором намотан дроссель, но как видно из формулы — понадобится большая индуктивность дросселя. 50%, я же практически всегда задаю 30%.

Теперь посчитаем минимальное значение индуктивности, которая понадобится для моих вводных данных, пульсации я заложу 30% как уже выше писал:

В процессе увеличения тока, действующего в обмотке, несколько уменьшается проницаемость сердечника и индуктивность дросселя БЕЗ тока и С током несколько отличается, у разных материалов это зависимость разная. Стоит понимать, что это минимальная индуктивность, требующаяся для работы buck-преобразователя в режиме неразрывных токов, но опять есть нюанс. Увеличения индуктивности на 10-15% будет достаточно для материала Kool Mu, а мой дроссель будет именно на нем. Чтобы не возникла ситуация, когда при увеличение тока в дросселе индуктивность уменьшилась ниже Lmin и dc/dc не ушел в режим разрывного тока, необходимо несколько увеличить индуктивность, то есть добавить парочку лишних витков при намотке.

Расчет и изготовление дросселя Шаг 3.

Для изготовления дросселя я возьму дроссель R26/14/11 (R — это кольцо, а цифры — габариты) из материала Kool Mu с проницаемостью 60, скачать документацию на него и купить можно тут — Лэпкос. Я хотел данную процедуру описать в разделе «макетирование», но тогда этап расчета индуктивности остался бы для вас менее понятным, да и по интересным картинкам наверное соскучились, поэтому опишу все здесь.

Начнем с количества витков пожалуй. Теперь нужно посчитать сколько витков и каким проводом надо намотать. Тут внимательно — витки в квадрате! В документации на сердечник есть такой удобный параметр — AL, который равен 75 нГн/виток2. Отсюда формула для нахождения количества витков выглядит так: Чтобы найти индуктивность сердечника необходимо умножить AL на количество витков в квадрате.

Берем кольцо и наматываем 43 витка, получаем вот такой дроссель: Чтобы получить минимальную необходимую индуктивность необходимо намотать 40 витков, но как мы уже обсудили — необходимо немного увеличить индуктивность, допустим накинем +3 витка.

Теперь ради интереса посчитаем какая индуктивность должна получиться:

И для надежности проверяем индуктивность дросселя пинцетом:

Результаты сошлись, погрешность в пределах ±8% для AL. 137 мкГн, прекрасно! Без возможности измерить индуктивность вы не сможете проверить AL и сможете сильно намучиться в поисках причины «бабаха» вашего преобразователя. Тут стоит лишь заметить — если у вас нет возможности измерить индуктивность, то не покупайте сердечники на алиэкспресс, в ЧиДе, компэле, электронщике и прочих «забегаловках» — там есть вероятность получить сердечник из другого материала или не той проницаемостью, но при правильной маркировке — проверено.

Может надо было больше?». Тут появится разумный вопрос — «а хватит ли нам сердечника и его габаритов? 5 Тл, на практике лучше не вылезать за порог выше 0. Для материала Kool Mu предел магнитной индукции составляет 0. Получается, что обмотка намотанная на сердечник не должная создавать индукцию в каждой точке сердечника больше 0. 45 Тл без явно необходимости. 45 Тл, так что проверим:

Из этого можно сделать 2 вывода: во-первых, дроссель не уйдет в насыщение, а во-вторых, сердечник сильно большой и мощно взять кольцо существенно меньшего размера. Как видим значение магнитной индукции 0,06 Тл сильно ниже, чем предельные 0,5 Тл. Я взял кольцо R26 просто потому, что у меня их целая коробка, никакого другого тайного смысла тут нет.

Во-первых, провод с диаметром более 1... Осталось определить какое сечение провода взять для дросселя. скин-эффект уже оказывает существенное влияние и уменьшает эффективное сечение. 1,2 мм я вам брать настоятельно не советую при таких высоких частотах, т.к. На малых мощностях (до 10-20 Вт) можно смело закладывать плотность тока 8.. Во-вторых, плотность тока в проводе нужно выбирать исходя из условий охлаждения и мощности. На мощностях до нескольких киловатт лучше закладывать плотность тока в интервале 5... 10 А/мм2 даже без обдува воздушным потоком. 4 А/мм2. 6 А/мм2, а при мощностях от 10 кВт и далее разумно будет снизить плотность тока до 3...

Его сечение соответственно составляет ~0,5 мм2. У меня под рукой оказался лакированный провод с диаметром 0,8 мм. Я мог бы использовать провод и вдвое меньшего сечения, но у меня сердечник достаточно большой, поэтому провод большего сечения без проблем влез. При токе в 2А получаем плотность тока в обмотке около 4 А/мм2. Когда же вы оптимизируете свое устройство, то вам придется сначала считать, а потом закупать провод нужного сечения, тогда вы сможете получить оптимальные габариты дросселя.

Расчет выходного конденсатора Шаг 4.

Соответственно если установите больше, то будет лучше и дальше увидите почему. На данном этапе, как и в случае с индуктивностью, мы будем считать минимальное значение емкости, которую необходимо установить в LC-фильтр на выходе buck-преобразователя. Посчитаем емкость:

ее емкость сильно уменьшается в зависимости от приложенного к ней напряжения. Разумеется емкость необходимо так же ставить с некоторым запасом, особенно если вы используете только керамику на выходе, т.к. Это максимальное значение пульсаций на выходе, то есть в идеале при емкости 147,8 мкФ амплитуда пульсаций будет 0,2В, то есть напряжение на выходе будет плавать в диапазоне 11,9... Еще стоит обратить на зависимость от пульсаций — переменная Vpulse. Хотите уменьшить пульсации? 12,1В. Так же необходимо учесть необходимость низкого ESR, для этого обычно ставят 1-2 электролита параллельно и параллельно им навешивают керамику на несколько мкФ с диэлектриком X7R желательно. Тогда уменьшайте их в формуле и значение полученной емкости соответственно увеличится, разумеется лабораторный блок питания вы не получите просто увеличивая выходную емкость. Если позволяет бюджет, то можно заменить электролитический конденсатор на полимерный тантал (как в GPU) и так керамика не нужна, ESR у них и так мизерный.

Рассуждения о рабочей частоте

Давайте я разделю выводы на несколько мыслей: Теперь, как и говорил, вернемся к вопросу выбора рабочей частоты преобразователя.

  • Как видите в формуле фигурирует частота, чем выше рабочая частота, тем меньшее значение индуктивности дросселя будет требоваться и меньшее количество витков придется мотать — экономим на меди и упрощаем изготовление моточных изделий
  • В формуле вычисления магнитной индукции присутствует индуктивность и количество витков, правда как помните индуктивность имеет квадратичную зависимость от витков, а значит при снижение количества витков в 2 раза индуктивность уменьшится в 4 раза. Из этого следует, что при повышение частоты уменьшается индуктивность и значение магнитной индукции, а значит можно применить сердечник меньшего размера, то есть уменьшаем габариты
  • Теперь посмотрим на формулу выходной емкости, тут тоже все очевидно — она линейно зависит от индуктивности, то есть чем меньше индуктивность, тем меньшая емкость нужна. Габариты уменьшаются!
  • Теперь о плохом… Частота растет, а значит увеличиваются динамические потери транзисторов и это очевидно ведет к снижению общего КПД. На сколько нибудь значительной мощности разумный предел частоты для buck-преобразователя на mosfet-ах составляет 200 кГц и ниже. Хотите большую мощность (сотни ватт) и огромную частоту? Добро пожаловать в мир GaN транзисторов или резонансных топологий

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

Глава 3. Собираем макет buck-преобразователя

Так, самая нудная, но важная, часть закончилась, теперь пойдет железо и код. Давайте соберем макет на котором будем реализовывать теоретические выкладки. Для этого понадобятся два модуля про которые я рассказывал в предыдущих статьях: силовой модуль и модуль управления на STM32F334. Так же вы можете сами на макетной плате собрать полумост из любого подручного мусора типа IR2110, а в качестве управления применить любой МК: STM32-Discovery, LaunchPad, Arduino и просто адаптировать логику работы и код под свой любимый МК, сложного ничего не будет, если вы поняли из первых двух глав как работает buck-преобразователь.

Теперь давайте сделаем нашу структурную схему buck-а более «реалистичной», добавив на нее номиналы всех компонентов и верно отразим количество конденсаторов, а так же отметим какую часть может реализовать мой силовой модуль:

Нам остается добавить дроссель, который мы уже изготовили и выходные конденсаторы, последние тоже кстати с запасом, т.к. Как видно по схеме модуль уже содержит в себе полумост (два транзистора) для реализации синхронного buck-а и входной конденсатор, он кстати в модуле стоит с огромным запасом — там 3 электролита по 1000 мкФ и 100В, этого хватит чтобы спокойно собрать buck на 500-800 Вт. На самом деле там хватит и 470 мкФ одного, но у меня такой мелочи в выводном исполнение просто не оказалось. на низкое напряжение я нашел только 4700 мкФ 25В, но они китайские какие-то поэтому еще и решил парочку запараллелить. Получается вот такая конструкция:

На сколько он светит не знаю, да и не очень интересно, но потребляет он при 12В как раз 21... Как ранее было сказано, в качестве нагрузки используется мощный светодиод на 20 Вт. Сам светодиод обмазал КПТ-8 и прикрутил к радиатору, его конечно мало, но на 5-7 минут работы его хватает без проблем (греется до +40... 22 Вт на которые и рассчитан мой buck-преобразователь. Подключаем от модуля управления 2 сигнала HRPWM, GND и через делитель цепляем выход buck-а на АЦП, в итоге имеем такой стенд: 50 oC), а большего мне и не нужно.

Глава 4. Пишем ПО и запускаем преобразователь

Теперь у нас есть все необходимое, чтобы начать писать код и оживить наш buck-преобразователь. Для начала давайте посмотрим на pin-out для микроконтроллера STM32F334R8T6, который стоит в модуле управления:

На самом модуле мне понадобится всего 1 из 5 каналов для управления силовой части, использовать будем канал «А». Теперь нам понятно, какие будут использоваться выводы микроконтроллера. У данного канала, как в прочем и у остальных, есть 2 выхода высокоточного ШИМа (HRPWM), 1 вход ошибки (его не используем), GND для объединения земли плат и 2 канала АЦП (будем использовать только один для напряжения).

Немного о HRPWM

В просторах русскоязычного сегмента интернета я практически не встречал каких-то обучающих материалов по HRPWM и не встречал совсем материалов о работе с HRPWM на базе микроконтроллеров STM32, а ведь это очень полезная периферия.

HRPWM или High Resolution PWM — это привычный нам ШИМ модуль, который обладает повышенным разрешением установки коэффициента заполнения (duty) и в дополнение обычно обладает более гибкими настройками. Я не буду в рамках данной статьи углубляться в теорию данной периферии, поэтому опишу суть.

  • У микроконтроллера STM32F334R8T6 имеется 10 каналов HRPWM, которые объединены в 5 групп по 2 канала. Эти 2 канала внутри группы могут работать как независимо, так образовывать комплементарную пару — последняя нам и нужна;
  • Внутри комплементарной пары между 2-мя сигналами ШИМ можно устанавливать аппаратный dead-time для защиты от сквозного тока;
  • Все 10 каналов тактируются от одного таймера — Master timer, благодаря этому они все синхронизируются между собой и вам не придется вручную настраивать цепочку из таймеров. Достаточно включить мастер и таймеры Timer A...E за тактируются от него;
  • Частота на HRPWM идет удвоенная, то есть при частоте ядра в 72 МГц на HRPWM идет 144 МГц после дополнительного множителя (х2) с PLL. Это дает возможность управлять преобразователями на частоте в сотни кГц;
  • Множество настроек для управления ШИМом, например, по мимо возможности привязать геренерацию ШИМа к началу и концу периода, есть еще 4 настраиваемых события (comp), которые позволяю перевести ШИМ в 0 или 1 в любой момент периода отличный от начала/конец периода;
  • Есть режимы для конкретных топологий, например, режим push-pull, который позволяет реализовать множество двухтактный топологий.

И это лишь малая часть особенностей, на схеме устройства HRPWM вы видите еще возможности синхронизации с кучей событий, ЦАПом, встроенными в МК компараторами, а по мимо данной блок-схемы есть еще множество задокументированных возможностей.

Для этого рассмотрим простой пример. Остался последний вопрос, который нужно осветить — «почему этот ШИМ высокоразрядный?». Нам нужно управлять полумостом на частоте 70 кГц, считаем какой шаг регулирования мы можем получить: 72 000 000 / 1025 шагов = 70 243 Гц. Представим, что решили использовать МК без HRPWM, допустим STM32F103C8T6, который так же работает на частоте 72 МГц. Теперь берем STM32F334, при частоте тактирования 144 МГц и разрядности сдвига таймера в 32 бита мы получаем эквивалентную частоту 144 МГц * 32 = 4,608 ГГц. Ага, у нас 1025 шагов и мы можем при регулировке менять напряжение на выходе с теоретическим шагом 1/1025 = ~0,1%. Для тех, кто испугался и усомнился в цифре:

Что нам это дает? Нет, это не рабочая частота, это частота эквивалентная. Теперь мы можем регулировать напряжение (или ток) на выходе с шагом 1 / 65 535 = ~0,001%, то есть в 100 раз точнее! Берем эквивалентную частоту 4 608 000 000 Гц / 70 300 Гц = 65 535 шагов.

У F103 получится 72 000 000 Гц / 700 000 Гц = 102 шага, что позволяет в лучшем случае получить регулирование 1%, но это 1% для duty, то есть в реальности с таким количество шагов у вас будет напряжение плавать на выходе как будто стабилизации и нет особо. А теперь сделаем так — частота у нас 700 кГц, что является нормально для многофазного buck-а, например. Получаем, что разрешение (шаг) установки скважности значительно выше/чаще, чем у обычного МК с стандартным ШИМ-модулем внутри. Тогда как для F334 количество шагов будет примерно 6500, что все еще позволяет строить очень точный регулятор напряжения или тока.

Настройка системы тактирования

В конце статьи будет архив с проектом именно для этой IDE. В качестве среды разработки в данной статье я использовал TrueSTUDIO, ибо бесплатная, не такая убогая как Keil или IAR да да, расскажите мне о его чудесном отладчике, кросс-платформенная и пожалуй лучшее решение для новичков и не только. Как создавать и настраивать проект я рассказывать не буду, просто оставлю ссылку на видео, где все подробно показано — смотреть.

После того как создали проект и помигали светодиодом, необходимо настроить систему тактирования, а именно с 8 МГц поднять частоту то 72 МГц и подать на ядро, а потом настроить делитель чтобы уменьшить частоту, подаваемую на АЦП:

void StartInitClock (void) RCC->CFGR &= ~RCC_CFGR_SW; RCC->CFGR |= RCC_CFGR_SW_PLL; // Select source SYSCLK = PLL while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_1) {} // Wait PLL }

Тут все просто думаю, алгоритм настройки следующий: переходит на внешний кварц (HSE) -> ждем когда переход завершится и выставится флаг готовности -> подаем на вход PLL сигнал с кварца -> умножаем 8 МГц на 9 -> частоту 72 МГц делим на 10 для тактирования АЦП -> включаем PLL -> ждем пока он включится и выставит флаг готовности -> подаем на системную шину и ядро сигнал с PLL -> ждем пока переключение завершится -> готово.

Настройка HRPWM

функционала у данного модуля очень много, куча всяких настроек и объем документации очень большой, но это и минус и одновременно плюс — за гибкость надо платить. Вот тут все несколько сложнее, т.к.

RCC->CFGR3 |= RCC_CFGR3_HRTIM1SW_PLL;
RCC->APB2ENR |= RCC_APB2ENR_HRTIM1EN;

Нужно указать, что HRTIM тактируется от PLL, множитель х2 по умолчанию уже включен. Дальше просто включаем тактирование для HRTIM, вот тут первая фича — как мы понимаем таймер тактируется от PLL, но включаем для APB2. Это не совсем логично, но в файлике с CMSIS легко ищется или в документации.

RCC->AHBENR |= RCC_AHBENR_GPIOAEN; GPIOA->MODER &= ~GPIO_MODER_MODER8; GPIOA->MODER |= GPIO_MODER_MODER8_1; // Alternative PP GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8; // Very high speed GPIOA->MODER &= ~GPIO_MODER_MODER9; GPIOA->MODER |= GPIO_MODER_MODER9_1; GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9; GPIOA->AFR[1] |= 0xDD; // PA8 and PA9 - AF13

PA8 и PA9 — это выход Timer A, который на моем модуле идет на канал №1, что вы можете видеть на схеме и pin-out. Ноги настраиваются как push-pull с альтернативной функцией, номер самой функции для обеих ног 13-й. Важно так же настроить на максимальную частоту GPIO, а то будет непонятный завал фронта и спада сигнала, что для силовой электроники крайне критично.

HRTIM1->sCommonRegs.DLLCR |= HRTIM_DLLCR_CAL | HRTIM_DLLCR_CALEN;
while ((HRTIM1->sCommonRegs.ISR & HRTIM_ISR_DLLRDY) == RESET);

Перед началом работы нужно откалибровать таймер, т.к. работает он при минимальных задержках, далее просто ждем флага готовности.

HRTIM1->sTimerxRegs[0].PERxR = PeriodTimerA; // Period for timer A
HRTIM1->sTimerxRegs[0].CMP1xR = 0; // Duty for timer A

Вот и пошла гибкость. Во-первых, мы можем задать свою частоту для каждого таймера A...E, тут просто записываем период нашего ШИМа. Во-вторых, по умолчанию у нас выравнивание ШИМа по началу периода, то есть сигнал переходит в лог.1 при начале нового периода, а теперь нам надо выбрать когда он вернутся в лог.0, в данном случае по компаратору №1, то есть я задаю в нем по суть коэффициент заполнения (duty).

0 по компаратору №2 и таким образом аппаратно двигать фазу. Для примера можно переводить ШИМ не по началу периода, а по компаратору №1, а возвращать в лог.

// Deadtime enable
HRTIM1->sTimerxRegs[0].OUTxR |= HRTIM_OUTR_DTEN; // Tdtg = 6.94 ns HRTIM1->sTimerxRegs[0].DTxR |= HRTIM_DTR_DTPRSC_0 | HRTIM_DTR_DTPRSC_1; // Deadtime rising = 15*Ttg = 104 ns HRTIM1->sTimerxRegs[0].DTxR |= HRTIM_DTR_DTR_0 | HRTIM_DTR_DTR_1 | HRTIM_DTR_DTR_2 | HRTIM_DTR_DTR_3; // Deadtime falling = 15*Ttg = 104 ns
HRTIM1->sTimerxRegs[0].DTxR |= HRTIM_DTR_DTF_0 | HRTIM_DTR_DTF_1 | HRTIM_DTR_DTF_2 | HRTIM_DTR_DTF_3; HRTIM1->sTimerxRegs[0].DTxR |= HRTIM_DTR_DTFSLK | HRTIM_DTR_DTRSLK;

На этом этапе мы включаем dead-time и настраиваем его, в принципе в комментариях есть все формулы, их можно так же найти в reference manual. DT длительностью ~100 мс вы уже видели на осциллограмме в теоретической главе данной статьи. Мертвое время можно задать отдельно по фронту и по спаду сигнала. Кстати, [0] — это Timer A, соответственно [1] — это Timer B и так далее.

// Samples in middle of ON time
HRTIM1->sTimerxRegs[0].CMP2xR = PeriodTimerA / 10; // ADC trigger 1 update: Timer A HRTIM1->sCommonRegs.CR1 |= HRTIM_CR1_ADC1USRC_0; // ADC trigger 1 event: Timer A compare 2 HRTIM1->sCommonRegs.ADC1R |= HRTIM_ADC1R_AD1TAC2;

Для меня это был самый не очевидный момент. Суть какая — я хочу сделать так, чтобы на 10% длительности периода Timer A генерировалось событие, которое бы запускал преобразование АЦП и измеряло сигнал с обратной связи. Почему 10%? Просто в идеале измерение не должно приходиться на момент перехода ШИМа из 0 в 1 или обратно, т.к. в этот момент в силовой части идут переходные процессы и помехи, а нам их измерять не надо. Поэтому 10% в моем случае оптимально, т.к. при 12В выходном и 30В входном напряжение коэффициента заполнения (duty) не упадет до 10% и момент переключения транзистора точно не совпадет с измерением АЦП.

Теперь нужно посмотреть на систему связи событий между HRTIM и АЦП:

Далее выбираем конкретный триггер в АЦП, который свяжется МК, нам доступен 1 или 3-й. В первой строке мы выбираем когда именно будет срабатывать компаратор, в моем случае это 10% от периода таймера А. Теперь просто указывает какое событие будет отправлять сигнал на АЦП, в моей случае это компаратор №2.

// Enable output PWM for TA1 and TA2
HRTIM1->sCommonRegs.OENR |= HRTIM_OENR_TA1OEN | HRTIM_OENR_TA2OEN; // Continuous mode
HRTIM1->sTimerxRegs[0].TIMxCR |= HRTIM_TIMCR_CONT; // Period for master timer
HRTIM1->sMasterRegs.MPER = 65000; // Enable counter for Master and timer A
HRTIM1->sMasterRegs.MCR |= HRTIM_MCR_MCEN | HRTIM_MCR_TACEN;

И финальный аккорд! Разрешаем HRTIM выводить сигналы от Timer A на наши GPIO. Теперь выбираем режим, бывает бесконечный (у меня он), а бывает что таймер включился на 1 период и после этого его опять надо запускать. Далее устанавливаем период для Master timer и последним шагом включаем его, он начинает тактировать таймеры каналов и на выходе появляется ШИМ сигнал.

Это была функция настройки, осталось сделать функцию, которая будет задавать коэффициент заполнения (duty), именно с ней мы и будем работать при создании регулятора:

void SetDutyTimerA (uint16_t duty) { HRTIM1->sTimerxRegs[0].CMP1xR = duty;
}

Листинг функции настройки и установки коэффициента заполнения

// f = 102,4 kHz
#define PeriodTimerA ((uint16_t)45000) void InitHRPWM (void) { RCC->CFGR3 |= RCC_CFGR3_HRTIM1SW_PLL;
RCC->APB2ENR |= RCC_APB2ENR_HRTIM1EN; /************************************************
* Setting GPIO
***********************************************/ RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // Alternative PP
GPIOA->MODER &= ~GPIO_MODER_MODER8;
GPIOA->MODER |= GPIO_MODER_MODER8_1; // Very high speed
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8; GPIOA->MODER &= ~GPIO_MODER_MODER9;
GPIOA->MODER |= GPIO_MODER_MODER9_1;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9; // PA8 and PA9 - AF13
GPIOA->AFR[1] |= 0xDD; /************************************************
* Setting timer A
***********************************************/ HRTIM1->sCommonRegs.DLLCR |= HRTIM_DLLCR_CAL | HRTIM_DLLCR_CALEN;
while ((HRTIM1->sCommonRegs.ISR & HRTIM_ISR_DLLRDY) == RESET); // Period for timer A
HRTIM1->sTimerxRegs[0].PERxR = PeriodTimerA; // Duty for timer A
HRTIM1->sTimerxRegs[0].CMP1xR = 0; // Deadtime enable
HRTIM1->sTimerxRegs[0].OUTxR |= HRTIM_OUTR_DTEN; // Tdtg = 6.94 ns
HRTIM1->sTimerxRegs[0].DTxR |= HRTIM_DTR_DTPRSC_0 | HRTIM_DTR_DTPRSC_1; // Deadtime rising = 15*Ttg = 104 ns
HRTIM1->sTimerxRegs[0].DTxR |= HRTIM_DTR_DTR_0 | HRTIM_DTR_DTR_1 | HRTIM_DTR_DTR_2 | HRTIM_DTR_DTR_3; // Deadtime falling = 15*Ttg = 104 ns
HRTIM1->sTimerxRegs[0].DTxR |= HRTIM_DTR_DTF_0 | HRTIM_DTR_DTF_1 | HRTIM_DTR_DTF_2 | HRTIM_DTR_DTF_3; HRTIM1->sTimerxRegs[0].DTxR |= HRTIM_DTR_DTFSLK | HRTIM_DTR_DTRSLK; // Event forces the output to active state for TA1
HRTIM1->sTimerxRegs[0].SETx1R |= HRTIM_SET1R_PER; // Event forces the output to inactive state for TA1
HRTIM1->sTimerxRegs[0].RSTx1R |= HRTIM_RST1R_CMP1; /************************************************
* ADC trigger intialization (with CMP2 event)
************************************************/ // Samples in middle of ON time
HRTIM1->sTimerxRegs[0].CMP2xR = PeriodTimerA / 10; // ADC trigger 1 update: Timer A
HRTIM1->sCommonRegs.CR1 |= HRTIM_CR1_ADC1USRC_0; // ADC trigger 1 event: Timer A compare 2
HRTIM1->sCommonRegs.ADC1R |= HRTIM_ADC1R_AD1TAC2; /************************************************
* HRTIM start
***********************************************/ // Enable output PWM for TA1 and TA2
HRTIM1->sCommonRegs.OENR |= HRTIM_OENR_TA1OEN | HRTIM_OENR_TA2OEN; // Continuous mode
HRTIM1->sTimerxRegs[0].TIMxCR |= HRTIM_TIMCR_CONT; // Period for master timer
HRTIM1->sMasterRegs.MPER = 65000; // Enable counter for Master and timer A HRTIM1->sMasterRegs.MCR |= HRTIM_MCR_MCEN | HRTIM_MCR_TACEN; } void SetDutyTimerA (uint16_t duty) { HRTIM1->sTimerxRegs[0].CMP1xR = duty; }

Теперь давайте выясним правильным ли путем мы движемся. В функции main инициализируем настройку HRTIM и устанавливаем скважность, допустим 22500. При входном напряжение 20В и периоде 45000 наш коэффициент заполнения будет 50% и на выходе будет около 10В. Этого не хватит чтобы раскачегарить светодиод на полную, но загореться он должен и мы поймем работает ли силовая часть, все хорошо ли с dt и прочее. У меня все стартануло с первого раза:

При фиксированном коэффициенте заполнения (duty) в 50% напряжение на выходе просто делилось на 2: 20В -> 10В, 22В -> 11В, 18В -> 9В. Вы можете видеть, что все предыдущие теоретические выкладки подтвердились. Теперь давайте сделаем так, чтобы напряжение на выходе было стабильным и не зависело от входного, то есть добавим обратную связь.

Настройка АЦП и регулятора

Об остальных настройках АЦП я расскажу кратко. Про АЦП в STM32 очень много уже написано до меня, я подробно остановился лишь на настройке триггера привязанного к компаратору HRTIM. Смотрим функцию инициализации:

void InitBasicADC (void) { RCC->AHBENR |= RCC_AHBENR_ADC12EN;
RCC->AHBENR |= RCC_AHBENR_GPIOCEN; /************************************************
* Calibration ***********************************************/ ADC2->CR &= ~ADC_CR_ADVREGEN;
ADC2->CR |= ADC_CR_ADVREGEN_0; // Vref enable
Delay(10);
ADC2->CR &= ~ADC_CR_ADCALDIF; ADC2->CR |= ADC_CR_ADCAL; // Start calibration
while (ADC2->CR & ADC_CR_ADCAL); // Wait end calibration /************************************************
* Select event trigger and channel
***********************************************/ // Enable start conversion external trigger
ADC2->CFGR |= ADC_CFGR_EXTEN_0; // Event 7 - HRTIM
ADC2->CFGR |= ADC_CFGR_EXTSEL_0 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_2; // Select ADC2 channel IN5
ADC2->SQR1 |= ADC_SQR1_SQ1_0 | ADC_SQR1_SQ1_2; // Length regular ADC channel = 1
ADC2->SQR1 &= ~ADC_SQR1_L; ADC2->IER |= ADC_IER_EOCIE; // Interrupt enable
NVIC_EnableIRQ(ADC1_2_IRQn); // enable interrupt ADC1 and ADC2 /************************************************
* Start ADC
***********************************************/ ADC2->CR |= ADC_CR_ADEN; // Enable ADC2
Delay(10);
ADC2->CR |= ADC_CR_ADSTART;
}

Я использую режим регулярных каналов, канал у меня всего один и он выбирается в регистре SQR1. Задействовал АЦП №2, а именно его вход IN5, он является быстрым и может работать на максимальной частоте семплирования, но не в этот раз. Частота выборки равна частоте ШИМа, т.к. 1 период = 1 выборка, в принципе этого более чем достаточно.

Смотрим в RM: Так же нам в регистре CFGR нужно выбрать событие по которому будет запускать преобразование, то есть Event 7, почему именно он?

Думаю теперь понятно как связать 2 модуля, в принципе алгоритм аналогичный для любой периферии и любого таймера, отличаться будет разве что название регистра. Trigger 1 от модуля HRPWM приходит на Event 7 для нашего АЦП №2, который в данном случае работает в роли slave, то он управляется от модуля HRPWM.

Именно в этом прерывание мы и организуем алгоритм управления. При достижения счетчика периода Master timer будет запускаться преобразования, которое спустя примерно 15 циклов (сколько точно смотрите в RM-е) вызовет прерывание и в нем можно будет забрать результат. Да, внутри прерывания что-то массивно лучше не делать, лучше выставлять флаг и передавать выполнение дальше, но я позволю себе такое упрощение, ибо в данном случае мой контроллер ничем особо не загружен и он с вероятностью 146% успеет посчитать и выйти из прерывания до появления нового.

Немного об управление

Вы чуть приоткрываете воду, трогаете рукой, холодная? Представьте, что вы вошли в ванную комнату и решили помыть руки в раковине. Хорошо! Добавляете еще горячей воды, теплее? Почти то, что надо? Добавляете еще горячей воды? Добавим еще горячей воды, пробуем рукой, обжегся? Хорошо! Хорошо? Давай теперь чуть убавим горячую. Это самый простой регулятор! И так до бесконечности вы будете крутить кран пока температура воды не станет идеальной.

Вместо руки у нас АЦП с измеренным результатом. Только у нас регулируется не количество горячей воды, а коэффициент заполнения ШИМа. Мы посчитаем что должен выдавать АЦП при 12В на выходе, а потом с помощью условия if заставим наш контроллер поддерживать это значение с помощью изменения коэффициента заполнения (duty). Остается лишь реализовать логику.

АЦП умеет измерять от 0 до +3,3В и если подать 12В, то микроконтроллер сгорит просто. Для начала давайте повесим на выход делитель напряжения, чтобы 12В уменьшить до 2-2,5В к примеру, т.к. Наш АЦП будет выдавать результат: adcResult = (Vout / k) / Vref * 212 = (12V / 6) / 3. Поэтому я поставлю делитель с номиналами 10 кОм и 2 кОм, который даст коэффициент деления 6 и соответственно наши +12В превратятся в +2В. Теперь пишем код для обработчика прерывания: 3 * 4095 = 2481.

void ADC1_2_IRQHandler (void) { ADC2->ISR |= ADC_ISR_EOC; adcResult = ADC2->DR; if (adcResult > 2480) { dutyControl = dutyControl - 10; } else { dutyControl = dutyControl + 10; } SetDutyTimerA(dutyControl);
}

Первым делом после попадания в обработчик прерывания нужно сбросить флаг этого самого прерывания, иначе второй раз вы в него уже не попадете. Затем считываем результат и сохраняем его в виде переменной adcResult. Теперь, зная напряжение на выходе, нужно внести корректировку значения коэффициента заполнения для ШИМ, я это реализовал просто через условие if. В каждом периоде ШИМ мы делаем измерение, увеличиваем или уменьшаем коэффициент заполнения и устанавливаем полученный результат для следующего периода. Все просто, быстро и видна суть. Смотрим на результат работы:

Сильно внимательные могут заметить небольшие иголки проскакивающие, тут нужно просто повесить керамику X7R на выход на 1-10 мкФ и они уйдут, мне просто лень уже искать ее и напаивать. Как видите все работает и при изменение входного напряжения сам выход остается стабильным в 12В. Теперь сама осциллограмма, чтобы не портить глаза:

Дело в том, что из-за алгоритма управления, чтобы заполнение достигло нужно значения от 0 до 10000, например, необходима тысяча периодов или примерно 10 мс. Тут вы можете увидеть как нарастает напряжение на выходе. Вообще в плане управления можно сделать много чего, так что поле для экспериментов у вас есть. Меня это устраивает ибо софт-старт, если вы хотите сократить время нарастания, то чуть усложните алгоритм и прибавляйте +1000, а не +10, и чем ближе вы будете подбираться к заданным 12В, тем меньше шаг регулирования делайте пока не дойдете до +10.

Дело в том, что после выключения питания моя цифровая часть продолжает работать от другого БП и она пытается удержать нужное значение на выходе. Еще интересный момент это колебания в момент выключения, такая «гармошка». Да из входных конденсатор, это те что по 1000 мкФ аж 3 штуки, вот такое интересное явление. Откуда берется энергия?

Заключение

Статья вышла не маленькая, но вы же хотели всё и сразу мол давай готовую железку — получите. Надеюсь статья вам понравится, постарался сделать ее скорее не научную, а научно-популярную, чтобы материал был доступен людям с разным уровнем знаний и опыта. Возможно в дальнейшем я разберу аналогично и другие топологии типа boost, full bridge и прочие.

Сейчас жду платы от PCBway, которые собственно и вызвались про спонсировать печатными платами мои открытые проекты, исходники на MPPT так же будут открыты как и на все мои модули. Кстати, данная статья и код послужат для нового MPPT контроллера на 20А, который проектирую.

Держите проект с кодом для TrueSTDIO — RAR. Самое главное забыл!

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть