6-шаговая коммутация BLDC моторов

f66ffb5c525b4d0f6165af7be124e5d1.gif

Продолжаем разработку контроллера сервоприводов MC50, о котором статьи здесь:

В контроллере применяется чип семейства Renesas Synergy S5D9, в нем есть специальный периферийный блок для 6-шагового управления. Освоим его.

Что такое 6-шаговая коммутация

По поводу этого есть много обучающих материалов. Например такой ролик или такой.
6-шаговая коммутация на максимальной мощности мотора не требует никакой широтно-импульсной модуляции (ШИМ) и этим отличается от управления синусоидальным напряжением, где ШИМ требуется всегда. 6-шаговая коммутация требует значительно меньше вычислительных ресурсов процессора.

Ещё специальная 6-шаговая коммутация позволяет увеличить до двух раз скорость вращения ротора по сравнению с максимально достижимой при синусоидальном управлении. (9.5.6 Expended Speed Operation, Bimal K.Bose »Modern Power Electronics And AC Drivers», ISBN 0–13–016743–6)

На заре появления BLDC моторы шли со встроенной схемой коммутации и прямо управлялись простейшей логикой от датчиков положения ротора без всяких микропроцессоров. Скорость вращения регулировалась изменением напряжения подаваемого на драйвер мотора. Это практически совпадало по характеристикам с управлением DC моторами. Отсюда и пошло название brushless dc motor (BLDC).

Схема простого 6-шагового управления BLDC мотором

Схема простого 6-шагового управления BLDC мотором

На алиэкспрессе можно встретить так называемые BLDC моторы с тремя выводами. Это моторы с уже встроенным драйвером. Два провода питания и один управляющий.

b17dec02505f50a364fa580cc1289e57.png

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

Мотор-редуктор

Мы выбрали для работы широко распространённый тип мотор-редуктора фигурирующего под названием «Planetary Gearbox Gear BLDC NEMA17 24V 90W Brushless DC Motor» на известных торговых площадках. Спецификация такого мотора с планетарным редуктором выглядит так:

aefb1fbce08fe1bd7defbff33e965510.png

Из спецификации следует, что мотор без нагрузки развивает скорость в 5000 оборотов в минуту. Поскольку у мотора 8 полюсов (т.е. 4 пары), то максимальная частота переменного сигнала на обмотках будет равна 5000×4 = 20000 об/мин или 333 Гц. Такую частоту и чуть больше мы должны обрабатывать с датчиков позиции ротора и с такой частотой обновлять состояние силового драйвера, т.е. каждые 3 мс. Для современных микроконтроллеров это более чем достаточное время если не придётся выполнять сложную фильтрацию или управление по сложной модели.

А действительно ли это BLDC мотор?

Тип мотора проверяют по обратной ЭДС во время ручного прокручивания. У BLDC моторов ЭДС должна быть трапецеидального вида. Как здесь:

Иллюстрация из книги Dr. Duane Hanselman

Иллюстрация из книги Dr. Duane Hanselman «BrushlessPermanent MagnetMotor Design»

Тут легко ошибиться пытаясь измерять обратную ЭДС между выводами мотора. Она может показаться синусоидальной. Но синус этот получается сложением двух трапецеидальных сигналов. Однако синус получается и сложением двух синусов. Поэтому измерять надо, как показано на рисунке ниже. Для этого придётся мотор распотрошить, найти в нем место соединения трех обмоток и припаяться к нему измерительным проводником.

ba2bba36fbe5ab54a20ee934c02a886e.gif

После всех этих действий получили вот такую осциллограмму.

Синяя и красная линии - напряжения на двух выводах мотора относительно средней точки, т.е. фазовые напряжения Зелёная линия -  разница между ними, т.е. напряжение Line-to-Line.

Синяя и красная линии — напряжения на двух выводах мотора относительно средней точки, т.е. фазовые напряжения Зелёная линия — разница между ними, т.е. напряжение Line-to-Line.

Мотор совсем не демонстрирует характеристику BLDC. Это PMSM с чистой синусоидальной характеристикой. Видимо, сказывается конструктивная особенность ротора, который представляется собой намагниченный цилиндр без визуально выделенных полюсов.

Из-за этого мы немного потеряем в эффективности при 6-шаговой коммутации.

Параметр Kv мотора будет равен ((362Hz/4poles paire)*60)/15V= 362

Кстати, сравнение 6-шаговой коммутации и векторного управления в применении к такому BLDC на похожей платформе можно найти здесь. Преимущества FOC, как следует из документа, довольно призрачны.

