Давным давно в далекой далекой галактике я написал небольшую статью о специализированных контроллера Piccolo от Texas Instruments, которые предназначены для управления силовыми преобразователями и электроприводом. Данные контроллеры являются очень мощным инструментов разработки во многих задачах и хотелось написать про них что-то еще… простое и полезное.
Недавно меня озадачили разработать контроллер для управления двигателем и соответственно образовалась тема для статьи — сегодня я расскажу о процессе формирования трехфазного ШИМа для управления двигателем, а так же объясню в чем выгодные отличия TMS320F28 от других контроллеров типа STM32F334, STM32G484, XMC4200 и остальных.
В качестве стенда я буду использовать разрабатываемый контроллер, увы, подробно про железную часть я рассказывать не могу. Правда, если я скажу, что контроллер построен на базе связки TMS320F28027 + DRV8353RSRGZT, то вы можете посмотреть в даташит на драйвер и увидеть общий концепт схемотехники + на данном камне есть отладка и reference design на нее открыт.
В принципе на однотипной схемотехнике можно управлять и BLDC двигателями, которые "потребляют" уровни напряжения и обычными трехфазниками, которые уже хотят синусоидальный выход. Я покажу оба варианта, т.к. путь к синус лежит через формирование уровней напряжения.
Немного о железе
Силовая часть драйвера идеологически представляет из себя 3 полумостовых преобразователя, подобным образом выполнены наверное все частотники и контроллеры для управления BLDC двигателями во всяких коптерах:
Отличие одно — у меня нет выпрямителя входного, т.к. контроллер изначальное питается от постоянного напряжения. Источник питания в моем случае это сборка из li-ion АКБ в виде ячеек 18650. Используемый драйвер DRV8353RSRGZT умеет как раз управлять 3-мя силовыми полумостами, так же в применяемой версии камня еще имеются встроенные ОУ для работы с шунтами в роли датчиков тока, встроенный dc/dc, который умеет переваривать до 70...80В и все это очень гибко настраивается через SPI. Например, очень удобно иметь возможность настроить максимальный импульсный ток управления транзисторами.
Так же в данной серии имеются драйверы с разным набором функций, например, есть с аналоговым управлением, а не SPI или без встроенного dc/dc и без ОУ. По цене они не особо отличаются и я взял самый "жирный" как вы уже наверное поняли. Все это дело выглядит очень красиво, но я довольно легкомысленно подошел к проектированию обвязки драйвера и у меня вылезло 2 существенные проблемы. На самом деле проблема одна — это сильный перегрев:
А вот вызвана эта проблема была уже 2-мя причинами. Собственно суть проблемы заключается в перегреве самого драйвера. На термограмме драйвер нагружен током 5А (для него это почти холостой ход) и не греется ничего кроме драйвера и чуть-чуть сам МК. Транзисторов даже не видно, они имеют температуру печатной платы, при 5А там мизерные потери на тепло.
- Ошибка №1
Меня на нее натолкнул мой знакомый, я честно говоря на такое подумал бы в самую последнюю очередь — в драйвер встроен dc/dc, который принимает на вход 15...50В и выдает 3.3В для питания МК, логики, компараторов и операционных усилителей. Казалось бы у меня в проектах стоят микросхемы LM5008 и LM5017 в виде отдельных микросхем и я спокойно снижал 60В в 3.3В без заметного нагрева при токе 100-150 мА, но тут все оказалось хитрее — общий КПД преобразователя получался около 65-70% при токе 300 мА! Дело в том, что сам преобразователь то может выдать 3.3В, но КПД будет мизерный, оптимально надо задавать выходное напряжение 10-12-15В. При выходе 12В 100 мА у меня драйвер перестал греться практически, а КПД достиг приятных 88%. Решение проблемы — встроенным dc/dc опускаем вход 15...50В до 12В, а дальше из 12В снижать в 3.3В уже внешним дешевым dc/dc.
- Ошибка №2
Вторая ошибка более очевидна и первым делом я грешил на нее как мог. Дело в том, что у микросхем в корпусе QFN основной отвод тепла осуществляется через "пузо", обычно оно сидит на GND и через несколько переходных отверстий (via) цепляются на земляной полигон и все тепло спокойно туда уходит. Изначально я не учел мизерный КПД встроенного dc/dc при большой разнице напряжений, поэтому меня не смутило, что термопад ("пузо") цеплялось к сплошному полигону GND на внутреннем слое, на наружном слое у меня меди под пузом не было как собственно и полигона GND. В итоге оказалось, что на микросхеме выделяется ~0.5 Вт тепла, а у меня оно рассеивается во внутренний слой платы, то есть эффективность очень плохая. Решение проблемы — нужно земляной полигон так же делать на наружном слое (Bottom Layer) и не делать так:
В итоге во второй ревизии железа были исправлены данные ошибки: добавился внешний dc/dc преобразователь 12-3.3В и на нижнем слое дополнительно залит полигон GND и пад микросхемы посажен на него + сохранился внутренний сплошной полигон земли. После таких доработок температура в продолжительном режиме работы снизилась с +82 до +43 oC:
Как видите за счет снижения потерь температура значительно снизилась при тех же самых условиях, а так же тепло теперь более равномерно распределяется по площади платы и не перегревает локально ни драйвер, ни микроконтроллер. В принципе по железу все, больше ничего интересного не произошло и заработало стабильно. Как итог могут рекомендовать использовать драйвер DRV8353.
Реализация аппаратного сдвига фаз на 120o
Особенностью трехфазной сети является то, что ток в фазах не синхронный, а сдвинут на 120o относительно соседней. Что вообще такое этот сдвиг фазы на 120o? Если говорить по простому, то это смещение точки старта генерации на 1/3 периода. Период сигнала с математической точки зрения равен 2?, а значит двигать второй сигнал надо на 2?/3, а третий на 4?/3. С точки зрения электроники период задается периодом отсчета нашего таймера. Например, при тактирование 60 МГц мы хотим получить ШИМ с частотой 50 кГц, значит период отсчета таймера будет от 0 до 1200 (60 000 000 Гц / 50 000 Гц = 1200). Теперь чтобы получить 3 фазы со сдвигом 120o нам надо 1-ю фазу не трогать, для 2-й фазы к текущему значению добавлять +400, для 3-й фазы к текущему значению добавлять +800.
Если мы используем микроконтроллеры на ядре cortex, то сдвиг мы можем реализовать или записав математическую формулу или задействовав синхронизацию по событиям. Для меня всегда было удивительно почему ST, NXP и прочие не сделали просто регистр куда записывалось бы значение сдвига. К счастью так сделали TI в своих TMS320F28xxx, для установки сдвига достаточно просто записать один регистр! Рассказывать почему софтверное решение не оптимально не буду, просто скажу, что считает формулы МК не очень быстро. Про вариант с синхронизацией от событий уже более адекватен и на stm я бы делал именно так, но такой вариант не позволяет менять значение фазы "на лету", то есть для какого-нибудь фазосдвигового моста опять только софтварный вариант остается. Является ли преимуществом возможность аппаратно управлять фазой? Это решать уже вам, моя задача рассказать, что так можно. Для меня же это очевидный плюс, когда мы говорим об управлении электроприводом или инверторах напряжения с трехфазным выходом.
Теперь давайте настроим генерацию ШИМ сигналов в виде 3-х комплементарный пар с dead time и сдвигом фазы. Пока без синуса. Я буду использовать следующие пары: EPWM1A + EPWM1B, EPWM2A + EPWM2B и EPWM4A + EPWM4B. Именно эти сигналы идут у меня от микроконтроллера к драйверу.
- Шаг 1
Необходимо настроить мультиплексор GPIO с помощью регистра GPAMUX на работу с PWM и отключить подтяжки выхода к питанию, чтобы в момент включения на всех ногах не оказалось лог.1 и не открылись ключи. Защита по току конечно спасет, но лучше так не делать. Так же стоит помнить, что для доступа к регистрам настройки его нужно получить командой EALLOW и затем обратно включить защиту от перезаписи командой EDIS.
void InitGPIOforPWM (void) {
EALLOW;
GpioCtrlRegs.GPAPUD.bit.GPIO0 = 1; // Disable pull-up on GPIO0 (EPWM1A)
GpioCtrlRegs.GPAPUD.bit.GPIO1 = 1; // Disable pull-up on GPIO1 (EPWM1B)
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // Configure GPIO0 as EPWM1A
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // Configure GPIO1 as EPWM1B
GpioCtrlRegs.GPAPUD.bit.GPIO2 = 1; // Disable pull-up on GPIO2 (EPWM2A)
GpioCtrlRegs.GPAPUD.bit.GPIO3 = 1; // Disable pull-up on GPIO3 (EPWM2B)
GpioCtrlRegs.GPAMUX1.bit.GPIO2 = 1; // Configure GPIO2 as EPWM2A
GpioCtrlRegs.GPAMUX1.bit.GPIO3 = 1; // Configure GPIO3 as EPWM2B
GpioCtrlRegs.GPAPUD.bit.GPIO6 = 1; // Disable pull-up on GPIO6 (EPWM4A)
GpioCtrlRegs.GPAPUD.bit.GPIO7 = 1; // Disable pull-up on GPIO7 (EPWM4B)
GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 1; // Configure GPIO6 as EPWM4A
GpioCtrlRegs.GPAMUX1.bit.GPIO7 = 1; // Configure GPIO7 as EPWM4B
EDIS;
}
- Шаг 2
Настраиваем генерацию ШИМ сигнала. Необходимо получить частоту 50 кГц и сдвиг фазы 120o. В данном случае я использую обычный PWM, ведь в данном контроллере имеется и HRPWM, это важно помнить. Тактируется модуль PWM на частоте ядра, то есть 60 МГц, как настроить частоту PLL я показывал в первой статье по TMS320, повторяться не буду, но в конце статьи будет архив с кодом и можно будет подсмотреть там.
void InitPWM (void) {
// EPWM Module 1 config
EPwm1Regs.TBPRD = 600; // Set priod
EPwm1Regs.TBPHS.half.TBPHS = 0; // Set phase
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN; // Symmetrical mode
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Master enable
EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW;
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Sync down-stream module
EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE; // enable dead-time module
EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC; // Active Hi complementary
EPwm1Regs.DBFED = 20; // dead-time on 20 tick
EPwm1Regs.DBRED = 20; // dead-time off 20 tick
// EPWM Module 2 config
EPwm2Regs.TBPRD = 600;
EPwm2Regs.TBPHS.half.TBPHS = 400; // Set phase = 400/1200 * 360 = 120 deg
EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;
EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE; // Slave enable
EPwm2Regs.TBCTL.bit.PHSDIR = TB_DOWN; // Count DOWN on sync (=120 deg)
EPwm2Regs.TBCTL.bit.PRDLD = TB_SHADOW;
EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN; // sync flow-through
EPwm2Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm2Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm2Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm2Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
EPwm2Regs.AQCTLA.bit.CAU = AQ_SET;
EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm2Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm2Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;
EPwm2Regs.DBFED = 20;
EPwm2Regs.DBRED = 20;
// EPWM Module 4 config
EPwm4Regs.TBPRD = 600;
EPwm4Regs.TBPHS.half.TBPHS = 400;
EPwm4Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;
EPwm4Regs.TBCTL.bit.PHSEN = TB_ENABLE;
EPwm4Regs.TBCTL.bit.PHSDIR = TB_UP;
EPwm4Regs.TBCTL.bit.PRDLD = TB_SHADOW;
EPwm4Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
EPwm4Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm4Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm4Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm4Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
EPwm4Regs.AQCTLA.bit.CAU = AQ_SET;
EPwm4Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm4Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm4Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;
EPwm4Regs.DBFED = 20;
EPwm4Regs.DBRED = 20;
}
Теперь чуть подробнее… в регистр TBPRD записываем период, вернее "период / 2", т.к. таймер считаем в обе стороны, получается что период 600 соответствует частоте выходного ШИМ-сигнала в 50 кГц в режиме комплементарной пары. В регистр TBPHS пишем значение фазы на которую нужно сдвинуть, в данном случае 400 из 600, что соответствует 2?/3. Тут стоит отметить, что 1-ю фазу мы не двигаем, поэтому для нее сдвиг равен 0, для 2-й фазы соответственно сдвиг равен 400, а вот для 3-й фазы логичным покажется вариант записать 800, но 800 из 600 как-то не очень… поэтому пишут сдвиг не относительно 1-й фазы, а относительной предыдущей, то есть 2-й. В итоге получаем, что в 3-ю фазу пишем 400 и это соответствует 2?/3 между фазой 2 и 3, а так как 2-я уже сдвинута, то между фазами 1 и 3 будет "2?/3 + 2?/3 = 4?/3" и с точки зрения электроники все выглядит логичным.
Чтобы фазы понимали кто относительно кого двигается — нужен начальник, поэтому EPWM1 настраивается с помощью бита PHSEN в режим master (ведущий), а EPWM2 и EPWM4 соответственно как slave (ведомые). Так же с помощью битов SYNCOSEL настраивается "точка" синхронизации, то есть откуда до куда считать сдвиг. EPWM1 синхронизируется с началом отсчета таймера, то есть с нулем периода, а EPWM2 и EPWM4 уже синхронизируются относительно фронта сигнала предыдущего канала: предыдущий канал для EPWM2 это EPWM1, а для EPWM4 это EPWM2.
Теперь осталось включить комплементарные пары и установить длительность dead-time. С помощью битов POLSEL устанавливаем не инверсный ШИМ, то есть по достижению заданного значения компаратора (отсчета) на выходе генерируется лог. 1. В OUT_MODE устанавливаем генерацию dead-time и по фронту и по спаду сигнала. Соответственно в регистры DBFED и DBRED записываем длительность мертвого времени в тактах.
- Шаг 3
Теперь остается записать значение коэффициента заполнения (duty) в соответствующие каждому каналу регистр CMPA и можно наблюдать результат.
EPwm1Regs.CMPA.half.CMPA = 300; // duty for output EPWM1A
EPwm2Regs.CMPA.half.CMPA = 300; // duty for output EPWM2A
EPwm4Regs.CMPA.half.CMPA = 300; // duty for output EPWM4A
Вуаля! Щупы осциллографа подключены на выход драйвера. Желтый канал — это наш EPWM1, то есть мастер. Синий канал это EPWM2 и он сдвинут на 2?/3 (или на 400 отсчетов) относительно желтого канала, а зеленый канал сдвинут еще на 400 отсчетов. Таким образом мы получаем 3 фазы, где каждая фаза сдвинута на 120o.
Давайте теперь перекинем щупы осциллографа с выхода силового моста на управляющие сигналы, которые выходят с микроконтроллера и проверим наличие dead-time внутри комплементарной пары:
Как видите установленное мертвое время соответствует реальному. Длительность одного отсчета 1 / 60 000 000 Гц = 16,6 нс и у нас получается 20 отсчетов, что равносильно длительности мертвого времени 20 16,6 нс = 332 нс,* что примерно и наблюдается на осциллограмме.
Собственно где такое может пригодиться, именно в том виде, что сейчас. Самый очевидный вариант — многофазные dc/dc преобразователи, для заинтересованных гуглить interleaved dc/dc converter. Это крайне интересное техническое решение, которое позволяет значительно уменьшить размеры силовых индуктивностей, уменьшить выходную емкость конденсаторов, а так же повысить КПД. На простеньком TMS320F28027 можно реализовать 4-х фазный преобразователь и все это будет очень просто реализовано в коде и только аппаратно.
Генерируем трехфазное переменное напряжение
Во многих задачах будет недостаточно получить на выходе дискретные значения 0 или VCC, нужна синусоида. У меня есть статья, где рассказывается про формирование однофазного переменного напряжения и там используется "табличный" метод, то есть значения для синусоиды изначально посчитаны. В принципе так можно сделать и для трехфазки, но хочется показать альтернативный вариант, а именно расчет значения заполнения (duty) в реальном времени или "на лету".
Тут есть одна особенность. Частота ШИМа в данном случае так же 50 кГц и сдвиг фазы устанавливается между периодами данного сигнала. Соответственно, когда мы будем модулировать синусоиду частотой 50 Гц, то аппаратный сдвиг фазы "потеряется", он все еще будет присутствовать между ШИМами, но не внутри синусоиды, поэтому его придется делать софтварно. Тригонометрия штука тяжеловесная для TMS320F28027, но у меня он не особо загружен, поэтому пусть считает. Если же у вас задача, требующая большого количества вычислений, то вам нужен контроллер с TMU и FPU, например, TMS320F280049, который сможет ворочать математику гораздо быстрее.
Для загрузки значений заполнения (duty) в ШИМ нам потребуется таймер, период которого задаст частоту дискретизации. Мне нужен период 20 мс (1/50Гц = 20 мс) и количество шагов в синусоиде возьму допустим 20, в итоге с частотой 0,02 с / 20 = 0,001 мс = 1 кГц должно генерироваться прерывание и в этом прерывании буду записывать значение в ШИМ. Для простоты возьму обычный таймер CPU0 и настрою его:
void InitTimer0ForGenerator (void) {
EALLOW;
PieVectTable.TINT0 = &cpu_timer0_isr;
EDIS;
InitCpuTimers();
ConfigCpuTimer(&CpuTimer0, 60, 1000);
CpuTimer0Regs.TCR.bit.TIE = 1;
CpuTimer0Regs.TCR.bit.TSS = 0;
IER |= M_INT1;
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // Enable TINT0 in the PIE: Group 1 interrupt 7
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global real-time interrupt DBGM
}
__interrupt void cpu_timer0_isr (void) {
CpuTimer0.InterruptCount++;
/*
* Кадило крутится - синус мутится. Потом...
*/
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge this interrupt to receive more interrupts from group 1
}
Функции InitCpuTimers и ConfigCpuTimer стандартные, вся настройка именно в них, нам нужно просто передать частоту ядра (60 МГц) и период счета в микросекундах (1000 мкс = 1 мс), что эквивалентно частоте 1 кГц, а нам так и надо было. Так где в функции настройки разрешаем прерывания и передаем адрес обработчика нашего прерывания, где все будет происходить.
Теперь необходимо заново "изобрести" формулу синуса, для этого потребуется знание школьной тригонометрии и все. И так… у нас есть функция y = sin(x) давайте построим график данной функции:
Как видно на графике амплитуда у изменяется от -1 до 1, мы же хотим от 0 до 1, т.к. при минимальной амплитуде у нас 0В, а при максимальной (эквивалент 1) у нас +VCC. Чтобы "нарисовать" -1...+1 нам нужно двухполярное питание, а его нет. Нужно сместить график в положительную сторону. Если мы его просто поднимем вверх, то он станет от 0 до +2, а мы может только до +1. Значит нужно поделить на 2 и всего-то! Давайте для начала просто поделим и построим график для y = (sin(x)/2):
Ага! Теперь график имеет размах от -0.5 до +0.5, то есть амплитуда составляет 1. Уже лучше, но мы еще не избавились от отрицательных значений, так давайте просто сместим график вверх на 0.5, для этого нужно просто прибавить данное значение к результату и получим формулу y = 0.5 + (sin(x)/2) и построим график для данной функции:
Вот теперь стало все совсем прекрасно: синусоида имеет амплитуду от 0 до 1, отрицательные значения отсутствуют. Формула y = 0.5 + (sin(x)/2) описывает 1-ю фазу, теперь необходимо добавить сдвиг фазы, чтобы получить фазы 2 и 3. Для этого из x отнимаем 2?/3 и 4?/3 соответственно и получаем формулы для оставшихся фаз y = 0.5 + (sin(x-2?/3)/2) и y = 0.5 + (sin(x-4?/3)/2). Строим 3 графика и смотрим похоже ли на правду:
Неплохо! Картинка похожа на то, что обычно рисуют в учебниках электротехники, когда говорят про трехфазную сеть или асинхронные двигатели. Кстати, 2.0943 это 2?/3, а 4,1866 соответственно 4?/3, я их просто сразу посчитал и они у меня фигурируют в коде. Итого у нас есть 3 уравнения:
- Фаза А — y = 0.5 + (sin(x)/2)
- Фаза В — y = 0.5 + (sin(x-2?/3)/2)
- Фаза С — y = 0.5 + (sin(x-4?/3)/2)
Со стороны математики вроде все просто и понятно, но теперь нужно адаптировать для микроконтроллерных реалий. Синусоида у нас не аналоговая, а имеет "ступеньки", то есть дискретна, ведь мы умеет задавать только напряжение или 0В или +15В (VCC) в моем случае. Ранее я писал, что шагов у меня будет 20, значит на 1 период у меня будет 20 вычислений.
Для начала определимся, что подставлять вместо x. Период нашей синусоиды 2?, а значит шаг дискретизации будет равен 2?/20. Соответственно синусоида будет состоять из 20 точек, мы как бы строим график по точкам, а между ними аппроксимируем. В итоге значение на первом шаге будет sin(2? * (1/20), на втором шаге sin (2? * (2/20)), на третьем шаге *sin (2? (3/20)) и так далее, когда мы дойдет до 20/20, то это будет означать окончание периода и надо будет начинать считать по новой. На основании полученных данных давай поправим формулы:
- Фаза А — y = 0.5 + (sin(2? * (n/N))/2)
- Фаза В — y = 0.5 + (sin(2? * (n/N)-2?/3)/2)
- Фаза С — y = 0.5 + (sin(2? * (n/N)-4?/3)/2)
Вот, теперь мы считаем значение синуса в каждой конкретной точки на графике. Соответственно n — текущий шаг, N — всего шагов (20). После этих формул мы получим значение от 0 до 1, но в реальности мы оперируем не с абстрактной амплитудой. Амплитуда в нашем случае зависит от коэффициента заполнения, т.к. duty меняется от 0 до 600 (из настроек ШИМа), то 0 это 0, а 1 эквивалентна 600. Исходя из этого давайте пересчитаем в реальную формулу для получения значения, которое будет загружаться в регистр ШИМа CMPA:
- Фаза А — duty1 = A (0.5 + (sin(2? (n/N))/2))
- Фаза В — duty2 = A (0.5 + (sin(2? (n/N)-2?/3)/2))
- Фаза С — duty4 = A (0.5 + (sin(2? (n/N)-4?/3)/2))
Соответственно А — это максимальное значение амплитуды, то есть 600, n — текущий шаг, N — всего шагов (20). Значения duty1, duty2, duty4 — это пересчитанное реальное значения коэффициента заполнения (duty), которое загружается в CMPA. Теперь давайте напишем код для обновленного обработчика прерываний и объявим все необходимые переменные:
float activeStep = 0.0;
float amplitude = 600.0;
float allStep = 20.0;
const float pi = 3.1415; // ?
const float piTwo = 6.2831; // 2?
const float phaseShifted120deg = 2.0943; // 2?/3
const float phaseShifted240deg = 4.1866; // 4?/3
__interrupt void cpu_timer0_isr (void) {
if (activeStep >= allStep) {activeStep = 0;}
activeStep++;
EPwm1Regs.CMPA.half.CMPA = ((uint16_t)(amplitude * (0.5 + (sinf(piTwo * (activeStep / allStep)) / 2))));
EPwm2Regs.CMPA.half.CMPA = ((uint16_t)(amplitude * (0.5 + (sinf(piTwo * (activeStep / allStep) - phaseShifted120deg) / 2))));
EPwm4Regs.CMPA.half.CMPA = ((uint16_t)(amplitude * (0.5 + (sinf(piTwo * (activeStep / allStep) - phaseShifted240deg) / 2))));
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge this interrupt to receive more interrupts from group 1
}
Код как видите простейший, если понимать что требовалось сделать и простую математику в решаемой задаче. При каждом вызове прерывания мы инкрементирует переменную activeStep, которая содержит в себе номер шага, она изменяется от 0 до 20 и потом сбрасывается. Получается, что за один период мы выполняет 20 шагов и 20 вычислений для каждой фазы. Чтобы не считать в формуле постоянно 2?/3 и 4?/3 я посчитал их сразу, чтобы использоваться как константы.
Вычислений получилось минимум, для данного МК это совсем ни о чем. При желании количество точек можно значительно увеличить, например, до 200. Все зависит от задачи. Изменение частоты ШИМа происходит с помощью изменения частоты вызова прерывания и количества шагов. Так же вы можете изменять переменную amplitude и изменять напряжение на выходе силового преобразователя.
После загрузки кода в микроконтроллер вы получите соответствующую картинку:
Если растянуть по Y график, то станет лучше видно дефекты сигнала. Это следствие малого количество шагов дискретизации, тут действует условное правило: чем больше точек, чем красивее сигнал.
Заключение
Сегодня я рассказал про процесс формирования сдвига фазы в многофазных системах, ничего сложного в принципе нет, особенно при использовании TMS320F28. Дальше остается дело за алгоритмами, в принципе на просторах рунета множество статей, где разжевано управление и бесколлекторными двигателями, и асинхронными и всякими другими, вам остается лишь переложить логику.
Надеюсь данный материал будет полезен и не особо скучен в прочтение. Как всегда исходник прилагается:
Комментарии (50)
hardegor
25.06.2019 22:02в драйвер встроен dc/dc, который принимает на вход 15...50В
в даташите DRV8353 однозначно написано «Low-Side Linear Regulator», поэтому и светился при понижении 50->3.3
А DC/DC там «High-Side Doubler Charge Pump» — это удвоитель на конденсаторах, только для питания high-side.NordicEnergy Автор
25.06.2019 23:14+3Сначала «400+400+400=1600», а теперь потерян dc/dc… Линейник там для питания драйвера транзисторов и он потребляет средний ток всего 4 мА, а речь идет о встроенном dc/dc, который специально для запитывания низковольтной части схемы:
mpa4b
25.06.2019 22:07-2Оставлю в покое жуткие флоаты с синусами там, где можно было сделать целочисленную табличку.
Задам вопрос вот о чём: зачем вы пишете о (и делаете) сдвиге фаз между PWM-сигналами, работающими на частоте 50 кГц? Сдвиг фаз должен быть между медленными синусоидами 50 Гц, но зачем он нужен между несущими частотами PWM? Какие проблемы решает этот сдвиг между PWM, работающими на частоте 50 кГц?Dima_Sharihin
26.06.2019 08:57Сдвиг между несущими PWM нужен:
- Для того, чтобы уменшить амплитуду "пилы" выходного ШИМ-сигнала и тока потребления
- Чтобы увеличить частоту реальной модуляции в N раз
- На бОльшую частоту проще сделать выходной фильтр
Таким образом достигается
- снижение электромагнитного излучения
- равномерное распределение нагрузки между фазами
- бОльший динамический диапазон: отдельные фазы мы можем и не включать, повышая общий КПД системы
RegisterWindowClassExA
26.06.2019 16:13Похоже, автор сделал несогласованное технически предложение. В нем сначала идет речь о сдвиге фаз напряжений в разных фазах, а затем идет речь о сдвиге фаз сигналов ШИМ. Первое понятно для чего — для того, чтобы синусоиды были сдвинуты, как и положено в трехфазной (многофазной) сети. Второе — видимо для того, чтобы драйверы силовых ключей потребляли ток поочередно, это повысит частоту и снизит амплитуду импульсов потребления тока драйверами, это благотворно скажется на уровне помех. ИМХО.
NordicEnergy Автор
25.06.2019 22:15+4где можно было сделать целочисленную табличку
Если бы читали не по диагонали (если вообще читали), то заметили бы, что я написал мол вариант с таблицей был в предыдущей моей статье и тут хочется мне через формулу считать.
Ах да… Через таблицу не всегда прокатит, например, если нужно синхронизироваться по напряжению внешнему в том же PFC или инверторе.
зачем вы пишете о (и делаете) сдвиге фаз между PWM-сигналами, работающими на частоте 50 кГц?
Теперь все понял… не читали. Тогда повторюсь — например, для interleaved dc/dc, там между фазами тоже нужен сдвиг.
Какие проблемы решает этот сдвиг между PWM, работающими на частоте 50 кГц?
Ваши проблем 100% тут ничего не решает.mozgoverflow
26.06.2019 15:16Просто в тексте очень часто упоминаются именно двигатели BLDC и трёхфазные обычные двигатели (асинхронники), потому складывается впечатление, что описывается именно схема управления двигателем. Да, в конце раздела есть абзац, что это всё же алгоритм многофазного dc-dc конвертера.
У меня вопрос про вторую часть, где синусоиды и 50Гц. Там нужно смещение фаз и deadtime?
RegisterWindowClassExA
26.06.2019 16:33Вариант через таблицу прокатит даже если нужно синхронизироваться по внешнему напряжению. Просто в этом случае к таблице нужно добавить коррекцию табличных значений с помощью ряда Тейлора функции синус. Либо кусочно-линейную или квадратичную аппроксимацию табличных значений в промежуточных точках. Т.к. фаза табличной синусоиды — это, по сути, индекс массива. И это будет быстрее, нежели считать синус. Но, опять же, если захотелось — охота пуще неволи, можно и синус считать. Просто нужно учесть, сколько тактов МК из 400 имеющихся в наличии уйдёт на вычисление синусов и сопутствующие вопросы (если ШИМ 50 кГц при частоте ядра 60 МГц, то у Вас остается 400 тактов на выбор скважности ШИМ).
Dima_Sharihin
26.06.2019 16:43И это будет быстрее, нежели считать синус
А вы давно заглядывали в библиотечный код вычисления синуса? Из избыточного там только приведение радиан к [0;1]
RegisterWindowClassExA
26.06.2019 16:58Из избыточного там, как минимум, использование типа float. И точность вычисления синуса там избыточная. Т.к. если у Вас период ШИМ равен 1200 тактов ядра, а максимальная скважность ШИМ и того меньше, Вам общепрограммная точность синуса не нужна, достаточно считать с точностью до 0.0005. Все это можно сделать из глины и палок в операциях целочисленной арифметики, и это будет очень быстро. Кроме того, если аргумент у Вас не в радианах, а в целочисленных попугаях, то дело пойдёт еще быстрее. Труднее поддерживать такой код будет, это да. Но скорость работы будет отменная.
RegisterWindowClassExA
26.06.2019 17:04Более того, т.к. у Вас период ШИМ равен 1200 тактов ядра, и точность синуса выше 0.0005 не нужна, то… Вы можете четверть периода синуса, умноженного на Вашу максимальную скважность, запихнуть в таблицу на 2000 ячеек (т.к. синусоида растет медленнее, чем y=x) типа int16_t (т.к. Ваши значения не превосходят 1200), и вычислять скважность ШИМ с необходимой для вашей задачи точностью за несколько тактов, просто вычисляя нужный индекс массива.
DenisHW
26.06.2019 00:12общий КПД преобразователя получался около 65-70% при токе 300 мА
В даташите указан кпд более 80% для 95Vin и почти 90% при 48В. Но даже для 0.5Вт Rth case-ambient = 25 градусов на ватт. То есть перегрев порядка 13 градусов. Возможно, индуктивность была мала и пиковые токи были большие. Там Ton фиксировано для LM5008.
Спасибо за статью.NordicEnergy Автор
26.06.2019 06:23По идее корпус должен выдерживать где-то 3Вт, но суммарно там и 1Вт не было и уже грелось. Я думаю на внутренний полигон, была плохая идея в него рассеивать.
индуктивность была мала и пиковые токи были большие
Надо попробовать, брал расчетную + 25%, не думал в ту сторону.
В даташите указан кпд более 80% для 95Vin и почти 90% при 48В
При выходном 10...12В так и есть, а вот при 3.3В все очень плохо становится. dc/dc косит под линейник :))amartology
26.06.2019 07:57А на какой частоте вы включали LM5008a?
NordicEnergy Автор
26.06.2019 13:17А точно не помню, вроде сотни кГц, то ли 300, то ли 500.
amartology
26.06.2019 14:08А точно не помню, вроде сотни кГц, то ли 300, то ли 500.
Ага, я так и подумал. Собственно, выше вам уже указали на проблему:
Возможно, индуктивность была мала и пиковые токи были большие. Там Ton фиксировано для LM5008.
Если бы вы, вместо того, чтобы ставить «то ли 300, то ли 500 кГц», почитали даташит на LM5008a, то смогли бы обнаружить в нем на седьмой странице рисунок 5, показывающий предельную частоту для разных сочетаний входного и выходного напряжения. У LM5008a minimum on-time 400 нс (третья страница, описание шестого пина RT/SD), и для входа 95В и выхода 3.3В частота должна быть не больше 50 кГц. Удивительно, как он вообще в таких условиях какое-то разумное напряжение на выход выдавал.
Но, конечно, проще поставить еще один преобразователь, чем прочитать даташит на тот, который уже есть)DenisHW
26.06.2019 14:34+1Там еще эта микросхема требует минимального риппла на выходе для стабильной работы, ESR выходного конденсатора должен быть конечным (добавлен Rout на схеме выше).
However, the regulator requires a minimum amount of ripple voltage at the feedback input for proper loop operation. For the LM5008A the minimum ripple required at pin 5 is 25 mVp-p, requiring a minimum ripple at VOUT of 100 mV.
У техаса целая серия подобных преобразователей. Toff там определяется рипплом на выходе.
Конкретно для LM5008A явно подробно не написано, но для LM5160 целый раздел как быть с этим рипплом.
NordicEnergy Автор
26.06.2019 17:46+1Вы странный человек… Стою я в пробке, у меня спрашивают про частоту микросхемы в одном из проектов и вы думаете я это помню?)) Мне не хочется такое запоминать.
У меня есть сниппет схемы, который я один раз посчитал для выхода 3.3В и входа 12...50 под микросхему lm5008. Схема никогда не вызывала проблем. В это драйвере стоит такая же микра по сути, я просто перенес обвязку с теми же номиналами и при выходе 3.3В она греется, на другой плате нет. Подозреваю, что дело не в недостаточной индуктивности или ёмкости.
amartology
26.06.2019 18:27один раз посчитал для выхода 3.3В и входа 12...50 под микросхему lm5008. Схема никогда не вызывала проблем.
Не вызывала проблем, потому что вы никогда не запускали ее от входа 95 В. Предельно допустимая частота LM5008A пропорциональна отношению выхода к входу, а вы это, вероятно, не учли, просто переиспользовав готовое решение из другого проекта и не перепроверив его пригодность к новым исходным данным. Не стоит так делать никогда, это называется «халтура», а не «качественная работа».Karlson_rwa
26.06.2019 19:24Дык, что вы хотите? Про автора ходят всякие интересные слухи насчет кидалова. И здесь проскакивало и на электрониксе.
NordicEnergy Автор
26.06.2019 19:33+1У меня она в обоих случаях не работает при 95В. Откуда вообще информация что я на пределе ее запускаю? Вроде вполне понятно писал, что вход 15...50В в одном случае и 12...50В во втором. И почему бы в таком случае не переиспользовать?
Вся ваша критика сводится к "вы это ВЕРОЯТНО не учли", прекрасно… Вы угадываете что я сделал и потом свои же догадки критикует, не странно ли это?
amartology
27.06.2019 12:00Откуда вообще информация что я на пределе ее запускаю?
Видимо, не совсем верно понял ваш комментарий выше.
В даташите указан кпд более 80% для 95Vin и почти 90% при 48В
При выходном 10...12В так и есть, а вот при 3.3В все очень плохо становится. dc/dc косит под линейник :))
Энивэй, если вы запускаете микросхему внутри ее рабочего диапазона, заданного в даташите, то она должна работать нормально. Причина имеющихся проблем может быть разной, но «встроенным dc/dc опускаем вход 15...50В до 12В, а дальше из 12В снижать в 3.3В уже внешним дешевым dc/dc» — это не решение проблемы, а костыль, прямо из парижской палаты мер и весов, из секции «как не надо делать ни при каких обстоятельствах».DenisHW
27.06.2019 12:18+1Да, правильно разобраться до конца, если не видно соответствия даташиту. Но автор все-таки чесно описал, уже хорошо, урок. Вот можно было бы автора попросить схему, было бы интересно посмотреть.
NordicEnergy Автор
27.06.2019 13:19чесно описал
Так задача рассказать чего было, а не просто — «смотри как я поездат», да и ваши дельные подсказки куда посмотреть и что проверить тоже часто помогают.
NordicEnergy Автор
27.06.2019 13:17это не решение проблемы, а костыль, прямо из парижской палаты мер и весов
Это было быстрое решение, которое потребовало 10 минут на переработку платы, о «костыле» в принципе даже не спорю, но когда заказчик подгоняет каждый день на исследования места не остается :))
Вообще с данным драйвером еще буду ковыряться, попытки «с наскока» поиграться с индуктивностью +-50% от расчетной к решению проблемы не привели. Если получится в итоге получить >80% при 50В входе и 3.3В выходе, то сам буду рад, но пока проблему «отложил на полку» до следующей ревизии.
assasinmaxy
27.06.2019 13:19Если хотите, есть спец документ у TI формата excel в котором можно всё посчитать. Эдакий калькулятор для данной микросхемы dc-dc. У меня даже вроде сохранен где-то был.
SergeyMax
26.06.2019 17:48Но, конечно, проще поставить еще один преобразователь, чем прочитать даташит на тот
Хм, а не вы ли пару дней назад объясняли, что лучше поставить побольше конденсаторов, чем считать какой-то там ESR?amartology
26.06.2019 18:33+1сtrl+F на мой никнейм в обсуждении по вашему линку не дает никаких результатов. Видимо, это был не я. И, если что, фраза «проще поставить еще один преобразователь, чем прочитать даташит» — это не мое предложение, а критика того, что сделано в этой статье:
Решение проблемы — встроенным dc/dc опускаем вход 15...50В до 12В, а дальше из 12В снижать в 3.3В уже внешним дешевым dc/dc
Dima_Sharihin
26.06.2019 08:45Спасибо, отличная статья.
Вы не пробовали заменить float'овскую математику на библиотеку IQMath? Код получится чуток эффективнее за счет аппаратной поддержки C2000-ым ядром. У TMS320F28027 FPU-то нет.
P.S. теперь жду статьи advanced-уровня "как поддерживать сдвиг фазы в ШИМ переменной частоты" (:
NordicEnergy Автор
26.06.2019 13:19IQmath это правильный путь, я же показал "велосипед" чтобы было понятен сам принцип и откуда ноги растут. А так на либы ti хорошие доки + примеры, пересказывать их особо нет смысла.
Сейчас в черновиках лежит заготовка Парк/Кларк, так что про синхронизацию когда нибудь тоже выдам. Можно даже скоро :))
sanders1967
26.06.2019 15:01Синус на STM32H7 легко генерится софтом. Рабочий пример из программы генерации 2 фаз со сдвигом 180 градусов, т.е. в противофазе. Используются функции из DSP LIB.
Две переменные объявлены в main:
float angle_pointer_step = 3.6; //шаг разворачивания синуса
float angle_degree_offset_float = 180; //смещение второго канала
Остальное в прерывании:
//1 фаза угол
angle_degree_float = angle_degree_float + angle_pointer_step;
if (angle_degree_float >= 360 ) angle_degree_float = 0;
angle_rad_A = (angle_degree_float * _Pi) / 180.0;
PWM_A_float = arm_sin_f32(angle_rad_A) * amplitude;
PWM_A = (signed long) PWM_A_float;
//2 фаза со смещение
angle_degree2_float = angle_degree_float + angle_degree_offset_float;
if (angle_degree2_float > 360)
{
angle_degree2_float = angle_degree2_float — 360;
}
angle_rad_B = (angle_degree2_float * _Pi) / 180.0;
PWM_B_float = arm_sin_f32(angle_rad_B) * amplitude;
PWM_B = (signed long) PWM_B_float;
Вывод в ЦАП для отладки
DAC1->DHR12R1 = 2048 + PWM_A;
DAC1->DHR12R2 = 2048 + PWM_B;
TIM4->SR &= ~TIM_SR_UIF; //сброс флага UIE: Update interrupt enable
Dima_Sharihin
26.06.2019 15:41Сравнили новомодный Cortex M7 и древний C2000, кхе-кхе.
Между прочим "быстрое" вычисление синуса в ARM — это табличный метод + интерполяция. Собственно так делают все библиотеки DSP, если у процессора нет аппаратного ускорителя тригонометрии
И кстати, ради разворота синуса на 180 градусов не нужно ничего делать, просто инвертируйте результат.
NordicEnergy Автор
26.06.2019 19:40Ну С2000 не совсем древний, конечно ti клепает камни не как в st, все таки это железо для разных применений. Например, не так давно сделали среднебюджетный tms320f280049c с fpu, tmu и прочими cla. Он дешевле H7 и шустрее в задачах управления приводом или преобразователями
Dima_Sharihin
26.06.2019 19:44Ну, 280049 у меня достаточно валяется в тумбочке. Но он не то же самое, что F28027. FPU+TMU сильно увеличили производительность на такт, да и периферия серьезно продвинулась вперед.
А в дельфинах уже завезли 5-ядерного (2 C2000+CLA, Cortex M4) зверя с EtherCAT Slave и плавучкой двойной сложности. Теперь железные алгоритмы можно мониторить в реальном времени на ПК, если найти способ это дело использовать.
NordicEnergy Автор
26.06.2019 19:52Но сравнивать F28027 с упомянутым H7 не совсем корректно. 28027 не далеко ушел от самого обычного МК за пару баксов, из плюшек отличительных HRPWM да поддержка тексасовских либ. TMU ему по сути не нужен, а вот FPU бы конечно хотелось.
RegisterWindowClassExA
26.06.2019 17:50Никак не могу разрешить один вопрос… Если я правильно понял, то Вы неудачно смешали в этом предложении
Особенностью трехфазной сети является то, что ток в фазах не синхронный, а сдвинут на 120o относительно соседней. Что вообще такое этот сдвига фазы на 120o? Если говорить по простому, то это смещение точки старта генерации на 1/3 периода. Период сигнала с математической точки зрения равен 2?, а значит двигать второй сигнал надо на 2?/3, а третий на 4?/3. С точки зрения электроники период задается периодом отсчета нашего таймера. Например, при тактирование 60 МГц мы хотим получить ШИМ с частотой 50 кГц, значит период отсчета таймера будет от 0 до 1200 (60 000 000 Гц / 50 000 Гц = 1200). Теперь чтобы получить 3 фазы со сдвигом 120o нам надо 1-ю фазу не трогать, для 2-й фазы к текущему значению добавлять +400, для 3-й фазы к текущему значению добавлять +800.
сдвиг фазы напряжения в трехфазной сети и сдвиг сигналов ШИМ для ключей в этой сети. Это не связанные между собой вещи. Они обе нужные, но решают совершенно разные задачи и происходит это совершенно по-разному.
Сдвиг переменных напряжений в фазах происходит за счет того, что при вычислении скважности ШИМ для разных фаз Вы к аргументу прибавляете 1/3 и 2/3 периода синусоиды соответственно для фаз B и C. Это абсолютно необходимо, т.к. иначе Ваш преобразователь не будет трехфазным.
Сдвиг сигналов ШИМ происходит за счет смещения значений таймеров на 1/3 и 2/3 периода ШИМ, благодаря этому все ключи не открываются одновременно, потребление тока получается более равномерным. Без этого Ваш преобразователь все равно останется трехфазным, но качество входного тока и выходного напряжения будет ниже, а также выше будет уровень помех.
Или я чего не понял?
Alecksey1978
26.06.2019 17:52+2Сдвиг таймеров ШИМ для питания трехфазных электродвигателей вреден. Нельзя строить аналогию с DCDC-преобразователями.
Ниже картинка с некоторым маленьким вектором напряжения в секторе между векторами 0, 1 и 3. Первая без сдвига, где вектор реализуется векторами треугольника в последовательности: включены все нижние ключи, включается верхний ключ фазы А, включается верхний ключ фазы В, включены все верхние ключи, и в обратном порядке.
А вот если таймеры сместить на 120 градусов, то этот же вектор напряжения делается обеганием всех вершин окружности. Угадайте, в каком случае пульсация тока будет меньше?
Между тем, иногда режим сдвига таймеров делают специально в ущерб КПД, чтобы ввести в напряжение вот такую вращающуюся составляющую, как на рисунке справа. Тогда по отклику в токе на такую инжекцию можно определить положение ротора электродвигателя, но это не наш случай.NordicEnergy Автор
26.06.2019 19:44+1Делать сдвиг между ШИМами при формировании синусоиды действительно не надо. В этом банально нет смысла в большинстве случаев, хотя ваш пример весьма занятный, не встречал такого на практике в железе)
Alecksey1978
26.06.2019 19:58+2Это не мой пример, а из статьи. Вот на осциллограме сдвинутый ШИМ, как у меня справа на рисунке, и я объяснил, чем это плохо.
NordicEnergy Автор
26.06.2019 20:04А да это просто пример реализации сдвига, эта осциллограмма более интересная для интерливед конвертеров о чем написал в статье. Про синусоиду там отдельная глава и она в принципе не привязана к управлению электропривода, скорее хотел показать как ее изобразить и сдвинуть.
Но упомянутая вами ошибка действительно имеет место быть, в архиве с проектом я прикрепил код где и синус и сдвиг, забыл убрать в настройках таймера и осталось от первого примера.
P.S. за объяснение отдельное спасибо
hardegor
На картинке Шаг 3: T = 400+400+400=1200
NordicEnergy Автор
Так-с… что-то я туплю, ошибка? На картинке 400 + 400 + 400 = 1200. Полные период 1200 (или 600 если считаем в обе стороны).