Pull to refresh

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

Level of difficultyMedium
Reading time19 min
Views16K

Продолжаем разработку контроллера сервоприводов 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 мкс. Уменьшение времени выборки может ухудшить точность измерения.

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

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

Tags:
Hubs:
Total votes 23: ↑22 and ↓1+26
Comments20

Articles