b2798597da8f1d1b5bc1f297f564ce26.jpg

Датчики Холла

Датчики Холла размещаются в промежутках между зубцами ротора. Особой точностью размещение не отличается.

1559f59480595f849073fd35b8a906ca.pngffc24623028148410ddc41a1f404b03d.png

Модель датчика можно прочитать на корпусе чипа.

Это аналог хорошо документированной модели SS41F.  Датчики с открытым коллектором, поэтому на плате установлены резисторы подтяжки R60, R61, R62.

Подтяжка к +5 С целью увеличения амплитуды сигнала на фоне помех. Далее идёт делитель с фильтром снижающий размах сигнала до 3.3 В пригодный для подачи на микроконтроллер. 

898323feb517c26b292aa7fac87b238c.png

Фильтр создаёт некоторое запаздывание сигнала, но без него могут присутствовать сильные помехи в сигнале. Сами сенсоры имеют некоторый разброс чувствительности и могут быть установлены с некоторым произвольным наклоном. Все это провоцирует возможность запуска вращения мотора с неправильной последовательностью коммутации. При этом момент силы развиваемый мотором может не сильно отличаться от момента развиваемого при правильной коммутации. Единственным признаком ошибочной коммутации может служить повышенный в несколько раз ток потребления. Но поскольку по абсолютной величине ток остаётся приемлемым, в пределах 1–2 А, то не зная истинного нормального тока  может показаться, что все  нормально. Однако попытавшись туже последовательность коммутации подать в реверсном направлении вращение не начинается. А это уже верный признак неверной коммутации. 

8-полюсный и 4-х полюсный ротор. Что это за полюса.

Полюса находятся на роторе. На статоре находятся зубцы. Не всем это сразу понятно. И автор тоже тут немного лагал в начале. Бывает что ссылаются на количество пар полюсов. Например у 4-полюсного ротора будет две пары полюсов. Ну и естественно у ротора всегда чётное количество полюсов. А вот зубцов может быть нечётное количество.

В интернетах трудно встретить описание коммутации, где ротор был бы не 4-полюсный 6-зубцовый (ну всем так проще рисовать). Даже 2-полюсный 3-зубцовый рисуют, хотя промышленно таких моторов не делают. И конечно не сообщают, что для вращения ротора с другим количеством полюсов в том же направлении понадобится другая таблица коммутации.  Существует множество комбинаций полюсов и зубцов. Вот неполная таблица найденная в интернете

2748c488ef18a8ac4c45da3c6b91a564.png

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

Хороший источник информации по конфигурации полюсов, зубцов, слотов и обмоток BLDC моторов — книга Dr. Duane Hanselman »BrushlessPermanent MagnetMotor Design. Second Edition», ISBN 1–881855–15–5

Коммутировать BLDC просто, но не совсем.

В микроконтроллерах семейства Renesas Synergy S5D9 есть периферийный блок под названием Output Phase Switching (OPS).

38d7eb2bfbd6f46efbd1df1ea5ca86c4.gif

Если в обычных микроконтроллерах для управления 6 сигналами управления драйвером двигателя требуется минимум таймер с 3 компараторными блоками с взаимно инвертированными выходами, то тут нужен всего один компаратор и не используются выходы компараторов. Это экономит ресурсы таймеров для других задач с одой стороны и не приводит к асинхронности перестройки компараторов с другой.

Однако таблица коммутации в S5D9 всего одна. Если мотор надо крутить только в одну сторону, то можно полностью отказаться от услуг процессора и заставить крутиться мотор автоматически по сигналам с датчиков положения ротора (датчиков Холла в нашем случае). Но если надо изменять направление вращения ротора, то придётся по прерываниям от датчиков Холла явно выбирать строку с таблице коммутации. И тут поджидает проблема приведения в соответствие позиции строк сигналам от датчиков Холла.

Иногда можно слышать от «специалистов» по асинхронным моторам, что для изменения направления достаточно поменять просто фазы на моторе. С BLDC так не работает. Просто поменять фазы мотора нельзя. Не сработает даже если поменять местами произвольные фазы и на моторе и от датчиков Холла. Поменять можно только взаимно связанные фазы мотора и датчиков Холла, но это уже не так дёшево схематически.

Ниже дана диаграмма использованная для определения последовательности переключений транзисторов драйвера и отображения битовых масок совокупности сигналов с датчиков Холла на строки встроенной таблицы коммутаций микроконтроллера.

