Продолжаем разработку контроллера сервоприводов 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 моторы с тремя выводами. Это моторы с уже встроенным драйвером. Два провода питания и один управляющий.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Программирование таймера с компараторами для модуля 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 в некоторой литературе) ШИМ подаётся и на верхние и на нижние плечи. Ток обмоток при низком уровне импульса ШИМ рассасывается в шину питания через диоды верхних и нижних транзисторов либо через замкнутые противоположные транзисторы. Спад тока происходит быстро. Управление током можно обеспечить максимально быстрое.

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

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

По умолчанию мы применяем жёсткую коммутацию способом 1. В этом случае происходит максимально быстрое рассасывание тока катушки мотора.

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

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

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

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

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

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

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

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

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

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

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

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();
}

Связь между АЦП и таймером GPT0, который вырабатывает ШИМ для модуля OPS, происходит посредством модуля Event Link Controller (ELC). ELC - это отдельный модуль в наборе периферии чипа предназначенный для управления маршрутизацией дискретных сигналов между различной периферией. Всего модуль на входе может оперировать 511 сигналами и перенаправлять их на 19 определенных получателей. Среди получателей есть оба набортных АЦП по два выделенных сигнала каждому (у каждого возможно организовать две группы с отдельными конфигурациям сканирования)

Старт выборки АЦП инициируется сигналом ELC_EVENT_GPT0_AD_TRIG_A (184) исходящим из таймера GPT0 в момент когда его значение совпадёт со значением регистра компаратора триггера АЦП. В таймерах GPT есть специальные регистры компараторов для генерации вспомогательных сигналов именно для отправки на АЦП.

Инициализация АЦП выглядит так:

/*-------------------------------------------------------------------------------------------------------------
  Инициализация ADC для работы синхронно с ШИМ управления моторм   
 
  В качестве запускающего тригера используем сигнал с копмпаратора GTADTRA GPT0
-------------------------------------------------------------------------------------------------------------*/
void ADC_init(void)
{

  R_MSTP->MSTPCRD_b.MSTPD15 = 0; // 12-Bit A/D Converter 1 Module. 0: Cancel the module-stop state
  R_MSTP->MSTPCRD_b.MSTPD16 = 0; // 12-Bit A/D Converter 0 Module. 0: Cancel the module-stop state
  R_MSTP->MSTPCRD_b.MSTPD22 = 0; // Temperature Sensor Module.     0: Cancel the module-stop state


  //
  R_S12ADC0->ADCSR = 0
                    + LSHIFT(0, 15) // ADST        | A/D Conversion Start  | 1: Start A/D conversion process.
                    + LSHIFT(0, 13) // ADCS[1:0]   | Scan Mode Select      | 0 0: Single scan mode, 0 1: Group scan mode, 1 0: Continuous scan mode, 1 1: Setting prohibited
                    + LSHIFT(1,  9) // TRGE        | Trigger Start Enable  | 1: Enable A/D conversion to be started by the synchronous or asynchronous trigger.
                    + LSHIFT(0,  8) // EXTRG       | Trigger Select        | 0: Start A/D conversion by a synchronous trigger (ELC), 1: Start A/D conversion by the asynchronous trigger (ADTRGi).
                    + LSHIFT(0,  7) // DBLE        | Double Trigger Mode   | 0: Deselect double-trigger mode, 1: Select double-trigger mode.
                    + LSHIFT(0,  6) // GBADIE      | Group B Scan          | 1: Enable ADC12i_GBADI interrupt generation on Group B scan completion
                    + LSHIFT(0,  0) // DBLANS[4:0] | Double Trigger Channel Select
  ;
  R_S12ADC1->ADCSR = 0
                    + LSHIFT(0, 15) // ADST        | A/D Conversion Start  | 1: Start A/D conversion process.
                    + LSHIFT(0, 13) // ADCS[1:0]   | Scan Mode Select      | 0 0: Single scan mode, 0 1: Group scan mode, 1 0: Continuous scan mode, 1 1: Setting prohibited
                    + LSHIFT(1,  9) // TRGE        | Trigger Start Enable  | 1: Enable A/D conversion to be started by the synchronous or asynchronous trigger.
                    + LSHIFT(0,  8) // EXTRG       | Trigger Select        | 0: Start A/D conversion by a synchronous trigger (ELC), 1: Start A/D conversion by the asynchronous trigger (ADTRGi).
                    + LSHIFT(0,  7) // DBLE        | Double Trigger Mode   | 0: Deselect double-trigger mode, 1: Select double-trigger mode.
                    + LSHIFT(0,  6) // GBADIE      | Group B Scan          | 1: Enable ADC12i_GBADI interrupt generation on Group B scan completion
                    + LSHIFT(0,  0) // DBLANS[4:0] | Double Trigger Channel Select
  ;

  // Устанавливаем путь сигнал минуя PGA
  R_S12ADC0->ADPGACR_b.P000SEL0 = 1;
  R_S12ADC0->ADPGACR_b.P001SEL0 = 1;
  R_S12ADC0->ADPGACR_b.P002SEL0 = 1;
  R_S12ADC0->ADPGACR_b.P003SEL0 = 1;

  R_S12ADC1->ADPGACR_b.P000SEL0 = 1;
  R_S12ADC1->ADPGACR_b.P001SEL0 = 1;
  R_S12ADC1->ADPGACR_b.P002SEL0 = 1;
  R_S12ADC1->ADPGACR_b.P003SEL0 = 1;

  // Отключаем PGA
  R_S12ADC0->ADPGADCR0_b.P000DEN = 0;
  R_S12ADC0->ADPGADCR0_b.P001DEN = 0;
  R_S12ADC0->ADPGADCR0_b.P002DEN = 0;
  R_S12ADC0->ADPGADCR0_b.P003DEN = 0;

  R_S12ADC1->ADPGADCR0_b.P000DEN = 0;
  R_S12ADC1->ADPGADCR0_b.P001DEN = 0;
  R_S12ADC1->ADPGADCR0_b.P002DEN = 0;
  R_S12ADC1->ADPGADCR0_b.P003DEN = 0;

  // 1 такт ADC = 16.7 ns
  R_S12ADC0->ADANSA0_b.ANSA00 = 1; //    AN000  V_IU   - измеритель тока в фазе U
  R_S12ADC0->ADSSTR0n_b[0].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки

  R_S12ADC0->ADANSA0_b.ANSA01 = 1; //    AN001  V_IV   - измеритель тока в фазе V
  R_S12ADC0->ADSSTR0n_b[1].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки

  R_S12ADC0->ADANSA0_b.ANSA02 = 1; //    AN002  V_IW   - измеритель тока в фазе W
  R_S12ADC0->ADSSTR0n_b[2].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки

  R_S12ADC0->ADANSA0_b.ANSA03 = 1; //    AN003  V_IPWR - измеритель тока в шине питания
  R_S12ADC0->ADSSTR0n_b[3].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки

  R_S12ADC0->ADANSA0_b.ANSA07 = 1; //    AN007  VREF_R - измеритель референсного напряжения
  R_S12ADC0->ADSSTR0n_b[7].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки


  R_S12ADC1->ADANSA0_b.ANSA00 = 1; //    AN100  POS_SENS - изменитель напряжения на сенсоре позиции
  R_S12ADC1->ADSSTR0n_b[0].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки

  R_S12ADC1->ADANSA0_b.ANSA01 = 1; //    AN101  EXT_TEMP - изменитель напряжения на внешнем сенсоре температуры
  R_S12ADC1->ADSSTR0n_b[1].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки

  R_S12ADC1->ADANSA0_b.ANSA02 = 1; //    AN102  MISC     - измеритель без назначенной функции
  R_S12ADC1->ADSSTR0n_b[2].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки

  R_S12ADC1->ADANSA0_b.ANSA05 = 1; //    AN105  TEMP     - измеритель температуры у силового драйвера
  R_S12ADC1->ADSSTR0n_b[5].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки

  R_S12ADC1->ADANSA0_b.ANSA07 = 1; //    AN107  V_VPWR   - измениитель напряжения на шине питания
  R_S12ADC1->ADSSTR0n_b[7].SST = 0x20; // 32 тактов (0.334 мкс) - время выборки


  R_S12ADC0->ADSSTRT_b.SST = 0x20; // 32 тактов (0.334 мкс) - время выборки температурного сенсора
  R_S12ADC0->ADSSTRO_b.SST = 0x20; // 32 тактов (0.334 мкс) - время выборки референсного напряжения

  // Здесь настраиваем одновременную выборку для каналов 0 1 2 ADC в обоих модулях ADC
  // В этих каналах у ADC0 производится измерение токов в фазах мотора, 
  // и одновременная выборка исключает неточность вызванную неодновременностью измерения токов
  R_S12ADC0->ADSHCR_b.SSTSH = 0x20; // 32 тактов (0.334 мкс) - время выборки
  R_S12ADC0->ADSHCR_b.SHANS0 = 1;   // AN000 sample-and-hold circuit Select
  R_S12ADC0->ADSHCR_b.SHANS1 = 1;   // AN001 sample-and-hold circuit Select
  R_S12ADC0->ADSHCR_b.SHANS2 = 1;   // AN002 sample-and-hold circuit Select

  R_S12ADC1->ADSHCR_b.SSTSH = 0x20; // 32 тактов (0.334 мкс) - время выборки
  R_S12ADC1->ADSHCR_b.SHANS0 = 1;   // AN100 sample-and-hold circuit Select
  R_S12ADC1->ADSHCR_b.SHANS1 = 1;   // AN101 sample-and-hold circuit Select
  R_S12ADC1->ADSHCR_b.SHANS2 = 1;   // AN102 sample-and-hold circuit Select

  //
  R_S12ADC0->ADCER = 0
                    + LSHIFT(0, 15) // ADRFMT       | A/D Data Register Format Select             | 0: Select flush-right for the A/D data register format, 1: Select flush-left for the A/D data register format.
                    + LSHIFT(0, 11) // DIAGM        | Self-Diagnosis Enable                       | 0: Disable ADC12 self-diagnosis, 1: Enable ADC12 self-diagnosis.
                    + LSHIFT(0, 10) // DIAGLD       | Self-Diagnosis Mode Select                  | 0: Select rotation mode for self-diagnosis voltage, 1: Select fixed mode for self-diagnosis voltage.
                    + LSHIFT(0,  8) // DIAGVAL[1:0] | Self-Diagnosis Conversion Voltage Select    | 0 0: Setting prohibited when self-diagnosis is enabled, 0 1: 0 V, 1 0: Reference power supply voltage x 1/2, 1 1: Reference power supply voltage.
                    + LSHIFT(0,  5) // ACE          | A/D Data Register Automatic Clearing Enable | 1: Enable automatic clearing.
                    + LSHIFT(0,  1) // ADPRC[1:0]   | A/D Conversion Accuracy Specify             | 0 0: 12-bit accuracy
  ;

  R_S12ADC1->ADCER = 0
                    + LSHIFT(0, 15) // ADRFMT       | A/D Data Register Format Select             | 0: Select flush-right for the A/D data register format, 1: Select flush-left for the A/D data register format.
                    + LSHIFT(0, 11) // DIAGM        | Self-Diagnosis Enable                       | 0: Disable ADC12 self-diagnosis, 1: Enable ADC12 self-diagnosis.
                    + LSHIFT(0, 10) // DIAGLD       | Self-Diagnosis Mode Select                  | 0: Select rotation mode for self-diagnosis voltage, 1: Select fixed mode for self-diagnosis voltage.
                    + LSHIFT(0,  8) // DIAGVAL[1:0] | Self-Diagnosis Conversion Voltage Select    | 0 0: Setting prohibited when self-diagnosis is enabled, 0 1: 0 V, 1 0: Reference power supply voltage x 1/2, 1 1: Reference power supply voltage.
                    + LSHIFT(0,  5) // ACE          | A/D Data Register Automatic Clearing Enable | 1: Enable automatic clearing.
                    + LSHIFT(0,  1) // ADPRC[1:0]   | A/D Conversion Accuracy Specify             | 0 0: 12-bit accuracy
  ;


  //
  R_S12ADC0->ADEXICR = 0
                      + LSHIFT(0, 11) // OCSB  | Internal Reference Voltage A/D Conversion Select for Group B               | 1: Enable A/D conversion of temperature sensor output.
                      + LSHIFT(0, 10) // TSSB  | Temperature Sensor Output A/D Conversion Select for Group B                | 1: Enable A/D conversion of temperature sensor output.
                      + LSHIFT(1,  9) // OCSA  | Internal Reference Voltage A/D Conversion Select                           | 1: Enable A/D conversion of internal reference voltage.
                      + LSHIFT(1,  8) // TSSA  | Temperature Sensor Output A/D Conversion Select                            | 1: Enable A/D conversion of temperature sensor output.
                      + LSHIFT(0,  1) // OCSAD | Internal Reference Voltage A/DConverted Value Addition/Average Mode Select | 1: Select addition/average mode for internal reference voltage.
                      + LSHIFT(0,  0) // TSSAD | Temperature Sensor Output A/D Converted Value Addition/Average Mode Select | 1: Select addition/average mode for temperature sensor output.
  ;

  //
  R_S12ADC1->ADEXICR = 0
                      + LSHIFT(0, 11) // OCSB  | Internal Reference Voltage A/D Conversion Select for Group B               | 1: Enable A/D conversion of temperature sensor output.
                      + LSHIFT(0, 10) // TSSB  | Temperature Sensor Output A/D Conversion Select for Group B                | 1: Enable A/D conversion of temperature sensor output.
                      + LSHIFT(0,  9) // OCSA  | Internal Reference Voltage A/D Conversion Select                           | 1: Enable A/D conversion of internal reference voltage.
                      + LSHIFT(0,  8) // TSSA  | Temperature Sensor Output A/D Conversion Select                            | 1: Enable A/D conversion of temperature sensor output.
                      + LSHIFT(0,  1) // OCSAD | Internal Reference Voltage A/DConverted Value Addition/Average Mode Select | 1: Select addition/average mode for internal reference voltage.
                      + LSHIFT(0,  0) // TSSAD | Temperature Sensor Output A/D Converted Value Addition/Average Mode Select | 1: Select addition/average mode for temperature sensor output.
  ;

  R_TSN->TSCR_b.TSEN = 1; // Включаем температурный сенсор
  Wait_ms(1);             // Задержка для стабилизации сенсора
  R_TSN->TSCR_b.TSOE = 1; // Включаем выход температурного сенсора


  // Для ADC0 для группы А устанвливаем запускающий триггер
  R_S12ADC0->ADSTRGR_b.TRSA = 0x09; // Триггером будут служить сигналы  ELC_PERIPHERAL_ADC0 и ELC_PERIPHERAL_ADC1 от модуля Event Link Controller
  // Для ADC1 для группы А устанвливаем запускающий триггер
  R_S12ADC1->ADSTRGR_b.TRSA = 0x09; // Триггером будут служить сигналы  ELC_PERIPHERAL_ADC0 и ELC_PERIPHERAL_ADC1 от модуля Event Link Controller

  // Программируем Event Link Setting Register (ELSRn) где указаываем какие события от сторонней периферии будут направлены на триггеры ADC
  R_ELC->ELSRnRC0[ELC_PERIPHERAL_ADC0].ELSRn = ELC_EVENT_GPT0_AD_TRIG_A; // Направляем сигнал с канала A компаратора GPT0 на вход тригера ADC0
  R_ELC->ELSRnRC0[ELC_PERIPHERAL_ADC1].ELSRn = ELC_EVENT_GPT0_AD_TRIG_A; // Направляем сигнал с канала A компаратора GPT0 на вход тригера ADC1


  adc0_scan_int_num = (IRQn_Type)Find_IRQ_number_by_evt(ELC_EVENT_ADC0_SCAN_END);
  NVIC_SetPriority(adc0_scan_int_num, INT_ADC_SCAN_PRIO);

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

}