aa920c91ec69e2fdd59e208a40ae107c.png

Программирование таймера с компараторами для модуля OPS делается довольно просто если не пользоваться API с лишними слоями абстракции. В коде ниже инициализируется работа таймера в треугольно-волновом режиме. Т.е. таймер периодически сначала инкрементируется, а потом декрементируется. Тут же настраивается компаратор для сигала триггера АЦП. Сам OPS инициализировать не нужно. У OPS всего один регистр и он записывается нужным значением сразу в прерывании вызываемом по окончании преобразования АЦП. Прерывания АЦП служат и для смены состояния коммутации силовых транзисторов.

По датчикам Холла прерывания не используются. Поскольку сигналы с датчиков приходят асинхронно с ШИМ, то мгновенная реакция на изменение состояния датчиков Холла приводила бы часто к необходимости укорочения импульсов ШИМ и изменениям в периоде выборки АЦП. А как показано ниже синхронность и предсказуемость работы АЦП очень важны.

/*-----------------------------------------------------------------------------------------------------
  Настройка ШИМ на треугольный режим с перегрузкой на впадине

  PCLKD_FREQ   =  120000000ul

  \param void

  \return uint32_t
-----------------------------------------------------------------------------------------------------*/
uint32_t GPT0_PWM_init(uint32_t freq)
{
  uint32_t mod = PCLKD_FREQ / (freq*2);

  R_MSTP->MSTPCRD_b.MSTPD5 = 0;     // Разрешаем работу модулей GPT ch7-ch0

  R_GPTA0->GTWP_b.PRKEY  = 0xA5;    // Разрешаем запись в бит WP этого регистра
  R_GPTA0->GTWP_b.WP     = 0;       // 0: Enable writes to the register Разрешаем запись в остальные регистры таймера

  R_GPTA0->GTCR_b.CST    = 0;       // Останавливаем счет

  R_GPTA0->GTCNT         = 0;       // Обнуляем таймер
  R_GPTA0->GTPR          = mod - 1; // Устанавливаем регистр задающий верхний предел таймера
  R_GPTA0->GTIOR         = 0;       // Очищаем настройки выходов. Все запрещены

  R_GPTA0->GTCR_b.TPCS   = 0;       // Timer Prescaler Select. 0 0 0: PCLKD/1
  R_GPTA0->GTCR_b.MD     = 4;       // 100: Triangle-wave PWM mode 1 (32-bit transfer at trough) (single buffer or double buffer possible)

  // Значение для компаратора тригера АЦП буфферизируем
  R_GPTA0->GTBER_b.CCRA  = 1;       // GTCCRA Buffer Operation. 01: Single buffer operation (GTCCRA ↔ GTCCRC).
  // Передаем новое значение в компаратор тригера АЦП на пике треугольника. Т.е. когда счетчик таймера достиг максимального значения
  R_GPTA0->GTBER_b.ADTTA = 1;       // GTADTRA Buffer Transfer Timing Select. 01: Transfer at crest.

  R_GPTA0->GTCCRA        = 0;       // Загружаем начальное значение в компаратор A
  R_GPTA0->GTCCRC        = 0;       // Загружаем буфферизированное начальное значение в компаратор A
  R_GPTA0->GTADTRA       = mod - 1; // Назначаем момент подачи сигнала триггера ADC. В данном случае тригеер сработает ровно по середине импульса ШИМ
  R_GPTA0->GTADTBRA      = mod - 1; // Назначаем момент подачи сигнала триггера ADC в буфферный регистр
  R_GPTA0->GTINTAD_b.ADTRAUEN = 1;  // Разрешаем выдачу сигнала триггера ADC только в фазе нарастания значений счетчика.
                                    // Чтобы не было двух сигналов от компаратора - при нарастании счетчика и при убывании счетчика
  R_GPTA0->GTIOR_b.GTIOA = 0x03;    // Set initial output low, Retain output at cycle end, Toggle output at GTCCRA/GTCCRB compare match
  R_GPTA0->GTIOR_b.OAE   = 1;       // Разрешаем выход компаратора A. Этот сигнал пойдет на модуль OPS 

  // Отклчючаем все флаги счета внешних импульсов
  R_GPTA0->GTUPSR = 0;
  R_GPTA0->GTDNSR = 0;

  R_GPTA0->GTCR_b.CST = 1; // Начинаем счет
  return RES_OK;
}

Feedback mode and freewheeling mode

Не подыскал подходящего перевода данных терминов, но суть дела в том, что ШИМ можно подавать только на нижние плечи мостов, а верхние в течении своих периодов остаются открытые. Это позволяет немного уменьшить потери на коммутацию.

В режиме feedback ШИМ подаётся и на верхние и на нижние плечи. Ток обмоток при низком уровне импульса ШИМ рассасывается в шину питания через диоды верхних и нижних транзисторов.

В режиме freewheeling ШИМ подаётся только на нижние транзисторы и ток обмоток при низком уровне импульса ШИМ закорачивается через верхний открытый транзистор и диод другого верхнего транзистора. Спад тока происходит медленней, но и управление получается с запаздыванием.

Модуль OPS микроконтроллера позволяет выполнять управление в обоих режимах, однако необходимо использовать прерывания.

Чтобы управлять мотором нужно знать ток. Точно знать.

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

Очень точно измеряют сигма-дельта АЦП. И они дешёвые. Но это опасный соблазн. Коммутация силовых транзисторов — это сплошное нагромождение шума, даже когда нет ШИМа. Дешёвые сигма-дельта АЦП сэмплируют по одному биту, но с высокой частотой поэтому они захватывают моменты переходных процессов из-за чего в сигнале получают гораздо больше шума.

Красный и синий - сигналы на выводах мотора относительно земли во время коммутации без использования ШИМ. Фиолетовый - сумма этих двух сигналов

Красный и синий — сигналы на выводах мотора относительно земли во время коммутации без использования ШИМ. Фиолетовый — сумма этих двух сигналов

Общепринятым способом является синхронизированное с ШИМ измерение. Выборку АЦП выполняют по середине импульсов ШИМ модулятора. В это время не происходит переключение транзисторов и как будто не должно быть никаких помех.

Глядя на осциллограммы сигналов на выводах мотора можно увидеть достаточно безопасные места для взятия отсчётов АЦП.

Глядя на осциллограммы сигналов на выводах мотора можно увидеть достаточно безопасные места для взятия отсчётов АЦП.

Но помехи все же будут, они возникают при резонансных осцилляциях в тех обмотках в которых происходит прерывание тока. Но семплирование в этих точках с одной стороны можно игнорировать, с другой стороны помехи в эти моменты не так мощны.
Другой проблемой является длительность выборки отсчётов АЦП. АЦП микроконтроллера многоканальное. Нам нужно за время покоя оцифровать множество сигналов, это не только токи мотора, но значения с датчиков температуры, с датчиков напряжения, с серво-сенсора и т.д. Это занимает до десятка микросекунд. Значит минимальная длительность активного импульса не может быть слишком маленькой. Т.е. коэффициент заполнения не должен быть меньше 10% при частоте ШИМ 16 КГц, иначе какие-то отсчёты попадут на момент коммутации.

Почему нужен именно треугольно-волновой ШИМ?

Ведь можно просто использовать ШИМ по счётчику с нарастанием и сбросом, т.е. по пилообразному сигналу. Это просто и понятно. Но проблема возникает тогда с синхронизацией работы АЦП.

Triangle-wave PWM — так называется этот режим у Renesas, у других можно встретить определение как симметричный ШИМ. Симметричный ШИМ нужен для того чтобы выборки АЦП можно было привязать ровно к середине импульса и периодичность выборок не колебалась бы при изменении скважности.

750fc013fcd8affcded0a440e8b265f9.png

Сама процедура обслуживания прерывания АЦП не сложная и выглядит вот так:

T_int_adc_res  int_adc_res;

//uint8_t forward_rotation_tbl[8] =  {0, 1, 2, 3, 4, 5, 6, 0}; // Эта таблица тоже вращает мотор но с большей частотой и большим в три раза потреблением тока
uint8_t forward_rotation_tbl[8] = {0, 5, 3, 1, 6, 4, 2, 0};
uint8_t reverse_rotation_tbl[8] = {0, 2, 4, 6, 1, 3, 5, 0};

uint32_t  ops_deb;
uint8_t   prev_hall_sens_state;
uint8_t   skip_adc_res;