Важный момент заключается в том что чип S5D9 способен одновременно сэмплировать до 6 аналоговых сигналов. Это позволяет избавиться от ошибки при последовательном измерении, возникающую у обычных микроконтроллеров с 2-х модульными многоканальными АЦП. Взятые семплы потом последовательно оцифровываются один за другим. Остальные каналы сэмплируются и оцифровываются последовательно один за другим без паузы. В сумме весь процесс взятия выборок и оцифровки занимает при текущей настройке около 2 мкс. Уменьшение времени выборки может ухудшить точность измерения.

Порядок семплирования определяется номером канала. Каналы с меньшим номером семплируются первыми. Кроме того сэмплируются ещё внутренние каналы встроенного датчика температуры и референсного напряжения. Они сэмплируются последними.

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

Комментарии (18)


  1. Karlson_rwa
    07.07.2023 20:15
    +4

    Преимущества FOC, как следует из документа, довольно призрачны.

    Крайне смелое утверждение, если внимательно посмотреть на вот эту картинку

    и внимательно почитать

    выводы документа.

    5 Summary Hardware, software and test results of BLDC block commutation with Hall sensors and sensorless FOC for three-phase BLDC motors in power tool applications have been compared. Compared with BLDC block commutation with Hall sensors, sensorless FOC results in lower power losses and higher efficiency for the power board. To further reduce MOSFET power loss, for BLDC block commutation with Hall sensors it is desirable to choose MOSFETs with body-diode or low forward voltage (e.g. Infineon 40 V MOSFETs with integrated monolithic Schottky-like diode) as the body-diode conduction loss of MOSFETs is significant, while for sensorless FOC choosing faster switching silicon MOSFETs or gallium nitride (GaN) FETs will help to reduce the dominant switching loss. The motor current of sensorless FOC is sinusoidal or close to sinusoidal depending on motor design. This will result in smoother motor output torque, which is beneficial to applications such as sanders and grinders for better polishing. It will also achiever quieter (acoustic) motor operation and fewer EMI problems than BLDC block commutation with Hall sensors. The robust motor start-up of sensorless FOC is especially valuable, as load torque is often unknown or changing rapidly in power tool applications. By eliminating rotor sensors and their cables, connectors and PCBs, sensorless FOC is well-suited to power tool applications in harsh environments due to severe moisture, humidity, temperature or vibrations, or where conventional rotor sensors cannot be accommodated due to reliability concerns, cost or physical constraints.


    1. Indemsys Автор
      07.07.2023 20:15
      +1

      Если еще внимательнее посмотреть, то это не результаты измерений, а результаты симуляции в MATLAB. Причем парни не использовали freewheeling mode. А еще 6-step может работать вообще без ШИМ и в плане экономии значительно превзойдет FOC.

      Да и ротор у них 4-полюсный. А это большая разница.


      1. Karlson_rwa
        07.07.2023 20:15
        +1

        Что заставляет вас думать, что их симуляция далека от реальности? Я не смог быстро найти шестую ссылку в открытом доступе. Если у вас есть этот документ, не сочтите за труд, пожалуйста, положить его куда-нибудь и маякнуть мне в лс. Мне любопытно было бы почитать.

        Да, 1,6 процента на реальном железе не очень существенный выигрыш, но мне искренне представляется, что в тех применениях, где необходимо аккуратное и точное управление моментом, векторное управление вне конкуренции. Поправьте меня, если я не прав.


        1. Indemsys Автор
          07.07.2023 20:15

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

          С другой стороны они рассматривают потери вызванные только способом модуляции.
          Вы же не будете спорить, что 6-step коммутация без модуляции значительно экономичнее векторной модуляции?

          Поскольку в статье проводится симуляция в MATLAB, то там наверняка использовали готовый компонент MATLAB PWM Generator (Three-phase, Two-level)

          Там можете выбрать модуляцию на любой вкус. Кстати, овермодуляция как раз вырождается в 6-step. К овермодуляции прибегают даже брендовые инвертеры, когда хотят выдать предельную мощность.

          Ну и когда говорите по поводу векторного управления, то всегда оговаривайте способ модуляции, тип обсервера и количество и архитектуру петлей управления. Потому что 6-step тоже в некотором роде векторное управление (см. овермодуляцию).

          А по жизни векторное безсенсорное управление, скажем в инвертерах для асинхронных двигателей в подъёмных устройствах, работает довольно отвратительно.
          Часто приходится вместо него использовать скалярное. Скалярное тише и стабильнее работает.
          Лучше всего векторное с прецизионными энкодерами, вот там да, качество отменное.


  1. Didimus
    07.07.2023 20:15

    Сильно гудит мотор-редуктор в рекуператоре (см. фото). С чем это может быть связано? Гул такой, как будто обмотки перепутаны, как я могу предположить

    Hidden text


    1. Indemsys Автор
      07.07.2023 20:15

      Однофазные асинхронные моторы всегда сильно гудят. Звукоизоляция видимо прохудилась.


      1. Didimus
        07.07.2023 20:15

        Ну там гудит как трансформатор на максималках


        1. Indemsys Автор
          07.07.2023 20:15

          Значит винты на редукторе надо подтянуть


    1. diesel80
      07.07.2023 20:15
      +1

      Фазосдвигающий конденсатор проверьте на емкость.


      1. Didimus
        07.07.2023 20:15

        Похоже, у меня на советском станочке так было, мотор дрожал, гудел, но не крутится


  1. pdkdrp
    07.07.2023 20:15
    +2

    >> Однако синус получается и сложением двух синусов. Поэтому измерять надо, как показано на рисунке ниже

    А зачем?? Если выглядит он снаружи как машина с sin ЭДС то и управлять им надо синусом. Ну и что там в обмотках трапеция, это всего лишь значит, что точка соединения обмоток не является “нулем”, но она, обычно, даже наружу не выводится и для управления знать её потенциал не требуется.

     

    >> Преимущества FOC, как следует из документа, довольно призрачны.

    Покажите, где именно это следует:

    ·         То, что они намеряли в этом документе, это снижения тепловыделения в 1.5 раз…. Ничего себе «призрачно».

    ·         FOC в пределе вырождается в 6-ти шаговый режим, когда вектор престает умещаться в шетиугольник SVM вектором. В статье на Fig.7 именно такая картина.

    ·         Важное – от FOC шума сильно меньше. И это для отдельных задач ОЧЕНЬ важно. Близкий пример: переход на шаговиках на продвинутые драйверы tmc2209 (примерный аналог FOC) привел к тому что в 3д принтерах двигателей не слышно совсем, а раньше они в жилом помещении очень мешали .


    1. Indemsys Автор
      07.07.2023 20:15
      +1

      то и управлять им надо синусом

      Синус даёт меньше мощности. Зачем, скажем в коптере синус, если надо добиться максимальной скорости?

      Важное – от FOC шума сильно меньше

      Так это заслуга не FOС, а синусоидальной модуляции. А сам FOC ещё как может шуметь на низких скоростях, когда обсервер начинает ошибаться и осциллировать в контуре тока.


  1. pdkdrp
    07.07.2023 20:15

    >> Синус даёт меньше мощности. Зачем, скажем в коптере синус, если надо добиться максимальной скорости?

    В общем утверждение не верное. При каком наборе ограничений? Для конкретной силовой установки (мотор+АКБ)? Если вместо sin подать 6step, да мощность на валу будет больше, но если с конкретного мотора надо максимум продолжительной мощности снять, то просто увеличьте напряжение питания и используйте sin, долговременная мощность будет выше.

    Если пиковая мощность нужна, то тоже SVM хорошо это отработает – когда напряжения не хватит для генерации sin, вершины синусоиды будут постепенно подрезаться, переходя в пределе к 6step. Причем переход плавный (как раз для коптеров это хорошо – основой режим векторный, максимальная мощность развивается на пике в 5step). Правда, не знаю как это реализовано в современных ESC кодах, в T-Motor U5 точно не реализовано.

     

    >> Так это заслуга не FOС, а синусоидальной модуляции. А сам FOC ещё как может шуметь на низких скоростях, когда обсервер начинает ошибаться и осциллировать в контуре тока.

    Мы же все еще про Ваш мотор с 3мя датчиками Холла говорим? Там при минимальных оборотах осцилляций не будет


    1. Indemsys Автор
      07.07.2023 20:15

      Там при минимальных оборотах осцилляций не будет

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


  1. pdkdrp
    07.07.2023 20:15

    В какой момент АЦП и безсенсорный FOC возник? Сенсорный! Я лишь утверждаю, говоря про Ваш мотор, с тремя датчиками холла, что если его запитать от SVM, который будет управляться этим самым энкодером на 6 положений + простейший интерполятор то, согласно приведенной Вами статье, получим заметное преимущество, почти бесплатно, простейшей модификацией ПО.


    1. Indemsys Автор
      07.07.2023 20:15

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


      1. Karlson_rwa
        07.07.2023 20:15

        Кроме люфта.


        1. Indemsys Автор
          07.07.2023 20:15

          С люфтом боремся с помощью фильтра Калмана и дополнительных сенсоров. Про его использование в этом применении статья ждёт своей очереди.