/*-----------------------------------------------------------------------------------------------------
  Используем прерывание от ADC0 для обслуживания результатов и от ADC0 и от ADC1
  Частота вызова определяется ШИМ мотора и равно по умолчанию 16 КГц

  \param void
-----------------------------------------------------------------------------------------------------*/
void  ADC0_SCAN_END_isr(void)
{
  SF_CONTEXT_SAVE;

  GREEN_LED = 1;  // Зажигаем зеленый светодиод энкодера в отладочных целях

  if (skip_adc_res==0)
  {
    // Сохраняем в рабочие переменные результаты работы АЦП 
    int_adc_res.smpl_V_IU     = R_S12ADC0->ADDRn[0]; // ADC0 AN000
    int_adc_res.smpl_V_IV     = R_S12ADC0->ADDRn[1]; // ADC0 AN001
    int_adc_res.smpl_V_IW     = R_S12ADC0->ADDRn[2]; // ADC0 AN002
    int_adc_res.smpl_V_IPWR   = R_S12ADC0->ADDRn[3]; // ADC0 AN003
    int_adc_res.smpl_VREF_R   = R_S12ADC0->ADDRn[7]; // ADC0 AN007

    int_adc_res.smpl_POS_SENS = R_S12ADC1->ADDRn[0]; // ADC1 AN000
    int_adc_res.smpl_EXT_TEMP = R_S12ADC1->ADDRn[1]; // ADC1 AN001
    int_adc_res.smpl_MISC     = R_S12ADC1->ADDRn[2]; // ADC1 AN002
    int_adc_res.smpl_TEMP     = R_S12ADC1->ADDRn[5]; // ADC1 AN005
    int_adc_res.smpl_V_VPWR   = R_S12ADC1->ADDRn[7]; // ADC1 AN007

    int_adc_res.smpl_CPU_temp     = R_S12ADC0->ADTSDR;
    int_adc_res.smpl_INT_REF_V    = R_S12ADC0->ADOCDR;
  }
  else
  {
    // Здесь если игнорируем некторые выборки в которых вероятно присутствие сильных помех 
    skip_adc_res--;
  }

  // Здесь процедура управления коммутацией
  if (mot_cbl.state != 0)
  {
    if (mot_cbl.pwm_val == 0)
    {
      mot_cbl.opscr.EN = 0;
      ops_deb = mot_cbl.opscr.w;
      R_GPT_OPS->OPSCR = ops_deb;
    }
    else
    {
      // На предельных уровнях мощности не используем модуляцию
      if ((mot_cbl.pwm_val == 100) || (mot_cbl.pwm_val == -100))
      {
        mot_cbl.opscr.P = 0;
        mot_cbl.opscr.N = 0;
      }
      else
      {
        mot_cbl.opscr.P = 1;
        mot_cbl.opscr.N = 1;
      }

      uint8_t hall_sens_state = R_IOPORT5->PCNTR2 & 0x7;

      if (mot_cbl.pwm_val < 0)
      {
        // Переключаем состояние коммутации для вращения в прямом направлении
        ops_deb = mot_cbl.opscr.w | forward_rotation_tbl[hall_sens_state];
        R_GPT_OPS->OPSCR = ops_deb;
      }
      else
      {
        // Переключаем состояние коммутации для вращения в обратном направлении
        ops_deb = mot_cbl.opscr.w | reverse_rotation_tbl[hall_sens_state];
        R_GPT_OPS->OPSCR = ops_deb;
      }

      // Первое включение сигнала разрешения EN. Первое его включение должно быть после того как будут установлены другие биты в регистре. 
      if (mot_cbl.opscr.EN == 0)
      {
        R_GPT_OPS->OPSCR_b.EN = 1;
        mot_cbl.opscr.EN      = 1;
      }

      if (prev_hall_sens_state != hall_sens_state)
      {
        prev_hall_sens_state = hall_sens_state;
        //skip_adc_res = 3; // При смене коммутационной конфигурации , происходят рандомные осцилляции,
                            // поэтому отсчеты АЦП на первом периоде ШИМ после перекоммутации игнорируем
                            // Фича требует перепроверки
      }

    }
  }
  else
  {
    // Здесь если мотор крутить не надо 
    mot_cbl.opscr.EN = 0;
    R_GPT_OPS->OPSCR = mot_cbl.opscr.w;
  }

  Manual_encoder_processing(); // Обрабатываем сигналы с ручного энкодера

  FMSTR_Recorder(0); // Вызываем функцию записи сигнала для инструмента FreeMaster

  R_ICU->IELSRn_b[adc0_scan_int_num].IR = 0;  // Сбрасываем IR флаг в ICU

  GREEN_LED = 0;  // Гасим зеленый светодиоод

  SF_CONTEXT_RESTORE;
  __DSB();
}

Продолжение следует…

© Habrahabr.ru